X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=src%2Flist_window.c;h=10e06292ad605719ff677f75b9f76d9646689d7e;hb=cc17dcad34c4b6c42a9a73564c537443360ee079;hp=5a4ccf36d7906d7f9cffa8d0bb8e34d2313e96b3;hpb=0fd4d0c99d31b69356c289d3bb1dcd791b898028;p=ncmpc.git diff --git a/src/list_window.c b/src/list_window.c index 5a4ccf3..10e0629 100644 --- a/src/list_window.c +++ b/src/list_window.c @@ -1,29 +1,31 @@ -/* - * $Id$ - * - * (c) 2004 by Kalle Wallin - * +/* 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 * (at your option) any later version. - * + * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ + + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ #include "list_window.h" #include "config.h" #include "options.h" -#include "support.h" +#include "charset.h" +#include "match.h" #include "command.h" #include "colors.h" +#include "screen_message.h" +#include "i18n.h" #include #include @@ -37,38 +39,39 @@ list_window_init(WINDOW *w, unsigned width, unsigned height) { struct list_window *lw; - lw = g_malloc0(sizeof(list_window_t)); + lw = g_malloc0(sizeof(*lw)); lw->w = w; lw->cols = width; lw->rows = height; - lw->clear = 1; + lw->range_selection = false; return lw; } void list_window_free(struct list_window *lw) { - if (lw) { - memset(lw, 0, sizeof(list_window_t)); - g_free(lw); - } + assert(lw != NULL); + + g_free(lw); } void list_window_reset(struct list_window *lw) { lw->selected = 0; - lw->xoffset = 0; + lw->selected_start = 0; + lw->selected_end = 0; + lw->range_selection = false; + lw->range_base = 0; lw->start = 0; - lw->clear = 1; } -void -list_window_check_selected(struct list_window *lw, unsigned length) +static void +list_window_check_selected(struct list_window *lw) { - if (lw->start + lw->rows > length) { - if (length > lw->rows) - lw->start = length - lw->rows; + if (lw->start + lw->rows > lw->length) { + if (lw->length > lw->rows) + lw->start = lw->length - lw->rows; else lw->start = 0; } @@ -76,78 +79,255 @@ list_window_check_selected(struct list_window *lw, unsigned length) if (lw->selected < lw->start) lw->selected = lw->start; - if (length > 0 && lw->selected >= length) - lw->selected = length - 1; + if (lw->length == 0) + lw->selected = 0; + else if (lw->selected >= lw->length) + lw->selected = lw->length - 1; + + if(lw->range_selection) + { + if (lw->length == 0) { + lw->selected_start = 0; + lw->selected_end = 0; + lw->range_base = 0; + } else { + if (lw->selected_start >= lw->length) + lw->selected_start = lw->length - 1; + if (lw->selected_end >= lw->length) + lw->selected_end = lw->length - 1; + if (lw->range_base >= lw->length) + lw->range_base = lw->length - 1; + } + + if(lw->range_base > lw->selected_end) + lw->selected_end = lw->selected; + if(lw->range_base < lw->selected_start) + lw->selected_start = lw->selected; + } + else + { + lw->selected_start = lw->selected; + lw->selected_end = lw->selected; + } +} + +/** + * Scroll after the cursor was moved, the list was changed or the + * window was resized. + */ +static void +list_window_check_origin(struct list_window *lw) +{ + int start = lw->start; + + if ((unsigned) options.scroll_offset * 2 >= lw->rows) + // Center if the offset is more than half the screen + start = lw->selected - lw->rows / 2; + else + { + if (lw->selected < lw->start + options.scroll_offset) + start = lw->selected - options.scroll_offset; + + if (lw->selected >= lw->start + lw->rows - options.scroll_offset) + { + start = lw->selected - lw->rows + 1 + options.scroll_offset; + } + } + + if (start + lw->rows > lw->length) + start = lw->length - lw->rows; + + if (start < 0 || lw->length == 0) + start = 0; + + lw->start = start; +} + +void +list_window_resize(struct list_window *lw, unsigned width, unsigned height) +{ + lw->cols = width; + lw->rows = height; + + list_window_check_origin(lw); +} + +void +list_window_set_length(struct list_window *lw, unsigned length) +{ + lw->length = length; + + list_window_check_selected(lw); + list_window_check_origin(lw); } void -list_window_center(struct list_window *lw, unsigned rows, unsigned n) +list_window_center(struct list_window *lw, unsigned n) { if (n > lw->rows / 2) lw->start = n - lw->rows / 2; else lw->start = 0; - if (lw->start + lw->rows > rows) { - if (lw->rows < rows) - lw->start = rows - lw->rows; + if (lw->start + lw->rows > lw->length) { + if (lw->rows < lw->length) + lw->start = lw->length - lw->rows; else lw->start = 0; } +} + +void +list_window_set_cursor(struct list_window *lw, unsigned i) +{ + lw->range_selection = false; + lw->selected = i; + lw->selected_start = i; + lw->selected_end = i; - lw->repaint = lw->clear = 1; + list_window_check_selected(lw); + list_window_check_origin(lw); } void -list_window_set_selected(struct list_window *lw, unsigned n) +list_window_move_cursor(struct list_window *lw, unsigned n) { lw->selected = n; + if(lw->range_selection) + { + if(n >= lw->range_base) + { + lw->selected_end = n; + lw->selected_start = lw->range_base; + } + if(n <= lw->range_base) + { + lw->selected_start = n; + lw->selected_end = lw->range_base; + } + } + else + { + lw->selected_start = n; + lw->selected_end = n; + } + + list_window_check_selected(lw); + list_window_check_origin(lw); +} + +void +list_window_fetch_cursor(struct list_window *lw) +{ + if (lw->selected < lw->start + options.scroll_offset) { + if (lw->start > 0) + lw->selected = lw->start + options.scroll_offset; + if (lw->range_selection) { + if (lw->selected > lw->range_base) { + lw->selected_end = lw->selected; + lw->selected_start = lw->range_base; + } else { + lw->selected_start = lw->selected; + } + } else { + lw->selected_start = lw->selected; + lw->selected_end = lw->selected; + } + } else if (lw->selected > lw->start + lw->rows - 1 - options.scroll_offset) { + if (lw->start + lw->rows < lw->length) + lw->selected = lw->start + lw->rows - 1 - options.scroll_offset; + if (lw->range_selection) { + if (lw->selected < lw->range_base) { + lw->selected_start = lw->selected; + lw->selected_end = lw->range_base; + } else { + lw->selected_end = lw->selected; + } + } else { + lw->selected_start = lw->selected; + lw->selected_end = lw->selected; + } + } } static void -list_window_next(struct list_window *lw, unsigned length) +list_window_next(struct list_window *lw) { - if (lw->selected + 1 < length) - lw->selected++; + if (lw->selected + 1 < lw->length) + list_window_move_cursor(lw, lw->selected + 1); else if (options.list_wrap) - lw->selected = 0; + list_window_move_cursor(lw, 0); } static void -list_window_previous(struct list_window *lw, unsigned length) +list_window_previous(struct list_window *lw) { if (lw->selected > 0) - lw->selected--; + list_window_move_cursor(lw, lw->selected - 1); else if (options.list_wrap) - lw->selected = length - 1; + list_window_move_cursor(lw, lw->length - 1); +} + +static void +list_window_top(struct list_window *lw) +{ + if (lw->start == 0) + list_window_move_cursor(lw, lw->start); + else + if ((unsigned) options.scroll_offset * 2 >= lw->rows) + list_window_move_cursor(lw, lw->start + lw->rows / 2); + else + list_window_move_cursor(lw, lw->start + options.scroll_offset); +} + +static void +list_window_middle(struct list_window *lw) +{ + if (lw->length >= lw->rows) + list_window_move_cursor(lw, lw->start + lw->rows / 2); + else + list_window_move_cursor(lw, lw->length / 2); +} + +static void +list_window_bottom(struct list_window *lw) +{ + if (lw->length >= lw->rows) + if ((unsigned) options.scroll_offset * 2 >= lw->rows) + list_window_move_cursor(lw, lw->start + lw->rows / 2); + else + if (lw->start + lw->rows == lw->length) + list_window_move_cursor(lw, lw->length - 1); + else + list_window_move_cursor(lw, lw->start + lw->rows - 1 - options.scroll_offset); + else + list_window_move_cursor(lw, lw->length - 1); } static void list_window_first(struct list_window *lw) { - lw->xoffset = 0; - lw->selected = 0; + list_window_move_cursor(lw, 0); } static void -list_window_last(struct list_window *lw, unsigned length) +list_window_last(struct list_window *lw) { - lw->xoffset = 0; - if (length > 0) - lw->selected = length - 1; + if (lw->length > 0) + list_window_move_cursor(lw, lw->length - 1); else - lw->selected = 0; + list_window_move_cursor(lw, 0); } static void -list_window_next_page(struct list_window *lw, unsigned length) +list_window_next_page(struct list_window *lw) { if (lw->rows < 2) return; - if (lw->selected + lw->rows < length) - lw->selected += lw->rows - 1; + if (lw->selected + lw->rows < lw->length) + list_window_move_cursor(lw, lw->selected + lw->rows - 1); else - list_window_last(lw, length); + list_window_last(lw); } static void @@ -156,174 +336,343 @@ 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_move_cursor(lw, lw->selected - lw->rows + 1); else list_window_first(lw); } +static void +list_window_scroll_up(struct list_window *lw, unsigned n) +{ + if (lw->start > 0) { + if (n > lw->start) + lw->start = 0; + else + lw->start -= n; + + list_window_fetch_cursor(lw); + } +} + +static void +list_window_scroll_down(struct list_window *lw, unsigned n) +{ + if (lw->start + lw->rows < lw->length) + { + if ( lw->start + lw->rows + n > lw->length - 1) + lw->start = lw->length - lw->rows; + else + lw->start += n; + + list_window_fetch_cursor(lw); + } +} + +static void +list_window_paint_row(WINDOW *w, unsigned y, unsigned width, + bool selected, bool highlight, + const char *text, const char *second_column) +{ + unsigned text_width = utf8_width(text); + unsigned second_column_width; + +#ifdef NCMPC_MINI + second_column = NULL; + highlight = false; +#endif /* NCMPC_MINI */ + + if (second_column != NULL) { + second_column_width = utf8_width(second_column) + 1; + if (second_column_width < width) + width -= second_column_width; + else + second_column_width = 0; + } else + second_column_width = 0; + + if (highlight) + colors_use(w, COLOR_LIST_BOLD); + else + colors_use(w, COLOR_LIST); + + if (selected) + wattron(w, A_REVERSE); + + waddstr(w, text); + if (options.wide_cursor && text_width < width) + whline(w, ' ', width - text_width); + + if (second_column_width > 0) { + wmove(w, y, width); + waddch(w, ' '); + waddstr(w, second_column); + } + + if (selected) + wattroff(w, A_REVERSE); + + if (!options.wide_cursor && text_width < width) { + if (second_column_width == 0) + /* the cursor is at the end of the text; clear + the rest of this row */ + wclrtoeol(w); + else + /* there's a second column: clear the space + between the first and the second column */ + mvwhline(w, y, text_width, ' ', width - text_width); + } +} void -list_window_paint(struct list_window *lw, +list_window_paint(const struct list_window *lw, list_window_callback_fn_t callback, void *callback_data) { unsigned i; - int fill = options.wide_cursor; - int show_cursor = !(lw->flags & LW_HIDE_CURSOR); - - if (show_cursor) { - if (lw->selected < lw->start) { - lw->start = lw->selected; - lw->clear=1; - } + bool show_cursor = !lw->hide_cursor; - if (lw->selected >= lw->start + lw->rows) { - lw->start = lw->selected - lw->rows + 1; - lw->clear=1; - } - } + show_cursor = show_cursor && + (!options.hardware_cursor || lw->range_selection); for (i = 0; i < lw->rows; i++) { - int highlight = 0; const char *label; + bool highlight = false; + char *second_column = NULL; - label = callback(lw->start + i, &highlight, callback_data); wmove(lw->w, i, 0); - if( lw->clear && (!fill || !label) ) - wclrtoeol(lw->w); - if (label) { - int selected = lw->start + i == lw->selected; - size_t len = my_strlen(label); + if (lw->start + i >= lw->length) { + wclrtobot(lw->w); + break; + } - if (highlight) - colors_use(lw->w, COLOR_LIST_BOLD); - else - colors_use(lw->w, COLOR_LIST); + label = callback(lw->start + i, &highlight, &second_column, callback_data); + assert(label != NULL); - if (show_cursor && selected) - wattron(lw->w, A_REVERSE); +#ifdef NCMPC_MINI + highlight = false; + second_column = NULL; +#endif /* NCMPC_MINI */ - //waddnstr(lw->w, label, lw->cols); - waddstr(lw->w, label); - if (fill && len < lw->cols) - whline(lw->w, ' ', lw->cols-len); + list_window_paint_row(lw->w, i, lw->cols, + show_cursor && + lw->start + i >= lw->selected_start && + lw->start + i <= lw->selected_end, + highlight, + label, second_column); - if (selected) - wattroff(lw->w, A_REVERSE); - } + if (second_column != NULL) + g_free(second_column); } - lw->clear=0; + if (options.hardware_cursor && lw->selected >= lw->start && + lw->selected < lw->start + lw->rows) { + curs_set(1); + wmove(lw->w, lw->selected - lw->start, 0); + } } -int +bool list_window_find(struct list_window *lw, list_window_callback_fn_t callback, void *callback_data, const char *str, - int wrap) + bool wrap, + bool bell_on_wrap) { - int h; + bool h; unsigned i = lw->selected + 1; const char *label; - while (wrap || i == lw->selected + 1) { - while ((label = callback(i,&h,callback_data))) { - if (str && label && strcasestr(label, str)) { - lw->selected = i; - return 0; + assert(str != NULL); + + do { + while (i < lw->length) { + label = callback(i, &h, NULL, callback_data); + assert(label != NULL); + + if (match_line(label, str)) { + list_window_move_cursor(lw, i); + return true; } if (wrap && i == lw->selected) - return 1; + return false; i++; } if (wrap) { if (i == 0) /* empty list */ return 1; i=0; /* first item */ - screen_bell(); + if (bell_on_wrap) { + screen_bell(); + } } - } + } while (wrap); - return 1; + return false; } -int +bool list_window_rfind(struct list_window *lw, list_window_callback_fn_t callback, void *callback_data, const char *str, - int wrap, - unsigned rows) + bool wrap, + bool bell_on_wrap) { - int h; + bool h; int i = lw->selected - 1; const char *label; - if (rows == 0) - return 1; + assert(str != NULL); + + if (lw->length == 0) + return false; - while (wrap || i == (int)lw->selected - 1) { - while (i >= 0 && (label = callback(i,&h,callback_data))) { - if( str && label && strcasestr(label, str) ) { - lw->selected = i; - return 0; + do { + while (i >= 0) { + label = callback(i, &h, NULL, callback_data); + assert(label != NULL); + + if (match_line(label, str)) { + list_window_move_cursor(lw, i); + return true; } if (wrap && i == (int)lw->selected) - return 1; + return false; i--; } if (wrap) { - i = rows - 1; /* last item */ - screen_bell(); + i = lw->length - 1; /* last item */ + if (bell_on_wrap) { + screen_bell(); + } + } + } while (wrap); + + return false; +} + +static bool +jump_match(const char *haystack, const char *needle) +{ +#ifdef NCMPC_MINI + bool jump_prefix_only = true; +#else + bool jump_prefix_only = options.jump_prefix_only; +#endif + + assert(haystack != NULL); + assert(needle != NULL); + + return jump_prefix_only + ? g_ascii_strncasecmp(haystack, needle, strlen(needle)) == 0 + : match_line(haystack, needle); +} + +bool +list_window_jump(struct list_window *lw, + list_window_callback_fn_t callback, + void *callback_data, + const char *str) +{ + bool h; + const char *label; + + assert(str != NULL); + + for (unsigned i = 0; i < lw->length; ++i) { + label = callback(i, &h, NULL, callback_data); + assert(label != NULL); + + if (label[0] == '[') + label++; + + if (jump_match(label, str)) { + list_window_move_cursor(lw, i); + return true; } } - return 1; + return false; } /* perform basic list window commands (movement) */ -int -list_window_cmd(struct list_window *lw, unsigned rows, command_t cmd) +bool +list_window_cmd(struct list_window *lw, command_t cmd) { switch (cmd) { case CMD_LIST_PREVIOUS: - list_window_previous(lw, rows); + list_window_previous(lw); break; case CMD_LIST_NEXT: - list_window_next(lw, rows); + list_window_next(lw); + break; + case CMD_LIST_TOP: + list_window_top(lw); + break; + case CMD_LIST_MIDDLE: + list_window_middle(lw); + break; + case CMD_LIST_BOTTOM: + list_window_bottom(lw); break; case CMD_LIST_FIRST: list_window_first(lw); break; case CMD_LIST_LAST: - list_window_last(lw, rows); + list_window_last(lw); break; case CMD_LIST_NEXT_PAGE: - list_window_next_page(lw, rows); + list_window_next_page(lw); break; case CMD_LIST_PREVIOUS_PAGE: list_window_previous_page(lw); break; + case CMD_LIST_RANGE_SELECT: + if(lw->range_selection) + { + screen_status_printf(_("Range selection disabled")); + list_window_set_cursor(lw, lw->selected); + } + else + { + screen_status_printf(_("Range selection enabled")); + lw->range_base = lw->selected; + lw->range_selection = true; + } + break; + case CMD_LIST_SCROLL_UP_LINE: + list_window_scroll_up(lw, 1); + break; + case CMD_LIST_SCROLL_DOWN_LINE: + list_window_scroll_down(lw, 1); + break; + case CMD_LIST_SCROLL_UP_HALF: + list_window_scroll_up(lw, (lw->rows - 1) / 2); + break; + case CMD_LIST_SCROLL_DOWN_HALF: + list_window_scroll_down(lw, (lw->rows - 1) / 2); + break; default: - return 0; + return false; } - lw->repaint = 1; - return 1; + return true; } -int -list_window_scroll_cmd(struct list_window *lw, unsigned rows, command_t cmd) +bool +list_window_scroll_cmd(struct list_window *lw, command_t cmd) { switch (cmd) { + case CMD_LIST_SCROLL_UP_LINE: case CMD_LIST_PREVIOUS: if (lw->start > 0) lw->start--; break; + case CMD_LIST_SCROLL_DOWN_LINE: case CMD_LIST_NEXT: - if (lw->start + lw->rows < rows) + if (lw->start + lw->rows < lw->length) lw->start++; break; @@ -332,17 +681,17 @@ list_window_scroll_cmd(struct list_window *lw, unsigned rows, command_t cmd) break; case CMD_LIST_LAST: - if (rows > lw->rows) - lw->start = rows - lw->rows; + if (lw->length > lw->rows) + lw->start = lw->length - lw->rows; else lw->start = 0; break; case CMD_LIST_NEXT_PAGE: lw->start += lw->rows - 1; - if (lw->start + lw->rows > rows) { - if (rows > lw->rows) - lw->start = rows - lw->rows; + if (lw->start + lw->rows > lw->length) { + if (lw->length > lw->rows) + lw->start = lw->length - lw->rows; else lw->start = 0; } @@ -355,93 +704,54 @@ list_window_scroll_cmd(struct list_window *lw, unsigned rows, command_t cmd) lw->start = 0; break; + case CMD_LIST_SCROLL_UP_HALF: + if (lw->start > (lw->rows - 1) / 2) + lw->start -= (lw->rows - 1) / 2; + else + lw->start = 0; + break; + + case CMD_LIST_SCROLL_DOWN_HALF: + lw->start += (lw->rows - 1) / 2; + if (lw->start + lw->rows > lw->length) { + if (lw->length > lw->rows) + lw->start = lw->length - lw->rows; + else + lw->start = 0; + } + break; + default: - return 0; + return false; } - lw->repaint = lw->clear = 1; - return 1; + return true; } #ifdef HAVE_GETMOUSE -int -list_window_mouse(struct list_window *lw, unsigned rows, - unsigned long bstate, int y) +bool +list_window_mouse(struct list_window *lw, unsigned long bstate, int y) { assert(lw != NULL); - /* if the even occured above the list window move up */ + /* if the even occurred above the list window move up */ if (y < 0) { if (bstate & BUTTON3_CLICKED) list_window_first(lw); else list_window_previous_page(lw); - return 1; + return true; } - /* if the even occured below the list window move down */ - if ((unsigned)y >= rows) { + /* if the even occurred below the list window move down */ + if ((unsigned)y >= lw->length) { if (bstate & BUTTON3_CLICKED) - list_window_last(lw, rows); + list_window_last(lw); else - list_window_next_page(lw, rows); - return 1; + list_window_next_page(lw); + return true; } - return 0; + return false; } #endif - -list_window_state_t * -list_window_init_state(void) -{ - return g_malloc0(sizeof(list_window_state_t)); -} - -void -list_window_free_state(list_window_state_t *state) -{ - if (state) { - if (state->list) { - GList *list = state->list; - - while (list) { - g_free(list->data); - list->data = NULL; - list = list->next; - } - - g_list_free(state->list); - state->list = NULL; - } - - g_free(state); - } -} - -void -list_window_push_state(list_window_state_t *state, struct list_window *lw) -{ - if (state) { - struct list_window *tmp = g_malloc(sizeof(list_window_t)); - memcpy(tmp, lw, sizeof(list_window_t)); - state->list = g_list_prepend(state->list, (gpointer) tmp); - list_window_reset(lw); - } -} - -bool -list_window_pop_state(list_window_state_t *state, struct list_window *lw) -{ - if (state && state->list) { - struct list_window *tmp = state->list->data; - - memcpy(lw, tmp, sizeof(list_window_t)); - g_free(tmp); - state->list->data = NULL; - state->list = g_list_delete_link(state->list, state->list); - } - - // return TRUE if there are still states in the list - return (state && state->list) ? TRUE : FALSE; -}