Ticket #4165: MultiSearch_V4.1.patch
File MultiSearch_V4.1.patch, 88.4 KB (added by psprint, 4 years ago) |
---|
-
lib/keybind.c
From cbcbcc1b679a51c7a2eb712e66eca1c85c13f334 Mon Sep 17 00:00:00 2001 From: Sebastian Gniazdowski <sgniazdowski@gmail.com> Date: Thu, 14 Jan 2021 16:39:08 -0600 Subject: [PATCH] =?UTF-8?q?MultiSearch=20=E2=80=93=20an=20AND-chained=20fi?= =?UTF-8?q?ltering=20of=20any=20listbox?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/keybind.c | 1 + lib/keybind.h | 1 + lib/strutil.h | 30 ++- lib/strutil/strutil.c | 32 +-- lib/strutil/strutil8bit.c | 4 +- lib/strutil/strutilascii.c | 4 +- lib/strutil/strutilutf8.c | 35 ++- lib/widget.h | 2 + lib/widget/Makefile.am | 2 + lib/widget/dialog-switch.c | 3 +- lib/widget/filtering_listbox.c | 429 +++++++++++++++++++++++++++++++++ lib/widget/filtering_listbox.h | 49 ++++ lib/widget/forwarding_input.c | 166 +++++++++++++ lib/widget/forwarding_input.h | 37 +++ lib/widget/group.c | 11 +- lib/widget/history.c | 97 +++++--- lib/widget/history.h | 4 +- lib/widget/input.c | 64 +++-- lib/widget/input.h | 1 + lib/widget/input_complete.c | 138 +++++++---- lib/widget/listbox-window.c | 24 +- lib/widget/listbox-window.h | 4 +- lib/widget/listbox.c | 51 +++- lib/widget/listbox.h | 5 + lib/widget/widget-common.c | 2 + lib/widget/widget-common.h | 10 +- maint/utils/version.sh | 1 + src/editor/choosesyntax.c | 2 +- src/editor/editcmd_dialogs.c | 14 +- src/editor/editwidget.c | 8 +- src/file_history.c | 3 +- src/help.c | 5 +- src/keybind-defaults.c | 5 +- src/selcodepage.c | 2 +- src/usermenu.c | 48 ++-- 35 files changed, 1096 insertions(+), 198 deletions(-) create mode 100644 lib/widget/filtering_listbox.c create mode 100644 lib/widget/filtering_listbox.h create mode 100644 lib/widget/forwarding_input.c create mode 100644 lib/widget/forwarding_input.h diff --git a/lib/keybind.c b/lib/keybind.c index abd44d3e2..df3cbf110 100644
a b static name_keymap_t command_names[] = { 93 93 ADD_KEYMAP_NAME (SearchContinue), 94 94 ADD_KEYMAP_NAME (Replace), 95 95 ADD_KEYMAP_NAME (ReplaceContinue), 96 ADD_KEYMAP_NAME (MultiSearch), 96 97 ADD_KEYMAP_NAME (Help), 97 98 ADD_KEYMAP_NAME (Shell), 98 99 ADD_KEYMAP_NAME (Edit), -
lib/keybind.h
diff --git a/lib/keybind.h b/lib/keybind.h index af019df09..817158412 100644
a b enum 82 82 CK_SearchContinue, 83 83 CK_Replace, 84 84 CK_ReplaceContinue, 85 CK_MultiSearch, 85 86 CK_SearchStop, 86 87 CK_Help, 87 88 CK_Edit, -
lib/strutil.h
diff --git a/lib/strutil.h b/lib/strutil.h index a091c25aa..667f7df2e 100644
a b struct str_class 110 110 gboolean (*is_valid_string) (const char *); 111 111 /*I*/ int (*is_valid_char) (const char *, size_t); 112 112 /*I*/ void (*cnext_char) (const char **); 113 void (*cprev_char) (const char ** );113 void (*cprev_char) (const char **, const char *begin); 114 114 void (*cnext_char_safe) (const char **); 115 /*I*/ void (*cprev_char_safe) (const char ** );115 /*I*/ void (*cprev_char_safe) (const char **, const char *begin); 116 116 /*I*/ int (*cnext_noncomb_char) (const char **text); 117 117 /*I*/ int (*cprev_noncomb_char) (const char **text, const char *begin); 118 118 /*I*/ gboolean (*char_isspace) (const char *); … … int str_is_valid_char (const char *ch, size_t size); 262 262 char *str_get_next_char (char *text); 263 263 const char *str_cget_next_char (const char *text); 264 264 265 /* return previous characters before text , do not call on the start of strings265 /* return previous characters before text 266 266 */ 267 char *str_get_prev_char (char *text );268 const char *str_cget_prev_char (const char *text );267 char *str_get_prev_char (char *text, const char *begin); 268 const char *str_cget_prev_char (const char *text, const char *begin); 269 269 270 270 /* set text to next characters, do not call on the end of string 271 271 */ 272 272 void str_next_char (char **text); 273 273 void str_cnext_char (const char **text); 274 274 275 /* set text to previous characters , do not call on the start of strings275 /* set text to previous characters 276 276 */ 277 void str_prev_char (char **text );278 void str_cprev_char (const char **text );277 void str_prev_char (char **text, const char *begin); 278 void str_cprev_char (const char **text, const char *begin); 279 279 280 280 /* return next characters after text, do not call on the end of string 281 281 * works with invalid string … … void str_cprev_char (const char **text); 284 284 char *str_get_next_char_safe (char *text); 285 285 const char *str_cget_next_char_safe (const char *text); 286 286 287 /* return previous characters before text, do not call on the start of strings 288 * works with invalid string 287 /* return previous characters before text, works with invalid string 289 288 * I 290 289 */ 291 char *str_get_prev_char_safe (char *text );292 const char *str_cget_prev_char_safe (const char *text );290 char *str_get_prev_char_safe (char *text, const char *begin); 291 const char *str_cget_prev_char_safe (const char *text, const char *begin); 293 292 294 293 /* set text to next characters, do not call on the end of string 295 294 * works with invalid string … … const char *str_cget_prev_char_safe (const char *text); 298 297 void str_next_char_safe (char **text); 299 298 void str_cnext_char_safe (const char **text); 300 299 301 /* set text to previous characters, do not call on the start of strings 302 * works with invalid string 300 /* set text to previous characters, works with invalid string 303 301 * I 304 302 */ 305 void str_prev_char_safe (char **text );306 void str_cprev_char_safe (const char **text );303 void str_prev_char_safe (char **text, const char *begin); 304 void str_cprev_char_safe (const char **text, const char *begin); 307 305 308 306 /* set text to next noncombining characters, check the end of text 309 307 * return how many characters was skipped -
lib/strutil/strutil.c
diff --git a/lib/strutil/strutil.c b/lib/strutil/strutil.c index cf11d00d8..937256260 100644
a b str_cnext_char (const char **text) 507 507 /* --------------------------------------------------------------------------------------------- */ 508 508 509 509 char * 510 str_get_prev_char (char *text )510 str_get_prev_char (char *text, const char *begin) 511 511 { 512 used_class.cprev_char ((const char **) &text );512 used_class.cprev_char ((const char **) &text, begin); 513 513 return text; 514 514 } 515 515 516 516 /* --------------------------------------------------------------------------------------------- */ 517 517 518 518 const char * 519 str_cget_prev_char (const char *text )519 str_cget_prev_char (const char *text, const char *begin) 520 520 { 521 used_class.cprev_char (&text );521 used_class.cprev_char (&text, begin); 522 522 return text; 523 523 } 524 524 525 525 /* --------------------------------------------------------------------------------------------- */ 526 526 527 527 void 528 str_prev_char (char **text )528 str_prev_char (char **text, const char *begin) 529 529 { 530 used_class.cprev_char ((const char **) text );530 used_class.cprev_char ((const char **) text, begin); 531 531 } 532 532 533 533 /* --------------------------------------------------------------------------------------------- */ 534 534 535 535 void 536 str_cprev_char (const char **text )536 str_cprev_char (const char **text, const char *begin) 537 537 { 538 used_class.cprev_char (text );538 used_class.cprev_char (text, begin); 539 539 } 540 540 541 541 /* --------------------------------------------------------------------------------------------- */ … … str_cnext_char_safe (const char **text) 575 575 /* --------------------------------------------------------------------------------------------- */ 576 576 577 577 char * 578 str_get_prev_char_safe (char *text )578 str_get_prev_char_safe (char *text, const char *begin) 579 579 { 580 used_class.cprev_char_safe ((const char **) &text );580 used_class.cprev_char_safe ((const char **) &text, begin); 581 581 return text; 582 582 } 583 583 584 584 /* --------------------------------------------------------------------------------------------- */ 585 585 586 586 const char * 587 str_cget_prev_char_safe (const char *text )587 str_cget_prev_char_safe (const char *text, const char *begin) 588 588 { 589 used_class.cprev_char_safe (&text );589 used_class.cprev_char_safe (&text, begin); 590 590 return text; 591 591 } 592 592 593 593 /* --------------------------------------------------------------------------------------------- */ 594 594 595 595 void 596 str_prev_char_safe (char **text )596 str_prev_char_safe (char **text, const char *begin) 597 597 { 598 used_class.cprev_char_safe ((const char **) text );598 used_class.cprev_char_safe ((const char **) text, begin); 599 599 } 600 600 601 601 /* --------------------------------------------------------------------------------------------- */ 602 602 603 603 void 604 str_cprev_char_safe (const char **text )604 str_cprev_char_safe (const char **text, const char *begin) 605 605 { 606 used_class.cprev_char_safe (text );606 used_class.cprev_char_safe (text, begin); 607 607 } 608 608 609 609 /* --------------------------------------------------------------------------------------------- */ -
lib/strutil/strutil8bit.c
diff --git a/lib/strutil/strutil8bit.c b/lib/strutil/strutil8bit.c index fa7bbf6a4..755a66ddb 100644
a b str_8bit_cnext_char (const char **text) 112 112 /* --------------------------------------------------------------------------------------------- */ 113 113 114 114 static void 115 str_8bit_cprev_char (const char **text )115 str_8bit_cprev_char (const char **text, const char *begin) 116 116 { 117 117 (*text)--; 118 if (*text < begin) 119 *text = NULL; 118 120 } 119 121 120 122 /* --------------------------------------------------------------------------------------------- */ -
lib/strutil/strutilascii.c
diff --git a/lib/strutil/strutilascii.c b/lib/strutil/strutilascii.c index 7a585303e..a18d34bd7 100644
a b str_ascii_cnext_char (const char **text) 85 85 /* --------------------------------------------------------------------------------------------- */ 86 86 87 87 static void 88 str_ascii_cprev_char (const char **text )88 str_ascii_cprev_char (const char **text, const char *begin) 89 89 { 90 90 (*text)--; 91 if (*text < begin) 92 *text = NULL; 91 93 } 92 94 93 95 /* --------------------------------------------------------------------------------------------- */ -
lib/strutil/strutilutf8.c
diff --git a/lib/strutil/strutilutf8.c b/lib/strutil/strutilutf8.c index 5ac0015e6..5c76c7d33 100644
a b str_utf8_cnext_char (const char **text) 117 117 /* --------------------------------------------------------------------------------------------- */ 118 118 119 119 static void 120 str_utf8_cprev_char (const char **text )120 str_utf8_cprev_char (const char **text, const char *begin) 121 121 { 122 (*text) = g_utf8_ prev_char (*text);122 (*text) = g_utf8_find_prev_char (begin, *text); 123 123 } 124 124 125 125 /* --------------------------------------------------------------------------------------------- */ … … str_utf8_cnext_char_safe (const char **text) 136 136 /* --------------------------------------------------------------------------------------------- */ 137 137 138 138 static void 139 str_utf8_cprev_char_safe (const char **text )139 str_utf8_cprev_char_safe (const char **text, const char *begin) 140 140 { 141 141 const char *result, *t; 142 142 143 result = g_utf8_prev_char (*text); 143 result = g_utf8_find_prev_char (begin, *text); 144 /* Beginning of the string reached (if NULL)? */ 145 if (result == NULL) 146 { 147 *text = NULL; 148 return; 149 } 150 144 151 t = result; 145 152 str_utf8_cnext_char_safe (&t); 146 153 if (t == *text) 147 154 (*text) = result; 148 else 155 else if (*text > begin) 149 156 (*text)--; 157 else 158 /* Buffer underrun. */ 159 *text = NULL; 150 160 } 151 161 152 162 /* --------------------------------------------------------------------------------------------- */ … … str_utf8_cprev_noncomb_char (const char **text, const char *begin) 262 272 263 273 while ((*text) != begin) 264 274 { 265 str_utf8_cprev_char_safe (text );275 str_utf8_cprev_char_safe (text, begin); 266 276 count++; 267 277 if (!str_utf8_iscombiningmark (*text)) 268 278 break; … … str_utf8_trunc (const char *text, int width) 889 899 } 890 900 891 901 /* --------------------------------------------------------------------------------------------- */ 902 /* Converts a character index into a byte offset. */ 892 903 893 904 static int 894 905 str_utf8_offset_to_pos (const char *text, size_t length) 895 906 { 907 glong size; 908 896 909 if (str_utf8_is_valid_string (text)) 910 { 911 /* Limit index by string size. */ 912 size = g_utf8_strlen (text, -1); 913 if (size < (glong) length) 914 length = (size_t) size; 897 915 return g_utf8_offset_to_pointer (text, length) - text; 916 } 898 917 else 899 918 { 900 919 int result; … … str_utf8_offset_to_pos (const char *text, size_t length) 902 921 903 922 buffer = g_string_new (text); 904 923 str_utf8_fix_string (buffer->str); 924 /* Limit index by string size. */ 925 size = g_utf8_strlen (buffer->str, -1); 926 if (size < (glong) length) 927 length = (size_t) size; 905 928 result = g_utf8_offset_to_pointer (buffer->str, length) - buffer->str; 906 929 g_string_free (buffer, TRUE); 907 930 return result; -
lib/widget.h
diff --git a/lib/widget.h b/lib/widget.h index e3bb5cac2..cfe556891 100644
a b typedef struct WGroup WGroup; 30 30 #include "lib/widget/groupbox.h" 31 31 #include "lib/widget/label.h" 32 32 #include "lib/widget/listbox.h" 33 #include "lib/widget/filtering_listbox.h" 33 34 #include "lib/widget/menu.h" 34 35 #include "lib/widget/radio.h" 35 36 #include "lib/widget/input.h" 37 #include "lib/widget/forwarding_input.h" 36 38 #include "lib/widget/listbox-window.h" 37 39 #include "lib/widget/quick.h" 38 40 #include "lib/widget/wtools.h" -
lib/widget/Makefile.am
diff --git a/lib/widget/Makefile.am b/lib/widget/Makefile.am index 90f023bbc..9a4616c38 100644
a b libmcwidget_la_SOURCES = \ 16 16 history.c history.h \ 17 17 input.c input.h \ 18 18 input_complete.c \ 19 forwarding_input.c forwarding_input.h \ 19 20 listbox-window.c listbox-window.h \ 20 21 listbox.c listbox.h \ 22 filtering_listbox.c filtering_listbox.h \ 21 23 label.c label.h \ 22 24 menu.c menu.h \ 23 25 mouse.c mouse.h \ -
lib/widget/dialog-switch.c
diff --git a/lib/widget/dialog-switch.c b/lib/widget/dialog-switch.c index 93868b19d..a9a51ce0d 100644
a b dialog_switch_list (void) 241 241 else 242 242 title = g_strdup (""); 243 243 244 listbox_add_item (listbox->list, LISTBOX_APPEND_BEFORE, get_hotkey (i++), title, h, FALSE); 244 listbox_add_item (LISTBOX (listbox->list), LISTBOX_APPEND_BEFORE, get_hotkey (i++), title, 245 h, FALSE); 245 246 246 247 g_free (title); 247 248 } -
new file lib/widget/filtering_listbox.c
diff --git a/lib/widget/filtering_listbox.c b/lib/widget/filtering_listbox.c new file mode 100644 index 000000000..37aadc414
- + 1 /* 2 A class extending WListbox with dynamic filtering (i.e.: removal) of entries. 3 4 Copyright (C) <2021> 5 Free Software Foundation, Inc. 6 7 Written by: 8 Sebastian Gniazdowski <sgniazdowski@gmail.com>, 2021. 9 10 This file is part of the Midnight Commander. 11 12 The Midnight Commander is free software: you can redistribute it 13 and/or modify it under the terms of the GNU General Public License as 14 published by the Free Software Foundation, either version 3 of the License, 15 or (at your option) any later version. 16 17 The Midnight Commander is distributed in the hope that it will be useful, 18 but WITHOUT ANY WARRANTY; without even the implied warranty of 19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 GNU General Public License for more details. 21 22 You should have received a copy of the GNU General Public License 23 along with this program. If not, see <http://www.gnu.org/licenses/>. 24 */ 25 26 /** \file filtering_listbox.c 27 * \brief A WListbox inheriting class that adds dynamic filtering of entries. 28 * \author Sebastian Gniazdowski 29 * \date 2021 30 */ 31 32 #include <config.h> 33 34 #include "lib/global.h" 35 #include "lib/widget.h" 36 #include "lib/tty/tty.h" 37 38 /*** global variables ****************************************************************************/ 39 40 /*** file scope macro definitions ****************************************************************/ 41 42 /*** file scope type declarations ****************************************************************/ 43 44 /*** file scope variables ************************************************************************/ 45 46 /*** file scope functions ************************************************************************/ 47 /* --------------------------------------------------------------------------------------------- */ 48 49 static WLEntry * 50 filt_listbox_shallow_copy_entry (WLEntry * src, gboolean take_ownership) 51 { 52 WLEntry *copy; 53 copy = g_new (WLEntry, 1); 54 *copy = *src; 55 56 /* Who has the ownership of the data? */ 57 src->free_text = src->free_text && !take_ownership; 58 src->free_data = src->free_data && !take_ownership; 59 copy->free_text = copy->free_text && take_ownership; 60 copy->free_data = copy->free_data && take_ownership; 61 62 return copy; 63 } 64 65 /* --------------------------------------------------------------------------------------------- */ 66 67 static void 68 filt_listbox_make_one_line_room (WFilteringListbox * sl, int should_add_free_room) 69 { 70 WListbox *l = LISTBOX (sl); 71 Widget *w = WIDGET (l), *owner = WIDGET (WIDGET (w)->owner); 72 WRect r_dialog, r_listbox; 73 int new_dialog_height, new_dialog_ypos, new_listbox_height, take_give_from_to_owner = 1; 74 75 /* IF the enlarged dialog won't fit the screen, don't resize it but the listbox instead. */ 76 if ((sl->resize_strategy == FILT_LIST_DIALOG_AUTO_RESIZE && LINES <= owner->lines + 2) || 77 sl->resize_strategy == FILT_LIST_KEEP_DIALOG_SIZE) 78 take_give_from_to_owner = 0; 79 80 // Increase the height of the dialog by 1, so that the new input fits. 81 if (should_add_free_room) 82 { 83 new_dialog_height = owner->lines + take_give_from_to_owner; 84 new_listbox_height = w->lines + (-1 + take_give_from_to_owner); 85 new_dialog_ypos = owner->y - take_give_from_to_owner; 86 } 87 else 88 { 89 new_dialog_height = owner->lines - take_give_from_to_owner; 90 new_listbox_height = w->lines - (-1 + take_give_from_to_owner); 91 new_dialog_ypos = owner->y + take_give_from_to_owner; 92 } 93 rect_init (&r_dialog, new_dialog_ypos, owner->x, new_dialog_height, owner->cols); 94 rect_init (&r_listbox, w->y, w->x, new_listbox_height, w->cols); 95 send_message (w, NULL, MSG_RESIZE, 0, &r_listbox); 96 send_message (owner, NULL, MSG_RESIZE, 0, &r_dialog); 97 } 98 99 /* --------------------------------------------------------------------------------------------- */ 100 101 static void 102 filt_listbox_show_multi_search_widget (WFilteringListbox * sl) 103 { 104 WListbox *l = LISTBOX (sl); 105 Widget *w = WIDGET (l), *owner = WIDGET (WIDGET (l)->owner); 106 WForwardingInput *multi_search_in; 107 int distance_y = owner->y + owner->lines - (w->y + w->lines) + 1; 108 int distance_x = w->cols > 40 ? 5 : 1, small = w->cols <= 15 ? 1 : 0; 109 110 filt_listbox_make_one_line_room (sl, 1); 111 multi_search_in = forwarding_input_new (owner->lines - distance_y, distance_x, 112 input_colors, w->cols - 2 - distance_x + small, "", 113 "multi_search", INPUT_COMPLETE_NONE, w); 114 group_add_widget_autopos (GROUP (owner), multi_search_in, WPOS_KEEP_TOP | WPOS_CENTER_HORZ, 115 NULL); 116 117 repaint_screen (); 118 widget_draw (w); 119 widget_draw (WIDGET (multi_search_in)); 120 } 121 122 /* --------------------------------------------------------------------------------------------- */ 123 124 static void 125 filt_listbox_hide_multi_search_widget (WFilteringListbox * sl) 126 { 127 WListbox *l = LISTBOX (sl); 128 Widget *in; 129 in = WIDGET (WIDGET (l)->owner)->find_by_type (WIDGET (WIDGET (l)->owner), forw_input_callback); 130 if (in) 131 { 132 group_remove_widget (in); 133 filt_listbox_make_one_line_room (sl, 0); 134 group_select_next_widget (WIDGET (l)->owner); 135 widget_draw (WIDGET (l)); 136 widget_destroy (in); 137 } 138 repaint_screen (); 139 } 140 141 /* --------------------------------------------------------------------------------------------- */ 142 143 /* Return TRUE if given listbox is in WST_FILTER state. */ 144 static gboolean 145 filt_listbox_is_filter_state (WFilteringListbox * sl) 146 { 147 return ((WIDGET (sl)->state & WST_FILTER) != 0); 148 } 149 150 /* --------------------------------------------------------------------------------------------- */ 151 152 static void 153 filt_listbox_filter_list (WFilteringListbox * sl, const char *text) 154 { 155 WListbox *l = LISTBOX (sl); 156 int i, size; 157 GList *le; 158 g_auto (GStrv) query_terms = NULL; 159 160 /* Remove the list and allocate a new one. */ 161 if (l->list) 162 g_queue_free_full (l->list, g_free); 163 l->list = g_queue_new (); 164 165 /* Split the query into space delimeted strings. */ 166 query_terms = g_strsplit (text, " ", 10); 167 168 /* 169 * Get the size of the listbox and iterate over it testing each element against ·all· words in 170 * query_terms. 171 */ 172 size = g_queue_get_length (sl->list_keep); 173 le = g_queue_peek_head_link (sl->list_keep); 174 for (i = 0; i < size; i++, le = g_list_next (le)) 175 { 176 WLEntry *e = LENTRY (le->data); 177 gboolean match = TRUE; 178 179 /* Test the query against the list entry. */ 180 for (gchar ** p = query_terms; *p; p++) 181 { 182 if (**p && !strcasestr (e->text, *p)) 183 { 184 match = FALSE; 185 break; 186 } 187 } 188 189 /* If all the terms matched, then add the element to the list. */ 190 if (match) 191 g_queue_push_tail (l->list, filt_listbox_shallow_copy_entry (e, FALSE)); 192 } 193 if (listbox_is_empty (l)) 194 { 195 listbox_add_item (l, LISTBOX_APPEND_AT_END, 0, "<no search results>", NULL, FALSE); 196 LENTRY (g_queue_peek_head_link (l->list)->data)->index = -2; 197 } 198 size = g_queue_get_length (l->list); 199 if (l->pos >= size) 200 listbox_select_entry (l, size - 1); 201 else 202 listbox_select_entry (l, l->pos); 203 } 204 205 /* --------------------------------------------------------------------------------------------- */ 206 207 /* Restores original elements of the list (from sl->list_keep) and turns off the WST_FILTER state. */ 208 static gboolean 209 filt_listbox_set_to_normal_state (WFilteringListbox * sl) 210 { 211 WListbox *l = LISTBOX (sl); 212 /* The listbox is already in non-filter state? */ 213 if ((WIDGET (l)->state & WST_FILTER) == 0) 214 { 215 /* Return doing no change, just signal the error. */ 216 return FALSE; 217 } 218 219 /* The keep-list must be allocated (even if it's empty). */ 220 g_assert (sl->list_keep); 221 222 /* Mark the new state. */ 223 WIDGET (l)->set_state (WIDGET (l), WST_FILTER, 0); 224 225 /* Release the filtered list and replace it with the original, complete list. */ 226 g_queue_free_full (l->list, g_free); 227 l->list = sl->list_keep; 228 sl->list_keep = NULL; 229 return TRUE; 230 } 231 232 /* --------------------------------------------------------------------------------------------- */ 233 234 /* 235 * Sets the listbox into ·filter· state. In this state, there's a separate copy of all list 236 * entries, while the original (and displayed) field ->list is being a filtered version of the 237 * full copy. 238 */ 239 static gboolean 240 filt_listbox_set_to_filter_state (WFilteringListbox * sl) 241 { 242 WListbox *l = LISTBOX (sl); 243 GList *le; 244 245 /* The listbox is already in filter state? */ 246 if (WIDGET (l)->state & WST_FILTER) 247 { 248 /* Return doing no change, just signal the error. */ 249 return FALSE; 250 } 251 252 /* Mark the new state. */ 253 WIDGET (l)->set_state (WIDGET (l), WST_FILTER, 1); 254 255 /* No list copy when entering filter mode. */ 256 g_assert (sl->list_keep == NULL); 257 sl->list_keep = g_queue_new (); 258 259 /* Skip empty lists. */ 260 if (listbox_is_empty (l)) 261 return TRUE; 262 263 /* 264 * Remember the original position in the list in the field. It'll be used to determine the 265 * virtual_pos field. 266 */ 267 listbox_init_indices (l); 268 269 /* Perform a shallow copy of the original list. */ 270 for (le = g_queue_peek_head_link (l->list); le != NULL; le = g_list_next (le)) 271 { 272 WLEntry *copy = filt_listbox_shallow_copy_entry (LENTRY (le->data), TRUE); 273 g_queue_push_tail (sl->list_keep, copy); 274 } 275 276 return TRUE; 277 } 278 279 /* --------------------------------------------------------------------------------------------- */ 280 281 /* 282 * If the list is filtered it replaces from the back list (list_keep). It returns whether such 283 * change occurred – FALSE means that the list was already unfiltered. 284 */ 285 gboolean 286 filt_listbox_ensure_unfiltered_state (WFilteringListbox * sl) 287 { 288 gboolean ret = FALSE; 289 if (filt_listbox_is_filter_state (sl)) 290 ret = filt_listbox_set_to_normal_state (sl); 291 return ret; 292 } 293 294 /* --------------------------------------------------------------------------------------------- */ 295 296 gboolean 297 filt_listbox_conditionally_enable_multi_search_init (WFilteringListbox * sl) 298 { 299 int start_with_multi_search_active; 300 301 /* Option of starting the listbox with MultiSearch pre-activated. */ 302 start_with_multi_search_active = 303 mc_config_get_bool (mc_global.main_config, CONFIG_APP_SECTION, 304 "multi_search_active_by_default", 1); 305 306 /* CK_MultiSearch toggles the state. */ 307 if (start_with_multi_search_active) 308 send_message (WIDGET (sl), NULL, MSG_ACTION, CK_MultiSearch, NULL); 309 else 310 /* Only init embedded position indices. */ 311 listbox_init_indices (LISTBOX (sl)); 312 313 return start_with_multi_search_active; 314 } 315 316 /* --------------------------------------------------------------------------------------------- */ 317 /*** public functions ****************************************************************************/ 318 /* --------------------------------------------------------------------------------------------- */ 319 320 WFilteringListbox * 321 filtering_listbox_new (int y, int x, int height, int width, 322 gboolean deletable, lcback_fn callback, 323 filt_listbox_resize_strategy_t resize) 324 { 325 WListbox *base; 326 WFilteringListbox *object; 327 Widget *w; 328 329 /* Allocate memory for the object body. */ 330 object = g_new (WFilteringListbox, 1); 331 332 /* Forward the call to construct the inherited object. */ 333 base = listbox_new (y, x, height, width, deletable, callback); 334 335 /* Shallow copy and shallow release. */ 336 object->base = *base; 337 g_free (base); 338 339 /* Alter fields of base class. */ 340 w = WIDGET (object); 341 object->base_callback = w->callback; /* Save original callback function */ 342 w->callback = filt_listbox_callback; /* Set custom callback handler */ 343 344 /* Set extending fields of this class. */ 345 object->list_keep = NULL; /* No back buffer at startup */ 346 object->resize_strategy = resize; /* Save resize strategy */ 347 object->initialized = FALSE; 348 349 return object; 350 } 351 352 /* --------------------------------------------------------------------------------------------- */ 353 354 cb_ret_t 355 filt_listbox_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data) 356 { 357 WFilteringListbox *sl = FILT_LISTBOX (w); /* s* – from `screen`, a "screened" listbox */ 358 cb_ret_t ret = MSG_NOT_HANDLED; 359 long activity; 360 361 switch (msg) 362 { 363 case MSG_INIT: 364 if (sl->initialized == FALSE) 365 { 366 filt_listbox_conditionally_enable_multi_search_init (sl); 367 sl->initialized = TRUE; 368 } 369 /* WListbox doesn't have MSG_INIT, so don't forward. */ 370 ret = MSG_HANDLED; 371 break; 372 case MSG_ACTION: 373 if (parm == CK_MultiSearch) 374 { 375 gboolean retval; 376 /* Toggle the multi term searching of any listbox. */ 377 if (filt_listbox_is_filter_state (sl)) 378 { 379 /* Remove the input widget from the dialog. */ 380 filt_listbox_hide_multi_search_widget (sl); 381 /* Restore original (unfiltered) listbox contents. */ 382 retval = filt_listbox_set_to_normal_state (sl); 383 } 384 else 385 { 386 /* Add input widget for the filter query at the bottom of the dialog window. */ 387 filt_listbox_show_multi_search_widget (sl); 388 /* … and then turn on the filter state. */ 389 retval = filt_listbox_set_to_filter_state (sl); 390 } 391 if (!retval) 392 message (D_ERROR | D_CENTER, MSG_ERROR, 393 "An internal error #3 occurred (filtered listbox support)."); 394 395 ret = MSG_HANDLED; 396 } 397 break; 398 399 case MSG_KEY: 400 activity = widget_lookup_key (WIDGET (sl), parm); 401 if (activity == CK_MultiSearch) 402 ret = send_message (w, NULL, MSG_ACTION, CK_MultiSearch, NULL); 403 break; 404 405 case MSG_UPDATE_LIST: 406 if (widget_get_state (w, WST_FILTER)) 407 { 408 filt_listbox_filter_list (sl, (char *) data); 409 ret = MSG_HANDLED; 410 } 411 widget_draw (w); 412 break; 413 414 case MSG_DESTROY: 415 filt_listbox_ensure_unfiltered_state (sl); 416 /* ret is unhandled → the message will be forwarded to base class. */ 417 break; 418 default: 419 break; 420 } 421 422 /* Forward action to base class in case it's not yet handled. */ 423 if (ret == MSG_NOT_HANDLED) 424 ret = sl->base_callback (w, sender, msg, parm, data); 425 426 return ret; 427 } 428 429 /* --------------------------------------------------------------------------------------------- */ -
new file lib/widget/filtering_listbox.h
diff --git a/lib/widget/filtering_listbox.h b/lib/widget/filtering_listbox.h new file mode 100644 index 000000000..f51f1ec56
- + 1 #ifndef MC__FILTERING_LISTBOX_H 2 #define MC__FILTERING_LISTBOX_H 3 4 #include <glib-2.0/glib.h> /* GQueue */ 5 6 /*** typedefs(not structures) and defined constants **********************************************/ 7 8 /* Casting macros. */ 9 #define FILT_LISTBOX(x) ((WFilteringListbox *)(x)) 10 #define CONST_FILT_LISTBOX(x) ((const WFilteringListbox *)(x)) 11 12 /*** enums ***************************************************************************************/ 13 14 typedef enum filt_listbox_resize_strategy_e 15 { 16 FILT_LIST_EXTEND_DIALOG = 0, 17 FILT_LIST_KEEP_DIALOG_SIZE, 18 FILT_LIST_DIALOG_AUTO_RESIZE 19 } filt_listbox_resize_strategy_t; 20 21 /*** structures declarations (and typedefs of structures)*****************************************/ 22 23 typedef struct WFilteringListbox_s 24 { 25 WListbox base; 26 widget_cb_fn base_callback; /* Saved callback of base class */ 27 gboolean initialized; /* Whether MSG_INIT has been received. */ 28 29 /* Fields for new logic. */ 30 GQueue *list_keep; /* Unfiltered list (used in WST_FILTER state). */ 31 filt_listbox_resize_strategy_t resize_strategy; 32 } WFilteringListbox; 33 34 /*** global variables defined in .c file *********************************************************/ 35 36 /*** declarations of public functions ************************************************************/ 37 38 WFilteringListbox *filtering_listbox_new (int y, int x, int height, int width, 39 gboolean deletable, lcback_fn callback, 40 filt_listbox_resize_strategy_t resize); 41 gboolean filt_listbox_ensure_unfiltered_state (WFilteringListbox * l); 42 gboolean filt_listbox_conditionally_enable_multi_search_init (WFilteringListbox * l); 43 void filt_listbox_select_entry (WFilteringListbox * sl, int dest); 44 cb_ret_t filt_listbox_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, 45 void *data); 46 47 /*** inline functions ****************************************************************************/ 48 49 #endif /* MC__FILTERING_LISTBOX_H */ -
new file lib/widget/forwarding_input.c
diff --git a/lib/widget/forwarding_input.c b/lib/widget/forwarding_input.c new file mode 100644 index 000000000..be401227f
- + 1 /* 2 A key forwarding extended input class. 3 4 Copyright (C) 2021 5 Free Software Foundation, Inc. 6 7 Written by: 8 Sebastian Gniazdowski <sgniazdowski@gmail.com>, 2021. 9 10 This file is part of the Midnight Commander. 11 12 The Midnight Commander is free software: you can redistribute it 13 and/or modify it under the terms of the GNU General Public License as 14 published by the Free Software Foundation, either version 3 of the License, 15 or (at your option) any later version. 16 17 The Midnight Commander is distributed in the hope that it will be useful, 18 but WITHOUT ANY WARRANTY; without even the implied warranty of 19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 GNU General Public License for more details. 21 22 You should have received a copy of the GNU General Public License 23 along with this program. If not, see <http://www.gnu.org/licenses/>. 24 */ 25 26 /** \file forwarding-input.c 27 * \brief This input class has a feature of forwarding unrecognized keys to a widget passed at 28 * creation. 29 * \author Sebastian Gniazdowski 30 * \date 2021 31 * 32 * It's being used by MultiSearch to allow moving across listbox while typing to the input. 33 */ 34 35 #include <config.h> 36 37 #include "lib/global.h" 38 #include "lib/widget.h" 39 40 /*** global variables ****************************************************************************/ 41 42 /*** file scope macro definitions ****************************************************************/ 43 44 /*** file scope type declarations ****************************************************************/ 45 46 /*** file scope variables ************************************************************************/ 47 48 /*** file scope functions ************************************************************************/ 49 /* --------------------------------------------------------------------------------------------- */ 50 51 /* --------------------------------------------------------------------------------------------- */ 52 /*** public functions ****************************************************************************/ 53 /* --------------------------------------------------------------------------------------------- */ 54 55 WForwardingInput * 56 forwarding_input_new (int y, int x, const int *colors, 57 int len, const char *text, const char *histname, 58 input_complete_t completion_flags, Widget * forward_to_widget) 59 { 60 WInput *base; 61 WForwardingInput *object; 62 Widget *w; 63 64 /* Allocate memory for the object body. */ 65 object = g_new (WForwardingInput, 1); 66 67 /* Forward the call to construct the inherited object. */ 68 base = input_new (y, x, colors, len, text, histname, completion_flags); 69 70 /* Shallow copy and shallow release. */ 71 object->base = *base; 72 g_free (base); 73 74 /* Alter fields of base class. */ 75 w = WIDGET (object); 76 object->base_callback = w->callback; /* Save original callback function */ 77 w->callback = forw_input_callback; /* Set custom callback handler */ 78 79 /* Set extending fields of this class. */ 80 object->forward_to_widget = forward_to_widget; 81 return object; 82 } 83 84 /* --------------------------------------------------------------------------------------------- */ 85 86 cb_ret_t 87 forw_input_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data) 88 { 89 WForwardingInput *in = FORW_INPUT (w); 90 gboolean key_message = FALSE; 91 cb_ret_t ret; 92 93 switch (msg) 94 { 95 case MSG_KEY: 96 ret = forw_input_handle_char (in, parm); 97 key_message = TRUE; 98 break; 99 default: 100 break; 101 } 102 103 /* 104 * Simply pass on all messages to the base class (except for MSG_KEY, which might have 105 * been possibly already sent from forw_input_handle_char() function). 106 */ 107 108 if (!key_message) 109 ret = in->base_callback (WIDGET (in), sender, msg, parm, data); 110 111 return ret; 112 } 113 114 /* --------------------------------------------------------------------------------------------- */ 115 116 cb_ret_t 117 forw_input_handle_char (WForwardingInput * in, int key) 118 { 119 cb_ret_t ret = MSG_NOT_HANDLED; 120 gboolean sent_to_base = FALSE; 121 long activity; 122 char *str_cp; 123 124 /* Save to detect if a change happened. */ 125 str_cp = g_strdup (in->base.buffer); 126 127 /* Is this key recognized by the base object? */ 128 activity = widget_lookup_key (WIDGET (in), key); 129 if (activity != CK_IgnoreKey && activity != CK_Complete) 130 { 131 /* Yes → send the key to the upper class. */ 132 ret = in->base_callback (WIDGET (in), WIDGET (in), MSG_KEY, key, NULL); 133 sent_to_base = TRUE; 134 } 135 /* Should we try to forward the key to any paired widget? */ 136 if (in->forward_to_widget && ret == MSG_NOT_HANDLED) 137 { 138 /* Is it maybe recognized by forward_to_widget paired object? */ 139 activity = widget_lookup_key (WIDGET (in->forward_to_widget), key); 140 if (activity != CK_IgnoreKey) 141 { 142 /* Yes → forward the key to the paired widget (most probably WListbox). */ 143 ret = send_message (WIDGET (in->forward_to_widget), NULL, MSG_KEY, key, NULL); 144 } 145 } 146 147 /* 148 * If not handled yet, then send the key to the base object for general recognition (if 149 * not already done that). 150 */ 151 152 if (!sent_to_base && ret == MSG_NOT_HANDLED) 153 { 154 ret = in->base_callback (WIDGET (in), WIDGET (in), MSG_KEY, key, NULL); 155 sent_to_base = TRUE; /* unused */ 156 } 157 158 /* Send update signal to paired widget ·if· input's text has changed. */ 159 if (in->forward_to_widget && g_strcmp0 (str_cp, in->base.buffer)) 160 send_message (WIDGET (in->forward_to_widget), NULL, MSG_UPDATE_LIST, key, in->base.buffer); 161 g_free (str_cp); 162 163 return ret; 164 } 165 166 /* --------------------------------------------------------------------------------------------- */ -
new file lib/widget/forwarding_input.h
diff --git a/lib/widget/forwarding_input.h b/lib/widget/forwarding_input.h new file mode 100644 index 000000000..5b4e5961a
- + 1 #ifndef MC__FORWARDING_INPUT_H 2 #define MC__FORWARDING_INPUT_H 3 4 /*** typedefs(not structures) and defined constants **********************************************/ 5 6 /* Casting macros. */ 7 #define FORW_INPUT(x) ((WForwardingInput *)(x)) 8 #define CONST_FORW_INPUT(x) ((const WForwardingInput *)(x)) 9 10 typedef struct 11 { 12 WInput base; 13 widget_cb_fn base_callback; /* Saved callback of base class */ 14 15 /* Fields for new logic. */ 16 Widget *forward_to_widget; /* The paired widget to receive unhandled keys */ 17 } WForwardingInput; 18 19 /*** enums ***************************************************************************************/ 20 21 /*** structures declarations (and typedefs of structures)*****************************************/ 22 23 /*** global variables defined in .c file *********************************************************/ 24 25 /*** declarations of public functions ************************************************************/ 26 27 WForwardingInput *forwarding_input_new (int y, int x, const int *colors, 28 int len, const char *text, const char *histname, 29 input_complete_t completion_flags, 30 Widget * forward_to_widget); 31 32 cb_ret_t forw_input_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data); 33 cb_ret_t forw_input_handle_char (WForwardingInput * in, int key); 34 35 /*** inline functions ****************************************************************************/ 36 37 #endif /* MC__FORWARDING_INPUT_H */ -
lib/widget/group.c
diff --git a/lib/widget/group.c b/lib/widget/group.c index f8e318bd9..238f7ab1b 100644
a b group_add_widget_autopos (WGroup * g, void *w, widget_pos_flags_t pos_flags, con 749 749 * @param w Widget object 750 750 */ 751 751 void 752 group_remove_widget (void *w )752 group_remove_widget (void *wid) 753 753 { 754 Widget *w = WIDGET (wid); 754 755 WGroup *g; 755 756 GList *d; 756 757 757 758 /* Don't accept NULL widget. This shouldn't happen */ 758 759 assert (w != NULL); 759 760 760 g = WIDGET (w)->owner; 761 /* Invoke widget's pre unlink callback. */ 762 if (w->pre_unlink_func) 763 w->pre_unlink_func (w); 764 765 g = w->owner; 761 766 762 767 d = g_list_find (g->widgets, w); 763 768 if (d == g->current) … … group_remove_widget (void *w) 774 779 group_select_current_widget (g); 775 780 } 776 781 777 WIDGET (w)->owner = NULL;782 w->owner = NULL; 778 783 } 779 784 780 785 /* --------------------------------------------------------------------------------------------- */ -
lib/widget/history.c
diff --git a/lib/widget/history.c b/lib/widget/history.c index 775d02b1b..dfc6f4275 100644
a b typedef struct 66 66 /*** file scope functions ************************************************************************/ 67 67 68 68 static cb_ret_t 69 history_dlg_reposition (WDialog * dlg_head )69 history_dlg_reposition (WDialog * dlg_head, WRect * resize) 70 70 { 71 history_dlg_data *data;72 int x = 0, y, he, wi;73 71 WRect r; 74 72 75 /* guard checks */ 76 if ((dlg_head == NULL) || (dlg_head->data == NULL)) 77 return MSG_NOT_HANDLED; 78 79 data = (history_dlg_data *) dlg_head->data; 80 81 y = data->y; 82 he = data->count + 2; 83 84 if (he <= y || y > (LINES - 6)) 73 if (resize == NULL) 85 74 { 86 he = MIN (he, y - 1); 87 y -= he; 75 history_dlg_data *data; 76 int x = 0, y, he, wi; 77 78 /* guard checks */ 79 if ((dlg_head == NULL) || (dlg_head->data == NULL)) 80 return MSG_NOT_HANDLED; 81 82 data = (history_dlg_data *) dlg_head->data; 83 84 y = data->y; 85 he = data->count + 2; 86 87 if (he <= y || y > (LINES - 6)) 88 { 89 he = MIN (he, y - 1); 90 y -= he; 91 } 92 else 93 { 94 y++; 95 he = MIN (he, LINES - y); 96 } 97 98 if (data->x > 2) 99 x = data->x - 2; 100 101 wi = data->max_width + 4; 102 103 if ((wi + x) > COLS) 104 { 105 wi = MIN (wi, COLS); 106 x = COLS - wi; 107 } 108 rect_init (&r, y, x, he, wi); 88 109 } 89 110 else 90 111 { 91 y++;92 he = MIN (he, LINES - y);112 /* A resize from some other code (currently from the listbox filter). */ 113 r = *resize; 93 114 } 94 115 95 if (data->x > 2)96 x = data->x - 2;97 98 wi = data->max_width + 4;99 100 if ((wi + x) > COLS)101 {102 wi = MIN (wi, COLS);103 x = COLS - wi;104 }105 106 rect_init (&r, y, x, he, wi);107 116 108 117 return dlg_default_callback (WIDGET (dlg_head), NULL, MSG_RESIZE, 0, &r); 109 118 } … … history_dlg_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, v 116 125 switch (msg) 117 126 { 118 127 case MSG_RESIZE: 119 return history_dlg_reposition (DIALOG (w) );128 return history_dlg_reposition (DIALOG (w), data); 120 129 121 130 case MSG_NOTIFY: 122 131 { … … history_create_item (history_descriptor_t * hd, void *data) 158 167 width = str_term_width1 (text); 159 168 hd->max_width = MAX (width, hd->max_width); 160 169 161 listbox_add_item ( hd->listbox, LISTBOX_APPEND_AT_END, 0, text, NULL, TRUE);170 listbox_add_item (LISTBOX (hd->listbox), LISTBOX_APPEND_AT_END, 0, text, NULL, TRUE); 162 171 } 163 172 164 173 /* --------------------------------------------------------------------------------------------- */ … … history_descriptor_init (history_descriptor_t * hd, int y, int x, GList * histor 190 199 hd->action = CK_IgnoreKey; 191 200 hd->text = NULL; 192 201 hd->max_width = 0; 193 hd->listbox = listbox_new (1, 1, 2, 2, TRUE, NULL);202 hd->listbox = filtering_listbox_new (1, 1, 2, 2, TRUE, NULL, FILT_LIST_KEEP_DIALOG_SIZE); 194 203 /* in most cases history list contains string only and no any other data */ 195 204 hd->create = history_create_item; 196 205 hd->release = history_release_item; … … history_show (history_descriptor_t * hd) 205 214 GList *z, *hi; 206 215 size_t count; 207 216 WDialog *query_dlg; 217 WListbox *lw; 208 218 history_dlg_data hist_data; 209 219 int dlg_ret; 210 220 … … history_show (history_descriptor_t * hd) 217 227 hd->create (hd, z->data); 218 228 /* after this, the order of history items is following: recent at begin, oldest at end */ 219 229 220 count = listbox_get_length (hd->listbox); 230 /* Get the WListbox pointer for convenience. */ 231 lw = LISTBOX (hd->listbox); 232 count = listbox_get_length (lw); 221 233 222 234 hist_data.y = hd->y; 223 235 hist_data.x = hd->x; … … history_show (history_descriptor_t * hd) 244 256 { 245 257 /* history is above base widget -- revert order to place recent item at bottom */ 246 258 /* revert history direction */ 247 g_queue_reverse ( hd->listbox->list);259 g_queue_reverse (lw->list); 248 260 if (hd->current < 0 || (size_t) hd->current >= count) 249 listbox_select_last ( hd->listbox);261 listbox_select_last (lw); 250 262 else 251 listbox_select_entry ( hd->listbox, count - 1 - (size_t) hd->current);263 listbox_select_entry (lw, count - 1 - (size_t) hd->current); 252 264 } 253 265 else 254 266 { 255 267 /* history is below base widget -- keep order to place recent item on top */ 256 268 if (hd->current > 0) 257 listbox_select_entry ( hd->listbox, hd->current);269 listbox_select_entry (lw, hd->current); 258 270 } 259 271 260 272 dlg_ret = dlg_run (query_dlg); … … history_show (history_descriptor_t * hd) 274 286 hd->action = CK_Enter; 275 287 } 276 288 277 listbox_get_current (hd->listbox, &q, NULL); 278 hd->text = g_strdup (q); 289 listbox_get_current (lw, &q, NULL); 290 /* Can still be 0 if special entry "<no search results>" will be selected. */ 291 if (q) 292 hd->text = g_strdup (q); 279 293 } 280 294 295 /* If needed, restore normal listbox state, with no backlist (list_keep). */ 296 filt_listbox_ensure_unfiltered_state (hd->listbox); 297 281 298 /* get modified history from dialog */ 282 299 z = NULL; 283 for (hi = listbox_get_first_link ( hd->listbox); hi != NULL; hi = g_list_next (hi))300 for (hi = listbox_get_first_link (lw); hi != NULL; hi = g_list_next (hi)) 284 301 /* history is being reverted here again */ 285 302 z = g_list_prepend (z, hd->release (hd, LENTRY (hi->data))); 286 303 -
lib/widget/history.h
diff --git a/lib/widget/history.h b/lib/widget/history.h index 9c4b403d1..23f4d403d 100644
a b 11 11 /* forward declarations */ 12 12 struct history_descriptor_t; 13 13 struct WLEntry; 14 struct WListbox;14 typedef struct WFilteringListbox_s WFilteringListbox; 15 15 16 16 typedef void (*history_create_item_func) (struct history_descriptor_t * hd, void *data); 17 17 typedef void *(*history_release_item_func) (struct history_descriptor_t * hd, struct WLEntry * le); … … typedef struct history_descriptor_t 30 30 char *text; /**< return text of selected item */ 31 31 32 32 size_t max_width; /**< maximum width of sring in history */ 33 struct WListbox *listbox; /**< listbox widget to draw history */33 WFilteringListbox *listbox; /**< listbox widget to draw history */ 34 34 35 35 history_create_item_func create; /**< function to create item of @list */ 36 36 history_release_item_func release; /**< function to release item of @list */ -
lib/widget/input.c
diff --git a/lib/widget/input.c b/lib/widget/input.c index 471715c25..999c17559 100644
a b backward_word (WInput * in) 425 425 { 426 426 const char *p; 427 427 428 /* p is limited by strlen. */ 428 429 p = in->buffer + str_offset_to_pos (in->buffer, in->point); 429 430 430 while (p != in->buffer)431 while (p != NULL && p != in->buffer) 431 432 { 432 433 const char *p_tmp; 433 434 434 435 p_tmp = p; 435 str_cprev_char (&p );436 if ( !str_isspace (p) && !str_ispunct (p))436 str_cprev_char (&p, in->buffer); 437 if (p != NULL) 437 438 { 438 p = p_tmp; 439 break; 439 /* 440 * If preceding char isn't a word boundary (↔ we're ·in· a word), then undo last move and 441 * break from loop. 442 */ 443 444 if (!str_isspace (p) && !str_ispunct (p)) 445 { 446 p = p_tmp; 447 break; 448 } 449 in->point--; 440 450 } 441 in->point--;442 451 } 443 while (p != in->buffer) 452 453 /* Further skip the ·word· chars, if not outside the string. */ 454 while (p != NULL && p != in->buffer) 444 455 { 445 str_cprev_char (&p); 446 if (str_isspace (p) || str_ispunct (p)) 456 str_cprev_char (&p, in->buffer); 457 /* Stop when a word boundary char is detected or when the string ends. */ 458 if (p == NULL || str_isspace (p) || str_ispunct (p)) 447 459 break; 448 460 449 461 in->point--; … … input_save_history (const gchar * event_group_name, const gchar * event_name, 871 883 (void) event_group_name; 872 884 (void) event_name; 873 885 874 if (!in->is_password && (DIALOG (WIDGET (in)->owner)->ret_value != B_CANCEL)) 886 if (!in->is_password && WIDGET (in)->owner 887 && (DIALOG (WIDGET (in)->owner)->ret_value != B_CANCEL)) 875 888 { 876 889 ev_history_load_save_t *ev = (ev_history_load_save_t *) data; 877 890 … … input_mouse_callback (Widget * w, mouse_msg_t msg, mouse_event_t * event) 970 983 } 971 984 } 972 985 986 /* --------------------------------------------------------------------------------------------- */ 987 988 static void 989 listbox_unregister_history_events_cb (gpointer wid_ptr) 990 { 991 Widget *w = WIDGET (wid_ptr); 992 WDialog *h = DIALOG (w->owner); 993 994 /* unsubscribe from "history_load" event */ 995 mc_event_del (h->event_group, MCEVENT_HISTORY_LOAD, input_load_history, w); 996 /* unsubscribe from "history_save" event */ 997 mc_event_del (h->event_group, MCEVENT_HISTORY_SAVE, input_save_history, w); 998 } 999 973 1000 /* --------------------------------------------------------------------------------------------- */ 974 1001 /*** public functions ****************************************************************************/ 975 1002 /* --------------------------------------------------------------------------------------------- */ … … input_new (int y, int x, const int *colors, int width, const char *def_text, 1028 1055 if ((histname != NULL) && (*histname != '\0')) 1029 1056 in->history.name = g_strdup (histname); 1030 1057 /* history will be loaded later */ 1031 1032 1058 in->label = NULL; 1033 1059 1034 1060 return in; … … input_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *d 1050 1076 mc_event_add (h->event_group, MCEVENT_HISTORY_LOAD, input_load_history, w, NULL); 1051 1077 /* subscribe to "history_save" event */ 1052 1078 mc_event_add (h->event_group, MCEVENT_HISTORY_SAVE, input_save_history, w, NULL); 1079 /* unregister (via the func) the events in case of removal from dialog */ 1080 w->pre_unlink_func = listbox_unregister_history_events_cb; 1053 1081 if (in->label != NULL) 1054 1082 widget_set_state (WIDGET (in->label), WST_DISABLED, widget_get_state (w, WST_DISABLED)); 1055 1083 return MSG_HANDLED; … … input_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *d 1063 1091 return v; 1064 1092 } 1065 1093 1066 /* Keys we want others to handle */1094 /* Keys we want others to handle. */ 1067 1095 if (parm == KEY_UP || parm == KEY_DOWN || parm == ESC_CHAR 1068 1096 || parm == KEY_F (10) || parm == '\n') 1069 1097 return MSG_NOT_HANDLED; … … input_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *d 1097 1125 return MSG_HANDLED; 1098 1126 1099 1127 case MSG_DESTROY: 1100 /* unsubscribe from "history_load" event */ 1101 mc_event_del (h->event_group, MCEVENT_HISTORY_LOAD, input_load_history, w); 1102 /* unsubscribe from "history_save" event */ 1103 mc_event_del (h->event_group, MCEVENT_HISTORY_SAVE, input_save_history, w); 1128 /* …only, if there is an owner WGroup. */ 1129 if (h) 1130 { 1131 /* unsubscribe from "history_load" event */ 1132 mc_event_del (h->event_group, MCEVENT_HISTORY_LOAD, input_load_history, w); 1133 /* unsubscribe from "history_save" event */ 1134 mc_event_del (h->event_group, MCEVENT_HISTORY_SAVE, input_save_history, w); 1135 } 1104 1136 input_destroy (in); 1105 1137 return MSG_HANDLED; 1106 1138 -
lib/widget/input.h
diff --git a/lib/widget/input.h b/lib/widget/input.h index a753e6160..aca26d43d 100644
a b typedef struct 70 70 GList *current; /* current history item */ 71 71 gboolean changed; /* the history has changed */ 72 72 } history; 73 73 74 } WInput; 74 75 75 76 /*** global variables defined in .c file *********************************************************/ -
lib/widget/input_complete.c
diff --git a/lib/widget/input_complete.c b/lib/widget/input_complete.c index c23fe6063..53772a66a 100644
a b try_complete_commands_prepare (try_complete_automation_state_t * state, char *te 843 843 ti = text; 844 844 else 845 845 { 846 ti = str_get_prev_char (&text[*lc_start] );846 ti = str_get_prev_char (&text[*lc_start], text); 847 847 while (ti > text && whitespace (ti[0])) 848 str_prev_char (&ti );848 str_prev_char (&ti, text); 849 849 } 850 850 851 if (ti == text) 852 state->in_command_position++; 853 else if (strchr (command_separator_chars, ti[0]) != NULL) 851 if (!ti) 854 852 { 855 853 state->in_command_position++; 856 if (ti != text) 857 { 858 int this_char, prev_char; 854 ti = text; 855 } 859 856 860 /* Handle the two character tokens '>&', '<&', and '>|'. 861 We are not in a command position after one of these. */ 862 this_char = ti[0]; 863 prev_char = str_get_prev_char (ti)[0]; 857 /* Is there any more preceding text, and there's a command separator? */ 858 if (ti != text && strchr (command_separator_chars, ti[0]) != NULL) 859 { 860 int this_char, prev_char = '\0'; 861 char *prev_char_p; 862 state->in_command_position++; 864 863 865 /* Quoted */ 866 if ((this_char == '&' && (prev_char == '<' || prev_char == '>')) 867 || (this_char == '|' && prev_char == '>') || (ti != text 868 && str_get_prev_char (ti)[0] == '\\')) 869 state->in_command_position = 0; 870 } 864 /* Initial assumption that we're at command. */ 865 state->in_command_position++; 866 867 /* Handle the two character tokens '>&', '<&', and '>|'. 868 We are not in a command position after one of these. */ 869 this_char = ti[0]; 870 prev_char_p = str_get_prev_char (ti, text); 871 if (prev_char_p != NULL) 872 prev_char = prev_char_p[0]; 873 874 if ((this_char == '&' && (strchr ("<>", prev_char))) 875 || (this_char == '|' && prev_char == '>') || prev_char == '\\') 876 state->in_command_position = 0; 871 877 } 872 878 } 873 879 … … complete_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void 1017 1023 { 1018 1024 static int bl = 0; 1019 1025 1020 WGroup *g = GROUP (w);1021 1026 WDialog *h = DIALOG (w); 1027 WFilteringListbox *slw; 1028 cb_ret_t ret = MSG_NOT_HANDLED; 1029 1030 /* Find the listbox in dialog's group. */ 1031 slw = FILT_LISTBOX (WIDGET (h)->find_by_type (WIDGET (h), filt_listbox_callback)); 1022 1032 1023 1033 switch (msg) 1024 1034 { … … complete_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void 1027 1037 { 1028 1038 case KEY_LEFT: 1029 1039 case KEY_RIGHT: 1040 /* MultiSearch (list filtering) is allowing for left/right movement in query input. */ 1041 if (widget_get_state (WIDGET (slw), WST_FILTER)) 1042 break; 1030 1043 bl = 0; 1031 1044 h->ret_value = 0; 1032 1045 dlg_stop (h); 1033 return MSG_HANDLED; 1046 ret = MSG_HANDLED; 1047 break; 1034 1048 1035 1049 case KEY_BACKSPACE: 1050 /* MultiSearch is exclusive with completion widening. */ 1051 if (widget_get_state (WIDGET (slw), WST_FILTER)) 1052 break; 1036 1053 bl = 0; 1037 1054 /* exit from completion list if input line is empty */ 1038 1055 if (end == 0) … … complete_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void 1043 1060 /* Refill the list box and start again */ 1044 1061 else if (end == min_end) 1045 1062 { 1046 end = str_get_prev_char (&input->buffer[end]) - input->buffer; 1063 end = str_get_prev_char (&input->buffer[end], input->buffer) - input->buffer; 1064 if (end < 0) 1065 end = 0; 1047 1066 input_handle_char (input, parm); 1048 1067 h->ret_value = B_USER; 1049 1068 dlg_stop (h); … … complete_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void 1054 1073 int i; 1055 1074 GList *e; 1056 1075 1057 new_end = str_get_prev_char (&input->buffer[end]) - input->buffer; 1076 new_end = str_get_prev_char (&input->buffer[end], input->buffer) - input->buffer; 1077 /* The buffer is empty (no previous char available)? */ 1078 if (new_end < 0) 1079 new_end = 0; 1058 1080 1059 for (i = 0, e = listbox_get_first_link (LISTBOX ( g->current->data));1081 for (i = 0, e = listbox_get_first_link (LISTBOX (slw)); 1060 1082 e != NULL; i++, e = g_list_next (e)) 1061 1083 { 1062 1084 WLEntry *le = LENTRY (e->data); 1063 1085 1064 1086 if (strncmp (input->buffer + start, le->text, new_end - start) == 0) 1065 1087 { 1066 listbox_select_entry (LISTBOX ( g->current->data), i);1088 listbox_select_entry (LISTBOX (slw), i); 1067 1089 end = new_end; 1068 1090 input_handle_char (input, parm); 1069 widget_draw (WIDGET ( g->current->data));1091 widget_draw (WIDGET (slw)); 1070 1092 break; 1071 1093 } 1072 1094 } 1073 1095 } 1074 return MSG_HANDLED; 1096 ret = MSG_HANDLED; 1097 break; 1075 1098 1076 1099 default: 1077 1100 if (parm < 32 || parm > 255) 1078 1101 { 1079 1102 bl = 0; 1080 if (widget_lookup_key (WIDGET (input), parm) != CK_Complete) 1081 return MSG_NOT_HANDLED; 1082 1083 if (end == min_end) 1084 return MSG_HANDLED; 1085 1086 /* This means we want to refill the list box and start again */ 1087 h->ret_value = B_USER; 1088 dlg_stop (h); 1103 /* CK_Complete → Is completion up to date? */ 1104 if ((widget_lookup_key (WIDGET (input), parm) == CK_Complete) && (end != min_end)) 1105 { 1106 /* This means we want to refill the list box and start again. */ 1107 h->ret_value = B_USER; 1108 dlg_stop (h); 1109 } 1110 /* 1111 * else – key will be ignored by this function, so leave ret unchanged – allow other 1112 * widgets to process it. 1113 */ 1089 1114 } 1090 else 1115 /* 1116 * Do standard live entry lookup only when not filtering list via MultiSearch – it is 1117 * a feature that to a great extent replaces old engine. It can be still used as MultiSearch 1118 * can be dynamically enabled and disabled (default by alt-space and ctrl-space). 1119 */ 1120 else if (!widget_get_state (WIDGET (slw), WST_FILTER)) 1091 1121 { 1092 1122 static char buff[MB_LEN_MAX] = ""; 1093 1123 GList *e; … … complete_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void 1110 1140 break; 1111 1141 } 1112 1142 1113 for (i = 0, e = listbox_get_first_link (LISTBOX ( g->current->data));1143 for (i = 0, e = listbox_get_first_link (LISTBOX (slw)); 1114 1144 e != NULL; i++, e = g_list_next (e)) 1115 1145 { 1116 1146 WLEntry *le = LENTRY (e->data); … … complete_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void 1121 1151 if (need_redraw == 0) 1122 1152 { 1123 1153 need_redraw = 1; 1124 listbox_select_entry (LISTBOX ( g->current->data), i);1154 listbox_select_entry (LISTBOX (slw), i); 1125 1155 last_text = le->text; 1126 1156 } 1127 1157 else … … complete_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void 1172 1202 if (need_redraw == 2) 1173 1203 { 1174 1204 insert_text (input, last_text, low); 1175 widget_draw (WIDGET ( g->current->data));1205 widget_draw (WIDGET (slw)); 1176 1206 } 1177 1207 else if (need_redraw == 1) 1178 1208 { … … complete_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void 1180 1210 dlg_stop (h); 1181 1211 } 1182 1212 bl = 0; 1213 ret = MSG_HANDLED; 1183 1214 } 1184 1215 } 1185 return MSG_HANDLED;1216 break; 1186 1217 1187 1218 default: 1188 1219 return dlg_default_callback (w, sender, msg, parm, data); 1189 1220 } 1221 return ret; 1190 1222 } 1191 1223 1192 1224 /* --------------------------------------------------------------------------------------------- */ … … complete_engine (WInput * in, int what_to_do) 1223 1255 int start_x, start_y; 1224 1256 char **p, *q; 1225 1257 WDialog *complete_dlg; 1226 W Listbox *complete_list;1258 WFilteringListbox *complete_list; 1227 1259 1228 1260 for (p = in->completions + 1; *p != NULL; count++, p++) 1229 1261 { … … complete_engine (WInput * in, int what_to_do) 1266 1298 complete_dlg = 1267 1299 dlg_create (TRUE, y, x, complete_height, complete_width, WPOS_KEEP_DEFAULT, TRUE, 1268 1300 dialog_colors, complete_callback, NULL, "[Completion]", NULL); 1269 complete_list = listbox_new (1, 1, h - 2, w - 2, FALSE, NULL); 1301 complete_list = filtering_listbox_new (1, 1, h - 2, w - 2, FALSE, NULL, 1302 FILT_LIST_KEEP_DIALOG_SIZE); 1270 1303 group_add_widget (GROUP (complete_dlg), complete_list); 1271 1304 1272 1305 for (p = in->completions + 1; *p != NULL; p++) 1273 listbox_add_item (complete_list, LISTBOX_APPEND_AT_END, 0, *p, NULL, FALSE); 1306 listbox_add_item (LISTBOX (complete_list), LISTBOX_APPEND_AT_END, 0, *p, NULL, 1307 FALSE); 1274 1308 1275 1309 i = dlg_run (complete_dlg); 1276 1310 q = NULL; 1277 1311 if (i == B_ENTER) 1278 1312 { 1279 listbox_get_current ( complete_list, &q, NULL);1313 listbox_get_current (LISTBOX (complete_list), &q, NULL); 1280 1314 if (q != NULL) 1281 1315 insert_text (in, q, strlen (q)); 1282 1316 } 1283 1317 if (q != NULL || end != min_end) 1284 1318 input_complete_free (in); 1319 1285 1320 dlg_destroy (complete_dlg); 1286 1321 1287 1322 /* B_USER if user wants to start over again */ … … complete_engine_fill_completions (WInput * in) 1401 1436 { 1402 1437 char *s; 1403 1438 const char *word_separators; 1439 gboolean separator_found = FALSE; 1404 1440 1405 1441 word_separators = (in->completion_flags & INPUT_COMPLETE_SHELL_ESC) ? " \t;|<>" : "\t;|<>"; 1406 1442 … … complete_engine_fill_completions (WInput * in) 1416 1452 str_next_char (&s); 1417 1453 } 1418 1454 1419 for (; s >= in->buffer; str_prev_char (&s))1455 for (; s; str_prev_char (&s, in->buffer)) 1420 1456 { 1421 1457 start = s - in->buffer; 1422 1458 if (strchr (word_separators, *s) != NULL && !strutils_is_char_escaped (in->buffer, s)) 1459 { 1460 separator_found = TRUE; 1423 1461 break; 1462 } 1424 1463 } 1425 1464 1465 /* Note: str_prev_char() returns NULL if no preceding character fitting within string found. */ 1466 if (s < in->buffer) 1467 s = in->buffer; 1468 1426 1469 if (start < end) 1427 1470 { 1428 str_next_char (&s); 1471 if (separator_found) 1472 str_next_char (&s); 1473 if (s > in->buffer + end) 1474 s = in->buffer + end; 1429 1475 start = s - in->buffer; 1430 1476 } 1431 1477 -
lib/widget/listbox-window.c
diff --git a/lib/widget/listbox-window.c b/lib/widget/listbox-window.c index 44548b485..eb0796a1e 100644
a b create_listbox_window_centered (int center_y, int center_x, int lines, int cols, 108 108 dlg_create (TRUE, ypos, xpos, lines + space, cols + space, pos_flags, FALSE, listbox_colors, 109 109 NULL, NULL, help, title); 110 110 111 listbox->list = listbox_new (2, 2, lines, cols, FALSE, NULL); 112 group_add_widget (GROUP (listbox->dlg), listbox->list); 111 listbox->list = 112 filtering_listbox_new (2, 2, lines, cols, FALSE, NULL, FILT_LIST_DIALOG_AUTO_RESIZE); 113 group_add_widget (GROUP (listbox->dlg), WIDGET (listbox->list)); 113 114 114 115 return listbox; 115 116 } … … create_listbox_window (int lines, int cols, const char *title, const char *help) 128 129 int 129 130 run_listbox (Listbox * l) 130 131 { 132 WListbox *lw = LISTBOX (l->list); 131 133 int val = -1; 132 134 133 135 if (dlg_run (l->dlg) != B_CANCEL) 134 val = l->list->pos; 136 { 137 /* Get the virtual index first, to support filtered listboxes. */ 138 val = lw->virtual_pos; 139 140 /* No virtual position → get pos. */ 141 if (val == -1) 142 val = lw->pos; 143 } 144 135 145 dlg_destroy (l->dlg); 136 146 g_free (l); 137 147 return val; … … run_listbox (Listbox * l) 149 159 void * 150 160 run_listbox_with_data (Listbox * l, const void *select) 151 161 { 162 WListbox *lw = LISTBOX (l->list); 152 163 void *val = NULL; 153 164 154 165 if (select != NULL) 155 listbox_select_entry (l ->list, listbox_search_data (l->list, select));166 listbox_select_entry (lw, listbox_search_data (lw, select)); 156 167 157 168 if (dlg_run (l->dlg) != B_CANCEL) 158 169 { 159 170 WLEntry *e; 160 e = listbox_get_nth_item (l->list, l->list->pos); 161 if (e != NULL) 171 e = listbox_get_nth_item (lw, lw->pos); 172 /* -2 means that "<no search results>" has been selected. */ 173 if (e != NULL && e->index != -2) 162 174 { 163 175 /* The assert guards against returning a soon-to-be deallocated 164 176 * pointer (as in listbox_add_item(..., TRUE)). */ -
lib/widget/listbox-window.h
diff --git a/lib/widget/listbox-window.h b/lib/widget/listbox-window.h index 5a6082956..dc20cee13 100644
a b 8 8 /*** typedefs(not structures) and defined constants **********************************************/ 9 9 10 10 #define LISTBOX_APPEND_TEXT(l,h,t,d,f) \ 11 listbox_add_item ( l->list, LISTBOX_APPEND_AT_END, h, t, d, f)11 listbox_add_item (LISTBOX(l->list), LISTBOX_APPEND_AT_END, h, t, d, f) 12 12 13 13 /*** enums ***************************************************************************************/ 14 14 … … 17 17 typedef struct 18 18 { 19 19 WDialog *dlg; 20 W Listbox *list;20 WFilteringListbox *list; 21 21 } Listbox; 22 22 23 23 /*** global variables defined in .c file *********************************************************/ -
lib/widget/listbox.c
diff --git a/lib/widget/listbox.c b/lib/widget/listbox.c index e20c1a82d..4c33e89b2 100644
a b listbox_entry_free (void *data) 77 77 { 78 78 WLEntry *e = data; 79 79 80 g_free (e->text); 80 if (e->free_text) 81 g_free (e->text); 81 82 if (e->free_data) 82 83 g_free (e->data); 83 84 g_free (e); … … listbox_key (WListbox * l, int key) 358 359 359 360 /* --------------------------------------------------------------------------------------------- */ 360 361 362 /* When called via g_queue_foreach it assigns the index field with an incremented int. */ 363 static void 364 listbox_foreach_apply_index (gpointer data, gpointer user_data) 365 { 366 WLEntry *e = data; 367 int *cur_idx = user_data; 368 e->index = *cur_idx; 369 *cur_idx = *cur_idx + 1; 370 } 371 372 /* --------------------------------------------------------------------------------------------- */ 373 361 374 /* Listbox item adding function */ 362 375 static inline void 363 376 listbox_append_item (WListbox * l, WLEntry * e, listbox_append_t pos) … … listbox_new (int y, int x, int height, int width, gboolean deletable, lcback_fn 556 569 l = g_new (WListbox, 1); 557 570 w = WIDGET (l); 558 571 widget_init (w, y, x, height, width, listbox_callback, listbox_mouse_callback); 559 w->options |= WOP_SELECTABLE | WOP_WANT_HOTKEY;572 w->options |= WOP_SELECTABLE; 560 573 w->keymap = listbox_map; 561 574 562 575 l->list = NULL; 563 l->top = l->pos = 0;576 l->top = l->pos = l->virtual_pos = 0; 564 577 l->deletable = deletable; 565 578 l->callback = callback; 566 579 l->allow_duplicates = TRUE; … … listbox_select_last (WListbox * l) 648 661 void 649 662 listbox_select_entry (WListbox * l, int dest) 650 663 { 664 WLEntry *e; 651 665 GList *le; 652 666 int pos; 653 667 gboolean top_seen = FALSE; 654 668 655 669 if (listbox_is_empty (l) || dest < 0) 670 { 671 /* Reset position to a minimal one. */ 672 l->pos = 0; 673 l->virtual_pos = 0; 656 674 return; 675 } 657 676 658 677 /* Special case */ 659 678 for (pos = 0, le = g_queue_peek_head_link (l->list); le != NULL; pos++, le = g_list_next (le)) … … listbox_select_entry (WListbox * l, int dest) 673 692 if (l->pos - l->top >= lines) 674 693 l->top = l->pos - lines + 1; 675 694 } 695 /* 696 * Set the virtual position, i.e.: a position in the initial, unfiltered list if the 697 * same element would be selected. 698 */ 699 e = LENTRY (le->data); 700 l->virtual_pos = e->index; 676 701 return; 677 702 } 678 703 } … … listbox_get_current (WListbox * l, char **string, void **extra) 701 726 if (l != NULL) 702 727 e = listbox_get_nth_item (l, l->pos); 703 728 704 ok = (e != NULL );729 ok = (e != NULL && e->index != -2); 705 730 706 731 if (string != NULL) 707 732 *string = ok ? e->text : NULL; … … listbox_get_nth_item (const WListbox * l, int pos) 720 745 GList *item; 721 746 722 747 item = g_queue_peek_nth_link (l->list, (guint) pos); 723 if (item != NULL )748 if (item != NULL && LENTRY (item->data)->index != -2) 724 749 return LENTRY (item->data); 725 750 } 726 751 … … listbox_remove_list (WListbox * l) 802 827 803 828 /* --------------------------------------------------------------------------------------------- */ 804 829 830 /* 831 * Initializes the listbox elements with their position index. This allows to alter (filter, in 832 * particular) the listbox elements order and still get the original index (when selecting an 833 * element). 834 */ 835 void 836 listbox_init_indices (WListbox * l) 837 { 838 int index = 0; 839 g_queue_foreach (l->list, listbox_foreach_apply_index, &index); 840 } 841 842 /* --------------------------------------------------------------------------------------------- */ 843 805 844 char * 806 845 listbox_add_item (WListbox * l, listbox_append_t pos, int hotkey, const char *text, void *data, 807 846 gboolean free_data) … … listbox_add_item (WListbox * l, listbox_append_t pos, int hotkey, const char *te 815 854 return NULL; 816 855 817 856 entry = g_new (WLEntry, 1); 857 entry->index = -1; /* Will be initialized when switching to the filter state */ 818 858 entry->text = g_strdup (text); 859 entry->free_text = 1; 819 860 entry->data = data; 820 861 entry->free_data = free_data; 821 862 entry->hotkey = hotkey; -
lib/widget/listbox.h
diff --git a/lib/widget/listbox.h b/lib/widget/listbox.h index 8b2236eff..461564f29 100644
a b typedef lcback_ret_t (*lcback_fn) (struct WListbox * l); 35 35 36 36 typedef struct WLEntry 37 37 { 38 int index; /* The location in the list (useful when it's filtered) */ 38 39 char *text; /* Text to display */ 40 gboolean free_text; /* Whether to free the text on entry's removal */ 39 41 int hotkey; 40 42 void *data; /* Client information */ 41 43 gboolean free_data; /* Whether to free the data on entry's removal */ … … typedef struct WListbox 46 48 Widget widget; 47 49 GQueue *list; /* Pointer to the list of WLEntry */ 48 50 int pos; /* The current element displayed */ 51 int virtual_pos; /* The initial index of the current element, works also for filtered listbox */ 49 52 int top; /* The first element displayed */ 50 53 gboolean allow_duplicates; /* Do we allow duplicates on the list? */ 51 54 gboolean scrollbar; /* Draw a scrollbar? */ … … extern const global_keymap_t *listbox_map; 61 64 /*** declarations of public functions ************************************************************/ 62 65 63 66 WListbox *listbox_new (int y, int x, int height, int width, gboolean deletable, lcback_fn callback); 67 64 68 int listbox_search_text (WListbox * l, const char *text); 65 69 int listbox_search_data (WListbox * l, const void *data); 66 70 void listbox_select_first (WListbox * l); … … void listbox_remove_current (WListbox * l); 74 78 gboolean listbox_is_empty (const WListbox * l); 75 79 void listbox_set_list (WListbox * l, GQueue * list); 76 80 void listbox_remove_list (WListbox * l); 81 void listbox_init_indices (WListbox * l); 77 82 char *listbox_add_item (WListbox * l, listbox_append_t pos, int hotkey, const char *text, 78 83 void *data, gboolean free_data); 79 84 -
lib/widget/widget-common.c
diff --git a/lib/widget/widget-common.c b/lib/widget/widget-common.c index e2fcd1222..8dd4a0d30 100644
a b widget_init (Widget * w, int y, int x, int lines, int cols, 338 338 w->find_by_type = widget_default_find_by_type; 339 339 w->find_by_id = widget_default_find_by_id; 340 340 341 w->pre_unlink_func = NULL; 342 341 343 w->set_state = widget_default_set_state; 342 344 w->get_colors = widget_default_get_colors; 343 345 } -
lib/widget/widget-common.h
diff --git a/lib/widget/widget-common.h b/lib/widget/widget-common.h index a20bcd671..c58bddeae 100644
a b typedef enum 43 43 MSG_ACTION, /* Send to widget to handle command */ 44 44 MSG_NOTIFY, /* Typically sent to dialog to inform it of state-change 45 45 * of listboxes, check- and radiobuttons. */ 46 MSG_UPDATE_LIST, /* Sent to listboxes to request the list regeneration (filtering) 47 * in the MultiSearch mode. */ 46 48 MSG_CURSOR, /* Sent to widget to position the cursor */ 47 49 MSG_IDLE, /* The idle state is active */ 48 50 MSG_RESIZE, /* Screen size has changed */ … … typedef enum 88 90 WST_CONSTRUCT = (1 << 15), /* Widget has been constructed but not run yet */ 89 91 WST_ACTIVE = (1 << 16), /* Dialog is visible and active */ 90 92 WST_SUSPENDED = (1 << 17), /* Dialog is suspended */ 91 WST_CLOSED = (1 << 18) /* Dialog is closed */ 93 WST_CLOSED = (1 << 18), /* Dialog is closed */ 94 95 WST_FILTER = (1 << 19) /* Listbox is filtering its contents */ 92 96 } widget_state_t; 93 97 94 98 /* Flags for widget repositioning on dialog resize */ … … struct Widget 165 169 cb_ret_t (*set_state) (Widget * w, widget_state_t state, gboolean enable); 166 170 /* *INDENT-ON* */ 167 171 172 GDestroyNotify pre_unlink_func; /* a function invoked right before removing from a WGroup by 173 * group_remove_widget; it can unregister any events, what depends 174 * on the `event_group` field of the group; */ 175 168 176 const int *(*get_colors) (const Widget * w); 169 177 }; 170 178 -
maint/utils/version.sh
diff --git a/maint/utils/version.sh b/maint/utils/version.sh index 64405191f..1a10de0cd 100755
a b 31 31 #*** file scope functions ********************************************** 32 32 33 33 mc_print_version(){ 34 exit 34 35 35 36 if [ ! -f "${VERSION_FILE}" \ 36 37 -o "${PREV_MC_VERSION}" != "${CURR_MC_VERSION}" ] -
src/editor/choosesyntax.c
diff --git a/src/editor/choosesyntax.c b/src/editor/choosesyntax.c index 09b6d75d5..71ec44e12 100644
a b exec_edit_syntax_dialog (const GPtrArray * names, const char *current_syntax) 84 84 name = g_ptr_array_index (names, i); 85 85 LISTBOX_APPEND_TEXT (syntaxlist, 0, name, NULL, FALSE); 86 86 if (current_syntax != NULL && strcmp (name, current_syntax) == 0) 87 listbox_select_entry ( syntaxlist->list, i + N_DFLT_ENTRIES);87 listbox_select_entry (LISTBOX (syntaxlist->list), i + N_DFLT_ENTRIES); 88 88 } 89 89 90 90 return run_listbox (syntaxlist); -
src/editor/editcmd_dialogs.c
diff --git a/src/editor/editcmd_dialogs.c b/src/editor/editcmd_dialogs.c index 8b3634f23..02d06cdcb 100644
a b editcmd_dialog_completion_show (const WEdit * edit, int max_len, GString ** comp 351 351 int start_x, start_y, offset, i; 352 352 char *curr = NULL; 353 353 WDialog *compl_dlg; 354 WListbox *compl_list; 354 WFilteringListbox *compl_list; 355 WListbox *lw; 355 356 int compl_dlg_h; /* completion dialog height */ 356 357 int compl_dlg_w; /* completion dialog width */ 357 358 … … editcmd_dialog_completion_show (const WEdit * edit, int max_len, GString ** comp 384 385 dialog_colors, NULL, NULL, "[Completion]", NULL); 385 386 386 387 /* create the listbox */ 387 compl_list = listbox_new (1, 1, compl_dlg_h - 2, compl_dlg_w - 2, FALSE, NULL); 388 compl_list = filtering_listbox_new (1, 1, compl_dlg_h - 2, compl_dlg_w - 2, FALSE, NULL, 389 FILT_LIST_KEEP_DIALOG_SIZE); 390 391 /* Save WListbox pointer for convenience. */ 392 lw = LISTBOX (compl_list); 388 393 389 394 /* add the dialog */ 390 395 group_add_widget (GROUP (compl_dlg), compl_list); 391 396 392 397 /* fill the listbox with the completions */ 393 398 for (i = num_compl - 1; i >= 0; i--) /* reverse order */ 394 listbox_add_item (compl_list, LISTBOX_APPEND_AT_END, 0, (char *) compl[i]->str, NULL, 395 FALSE); 399 listbox_add_item (lw, LISTBOX_APPEND_AT_END, 0, (char *) compl[i]->str, NULL, FALSE); 396 400 397 401 /* pop up the dialog and apply the chosen completion */ 398 402 if (dlg_run (compl_dlg) == B_ENTER) 399 403 { 400 listbox_get_current ( compl_list, &curr, NULL);404 listbox_get_current (lw, &curr, NULL); 401 405 curr = g_strdup (curr); 402 406 } 403 407 -
src/editor/editwidget.c
diff --git a/src/editor/editwidget.c b/src/editor/editwidget.c index 18ac00e66..f1989114a 100644
a b edit_window_list (const WDialog * h) 302 302 Listbox *listbox; 303 303 GList *w; 304 304 WEdit *selected; 305 WListbox *lw; 305 306 int i = 0; 306 307 307 308 lines = MIN ((size_t) (LINES * 2 / 3), dlg_num); … … edit_window_list (const WDialog * h) 309 310 310 311 listbox = create_listbox_window (lines, cols, _("Open files"), "[Open files]"); 311 312 313 /* Convenience variable. */ 314 lw = LISTBOX (listbox->list); 315 312 316 for (w = g->widgets; w != NULL; w = g_list_next (w)) 313 317 if (edit_widget_is_editor (CONST_WIDGET (w->data))) 314 318 { … … edit_window_list (const WDialog * h) 322 326 g_strdup_printf ("%c%s", e->modified ? '*' : ' ', 323 327 vfs_path_as_str (e->filename_vpath)); 324 328 325 listbox_add_item (l istbox->list, LISTBOX_APPEND_AT_END, get_hotkey (i++),326 str_term_trim (fname, WIDGET (l istbox->list)->cols - 2), e, FALSE);329 listbox_add_item (lw, LISTBOX_APPEND_AT_END, get_hotkey (i++), 330 str_term_trim (fname, WIDGET (lw)->cols - 2), e, FALSE); 327 331 g_free (fname); 328 332 } 329 333 -
src/file_history.c
diff --git a/src/file_history.c b/src/file_history.c index 4304655aa..e5751ab8a 100644
a b file_history_create_item (history_descriptor_t * hd, void *data) 155 155 width = str_term_width1 (fhd->file_name); 156 156 hd->max_width = MAX (width, hd->max_width); 157 157 158 listbox_add_item (hd->listbox, LISTBOX_APPEND_AT_END, 0, fhd->file_name, fhd->file_pos, TRUE); 158 listbox_add_item (LISTBOX (hd->listbox), LISTBOX_APPEND_AT_END, 0, fhd->file_name, 159 fhd->file_pos, TRUE); 159 160 /* fhd->file_pos is not copied, NULLize it to prevent double free */ 160 161 fhd->file_pos = NULL; 161 162 } -
src/help.c
diff --git a/src/help.c b/src/help.c index f5922f086..9c5aabec3 100644
a b move_backward2 (const char *c, int lines) 229 229 int line; 230 230 231 231 currentpoint = c; 232 for (line = 0, p = currentpoint; (*p != '\0') && ((int) (p - fdata) >= 0); str_cprev_char (&p)) 232 for (line = 0, p = currentpoint; p != NULL && (*p != '\0') && ((int) (p - fdata) >= 0); 233 str_cprev_char (&p, fdata)) 233 234 { 234 235 if (*p == CHAR_NODE_END) 235 236 { … … move_backward2 (const char *c, int lines) 240 241 return currentpoint = p + 2; /* Skip the newline following the start of the node */ 241 242 } 242 243 243 if ( *(p - 1) == '\n')244 if (p > fdata && *(p - 1) == '\n') 244 245 line++; 245 246 if (line == lines) 246 247 return currentpoint = p; -
src/keybind-defaults.c
diff --git a/src/keybind-defaults.c b/src/keybind-defaults.c index 7b87c2f5a..2972e5bbf 100644
a b static const global_keymap_ini_t default_listbox_keymap[] = { 293 293 {"Bottom", "end; alt-gt; c1"}, 294 294 {"PageUp", "pgup; alt-v"}, 295 295 {"PageDown", "pgdn; ctrl-v"}, 296 {"Delete", "delete ; d"},297 {"Clear", "shift-delete ; shift-d"},296 {"Delete", "delete"}, 297 {"Clear", "shift-delete"}, 298 298 {"View", "f3"}, 299 299 {"Edit", "f4"}, 300 {"MultiSearch", "alt-space; ctrl-space"}, 300 301 {"Enter", "enter"}, 301 302 {NULL, NULL} 302 303 }; -
src/selcodepage.c
diff --git a/src/selcodepage.c b/src/selcodepage.c index eba9d25bb..9ce48325b 100644
a b select_charset (int center_y, int center_x, int current_charset, gboolean seldis 107 107 ? ((current_charset < 0) ? codepages->len : (size_t) current_charset) 108 108 : ((size_t) current_charset + 1); 109 109 110 listbox_select_entry ( listbox->list, i);110 listbox_select_entry (LISTBOX (listbox->list), i); 111 111 112 112 listbox_result = run_listbox (listbox); 113 113 -
src/usermenu.c
diff --git a/src/usermenu.c b/src/usermenu.c index 1fecbeaac..13b97e0f3 100644
a b check_patterns (char *p) 133 133 point after argument. */ 134 134 135 135 static char * 136 extract_arg (char *p, char *arg, int size)136 extract_arg (char *p, char *arg, const char *begin, int size) 137 137 { 138 138 while (*p != '\0' && whiteness (*p)) 139 139 p++; … … extract_arg (char *p, char *arg, int size) 152 152 p = np; 153 153 } 154 154 *arg = '\0'; 155 if (*p == '\0' || *p == '\n') 156 str_prev_char (&p); 155 if (p != NULL && (*p == '\0' || *p == '\n')) 156 str_prev_char (&p, begin); 157 if (p == NULL) 158 p = (char *) begin; 157 159 return p; 158 160 } 159 161 … … test_type (WPanel * panel, char *arg) 215 217 p. Returns the point after condition. */ 216 218 217 219 static char * 218 test_condition (const WEdit * edit_widget, char *p, gboolean * condition)220 test_condition (const WEdit * edit_widget, char *p, const char *begin, gboolean * condition) 219 221 { 220 222 char arg[256]; 221 223 const mc_search_type_t search_type = easy_patterns ? MC_SEARCH_T_GLOB : MC_SEARCH_T_REGEX; 222 224 223 225 /* Handle one condition */ 224 for (; *p != '\n' && *p != '&' && *p != '|'; p++)226 for (; p && *p != '\n' && *p != '&' && *p != '|'; p++) 225 227 { 226 228 WPanel *panel = NULL; 227 229 … … test_condition (const WEdit * edit_widget, char *p, gboolean * condition) 238 240 switch (*p++) 239 241 { 240 242 case '!': 241 p = test_condition (edit_widget, p, condition);243 p = test_condition (edit_widget, p, begin, condition); 242 244 *condition = !*condition; 243 str_prev_char (&p );245 str_prev_char (&p, begin); 244 246 break; 245 247 case 'f': /* file name pattern */ 246 p = extract_arg (p, arg, sizeof (arg));248 p = extract_arg (p, arg, begin, sizeof (arg)); 247 249 #ifdef USE_INTERNAL_EDIT 248 250 if (edit_widget != NULL) 249 251 { … … test_condition (const WEdit * edit_widget, char *p, gboolean * condition) 267 269 syntax_type = edit_get_syntax_type (edit_widget); 268 270 if (syntax_type != NULL) 269 271 { 270 p = extract_arg (p, arg, sizeof (arg));272 p = extract_arg (p, arg, begin, sizeof (arg)); 271 273 *condition = mc_search (arg, DEFAULT_CHARSET, syntax_type, MC_SEARCH_T_NORMAL); 272 274 } 273 275 } 274 276 #endif 275 277 break; 276 278 case 'd': 277 p = extract_arg (p, arg, sizeof (arg));279 p = extract_arg (p, arg, begin, sizeof (arg)); 278 280 *condition = panel != NULL 279 281 && mc_search (arg, DEFAULT_CHARSET, vfs_path_as_str (panel->cwd_vpath), 280 282 search_type); 281 283 break; 282 284 case 't': 283 p = extract_arg (p, arg, sizeof (arg));285 p = extract_arg (p, arg, begin, sizeof (arg)); 284 286 *condition = panel != NULL && test_type (panel, arg); 285 287 break; 286 288 case 'x': /* executable */ 287 289 { 288 290 struct stat status; 289 291 290 p = extract_arg (p, arg, sizeof (arg));292 p = extract_arg (p, arg, begin, sizeof (arg)); 291 293 *condition = stat (arg, &status) == 0 && is_exe (status.st_mode); 292 294 break; 293 295 } … … debug_out (char *start, char *end, gboolean condition) 356 358 the point just before the end of line. */ 357 359 358 360 static char * 359 test_line (const WEdit * edit_widget, char *p, gboolean * result)361 test_line (const WEdit * edit_widget, char *p, const char *begin, gboolean * result) 360 362 { 361 363 char operator; 362 364 363 365 /* Repeat till end of line */ 364 while ( *p != '\0' && *p != '\n')366 while (p != NULL && *p != '\0' && *p != '\n') 365 367 { 366 368 char *debug_start, *debug_end; 367 369 gboolean condition = TRUE; … … test_line (const WEdit * edit_widget, char *p, gboolean * result) 384 386 break; 385 387 386 388 debug_start = p; 387 p = test_condition (edit_widget, p, &condition);389 p = test_condition (edit_widget, p, begin, &condition); 388 390 debug_end = p; 389 391 /* Add one debug statement */ 390 392 debug_out (debug_start, debug_end, condition); … … test_line (const WEdit * edit_widget, char *p, gboolean * result) 413 415 /* Report debug message */ 414 416 debug_out (NULL, NULL, TRUE); 415 417 416 if (*p == '\0' || *p == '\n') 417 str_prev_char (&p); 418 if (p != NULL && (*p == '\0' || *p == '\n')) 419 str_prev_char (&p, begin); 420 if (p == NULL) 421 p = (char *) begin; 418 422 return p; 419 423 } 420 424 … … user_menu_cmd (const WEdit * edit_widget, const char *menu_file, int selected_en 1037 1041 if (*(p + 1) == '=') 1038 1042 { 1039 1043 /* Combined adding and default */ 1040 p = test_line (edit_widget, p + 1, &accept_entry);1044 p = test_line (edit_widget, p + 1, data, &accept_entry); 1041 1045 if (selected == 0 && accept_entry) 1042 1046 selected = menu_lines; 1043 1047 } 1044 1048 else 1045 1049 { 1046 1050 /* A condition for adding the entry */ 1047 p = test_line (edit_widget, p, &accept_entry);1051 p = test_line (edit_widget, p, data, &accept_entry); 1048 1052 } 1049 1053 break; 1050 1054 … … user_menu_cmd (const WEdit * edit_widget, const char *menu_file, int selected_en 1052 1056 if (*(p + 1) == '+') 1053 1057 { 1054 1058 /* Combined adding and default */ 1055 p = test_line (edit_widget, p + 1, &accept_entry);1059 p = test_line (edit_widget, p + 1, data, &accept_entry); 1056 1060 if (selected == 0 && accept_entry) 1057 1061 selected = menu_lines; 1058 1062 } … … user_menu_cmd (const WEdit * edit_widget, const char *menu_file, int selected_en 1060 1064 { 1061 1065 /* A condition for making the entry default */ 1062 1066 i = 1; 1063 p = test_line (edit_widget, p, &i);1067 p = test_line (edit_widget, p, data, &i); 1064 1068 if (selected == 0 && i != 0) 1065 1069 selected = menu_lines; 1066 1070 } … … user_menu_cmd (const WEdit * edit_widget, const char *menu_file, int selected_en 1122 1126 extract_line (p, p + MAX_ENTRY_LEN), p, FALSE); 1123 1127 } 1124 1128 /* Select the default entry */ 1125 listbox_select_entry ( listbox->list, selected);1129 listbox_select_entry (LISTBOX (listbox->list), selected); 1126 1130 1127 1131 selected = run_listbox (listbox); 1128 1132 }