From: Romain Bignon Date: Tue, 10 Feb 2009 14:18:28 +0000 (+0100) Subject: support multiple lines selection X-Git-Tag: release-0.14~110 X-Git-Url: https://git.tokkee.org/?a=commitdiff_plain;h=b7dab37cfd1ace095b81998006cd022bafe6a59b;p=ncmpc.git support multiple lines selection --- diff --git a/src/command.c b/src/command.c index 2f3bafd..4558414 100644 --- a/src/command.c +++ b/src/command.c @@ -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 */ diff --git a/src/command.h b/src/command.h index 6a29533..f339910 100644 --- a/src/command.h +++ b/src/command.h @@ -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, diff --git a/src/list_window.c b/src/list_window.c index f7da79e..688ae8b 100644 --- a/src/list_window.c +++ b/src/list_window.c @@ -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; } diff --git a/src/list_window.h b/src/list_window.h index a103af2..e996039 100644 --- a/src/list_window.h +++ b/src/list_window.h @@ -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; diff --git a/src/screen_artist.c b/src/screen_artist.c index 3d03354..3b14e6b 100644 --- a/src/screen_artist.c +++ b/src/screen_artist.c @@ -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() */ diff --git a/src/screen_file.c b/src/screen_file.c index ba75158..f9325c0 100644 --- a/src/screen_file.c +++ b/src/screen_file.c @@ -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; } diff --git a/src/screen_help.c b/src/screen_help.c index 3e99ffa..8a33de3 100644 --- a/src/screen_help.c +++ b/src/screen_help.c @@ -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 }, diff --git a/src/screen_play.c b/src/screen_play.c index eae3ace..665a986 100644 --- a/src/screen_play.c +++ b/src/screen_play.c @@ -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: