Ticket #4165: 0001-Add-MultiSearch-command-a-multi-term-AND-conjugated-.patch
File 0001-Add-MultiSearch-command-a-multi-term-AND-conjugated-.patch, 37.3 KB (added by psprint, 4 years ago) |
---|
-
lib/keybind.c
From 7d412193dd93d16998b196cd09cf0187b22f08e9 Mon Sep 17 00:00:00 2001 From: Sebastian Gniazdowski <sgniazdowski@gmail.com> Date: Sat, 2 Jan 2021 15:54:32 -0600 Subject: [PATCH] =?UTF-8?q?Add=20MultiSearch=20command=20=E2=80=93=20a=20m?= =?UTF-8?q?ulti=20term=20AND-conjugated=20filtering=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/widget/group.c | 11 +- lib/widget/history.c | 74 +++++---- lib/widget/input.c | 106 +++++++++--- lib/widget/input.h | 2 + lib/widget/listbox-window.c | 13 +- lib/widget/listbox.c | 304 ++++++++++++++++++++++++++++++++++- lib/widget/listbox.h | 7 + lib/widget/widget-common.c | 2 + lib/widget/widget-common.h | 10 +- src/editor/choosesyntax.c | 6 + src/editor/editcmd_dialogs.c | 6 + src/editor/editwidget.c | 3 + src/editor/spell_dialogs.c | 6 + src/filemanager/listmode.c | 6 + src/keybind-defaults.c | 5 +- src/selcodepage.c | 6 + src/usermenu.c | 6 + 19 files changed, 511 insertions(+), 64 deletions(-) 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 9638bd651..96e3500ab 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/widget/group.c
diff --git a/lib/widget/group.c b/lib/widget/group.c index f8e318bd9..c06fbc58e 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..e0e673442 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;73 if (!resize) { 74 history_dlg_data *data; 75 int x = 0, y, he, wi; 78 76 79 data = (history_dlg_data *) dlg_head->data; 77 /* guard checks */ 78 if ((dlg_head == NULL) || (dlg_head->data == NULL)) 79 return MSG_NOT_HANDLED; 80 80 81 y = data->y; 82 he = data->count + 2; 81 data = (history_dlg_data *) dlg_head->data; 83 82 84 if (he <= y || y > (LINES - 6)) 85 { 86 he = MIN (he, y - 1); 87 y -= he; 88 } 89 else 90 { 91 y++; 92 he = MIN (he, LINES - y); 83 y = data->y; 84 he = data->count + 2; 85 86 if (he <= y || y > (LINES - 6)) 87 { 88 he = MIN (he, y - 1); 89 y -= he; 90 } 91 else 92 { 93 y++; 94 he = MIN (he, LINES - y); 95 } 96 97 if (data->x > 2) 98 x = data->x - 2; 99 100 wi = data->max_width + 4; 101 102 if ((wi + x) > COLS) 103 { 104 wi = MIN (wi, COLS); 105 x = COLS - wi; 106 } 107 rect_init (&r, y, x, he, wi); 108 } else { 109 /* A resize from some other code (currently from the listbox filter). */ 110 r = *resize; 93 111 } 94 112 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 113 108 114 return dlg_default_callback (WIDGET (dlg_head), NULL, MSG_RESIZE, 0, &r); 109 115 } … … history_dlg_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, v 116 122 switch (msg) 117 123 { 118 124 case MSG_RESIZE: 119 return history_dlg_reposition (DIALOG (w) );125 return history_dlg_reposition (DIALOG (w), data); 120 126 121 127 case MSG_NOTIFY: 122 128 { … … history_show (history_descriptor_t * hd) 257 263 listbox_select_entry (hd->listbox, hd->current); 258 264 } 259 265 266 /* Option of starting the listbox with MultiSearch pre-activated. */ 267 listbox_conditionally_enable_multi_search(hd->listbox); 268 260 269 dlg_ret = dlg_run (query_dlg); 261 270 if (dlg_ret != B_CANCEL) 262 271 { … … history_show (history_descriptor_t * hd) 278 287 hd->text = g_strdup (q); 279 288 } 280 289 290 /* If needed, restore normal listbox state, with no backlist (list_keep). */ 291 listbox_ensure_unfiltered_state(hd->listbox); 292 281 293 /* get modified history from dialog */ 282 294 z = NULL; 283 295 for (hi = listbox_get_first_link (hd->listbox); hi != NULL; hi = g_list_next (hi)) -
lib/widget/input.c
diff --git a/lib/widget/input.c b/lib/widget/input.c index b5cec7e6b..7dbb94f4c 100644
a b input_execute_cmd (WInput * in, long command) 827 827 break; 828 828 } 829 829 830 /* Signal the listbox about any text-altering command. */ 831 switch (command) 832 { 833 case CK_BackSpace: 834 case CK_Delete: 835 case CK_Clear: 836 case CK_Complete: 837 case CK_Paste: 838 case CK_Cut: 839 case CK_DeleteToWordBegin: 840 case CK_DeleteToWordEnd: 841 case CK_Remove: 842 case CK_DeleteToEnd: 843 case CK_HistoryPrev: 844 case CK_HistoryNext: 845 case CK_History: 846 send_message(WIDGET(in->forward_listbox), NULL, MSG_UPDATE_LIST, -1, in->buffer); 847 break; 848 default: 849 break; 850 } 830 851 return res; 831 852 } 832 853 … … input_save_history (const gchar * event_group_name, const gchar * event_name, 871 892 (void) event_group_name; 872 893 (void) event_name; 873 894 874 if (!in->is_password && (DIALOG (WIDGET (in)->owner)->ret_value != B_CANCEL))895 if (!in->is_password && WIDGET (in)->owner && (DIALOG (WIDGET (in)->owner)->ret_value != B_CANCEL)) 875 896 { 876 897 ev_history_load_save_t *ev = (ev_history_load_save_t *) data; 877 898 … … input_mouse_callback (Widget * w, mouse_msg_t msg, mouse_event_t * event) 970 991 } 971 992 } 972 993 994 /* --------------------------------------------------------------------------------------------- */ 995 996 static 997 void listbox_unregister_history_events_cb(gpointer wid_ptr) 998 { 999 Widget *w = WIDGET(wid_ptr); 1000 WDialog *h = DIALOG(w->owner); 1001 1002 /* unsubscribe from "history_load" event */ 1003 mc_event_del (h->event_group, MCEVENT_HISTORY_LOAD, input_load_history, w); 1004 /* unsubscribe from "history_save" event */ 1005 mc_event_del (h->event_group, MCEVENT_HISTORY_SAVE, input_save_history, w); 1006 } 1007 973 1008 /* --------------------------------------------------------------------------------------------- */ 974 1009 /*** public functions ****************************************************************************/ 975 1010 /* --------------------------------------------------------------------------------------------- */ … … input_new (int y, int x, const int *colors, int width, const char *def_text, 1028 1063 if ((histname != NULL) && (*histname != '\0')) 1029 1064 in->history.name = g_strdup (histname); 1030 1065 /* history will be loaded later */ 1031 1032 1066 in->label = NULL; 1067 in->forward_listbox = NULL; 1033 1068 1034 1069 return in; 1035 1070 } … … input_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *d 1050 1085 mc_event_add (h->event_group, MCEVENT_HISTORY_LOAD, input_load_history, w, NULL); 1051 1086 /* subscribe to "history_save" event */ 1052 1087 mc_event_add (h->event_group, MCEVENT_HISTORY_SAVE, input_save_history, w, NULL); 1088 /* unregister (via the func) the events in case of removal from dialog */ 1089 w->pre_unlink_func = listbox_unregister_history_events_cb; 1053 1090 if (in->label != NULL) 1054 1091 widget_set_state (WIDGET (in->label), WST_DISABLED, widget_get_state (w, WST_DISABLED)); 1055 1092 return MSG_HANDLED; … … input_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *d 1063 1100 return v; 1064 1101 } 1065 1102 1066 /* Keys we want others to handle */ 1067 if (parm == KEY_UP || parm == KEY_DOWN || parm == ESC_CHAR 1068 || parm == KEY_F (10) || parm == '\n') 1069 return MSG_NOT_HANDLED; 1070 1071 /* When pasting multiline text, insert literal Enter */ 1072 if ((parm & ~KEY_M_MASK) == '\n') 1073 { 1074 quote = TRUE; 1075 v = input_handle_char (in, '\n'); 1076 quote = FALSE; 1077 return v; 1103 /* If not forwarding the keys to a paired listbox forward_listbox… */ 1104 if (!in->forward_listbox || parm == ESC_CHAR || parm == '\n') { 1105 /* Keys we want others to handle, if not forwarding them to a paired listbox */ 1106 if (parm == KEY_UP || parm == KEY_DOWN || parm == ESC_CHAR 1107 || parm == KEY_F (10) || parm == '\n') 1108 return MSG_NOT_HANDLED; 1109 /* When pasting multiline text, insert literal Enter */ 1110 if ((parm & ~KEY_M_MASK) == '\n') 1111 { 1112 quote = TRUE; 1113 v = input_handle_char (in, '\n'); 1114 quote = FALSE; 1115 return v; 1116 } 1078 1117 } 1079 1118 1080 1119 return input_handle_char (in, parm); … … input_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *d 1097 1136 return MSG_HANDLED; 1098 1137 1099 1138 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); 1139 /* …only, if there is an owner WGroup. */ 1140 if(h) 1141 { 1142 /* unsubscribe from "history_load" event */ 1143 mc_event_del (h->event_group, MCEVENT_HISTORY_LOAD, input_load_history, w); 1144 /* unsubscribe from "history_save" event */ 1145 mc_event_del (h->event_group, MCEVENT_HISTORY_SAVE, input_save_history, w); 1146 } 1104 1147 input_destroy (in); 1105 1148 return MSG_HANDLED; 1106 1149 … … input_handle_char (WInput * in, int key) 1140 1183 command = widget_lookup_key (WIDGET (in), key); 1141 1184 if (command == CK_IgnoreKey) 1142 1185 { 1143 if (key > 255) 1144 return MSG_NOT_HANDLED; 1145 if (in->first) 1146 port_region_marked_for_delete (in); 1147 input_complete_free (in); 1148 v = insert_char (in, key); 1186 /* Should we forward keys to a paired listbox? */ 1187 if (in->forward_listbox) 1188 command = widget_lookup_key (WIDGET (in->forward_listbox), key); 1189 if (command == CK_IgnoreKey) 1190 { 1191 if (key > 255) 1192 return MSG_NOT_HANDLED; 1193 if (in->first) 1194 port_region_marked_for_delete (in); 1195 input_complete_free (in); 1196 v = insert_char (in, key); 1197 } else { 1198 /* Forward the listbox command to the paired listbox. */ 1199 send_message (WIDGET(in->forward_listbox), NULL, MSG_KEY, key, NULL); 1200 send_message (WIDGET(in->forward_listbox), NULL, MSG_DRAW, 0, NULL); 1201 return MSG_HANDLED; 1202 } 1149 1203 } 1150 1204 else 1151 1205 { … … input_handle_char (WInput * in, int key) 1158 1212 } 1159 1213 1160 1214 input_update (in, TRUE); 1215 1216 /* Signal any filtering listbox that the query has been updated. */ 1217 if (in->forward_listbox) 1218 send_message(WIDGET(in->forward_listbox), NULL, MSG_UPDATE_LIST, key, in->buffer); 1161 1219 return v; 1162 1220 } 1163 1221 -
lib/widget/input.h
diff --git a/lib/widget/input.h b/lib/widget/input.h index a753e6160..eaea031d0 100644
a b typedef struct 70 70 GList *current; /* current history item */ 71 71 gboolean changed; /* the history has changed */ 72 72 } history; 73 WListbox *forward_listbox; /* a listbox that should have some of the actions forwarded to */ 74 73 75 } WInput; 74 76 75 77 /*** global variables defined in .c file *********************************************************/ -
lib/widget/listbox-window.c
diff --git a/lib/widget/listbox-window.c b/lib/widget/listbox-window.c index 44548b485..42a38ac4e 100644
a b run_listbox (Listbox * l) 130 130 { 131 131 int val = -1; 132 132 133 /* Option of starting the listbox with MultiSearch activated. */ 134 listbox_conditionally_enable_multi_search(l->list); 135 133 136 if (dlg_run (l->dlg) != B_CANCEL) 134 val = l->list->pos; 137 /* Get the virtual index first, to support filtered listboxes. */ 138 val = l->list->virtual_pos; 139 140 /* If needed, restore normal listbox state, with no back-list (list_keep). */ 141 listbox_ensure_unfiltered_state(l->list); 142 135 143 dlg_destroy (l->dlg); 136 144 g_free (l); 137 145 return val; … … run_listbox_with_data (Listbox * l, const void *select) 167 175 } 168 176 } 169 177 178 /* If needed, restore normal listbox state, with no back-list (list_keep). */ 179 listbox_ensure_unfiltered_state(l->list); 180 170 181 dlg_destroy (l->dlg); 171 182 g_free (l); 172 183 return val; -
lib/widget/listbox.c
diff --git a/lib/widget/listbox.c b/lib/widget/listbox.c index e20c1a82d..2155fd1fe 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_entry_free (void *data) 85 86 86 87 /* --------------------------------------------------------------------------------------------- */ 87 88 89 static WLEntry* listbox_shallow_copy_entry(WLEntry *src, gboolean take_ownership) 90 { 91 WLEntry *copy; 92 copy = g_new(WLEntry, 1); 93 *copy = *src; 94 95 /* Who has the ownership of the data? */ 96 src->free_text = src->free_text && !take_ownership; 97 src->free_data = src->free_data && !take_ownership; 98 copy->free_text = copy->free_text && take_ownership; 99 copy->free_data = copy->free_data && take_ownership; 100 101 return copy; 102 } 103 104 /* --------------------------------------------------------------------------------------------- */ 105 106 /* 107 * Sets the listbox into ·filter· state. In this state, there's a separate copy of all list 108 * entries, while the original (and displayed) field ->list is being a filtered version of the 109 * full copy. 110 */ 111 static gboolean 112 listbox_set_to_filter_state (WListbox * l) 113 { 114 GList *le; 115 116 /* The listbox is already in filter state? */ 117 if (WIDGET(l)->state & WST_FILTER) { 118 /* Return doing no change, just signal the error. */ 119 return FALSE; 120 } 121 122 /* Mark the new state. */ 123 WIDGET(l)->set_state(WIDGET(l), WST_FILTER, 1); 124 125 /* No list copy when entering filter mode. */ 126 g_assert(!l->list_keep); 127 l->list_keep = g_queue_new(); 128 129 /* Skip empty lists. */ 130 if (listbox_is_empty (l)) 131 return TRUE; 132 133 /* 134 * Remember the original position in the list in the field. It'll be used to determine the 135 * virtual_pos field. 136 */ 137 listbox_init_indices(l); 138 139 /* Perform a shallow copy of the original list. */ 140 for (le = g_queue_peek_head_link (l->list); le != NULL; le = g_list_next (le)) 141 { 142 WLEntry *copy = listbox_shallow_copy_entry(LENTRY (le->data), TRUE); 143 g_queue_push_tail(l->list_keep, copy); 144 } 145 146 return TRUE; 147 } 148 149 /* --------------------------------------------------------------------------------------------- */ 150 151 /* Restores original elements of the list (from l->list_keep) and turns off the WST_FILTER state. */ 152 static gboolean 153 listbox_set_to_normal_state (WListbox * l) 154 { 155 /* The listbox is already in non-filter state? */ 156 if ((WIDGET(l)->state & WST_FILTER) == 0) { 157 /* Return doing no change, just signal the error. */ 158 return FALSE; 159 } 160 161 /* The keep-list must be allocated (even if it's empty). */ 162 g_assert(l->list_keep); 163 164 /* Mark the new state. */ 165 WIDGET(l)->set_state(WIDGET(l), WST_FILTER, 0); 166 167 /* Release the filtered list and replace it with the original, complete list. */ 168 g_queue_free_full(l->list, g_free); 169 l->list = g_steal_pointer(&l->list_keep); 170 171 return TRUE; 172 } 173 174 /* --------------------------------------------------------------------------------------------- */ 175 176 /* Return TRUE if given listbox is in WST_FILTER state. */ 177 static gboolean 178 listbox_is_filter_state(WListbox *l) { 179 return ((WIDGET(l)->state & WST_FILTER) != 0); 180 } 181 182 /* --------------------------------------------------------------------------------------------- */ 183 184 static 185 void listbox_filter_list(WListbox * l, const char *text) 186 { 187 int i, size; 188 GList *le; 189 g_auto(GStrv) query_terms = NULL; 190 191 /* Remove the list and allocate a new one. */ 192 if (l->list) 193 g_queue_free_full(l->list, g_free); 194 l->list = g_queue_new(); 195 196 /* Split the query into space delimeted strings. */ 197 query_terms = g_strsplit(text, " ", 10); 198 199 /* 200 * Get the size of the listbox and iterate over it testing each element against ·all· words in 201 * query_terms. 202 */ 203 size = g_queue_get_length(l->list_keep); 204 le = g_queue_peek_head_link (l->list_keep); 205 for (i = 0; i<size; i++, le = g_list_next (le)) 206 { 207 WLEntry *e = LENTRY (le->data); 208 gboolean match = TRUE; 209 210 /* Test the query against the list entry. */ 211 for (gchar **p=query_terms; *p; p++) { 212 if (**p && !strcasestr(e->text, *p)) { 213 match = FALSE; 214 break; 215 } 216 } 217 218 /* If all the terms matched, then add the element to the list. */ 219 if (match) 220 g_queue_push_tail(l->list, listbox_shallow_copy_entry(e, FALSE)); 221 } 222 if (listbox_is_empty(l)) { 223 listbox_add_item(l, LISTBOX_APPEND_AT_END, 0, "<no search results>", NULL, FALSE); 224 LENTRY(g_queue_peek_head_link (l->list)->data)->index = -1; 225 } 226 size = g_queue_get_length(l->list); 227 if (l->pos >= size) 228 listbox_select_entry (l, size - 1); 229 else 230 listbox_select_entry (l, l->pos); 231 } 232 233 /* --------------------------------------------------------------------------------------------- */ 234 88 235 static void 89 236 listbox_drawscroll (WListbox * l) 90 237 { … … listbox_back_n (WListbox * l, int n) 267 414 268 415 /* --------------------------------------------------------------------------------------------- */ 269 416 417 static void 418 listbox_make_one_line_room(WListbox *l, int should_add_free_room) { 419 Widget *w = WIDGET (l), *owner = WIDGET (WIDGET(w)->owner); 420 WRect r_dialog, r_listbox; 421 int new_dialog_height, new_listbox_height, take_give_from_to_owner = 1; 422 423 /* IF the enlarged dialog won't fit the screen, don't resize it but the listbox instead. */ 424 if (LINES <= owner->lines+2) 425 take_give_from_to_owner = 0; 426 427 // Increase the height of the dialog by 1, so that the new input fits. 428 if (should_add_free_room) { 429 new_dialog_height = owner->lines + take_give_from_to_owner; 430 new_listbox_height = w->lines + (-1+take_give_from_to_owner); 431 } else { 432 new_dialog_height = owner->lines - take_give_from_to_owner; 433 new_listbox_height = w->lines - (-1+take_give_from_to_owner); 434 } 435 rect_init (&r_dialog, owner->y, owner->x, new_dialog_height, owner->cols); 436 rect_init (&r_listbox, w->y, w->x, new_listbox_height, w->cols); 437 send_message (w, NULL, MSG_RESIZE, 0, &r_listbox); 438 send_message (owner, NULL, MSG_RESIZE, 0, &r_dialog); 439 } 440 441 /* --------------------------------------------------------------------------------------------- */ 442 443 static void 444 listbox_show_multi_search_input(WListbox *l) { 445 Widget *w = WIDGET(l), *owner = WIDGET(WIDGET(l)->owner); 446 WInput *multi_search_in; 447 int distance_y = owner->y + owner->lines - (w->y + w->lines) + 1; 448 int distance_x = w->cols > 40 ? 5 : 1, small = w->cols <= 15 ? 1 : 0; 449 450 listbox_make_one_line_room(l,1); 451 multi_search_in = input_new (owner->lines - distance_y, distance_x, 452 input_colors, w->cols-2-distance_x+small, "", 453 "multi_search", INPUT_COMPLETE_NONE); 454 multi_search_in->forward_listbox = l; 455 group_add_widget_autopos (GROUP(owner), multi_search_in, WPOS_KEEP_TOP | WPOS_CENTER_HORZ , NULL); 456 457 repaint_screen(); 458 send_message (l, NULL, MSG_DRAW, 0, NULL); 459 send_message (multi_search_in, NULL, MSG_DRAW, 0, NULL); 460 } 461 462 /* --------------------------------------------------------------------------------------------- */ 463 464 static void 465 listbox_hide_multi_search_widget(WListbox *l) { 466 Widget *in; 467 in = WIDGET(WIDGET(l)->owner)->find_by_type(WIDGET(WIDGET(l)->owner), input_callback); 468 if (in) { 469 group_remove_widget(in); 470 listbox_make_one_line_room(l,0); 471 group_select_next_widget(WIDGET(l)->owner); 472 send_message (l, NULL, MSG_DRAW, 0, NULL); 473 widget_destroy(in); 474 } 475 repaint_screen(); 476 } 477 478 /* --------------------------------------------------------------------------------------------- */ 479 270 480 static cb_ret_t 271 481 listbox_execute_cmd (WListbox * l, long command) 272 482 { … … listbox_execute_cmd (WListbox * l, long command) 320 530 D_ERROR, 2, _("&Yes"), _("&No")) == 0)) 321 531 listbox_remove_list (l); 322 532 break; 533 case CK_MultiSearch: 534 /* Toggle the multi term searching of any listbox. */ 535 if (listbox_is_filter_state(l)) { 536 /* Remove the input widget from the dialog. */ 537 listbox_hide_multi_search_widget(l); 538 /* Restore original (unfiltered) listbox contents. */ 539 ret = listbox_set_to_normal_state(l); 540 } else { 541 /* Add input widget for the filter query at the bottom of the dialog window. */ 542 listbox_show_multi_search_input(l); 543 /* … and then turn on the filter state. */ 544 ret = listbox_set_to_filter_state(l); 545 } 546 break; 323 547 case CK_View: 324 548 case CK_Edit: 325 549 case CK_Enter: … … listbox_key (WListbox * l, int key) 358 582 359 583 /* --------------------------------------------------------------------------------------------- */ 360 584 585 /* When called via g_queue_foreach it assigns the index field with an incremented int. */ 586 static void 587 listbox_foreach_apply_index (gpointer data, 588 gpointer user_data) 589 { 590 WLEntry *e = data; 591 int *cur_idx = user_data; 592 e->index = *cur_idx; 593 *cur_idx = *cur_idx + 1; 594 } 595 596 /* --------------------------------------------------------------------------------------------- */ 597 361 598 /* Listbox item adding function */ 362 599 static inline void 363 600 listbox_append_item (WListbox * l, WLEntry * e, listbox_append_t pos) … … listbox_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void 482 719 widget_gotoyx (l, l->cursor_y, 0); 483 720 return MSG_HANDLED; 484 721 722 case MSG_UPDATE_LIST: 723 listbox_filter_list(l, (char *) data); 724 repaint_screen(); 725 return MSG_HANDLED; 726 485 727 case MSG_DRAW: 486 728 listbox_draw (l, widget_get_state (w, WST_FOCUSED)); 487 729 return MSG_HANDLED; … … listbox_new (int y, int x, int height, int width, gboolean deletable, lcback_fn 556 798 l = g_new (WListbox, 1); 557 799 w = WIDGET (l); 558 800 widget_init (w, y, x, height, width, listbox_callback, listbox_mouse_callback); 559 w->options |= WOP_SELECTABLE | WOP_WANT_HOTKEY;801 w->options |= WOP_SELECTABLE; 560 802 w->keymap = listbox_map; 561 803 562 804 l->list = NULL; 805 l->list_keep = NULL; 563 806 l->top = l->pos = 0; 564 807 l->deletable = deletable; 565 808 l->callback = callback; … … listbox_new (int y, int x, int height, int width, gboolean deletable, lcback_fn 571 814 572 815 /* --------------------------------------------------------------------------------------------- */ 573 816 817 /* 818 * If the list is filtered it replaces from the back list (list_keep). It returns whether such 819 * change occurred – FALSE means that the list was already unfiltered. 820 */ 821 gboolean listbox_ensure_unfiltered_state(WListbox *l) 822 { 823 gboolean ret = FALSE; 824 if (listbox_is_filter_state(l)) 825 ret = listbox_set_to_normal_state(l); 826 return ret; 827 } 828 829 gboolean listbox_conditionally_enable_multi_search(WListbox *l) 830 { 831 int start_with_multi_search_active; 832 833 /* Option of starting the listbox with MultiSearch pre-activated. */ 834 start_with_multi_search_active = 835 mc_config_get_bool (mc_global.main_config, CONFIG_APP_SECTION, 836 "multi_search_active_by_default", 1); 837 838 /* CK_MultiSearch toggles the state. */ 839 if (start_with_multi_search_active) 840 send_message(l, NULL, MSG_ACTION, CK_MultiSearch, NULL); 841 842 return start_with_multi_search_active; 843 } 844 845 /* --------------------------------------------------------------------------------------------- */ 846 574 847 /** 575 848 * Finds item by its label. 576 849 */ … … listbox_select_entry (WListbox * l, int dest) 673 946 if (l->pos - l->top >= lines) 674 947 l->top = l->pos - lines + 1; 675 948 } 949 /* 950 * Set the virtual position, i.e.: a position in the initial, unfiltered list if the 951 * same element would be selected. 952 */ 953 if (listbox_is_filter_state(l)) { 954 WLEntry *e = listbox_get_nth_item(l, l->pos); 955 if (e) 956 l->virtual_pos = e->index; 957 else 958 l->virtual_pos = -1; 959 } else { 960 l->virtual_pos = pos; 961 } 676 962 return; 677 963 } 678 964 } … … listbox_remove_list (WListbox * l) 802 1088 803 1089 /* --------------------------------------------------------------------------------------------- */ 804 1090 1091 /* 1092 * Initializes the listbox elements with their position index. This allows to alter (filter, in 1093 * particular) the listbox elements order and still get the original index (when selecting an 1094 * element). 1095 */ 1096 void listbox_init_indices(WListbox *l) { 1097 int index = 0; 1098 g_queue_foreach(l->list, listbox_foreach_apply_index, &index); 1099 } 1100 1101 /* --------------------------------------------------------------------------------------------- */ 1102 805 1103 char * 806 1104 listbox_add_item (WListbox * l, listbox_append_t pos, int hotkey, const char *text, void *data, 807 1105 gboolean free_data) … … listbox_add_item (WListbox * l, listbox_append_t pos, int hotkey, const char *te 815 1113 return NULL; 816 1114 817 1115 entry = g_new (WLEntry, 1); 1116 entry->index = -1; /* Will be initialized when switching to the filter state */ 818 1117 entry->text = g_strdup (text); 1118 entry->free_text = 1; 819 1119 entry->data = data; 820 1120 entry->free_data = free_data; 821 1121 entry->hotkey = hotkey; -
lib/widget/listbox.h
diff --git a/lib/widget/listbox.h b/lib/widget/listbox.h index 8b2236eff..cc853d0ee 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 45 47 { 46 48 Widget widget; 47 49 GQueue *list; /* Pointer to the list of WLEntry */ 50 GQueue *list_keep; /* Unfiltered list (used in the WST_FILTER state). */ 48 51 int pos; /* The current element displayed */ 52 int virtual_pos; /* The initial index of the current element, works also for filtered listbox */ 49 53 int top; /* The first element displayed */ 50 54 gboolean allow_duplicates; /* Do we allow duplicates on the list? */ 51 55 gboolean scrollbar; /* Draw a scrollbar? */ … … extern const global_keymap_t *listbox_map; 61 65 /*** declarations of public functions ************************************************************/ 62 66 63 67 WListbox *listbox_new (int y, int x, int height, int width, gboolean deletable, lcback_fn callback); 68 gboolean listbox_ensure_unfiltered_state(WListbox *l); 69 gboolean listbox_conditionally_enable_multi_search(WListbox *l); 64 70 int listbox_search_text (WListbox * l, const char *text); 65 71 int listbox_search_data (WListbox * l, const void *data); 66 72 void listbox_select_first (WListbox * l); … … void listbox_remove_current (WListbox * l); 74 80 gboolean listbox_is_empty (const WListbox * l); 75 81 void listbox_set_list (WListbox * l, GQueue * list); 76 82 void listbox_remove_list (WListbox * l); 83 void listbox_init_indices(WListbox *l); 77 84 char *listbox_add_item (WListbox * l, listbox_append_t pos, int hotkey, const char *text, 78 85 void *data, gboolean free_data); 79 86 -
lib/widget/widget-common.c
diff --git a/lib/widget/widget-common.c b/lib/widget/widget-common.c index 405269ff9..b4236de17 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..fa3e329ae 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 -
src/editor/choosesyntax.c
diff --git a/src/editor/choosesyntax.c b/src/editor/choosesyntax.c index 09b6d75d5..015012a99 100644
a b exec_edit_syntax_dialog (const GPtrArray * names, const char *current_syntax) 87 87 listbox_select_entry (syntaxlist->list, i + N_DFLT_ENTRIES); 88 88 } 89 89 90 /* 91 * Initialize the `index` fields in the WLEntry elements so that it's possible to get 92 * the correct selected element index even when listbox is filtered (MultiSearch). 93 */ 94 listbox_init_indices(syntaxlist->list); 95 90 96 return run_listbox (syntaxlist); 91 97 } 92 98 -
src/editor/editcmd_dialogs.c
diff --git a/src/editor/editcmd_dialogs.c b/src/editor/editcmd_dialogs.c index 8b3634f23..217a2ea07 100644
a b editcmd_dialog_completion_show (const WEdit * edit, int max_len, GString ** comp 394 394 listbox_add_item (compl_list, LISTBOX_APPEND_AT_END, 0, (char *) compl[i]->str, NULL, 395 395 FALSE); 396 396 397 /* Option to start in the MultiSearch state. */ 398 listbox_conditionally_enable_multi_search(compl_list); 399 397 400 /* pop up the dialog and apply the chosen completion */ 398 401 if (dlg_run (compl_dlg) == B_ENTER) 399 402 { … … editcmd_dialog_completion_show (const WEdit * edit, int max_len, GString ** comp 401 404 curr = g_strdup (curr); 402 405 } 403 406 407 /* If needed, restore normal listbox state, with no back list (list_keep). */ 408 listbox_ensure_unfiltered_state(compl_list); 409 404 410 /* destroy dialog before return */ 405 411 dlg_destroy (compl_dlg); 406 412 -
src/editor/editwidget.c
diff --git a/src/editor/editwidget.c b/src/editor/editwidget.c index 18ac00e66..9835503a1 100644
a b edit_window_list (const WDialog * h) 327 327 g_free (fname); 328 328 } 329 329 330 /* Option of starting the listbox with MultiSearch pre-activated. */ 331 listbox_conditionally_enable_multi_search(listbox->list); 332 330 333 selected = run_listbox_with_data (listbox, g->current->data); 331 334 if (selected != NULL) 332 335 widget_select (WIDGET (selected)); -
src/editor/spell_dialogs.c
diff --git a/src/editor/spell_dialogs.c b/src/editor/spell_dialogs.c index 392f8e4ec..8a4e93c62 100644
a b spell_dialog_lang_list_show (GArray * languages) 174 174 for (i = 0; i < languages->len; i++) 175 175 LISTBOX_APPEND_TEXT (lang_list, 0, g_array_index (languages, char *, i), NULL, FALSE); 176 176 177 /* 178 * Initialize the `index` fields in the WLEntry elements so that it's possible to get 179 * the correct selected element index even when listbox is filtered (MultiSearch). 180 */ 181 listbox_init_indices(lang_list->list); 182 177 183 res = run_listbox (lang_list); 178 184 if (res >= 0) 179 185 selected_lang = g_strdup (g_array_index (languages, char *, (unsigned int) res)); -
src/filemanager/listmode.c
diff --git a/src/filemanager/listmode.c b/src/filemanager/listmode.c index 0aaa0e91f..8954045c9 100644
a b select_new_item (void) 124 124 FALSE); 125 125 } 126 126 127 /* 128 * Initialize the `index` fields in the WLEntry elements so that it's possible to get 129 * the correct selected element index even when listbox is filtered (MultiSearch). 130 */ 131 listbox_init_indices(listbox->list); 132 127 133 i = run_listbox (mylistbox); 128 134 if (i >= 0) 129 135 ret = g_strdup (possible_items[i]); -
src/keybind-defaults.c
diff --git a/src/keybind-defaults.c b/src/keybind-defaults.c index c423e6be4..f77c173e1 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..9eeba53dd 100644
a b select_charset (int center_y, int center_x, int current_charset, gboolean seldis 109 109 110 110 listbox_select_entry (listbox->list, i); 111 111 112 /* 113 * Initialize the `index` fields in the WLEntry elements so that it's possible to get 114 * the correct selected element index even when listbox is filtered (MultiSearch). 115 */ 116 listbox_init_indices(listbox->list); 117 112 118 listbox_result = run_listbox (listbox); 113 119 114 120 if (listbox_result < 0) -
src/usermenu.c
diff --git a/src/usermenu.c b/src/usermenu.c index 698244220..b9d6f8777 100644
a b user_menu_cmd (const WEdit * edit_widget, const char *menu_file, int selected_en 1124 1124 /* Select the default entry */ 1125 1125 listbox_select_entry (listbox->list, selected); 1126 1126 1127 /* 1128 * Initialize the `index` fields in the WLEntry elements so that it's possible to get 1129 * the correct selected element index even when listbox is filtered (MultiSearch). 1130 */ 1131 listbox_init_indices(listbox->list); 1132 1127 1133 selected = run_listbox (listbox); 1128 1134 } 1129 1135 if (selected >= 0)