X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=src%2Flist_window.c;h=3fbe531571584b13164a99cbbf55235732c50557;hb=c4aca0137a7a819270eabb3092e655ccaa721065;hp=3faf437f791a9b305d303c0139a28ec945a4ac5e;hpb=dde5f9a0b66a5539dfa8b1d9b9069801d6c2c6a1;p=ncmpc.git diff --git a/src/list_window.c b/src/list_window.c index 3faf437..3fbe531 100644 --- a/src/list_window.c +++ b/src/list_window.c @@ -1,21 +1,21 @@ /* ncmpc (Ncurses MPD Client) - * (c) 2004-2009 The Music Player Daemon Project + * (c) 2004-2017 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -*/ + */ #include "list_window.h" #include "config.h" @@ -24,7 +24,8 @@ #include "match.h" #include "command.h" #include "colors.h" -#include "screen_message.h" +#include "paint.h" +#include "screen_status.h" #include "i18n.h" #include @@ -59,176 +60,236 @@ void list_window_reset(struct list_window *lw) { lw->selected = 0; - lw->selected_start = 0; - lw->selected_end = 0; lw->range_selection = false; lw->range_base = 0; lw->start = 0; } +static unsigned +list_window_validate_index(const struct list_window *lw, unsigned i) +{ + if (lw->length == 0) + return 0; + else if (i >= lw->length) + return lw->length - 1; + else + return i; +} + +static void +list_window_check_selected(struct list_window *lw) +{ + lw->selected = list_window_validate_index(lw, lw->selected); + + if(lw->range_selection) + lw->range_base = + list_window_validate_index(lw, lw->range_base); +} + +/** + * 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) +{ + list_window_scroll_to(lw, lw->selected); +} + void -list_window_check_selected(struct list_window *lw, unsigned length) +list_window_resize(struct list_window *lw, unsigned width, unsigned height) { - if (lw->start + lw->rows > length) { - if (length > lw->rows) - lw->start = length - lw->rows; - else - lw->start = 0; - } + lw->cols = width; + lw->rows = height; - if (lw->selected < lw->start) - lw->selected = lw->start; + list_window_check_origin(lw); +} - if (length == 0) - lw->selected = 0; - else if (lw->selected >= length) - lw->selected = length - 1; +void +list_window_set_length(struct list_window *lw, unsigned length) +{ + if (length == lw->length) + return; - if(lw->range_selection) - { - if (length == 0) { - lw->selected_start = 0; - lw->selected_end = 0; - lw->range_base = 0; - } else { - if (lw->selected_start >= length) - lw->selected_start = length - 1; - if (lw->selected_end >= length) - lw->selected_end = length - 1; - if (lw->range_base >= length) - lw->range_base = length - 1; - } + lw->length = length; - 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; - } + 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_selected(struct list_window *lw, unsigned n) +list_window_scroll_to(struct list_window *lw, unsigned n) +{ + int start = lw->start; + + if ((unsigned) options.scroll_offset * 2 >= lw->rows) + // Center if the offset is more than half the screen + start = n - lw->rows / 2; + else { + if (n < lw->start + options.scroll_offset) + start = n - options.scroll_offset; + + if (n >= lw->start + lw->rows - options.scroll_offset) + start = n - 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_set_cursor(struct list_window *lw, unsigned i) +{ + lw->range_selection = false; + lw->selected = i; + + list_window_check_selected(lw); + list_window_check_origin(lw); +} + +void +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; + + list_window_check_selected(lw); + list_window_check_origin(lw); +} + +void +list_window_fetch_cursor(struct list_window *lw) +{ + if (lw->start > 0 && + lw->selected < lw->start + options.scroll_offset) + list_window_move_cursor(lw, lw->start + options.scroll_offset); + else if (lw->start + lw->rows < lw->length && + lw->selected > lw->start + lw->rows - 1 - options.scroll_offset) + list_window_move_cursor(lw, lw->start + lw->rows - 1 - options.scroll_offset); +} + +void +list_window_get_range(const struct list_window *lw, + struct list_window_range *range) +{ + if (lw->length == 0) { + /* empty list - no selection */ + range->start = 0; + range->end = 0; + } else if (lw->range_selection) { + /* a range selection */ + if (lw->range_base < lw->selected) { + range->start = lw->range_base; + range->end = lw->selected + 1; + } else { + range->start = lw->selected; + range->end = lw->range_base + 1; } - } - else - { - lw->selected_start = n; - lw->selected_end = n; + } else { + /* no range, just the cursor */ + range->start = lw->selected; + range->end = lw->selected + 1; } } static void -list_window_next(struct list_window *lw, unsigned length) +list_window_next(struct list_window *lw) { - if (lw->selected + 1 < length) - list_window_set_selected(lw, lw->selected + 1); + if (lw->selected + 1 < lw->length) + list_window_move_cursor(lw, lw->selected + 1); else if (options.list_wrap) - list_window_set_selected(lw, 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) - list_window_set_selected(lw, lw->selected - 1); + list_window_move_cursor(lw, lw->selected - 1); else if (options.list_wrap) - list_window_set_selected(lw, 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_set_selected(lw, lw->start); + list_window_move_cursor(lw, lw->start); else if ((unsigned) options.scroll_offset * 2 >= lw->rows) - list_window_set_selected(lw, lw->start + lw->rows / 2); + list_window_move_cursor(lw, lw->start + lw->rows / 2); else - list_window_set_selected(lw, lw->start + options.scroll_offset); + list_window_move_cursor(lw, lw->start + options.scroll_offset); } static void -list_window_middle(struct list_window *lw, unsigned length) +list_window_middle(struct list_window *lw) { - if (length >= lw->rows) - list_window_set_selected(lw, lw->start + lw->rows / 2); + if (lw->length >= lw->rows) + list_window_move_cursor(lw, lw->start + lw->rows / 2); else - list_window_set_selected(lw, length / 2); + list_window_move_cursor(lw, lw->length / 2); } static void -list_window_bottom(struct list_window *lw, unsigned length) +list_window_bottom(struct list_window *lw) { - if (length >= lw->rows) + if (lw->length >= lw->rows) if ((unsigned) options.scroll_offset * 2 >= lw->rows) - list_window_set_selected(lw, lw->start + lw->rows / 2); + list_window_move_cursor(lw, lw->start + lw->rows / 2); else - if (lw->start + lw->rows == length) - list_window_set_selected(lw, length - 1); + if (lw->start + lw->rows == lw->length) + list_window_move_cursor(lw, lw->length - 1); else - list_window_set_selected(lw, lw->start + lw->rows - 1 - options.scroll_offset); + list_window_move_cursor(lw, lw->start + lw->rows - 1 - options.scroll_offset); else - list_window_set_selected(lw, length - 1); + list_window_move_cursor(lw, lw->length - 1); } static void list_window_first(struct list_window *lw) { - list_window_set_selected(lw, 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) { - if (length > 0) - list_window_set_selected(lw, length - 1); + if (lw->length > 0) + list_window_move_cursor(lw, lw->length - 1); else - list_window_set_selected(lw, 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) - list_window_set_selected(lw, 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 @@ -237,7 +298,7 @@ list_window_previous_page(struct list_window *lw) if (lw->rows < 2) return; if (lw->selected > lw->rows - 1) - list_window_set_selected(lw, lw->selected - lw->rows + 1); + list_window_move_cursor(lw, lw->selected - lw->rows + 1); else list_window_first(lw); } @@ -250,156 +311,98 @@ list_window_scroll_up(struct list_window *lw, unsigned n) lw->start = 0; else lw->start -= n; - if (lw->selected > lw->start + lw->rows - 1 - options.scroll_offset) { - 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; - } - } + + list_window_fetch_cursor(lw); } } static void -list_window_scroll_down(struct list_window *lw, unsigned length, unsigned n) +list_window_scroll_down(struct list_window *lw, unsigned n) { - if (lw->start + lw->rows < length) + if (lw->start + lw->rows < lw->length) { - if ( lw->start + lw->rows + n > length - 1) - lw->start = length - lw->rows; + if (lw->start + lw->rows + n > lw->length - 1) + lw->start = lw->length - lw->rows; else lw->start += n; - if (lw->selected < lw->start + options.scroll_offset) { - 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; - } - } + + 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) +list_window_paint_row(WINDOW *w, unsigned width, bool selected, + const char *text) { - unsigned text_width = utf8_width(text); - unsigned second_column_width; + row_paint_text(w, width, COLOR_LIST, + selected, text); +} - 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; +void +list_window_paint(const struct list_window *lw, + list_window_callback_fn_t callback, + void *callback_data) +{ + bool show_cursor = !lw->hide_cursor && + (!options.hardware_cursor || lw->range_selection); + struct list_window_range range; - if (highlight) - colors_use(w, COLOR_LIST_BOLD); - else - colors_use(w, COLOR_LIST); + if (show_cursor) + list_window_get_range(lw, &range); - if (selected) - wattron(w, A_REVERSE); + for (unsigned i = 0; i < lw->rows; i++) { + wmove(lw->w, i, 0); - waddstr(w, text); - if (options.wide_cursor && text_width < width) - whline(w, ' ', width - text_width); + if (lw->start + i >= lw->length) { + wclrtobot(lw->w); + break; + } + + const char *label = callback(lw->start + i, callback_data); + assert(label != NULL); - if (second_column_width > 0) { - wmove(w, y, width); - waddch(w, ' '); - waddstr(w, second_column); + list_window_paint_row(lw->w, lw->cols, + show_cursor && + lw->start + i >= range.start && + lw->start + i < range.end, + label); } - if (selected) - wattroff(w, A_REVERSE); + row_color_end(lw->w); - 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); + 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); } } void -list_window_paint(struct list_window *lw, - list_window_callback_fn_t callback, - void *callback_data) +list_window_paint2(const struct list_window *lw, + list_window_paint_callback_t paint_callback, + const void *callback_data) { - unsigned i; - bool show_cursor = !lw->hide_cursor; - bool highlight = false; - - if (show_cursor) { - 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 < 0) - lw->start = 0; - else - { - while ( start > 0 && callback(start + lw->rows - 1, &highlight, NULL, callback_data) == NULL) - start--; - lw->start = start; - } - } - - show_cursor = show_cursor && + bool show_cursor = !lw->hide_cursor && (!options.hardware_cursor || lw->range_selection); + struct list_window_range range; - for (i = 0; i < lw->rows; i++) { - const char *label; - char *second_column = NULL; - highlight = false; + if (show_cursor) + list_window_get_range(lw, &range); - label = callback(lw->start + i, &highlight, &second_column, callback_data); + for (unsigned i = 0; i < lw->rows; i++) { wmove(lw->w, i, 0); - if (label) { - 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 (second_column != NULL) - g_free(second_column); - } else - wclrtoeol(lw->w); + if (lw->start + i >= lw->length) { + wclrtobot(lw->w); + break; + } + + bool selected = show_cursor && + lw->start + i >= range.start && + lw->start + i < range.end; + + paint_callback(lw->w, lw->start + i, i, lw->cols, + selected, callback_data); } if (options.hardware_cursor && lw->selected >= lw->start && @@ -417,18 +420,17 @@ list_window_find(struct list_window *lw, bool wrap, bool bell_on_wrap) { - bool h; unsigned i = lw->selected + 1; - const char *label; + + assert(str != NULL); do { - while ((label = callback(i,&h,NULL,callback_data))) { - if (str && label && match_line(label, str)) { - lw->selected = i; - if(!lw->range_selection || i > lw->selected_end) - lw->selected_end = i; - if(!lw->range_selection || i < lw->selected_start) - lw->selected_start = i; + while (i < lw->length) { + const char *label = callback(i, callback_data); + assert(label != NULL); + + if (match_line(label, str)) { + list_window_move_cursor(lw, i); return true; } if (wrap && i == lw->selected) @@ -454,24 +456,22 @@ list_window_rfind(struct list_window *lw, void *callback_data, const char *str, bool wrap, - bool bell_on_wrap, - unsigned rows) + bool bell_on_wrap) { - bool h; int i = lw->selected - 1; - const char *label; - if (rows == 0) + assert(str != NULL); + + if (lw->length == 0) return false; do { - while (i >= 0 && (label = callback(i,&h,NULL,callback_data))) { - if( str && label && match_line(label, str) ) { - lw->selected = i; - if(!lw->range_selection || i > (int)lw->selected_end) - lw->selected_end = i; - if(!lw->range_selection || i < (int)lw->selected_start) - lw->selected_start = i; + while (i >= 0) { + const char *label = callback(i, callback_data); + assert(label != NULL); + + if (match_line(label, str)) { + list_window_move_cursor(lw, i); return true; } if (wrap && i == (int)lw->selected) @@ -479,7 +479,7 @@ list_window_rfind(struct list_window *lw, i--; } if (wrap) { - i = rows - 1; /* last item */ + i = lw->length - 1; /* last item */ if (bell_on_wrap) { screen_bell(); } @@ -489,67 +489,82 @@ list_window_rfind(struct list_window *lw, return false; } +#ifdef NCMPC_MINI bool list_window_jump(struct list_window *lw, list_window_callback_fn_t callback, void *callback_data, const char *str) { - bool h; - unsigned i = 0; - const char *label; - - while ((label = callback(i,&h,NULL,callback_data))) { - if (label && label[0] == '[') - label++; -#ifndef NCMPC_MINI - if (str && label && - ((options.jump_prefix_only && g_ascii_strncasecmp(label, str, strlen(str)) == 0) || - (!options.jump_prefix_only && match_line(label, str))) ) + assert(str != NULL); + + for (unsigned i = 0; i < lw->length; i++) { + const char *label = callback(i, callback_data); + assert(label != NULL); + + if (g_ascii_strncasecmp(label, str, strlen(str)) == 0) { + list_window_move_cursor(lw, i); + return true; + } + } + return false; +} #else - if (str && label && g_ascii_strncasecmp(label, str, strlen(str)) == 0) -#endif - { - lw->selected = i; - if(!lw->range_selection || i > lw->selected_end) - lw->selected_end = i; - if(!lw->range_selection || i < lw->selected_start) - lw->selected_start = i; +bool +list_window_jump(struct list_window *lw, + list_window_callback_fn_t callback, + void *callback_data, + const char *str) +{ + assert(str != NULL); + + GRegex *regex = compile_regex(str, options.jump_prefix_only); + if (regex == NULL) + return false; + + for (unsigned i = 0; i < lw->length; i++) { + const char *label = callback(i, callback_data); + assert(label != NULL); + + if (match_regex(regex, label)) { + g_regex_unref(regex); + list_window_move_cursor(lw, i); return true; } - i++; } + g_regex_unref(regex); return false; } +#endif /* perform basic list window commands (movement) */ bool -list_window_cmd(struct list_window *lw, unsigned rows, command_t cmd) +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,rows); + list_window_middle(lw); break; case CMD_LIST_BOTTOM: - list_window_bottom(lw,rows); + 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); @@ -558,8 +573,7 @@ list_window_cmd(struct list_window *lw, unsigned rows, command_t cmd) if(lw->range_selection) { screen_status_printf(_("Range selection disabled")); - lw->range_selection = false; - list_window_set_selected(lw, lw->selected); + list_window_set_cursor(lw, lw->selected); } else { @@ -572,13 +586,13 @@ list_window_cmd(struct list_window *lw, unsigned rows, command_t cmd) list_window_scroll_up(lw, 1); break; case CMD_LIST_SCROLL_DOWN_LINE: - list_window_scroll_down(lw, rows, 1); + 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, rows, (lw->rows - 1) / 2); + list_window_scroll_down(lw, (lw->rows - 1) / 2); break; default: return false; @@ -588,7 +602,7 @@ list_window_cmd(struct list_window *lw, unsigned rows, command_t cmd) } bool -list_window_scroll_cmd(struct list_window *lw, unsigned rows, command_t cmd) +list_window_scroll_cmd(struct list_window *lw, command_t cmd) { switch (cmd) { case CMD_LIST_SCROLL_UP_LINE: @@ -599,7 +613,7 @@ list_window_scroll_cmd(struct list_window *lw, unsigned rows, command_t cmd) 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; @@ -608,17 +622,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; + lw->start += lw->rows; + if (lw->start + lw->rows > lw->length) { + if (lw->length > lw->rows) + lw->start = lw->length - lw->rows; else lw->start = 0; } @@ -640,9 +654,9 @@ list_window_scroll_cmd(struct list_window *lw, unsigned rows, command_t cmd) case CMD_LIST_SCROLL_DOWN_HALF: lw->start += (lw->rows - 1) / 2; - 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; } @@ -657,8 +671,7 @@ list_window_scroll_cmd(struct list_window *lw, unsigned rows, command_t cmd) #ifdef HAVE_GETMOUSE bool -list_window_mouse(struct list_window *lw, unsigned rows, - unsigned long bstate, int y) +list_window_mouse(struct list_window *lw, unsigned long bstate, int y) { assert(lw != NULL); @@ -672,11 +685,11 @@ list_window_mouse(struct list_window *lw, unsigned rows, } /* if the even occurred below the list window move down */ - if ((unsigned)y >= rows) { + 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); + list_window_next_page(lw); return true; }