Code

support multiple lines selection
authorRomain Bignon <romain@peerfuse.org>
Tue, 10 Feb 2009 14:18:28 +0000 (15:18 +0100)
committerMax Kellermann <max@duempel.org>
Tue, 10 Feb 2009 14:20:48 +0000 (15:20 +0100)
src/command.c
src/command.h
src/list_window.c
src/list_window.h
src/screen_artist.c
src/screen_file.c
src/screen_help.c
src/screen_play.c

index 2f3bafd85a4b53a87b6215d269dda59226ed076e..45584143630b33c8742803c2cd42ecc033e5e1c7 100644 (file)
@@ -1,7 +1,7 @@
 /* ncmpc (Ncurses MPD Client)
  * (c) 2004-2009 The Music Player Daemon Project
  * Project homepage: http://musicpd.org
+
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -84,6 +84,8 @@ static command_definition_t cmds[] = {
          N_("Page up") },
        { { PGDN, 0, 0 }, 0, CMD_LIST_NEXT_PAGE, "pgdn",
          N_("Page down") },
+       { { 'v',  0, 0 }, 0, CMD_LIST_VISUAL_SELECT, "visual-select",
+         N_("Visual selection") },
 
 
        /* basic screens */
index 6a295331b4a1c90ef47758d2843952f2e9c98f02..f339910e3a9c5a67345b7701561727148afbc18b 100644 (file)
@@ -1,7 +1,7 @@
 /* ncmpc (Ncurses MPD Client)
  * (c) 2004-2009 The Music Player Daemon Project
  * Project homepage: http://musicpd.org
+
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -69,6 +69,7 @@ typedef enum {
        CMD_LIST_RFIND_NEXT,
        CMD_LIST_MOVE_UP,
        CMD_LIST_MOVE_DOWN,
+       CMD_LIST_VISUAL_SELECT,
        CMD_MOUSE_EVENT,
        CMD_SCREEN_UPDATE,
        CMD_SCREEN_PREVIOUS,
index f7da79ea147973f67db1b44acc86e791bd3076b3..688ae8bd70600ced1c53c0fac83e6144e7143b8d 100644 (file)
@@ -1,7 +1,7 @@
 /* ncmpc (Ncurses MPD Client)
  * (c) 2004-2009 The Music Player Daemon Project
  * Project homepage: http://musicpd.org
+
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -41,6 +41,7 @@ list_window_init(WINDOW *w, unsigned width, unsigned height)
        lw->w = w;
        lw->cols = width;
        lw->rows = height;
+       lw->visual_selection = false;
        return lw;
 }
 
@@ -57,6 +58,10 @@ void
 list_window_reset(struct list_window *lw)
 {
        lw->selected = 0;
+       lw->selected_start = 0;
+       lw->selected_end = 0;
+       lw->visual_selection = false;
+       lw->visual_base = 0;
        lw->xoffset = 0;
        lw->start = 0;
 }
@@ -78,6 +83,19 @@ list_window_check_selected(struct list_window *lw, unsigned length)
                lw->selected = 0;
        else if (lw->selected >= length)
                lw->selected = length - 1;
+
+       if(lw->visual_selection)
+       {
+               if(lw->visual_base > lw->selected_end)
+                         lw->selected_end = lw->selected;
+               if(lw->visual_base < lw->selected_start)
+                         lw->selected_start = lw->selected;
+       }
+       else
+       {
+               lw->selected_start = lw->selected;
+               lw->selected_end = lw->selected;
+       }
 }
 
 void
@@ -100,31 +118,43 @@ void
 list_window_set_selected(struct list_window *lw, unsigned n)
 {
        lw->selected = n;
+       if(lw->visual_selection)
+       {
+               if(n >= lw->visual_base)
+                       lw->selected_end = n;
+               if(n <= lw->visual_base)
+                       lw->selected_start = n;
+       }
+       else
+       {
+               lw->selected_start = n;
+               lw->selected_end = n;
+       }
 }
 
 static void
 list_window_next(struct list_window *lw, unsigned length)
 {
        if (lw->selected + 1 < length)
-               lw->selected++;
+               list_window_set_selected(lw, lw->selected + 1);
        else if (options.list_wrap)
-               lw->selected = 0;
+               list_window_set_selected(lw, 0);
 }
 
 static void
 list_window_previous(struct list_window *lw, unsigned length)
 {
        if (lw->selected > 0)
-               lw->selected--;
+               list_window_set_selected(lw, lw->selected - 1);
        else if (options.list_wrap)
-               lw->selected = length - 1;
+               list_window_set_selected(lw, length-1);
 }
 
 static void
 list_window_first(struct list_window *lw)
 {
        lw->xoffset = 0;
-       lw->selected = 0;
+       list_window_set_selected(lw, 0);
 }
 
 static void
@@ -132,9 +162,9 @@ list_window_last(struct list_window *lw, unsigned length)
 {
        lw->xoffset = 0;
        if (length > 0)
-               lw->selected = length - 1;
+               list_window_set_selected(lw, length - 1);
        else
-               lw->selected = 0;
+               list_window_set_selected(lw, 0);
 }
 
 static void
@@ -143,7 +173,7 @@ list_window_next_page(struct list_window *lw, unsigned length)
        if (lw->rows < 2)
                return;
        if (lw->selected + lw->rows < length)
-               lw->selected += lw->rows - 1;
+               list_window_set_selected(lw, lw->selected + lw->rows - 1);
        else
                list_window_last(lw, length);
 }
@@ -154,7 +184,7 @@ list_window_previous_page(struct list_window *lw)
        if (lw->rows < 2)
                return;
        if (lw->selected > lw->rows - 1)
-               lw->selected -= lw->rows - 1;
+               list_window_set_selected(lw, lw->selected - lw->rows - 1);
        else
                list_window_first(lw);
 }
@@ -185,7 +215,7 @@ list_window_paint(struct list_window *lw,
                wmove(lw->w, i, 0);
 
                if (label) {
-                       bool selected = lw->start + i == lw->selected;
+                       bool selected = (lw->start + i >= lw->selected_start && lw->start + i <= lw->selected_end);
                        unsigned len = utf8_width(label);
 
                        if (highlight)
@@ -227,6 +257,10 @@ list_window_find(struct list_window *lw,
                while ((label = callback(i,&h,callback_data))) {
                        if (str && label && match_line(label, str)) {
                                lw->selected = i;
+                               if(!lw->visual_selection || i > lw->selected_end)
+                                         lw->selected_end = i;
+                               if(!lw->visual_selection || i < lw->selected_start)
+                                         lw->selected_start = i;
                                return true;
                        }
                        if (wrap && i == lw->selected)
@@ -266,6 +300,10 @@ list_window_rfind(struct list_window *lw,
                while (i >= 0 && (label = callback(i,&h,callback_data))) {
                        if( str && label && match_line(label, str) ) {
                                lw->selected = i;
+                               if(!lw->visual_selection || i > (int)lw->selected_end)
+                                         lw->selected_end = i;
+                               if(!lw->visual_selection || i < (int)lw->selected_start)
+                                         lw->selected_start = i;
                                return true;
                        }
                        if (wrap && i == (int)lw->selected)
@@ -306,6 +344,18 @@ list_window_cmd(struct list_window *lw, unsigned rows, command_t cmd)
        case CMD_LIST_PREVIOUS_PAGE:
                list_window_previous_page(lw);
                break;
+       case CMD_LIST_VISUAL_SELECT:
+               if(lw->visual_selection)
+               {
+                       lw->visual_selection = false;
+                       list_window_set_selected(lw, lw->selected);
+               }
+               else
+               {
+                       lw->visual_base = lw->selected;
+                       lw->visual_selection = true;
+               }
+               break;
        default:
                return false;
        }
index a103af2608c8c8cce2d8b2eaadd43f94551a97b8..e996039a739f4457356614aa634654888437a78a 100644 (file)
@@ -1,7 +1,7 @@
 /* ncmpc (Ncurses MPD Client)
  * (c) 2004-2009 The Music Player Daemon Project
  * Project homepage: http://musicpd.org
+
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -42,6 +42,10 @@ typedef struct list_window {
 
        unsigned start;
        unsigned selected;
+       unsigned selected_start;     /* for visual selection, first selected item */
+       unsigned selected_end;       /* for visual selection, last selected item */
+       unsigned visual_base;        /* represents the base item. */
+       bool visual_selection;       /* visual selection activated */
        unsigned xoffset;
 
        bool hide_cursor;
index 3d03354bdd4d19715f1265a0938f54295aebebe6..3b14e6bd7a90b0db0b45154bed930e25909fba31 100644 (file)
@@ -1,7 +1,7 @@
 /* ncmpc (Ncurses MPD Client)
  * (c) 2004-2009 The Music Player Daemon Project
  * Project homepage: http://musicpd.org
+
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -612,22 +612,33 @@ artist_cmd(mpdclient_t *c, command_t cmd)
        case CMD_ADD:
                switch(mode) {
                case LIST_ARTISTS:
-                       selected = g_ptr_array_index(artist_list,
-                                                    browser.lw->selected);
-                       add_query(c, MPD_TABLE_ARTIST, selected);
-                       cmd = CMD_LIST_NEXT; /* continue and select next item... */
+               {
+                       unsigned i;
+                       for(i = browser.lw->selected_start; i <= browser.lw->selected_end; ++i)
+                       {
+                               selected = g_ptr_array_index(artist_list, i);
+                               add_query(c, MPD_TABLE_ARTIST, selected);
+                               cmd = CMD_LIST_NEXT; /* continue and select next item... */
+                       }
                        break;
-
+               }
                case LIST_ALBUMS:
-                       if (browser.lw->selected == album_list->len + 1)
-                               add_query(c, MPD_TABLE_ARTIST, artist);
-                       else if (browser.lw->selected > 0) {
-                               selected = g_ptr_array_index(album_list,
-                                                            browser.lw->selected - 1);
-                               add_query(c, MPD_TABLE_ALBUM, selected);
-                               cmd = CMD_LIST_NEXT; /* continue and select next item... */
+               {
+                       unsigned i;
+                       for(i = browser.lw->selected_start; i <= browser.lw->selected_end; ++i)
+                       {
+                               if(i == album_list->len + 1)
+                                       add_query(c, MPD_TABLE_ARTIST, artist);
+                               else if (i > 0)
+                               {
+                                       selected = g_ptr_array_index(album_list,
+                                                                    browser.lw->selected - 1);
+                                       add_query(c, MPD_TABLE_ALBUM, selected);
+                                       cmd = CMD_LIST_NEXT; /* continue and select next item... */
+                               }
                        }
                        break;
+               }
 
                case LIST_SONGS:
                        /* handled by browser_cmd() */
index ba751581b9db170f132c9f125a4d616246135567..f9325c092631ac3ffec83ee4b167eb0525ed2d1d 100644 (file)
@@ -1,7 +1,7 @@
 /* ncmpc (Ncurses MPD Client)
  * (c) 2004-2009 The Music Player Daemon Project
  * Project homepage: http://musicpd.org
+
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -82,16 +82,20 @@ handle_save(mpdclient_t *c)
        filelist_entry_t *entry;
        char *defaultname = NULL;
        int ret;
+       unsigned selected;
 
        if (browser.lw->selected >= filelist_length(browser.filelist))
                return -1;
 
-       entry = filelist_get(browser.filelist, browser.lw->selected);
-       if( entry && entry->entity ) {
-               mpd_InfoEntity *entity = entry->entity;
-               if( entity->type==MPD_INFO_ENTITY_TYPE_PLAYLISTFILE ) {
-                       mpd_PlaylistFile *plf = entity->info.playlistFile;
-                       defaultname = plf->path;
+       for(selected = browser.lw->selected_start; selected <= browser.lw->selected_end; ++selected)
+       {
+               entry = filelist_get(browser.filelist, selected);
+               if( entry && entry->entity ) {
+                       mpd_InfoEntity *entity = entry->entity;
+                       if( entity->type==MPD_INFO_ENTITY_TYPE_PLAYLISTFILE ) {
+                               mpd_PlaylistFile *plf = entity->info.playlistFile;
+                               defaultname = plf->path;
+                       }
                }
        }
 
@@ -110,43 +114,47 @@ handle_delete(mpdclient_t *c)
        mpd_PlaylistFile *plf;
        char *str, *buf;
        int key;
+       unsigned selected;
+
+       for(selected = browser.lw->selected_start; selected <= browser.lw->selected_end; ++selected)
+       {
+               if (selected >= filelist_length(browser.filelist))
+                       return -1;
+
+               entry = filelist_get(browser.filelist, selected);
+               if( entry==NULL || entry->entity==NULL )
+                       continue;
+
+               entity = entry->entity;
+
+               if( entity->type!=MPD_INFO_ENTITY_TYPE_PLAYLISTFILE ) {
+                       /* translators: the "delete" command is only possible
+                          for playlists; the user attempted to delete a song
+                          or a directory or something else */
+                       screen_status_printf(_("Deleting this item is not possible"));
+                       screen_bell();
+                       continue;
+               }
 
-       if (browser.lw->selected >= filelist_length(browser.filelist))
-               return -1;
-
-       entry = filelist_get(browser.filelist, browser.lw->selected);
-       if( entry==NULL || entry->entity==NULL )
-               return -1;
-
-       entity = entry->entity;
+               plf = entity->info.playlistFile;
+               str = utf8_to_locale(g_basename(plf->path));
+               buf = g_strdup_printf(_("Delete playlist %s [%s/%s] ? "), str, YES, NO);
+               g_free(str);
+               key = tolower(screen_getch(screen.status_window.w, buf));
+               g_free(buf);
+               if( key != YES[0] ) {
+                       /* translators: a dialog was aborted by the user */
+                       screen_status_printf(_("Aborted"));
+                       return 0;
+               }
 
-       if( entity->type!=MPD_INFO_ENTITY_TYPE_PLAYLISTFILE ) {
-               /* translators: the "delete" command is only possible
-                  for playlists; the user attempted to delete a song
-                  or a directory or something else */
-               screen_status_printf(_("Deleting this item is not possible"));
-               screen_bell();
-               return -1;
-       }
+               if( mpdclient_cmd_delete_playlist(c, plf->path) )
+                       continue;
 
-       plf = entity->info.playlistFile;
-       str = utf8_to_locale(g_basename(plf->path));
-       buf = g_strdup_printf(_("Delete playlist %s [%s/%s] ? "), str, YES, NO);
-       g_free(str);
-       key = tolower(screen_getch(screen.status_window.w, buf));
-       g_free(buf);
-       if( key != YES[0] ) {
-               /* translators: a dialog was aborted by the user */
-               screen_status_printf(_("Aborted"));
-               return 0;
+               /* translators: MPD deleted the playlist, as requested by the
+                  user */
+               screen_status_printf(_("Playlist deleted"));
        }
-
-       if( mpdclient_cmd_delete_playlist(c, plf->path) )
-               return -1;
-
-       /* translators: MPD deleted the playlist, as requested by the
-          user */
-       screen_status_printf(_("Playlist deleted"));
        return 0;
 }
 
index 3e99ffa3a50a5a57d3dd52f16713430bdfbf1170..8a33de3ef23d3876511fa23a7878ac70a884fe09 100644 (file)
@@ -1,7 +1,7 @@
 /* ncmpc (Ncurses MPD Client)
  * (c) 2004-2009 The Music Player Daemon Project
  * Project homepage: http://musicpd.org
+
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -43,6 +43,7 @@ static help_text_row_t help_text[] = {
        { 0, CMD_LIST_NEXT_PAGE, NULL },
        { 0, CMD_LIST_FIRST, NULL },
        { 0, CMD_LIST_LAST, NULL },
+       { 0, CMD_LIST_VISUAL_SELECT, NULL },
        { 0, CMD_NONE, NULL },
        { 0, CMD_SCREEN_PREVIOUS,NULL },
        { 0, CMD_SCREEN_NEXT, NULL },
index eae3ace8c50098fbc5f2ab1b7bb5ce3bb9df2853..665a986c3080dece6d2715046cd676bccfb10035 100644 (file)
@@ -1,7 +1,7 @@
 /* ncmpc (Ncurses MPD Client)
  * (c) 2004-2009 The Music Player Daemon Project
  * Project homepage: http://musicpd.org
+
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
@@ -81,9 +81,12 @@ playlist_changed_callback(mpdclient_t *c, int event, gpointer data)
        case PLAYLIST_EVENT_DELETE:
                break;
        case PLAYLIST_EVENT_MOVE:
-               lw->selected = *((int *) data);
-               if (lw->selected < lw->start)
-                       lw->start--;
+               if(lw->visual_selection < 0)
+               {
+                       lw->selected = *((int *) data);
+                       if (lw->selected < lw->start)
+                               lw->start--;
+               }
                break;
        default:
                break;
@@ -153,6 +156,8 @@ center_playing_item(mpdclient_t *c)
 
        /* make sure the cursor is in the window */
        lw->selected = lw->start+offset;
+       lw->selected_start = lw->selected;
+       lw->selected_end = lw->selected;
        list_window_check_selected(lw, length);
 }
 
@@ -568,8 +573,21 @@ play_cmd(mpdclient_t *c, command_t cmd)
                mpdclient_cmd_play(c, lw->selected);
                return true;
        case CMD_DELETE:
-               mpdclient_cmd_delete(c, lw->selected);
+       {
+               int i = lw->selected_end, start = lw->selected_start;
+               for(; i >= start; --i)
+                       mpdclient_cmd_delete(c, i);
+
+               i++;
+               if(i >= (int)playlist_length(&c->playlist))
+                       i--;
+               lw->selected = i;
+               lw->selected_start = i;
+               lw->selected_end = i;
+               lw->visual_selection = false;
+
                return true;
+       }
        case CMD_SAVE_PLAYLIST:
                playlist_save(c, NULL, NULL);
                return true;
@@ -580,12 +598,39 @@ play_cmd(mpdclient_t *c, command_t cmd)
                center_playing_item(c);
                playlist_repaint();
                return false;
-
        case CMD_LIST_MOVE_UP:
-               mpdclient_cmd_move(c, lw->selected, lw->selected-1);
+               if(lw->selected_start == 0)
+                       return false;
+               if(lw->visual_selection)
+               {
+                       unsigned i = lw->selected_start;
+                       unsigned last_selected = lw->selected;
+                       for(; i <= lw->selected_end; ++i)
+                               mpdclient_cmd_move(c, i, i-1);
+                       lw->selected_start--;
+                       lw->selected_end--;
+                       lw->selected = last_selected - 1;
+                       lw->visual_base--;
+               }
+               else
+                       mpdclient_cmd_move(c, lw->selected, lw->selected-1);
                return true;
        case CMD_LIST_MOVE_DOWN:
-               mpdclient_cmd_move(c, lw->selected, lw->selected+1);
+               if(lw->selected_end+1 >= playlist_length(&c->playlist))
+                       return false;
+               if(lw->visual_selection)
+               {
+                       int i = lw->selected_end;
+                       unsigned last_selected = lw->selected;
+                       for(; i >= (int)lw->selected_start; --i)
+                               mpdclient_cmd_move(c, i, i+1);
+                       lw->selected_start++;
+                       lw->selected_end++;
+                       lw->selected = last_selected + 1;
+                       lw->visual_base++;
+               }
+               else
+                       mpdclient_cmd_move(c, lw->selected, lw->selected+1);
                return true;
        case CMD_LIST_FIND:
        case CMD_LIST_RFIND: