Ticket #4165: 0001-Add-MultiSearch-command-a-multi-term-AND-conjugated-v2.2.patch
File 0001-Add-MultiSearch-command-a-multi-term-AND-conjugated-v2.2.patch, 34.1 KB (added by psprint, 4 years ago) |
---|
-
lib/keybind.c
From dc4e06b7adf219fe00e870650d33972aba2bd591 Mon Sep 17 00:00:00 2001 From: Sebastian Gniazdowski <sgniazdowski@gmail.com> Date: Sun, 3 Jan 2021 04:57:58 -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 | 75 ++++---- lib/widget/input.c | 108 +++++++++--- lib/widget/input.h | 2 + lib/widget/listbox-window.c | 13 +- lib/widget/listbox.c | 328 ++++++++++++++++++++++++++++++++++- lib/widget/listbox.h | 7 + lib/widget/widget-common.c | 2 + lib/widget/widget-common.h | 10 +- src/editor/editcmd_dialogs.c | 6 + src/editor/editwidget.c | 3 + src/keybind-defaults.c | 5 +- 14 files changed, 510 insertions(+), 62 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..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..7cd165e21 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) 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_show (history_descriptor_t * hd) 257 266 listbox_select_entry (hd->listbox, hd->current); 258 267 } 259 268 269 /* Option of starting the listbox with MultiSearch pre-activated. */ 270 listbox_conditionally_enable_multi_search (hd->listbox); 271 260 272 dlg_ret = dlg_run (query_dlg); 261 273 if (dlg_ret != B_CANCEL) 262 274 { … … history_show (history_descriptor_t * hd) 278 290 hd->text = g_strdup (q); 279 291 } 280 292 293 /* If needed, restore normal listbox state, with no backlist (list_keep). */ 294 listbox_ensure_unfiltered_state (hd->listbox); 295 281 296 /* get modified history from dialog */ 282 297 z = NULL; 283 298 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..625b394a9 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 896 && (DIALOG (WIDGET (in)->owner)->ret_value != B_CANCEL)) 875 897 { 876 898 ev_history_load_save_t *ev = (ev_history_load_save_t *) data; 877 899 … … input_mouse_callback (Widget * w, mouse_msg_t msg, mouse_event_t * event) 970 992 } 971 993 } 972 994 995 /* --------------------------------------------------------------------------------------------- */ 996 997 static void 998 listbox_unregister_history_events_cb (gpointer wid_ptr) 999 { 1000 Widget *w = WIDGET (wid_ptr); 1001 WDialog *h = DIALOG (w->owner); 1002 1003 /* unsubscribe from "history_load" event */ 1004 mc_event_del (h->event_group, MCEVENT_HISTORY_LOAD, input_load_history, w); 1005 /* unsubscribe from "history_save" event */ 1006 mc_event_del (h->event_group, MCEVENT_HISTORY_SAVE, input_save_history, w); 1007 } 1008 973 1009 /* --------------------------------------------------------------------------------------------- */ 974 1010 /*** public functions ****************************************************************************/ 975 1011 /* --------------------------------------------------------------------------------------------- */ … … input_new (int y, int x, const int *colors, int width, const char *def_text, 1028 1064 if ((histname != NULL) && (*histname != '\0')) 1029 1065 in->history.name = g_strdup (histname); 1030 1066 /* history will be loaded later */ 1031 1032 1067 in->label = NULL; 1068 in->forward_listbox = NULL; 1033 1069 1034 1070 return in; 1035 1071 } … … input_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *d 1050 1086 mc_event_add (h->event_group, MCEVENT_HISTORY_LOAD, input_load_history, w, NULL); 1051 1087 /* subscribe to "history_save" event */ 1052 1088 mc_event_add (h->event_group, MCEVENT_HISTORY_SAVE, input_save_history, w, NULL); 1089 /* unregister (via the func) the events in case of removal from dialog */ 1090 w->pre_unlink_func = listbox_unregister_history_events_cb; 1053 1091 if (in->label != NULL) 1054 1092 widget_set_state (WIDGET (in->label), WST_DISABLED, widget_get_state (w, WST_DISABLED)); 1055 1093 return MSG_HANDLED; … … input_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *d 1063 1101 return v; 1064 1102 } 1065 1103 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') 1104 /* If not forwarding the keys to a paired listbox forward_listbox… */ 1105 if (!in->forward_listbox || parm == ESC_CHAR || parm == '\n') 1073 1106 { 1074 quote = TRUE; 1075 v = input_handle_char (in, '\n'); 1076 quote = FALSE; 1077 return v; 1107 /* Keys we want others to handle, if not forwarding them to a paired listbox */ 1108 if (parm == KEY_UP || parm == KEY_DOWN || parm == ESC_CHAR 1109 || parm == KEY_F (10) || parm == '\n') 1110 return MSG_NOT_HANDLED; 1111 /* When pasting multiline text, insert literal Enter */ 1112 if ((parm & ~KEY_M_MASK) == '\n') 1113 { 1114 quote = TRUE; 1115 v = input_handle_char (in, '\n'); 1116 quote = FALSE; 1117 return v; 1118 } 1078 1119 } 1079 1120 1080 1121 return input_handle_char (in, parm); … … input_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *d 1097 1138 return MSG_HANDLED; 1098 1139 1099 1140 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); 1141 /* …only, if there is an owner WGroup. */ 1142 if (h) 1143 { 1144 /* unsubscribe from "history_load" event */ 1145 mc_event_del (h->event_group, MCEVENT_HISTORY_LOAD, input_load_history, w); 1146 /* unsubscribe from "history_save" event */ 1147 mc_event_del (h->event_group, MCEVENT_HISTORY_SAVE, input_save_history, w); 1148 } 1104 1149 input_destroy (in); 1105 1150 return MSG_HANDLED; 1106 1151 … … input_handle_char (WInput * in, int key) 1140 1185 command = widget_lookup_key (WIDGET (in), key); 1141 1186 if (command == CK_IgnoreKey) 1142 1187 { 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); 1188 /* Should we forward keys to a paired listbox? */ 1189 if (in->forward_listbox) 1190 command = widget_lookup_key (WIDGET (in->forward_listbox), key); 1191 if (command == CK_IgnoreKey) 1192 { 1193 if (key > 255) 1194 return MSG_NOT_HANDLED; 1195 if (in->first) 1196 port_region_marked_for_delete (in); 1197 input_complete_free (in); 1198 v = insert_char (in, key); 1199 } 1200 else 1201 { 1202 /* Forward the listbox command to the paired listbox. */ 1203 send_message (WIDGET (in->forward_listbox), NULL, MSG_KEY, key, NULL); 1204 send_message (WIDGET (in->forward_listbox), NULL, MSG_DRAW, 0, NULL); 1205 return MSG_HANDLED; 1206 } 1149 1207 } 1150 1208 else 1151 1209 { … … input_handle_char (WInput * in, int key) 1158 1216 } 1159 1217 1160 1218 input_update (in, TRUE); 1219 1220 /* Signal any filtering listbox that the query has been updated. */ 1221 if (in->forward_listbox) 1222 send_message (WIDGET (in->forward_listbox), NULL, MSG_UPDATE_LIST, key, in->buffer); 1161 1223 return v; 1162 1224 } 1163 1225 -
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..c242b04e9 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..c71655acb 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 * 90 listbox_shallow_copy_entry (WLEntry * src, gboolean take_ownership) 91 { 92 WLEntry *copy; 93 copy = g_new (WLEntry, 1); 94 *copy = *src; 95 96 /* Who has the ownership of the data? */ 97 src->free_text = src->free_text && !take_ownership; 98 src->free_data = src->free_data && !take_ownership; 99 copy->free_text = copy->free_text && take_ownership; 100 copy->free_data = copy->free_data && take_ownership; 101 102 return copy; 103 } 104 105 /* --------------------------------------------------------------------------------------------- */ 106 107 /* 108 * Sets the listbox into ·filter· state. In this state, there's a separate copy of all list 109 * entries, while the original (and displayed) field ->list is being a filtered version of the 110 * full copy. 111 */ 112 static gboolean 113 listbox_set_to_filter_state (WListbox * l) 114 { 115 GList *le; 116 117 /* The listbox is already in filter state? */ 118 if (WIDGET (l)->state & WST_FILTER) 119 { 120 /* Return doing no change, just signal the error. */ 121 return FALSE; 122 } 123 124 /* Mark the new state. */ 125 WIDGET (l)->set_state (WIDGET (l), WST_FILTER, 1); 126 127 /* No list copy when entering filter mode. */ 128 g_assert (!l->list_keep); 129 l->list_keep = g_queue_new (); 130 131 /* Skip empty lists. */ 132 if (listbox_is_empty (l)) 133 return TRUE; 134 135 /* 136 * Remember the original position in the list in the field. It'll be used to determine the 137 * virtual_pos field. 138 */ 139 listbox_init_indices (l); 140 141 /* Perform a shallow copy of the original list. */ 142 for (le = g_queue_peek_head_link (l->list); le != NULL; le = g_list_next (le)) 143 { 144 WLEntry *copy = listbox_shallow_copy_entry (LENTRY (le->data), TRUE); 145 g_queue_push_tail (l->list_keep, copy); 146 } 147 148 return TRUE; 149 } 150 151 /* --------------------------------------------------------------------------------------------- */ 152 153 /* Restores original elements of the list (from l->list_keep) and turns off the WST_FILTER state. */ 154 static gboolean 155 listbox_set_to_normal_state (WListbox * l) 156 { 157 /* The listbox is already in non-filter state? */ 158 if ((WIDGET (l)->state & WST_FILTER) == 0) 159 { 160 /* Return doing no change, just signal the error. */ 161 return FALSE; 162 } 163 164 /* The keep-list must be allocated (even if it's empty). */ 165 g_assert (l->list_keep); 166 167 /* Mark the new state. */ 168 WIDGET (l)->set_state (WIDGET (l), WST_FILTER, 0); 169 170 /* Release the filtered list and replace it with the original, complete list. */ 171 g_queue_free_full (l->list, g_free); 172 l->list = g_steal_pointer (&l->list_keep); 173 174 return TRUE; 175 } 176 177 /* --------------------------------------------------------------------------------------------- */ 178 179 /* Return TRUE if given listbox is in WST_FILTER state. */ 180 static gboolean 181 listbox_is_filter_state (WListbox * l) 182 { 183 return ((WIDGET (l)->state & WST_FILTER) != 0); 184 } 185 186 /* --------------------------------------------------------------------------------------------- */ 187 188 static void 189 listbox_filter_list (WListbox * l, const char *text) 190 { 191 int i, size; 192 GList *le; 193 g_auto (GStrv) query_terms = NULL; 194 195 /* Remove the list and allocate a new one. */ 196 if (l->list) 197 g_queue_free_full (l->list, g_free); 198 l->list = g_queue_new (); 199 200 /* Split the query into space delimeted strings. */ 201 query_terms = g_strsplit (text, " ", 10); 202 203 /* 204 * Get the size of the listbox and iterate over it testing each element against ·all· words in 205 * query_terms. 206 */ 207 size = g_queue_get_length (l->list_keep); 208 le = g_queue_peek_head_link (l->list_keep); 209 for (i = 0; i < size; i++, le = g_list_next (le)) 210 { 211 WLEntry *e = LENTRY (le->data); 212 gboolean match = TRUE; 213 214 /* Test the query against the list entry. */ 215 for (gchar ** p = query_terms; *p; p++) 216 { 217 if (**p && !strcasestr (e->text, *p)) 218 { 219 match = FALSE; 220 break; 221 } 222 } 223 224 /* If all the terms matched, then add the element to the list. */ 225 if (match) 226 g_queue_push_tail (l->list, listbox_shallow_copy_entry (e, FALSE)); 227 } 228 if (listbox_is_empty (l)) 229 { 230 listbox_add_item (l, LISTBOX_APPEND_AT_END, 0, "<no search results>", NULL, FALSE); 231 LENTRY (g_queue_peek_head_link (l->list)->data)->index = -1; 232 } 233 size = g_queue_get_length (l->list); 234 if (l->pos >= size) 235 listbox_select_entry (l, size - 1); 236 else 237 listbox_select_entry (l, l->pos); 238 } 239 240 /* --------------------------------------------------------------------------------------------- */ 241 88 242 static void 89 243 listbox_drawscroll (WListbox * l) 90 244 { … … listbox_back_n (WListbox * l, int n) 267 421 268 422 /* --------------------------------------------------------------------------------------------- */ 269 423 424 static void 425 listbox_make_one_line_room (WListbox * l, int should_add_free_room) 426 { 427 Widget *w = WIDGET (l), *owner = WIDGET (WIDGET (w)->owner); 428 WRect r_dialog, r_listbox; 429 int new_dialog_height, new_listbox_height, take_give_from_to_owner = 1; 430 431 /* IF the enlarged dialog won't fit the screen, don't resize it but the listbox instead. */ 432 if (LINES <= owner->lines + 2) 433 take_give_from_to_owner = 0; 434 435 // Increase the height of the dialog by 1, so that the new input fits. 436 if (should_add_free_room) 437 { 438 new_dialog_height = owner->lines + take_give_from_to_owner; 439 new_listbox_height = w->lines + (-1 + take_give_from_to_owner); 440 } 441 else 442 { 443 new_dialog_height = owner->lines - take_give_from_to_owner; 444 new_listbox_height = w->lines - (-1 + take_give_from_to_owner); 445 } 446 rect_init (&r_dialog, owner->y, owner->x, new_dialog_height, owner->cols); 447 rect_init (&r_listbox, w->y, w->x, new_listbox_height, w->cols); 448 send_message (w, NULL, MSG_RESIZE, 0, &r_listbox); 449 send_message (owner, NULL, MSG_RESIZE, 0, &r_dialog); 450 } 451 452 /* --------------------------------------------------------------------------------------------- */ 453 454 static void 455 listbox_show_multi_search_input (WListbox * l) 456 { 457 Widget *w = WIDGET (l), *owner = WIDGET (WIDGET (l)->owner); 458 WInput *multi_search_in; 459 int distance_y = owner->y + owner->lines - (w->y + w->lines) + 1; 460 int distance_x = w->cols > 40 ? 5 : 1, small = w->cols <= 15 ? 1 : 0; 461 462 listbox_make_one_line_room (l, 1); 463 multi_search_in = input_new (owner->lines - distance_y, distance_x, 464 input_colors, w->cols - 2 - distance_x + small, "", 465 "multi_search", INPUT_COMPLETE_NONE); 466 multi_search_in->forward_listbox = l; 467 group_add_widget_autopos (GROUP (owner), multi_search_in, WPOS_KEEP_TOP | WPOS_CENTER_HORZ, 468 NULL); 469 470 repaint_screen (); 471 send_message (l, NULL, MSG_DRAW, 0, NULL); 472 send_message (multi_search_in, NULL, MSG_DRAW, 0, NULL); 473 } 474 475 /* --------------------------------------------------------------------------------------------- */ 476 477 static void 478 listbox_hide_multi_search_widget (WListbox * l) 479 { 480 Widget *in; 481 in = WIDGET (WIDGET (l)->owner)->find_by_type (WIDGET (WIDGET (l)->owner), input_callback); 482 if (in) 483 { 484 group_remove_widget (in); 485 listbox_make_one_line_room (l, 0); 486 group_select_next_widget (WIDGET (l)->owner); 487 send_message (l, NULL, MSG_DRAW, 0, NULL); 488 widget_destroy (in); 489 } 490 repaint_screen (); 491 } 492 493 /* --------------------------------------------------------------------------------------------- */ 494 270 495 static cb_ret_t 271 496 listbox_execute_cmd (WListbox * l, long command) 272 497 { … … listbox_execute_cmd (WListbox * l, long command) 320 545 D_ERROR, 2, _("&Yes"), _("&No")) == 0)) 321 546 listbox_remove_list (l); 322 547 break; 548 case CK_MultiSearch: 549 /* Toggle the multi term searching of any listbox. */ 550 if (listbox_is_filter_state (l)) 551 { 552 /* Remove the input widget from the dialog. */ 553 listbox_hide_multi_search_widget (l); 554 /* Restore original (unfiltered) listbox contents. */ 555 ret = listbox_set_to_normal_state (l); 556 } 557 else 558 { 559 /* Add input widget for the filter query at the bottom of the dialog window. */ 560 listbox_show_multi_search_input (l); 561 /* … and then turn on the filter state. */ 562 ret = listbox_set_to_filter_state (l); 563 } 564 break; 323 565 case CK_View: 324 566 case CK_Edit: 325 567 case CK_Enter: … … listbox_key (WListbox * l, int key) 358 600 359 601 /* --------------------------------------------------------------------------------------------- */ 360 602 603 /* When called via g_queue_foreach it assigns the index field with an incremented int. */ 604 static void 605 listbox_foreach_apply_index (gpointer data, gpointer user_data) 606 { 607 WLEntry *e = data; 608 int *cur_idx = user_data; 609 e->index = *cur_idx; 610 *cur_idx = *cur_idx + 1; 611 } 612 613 /* --------------------------------------------------------------------------------------------- */ 614 361 615 /* Listbox item adding function */ 362 616 static inline void 363 617 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 736 widget_gotoyx (l, l->cursor_y, 0); 483 737 return MSG_HANDLED; 484 738 739 case MSG_UPDATE_LIST: 740 listbox_filter_list (l, (char *) data); 741 repaint_screen (); 742 return MSG_HANDLED; 743 485 744 case MSG_DRAW: 486 745 listbox_draw (l, widget_get_state (w, WST_FOCUSED)); 487 746 return MSG_HANDLED; … … listbox_new (int y, int x, int height, int width, gboolean deletable, lcback_fn 556 815 l = g_new (WListbox, 1); 557 816 w = WIDGET (l); 558 817 widget_init (w, y, x, height, width, listbox_callback, listbox_mouse_callback); 559 w->options |= WOP_SELECTABLE | WOP_WANT_HOTKEY;818 w->options |= WOP_SELECTABLE; 560 819 w->keymap = listbox_map; 561 820 562 821 l->list = NULL; 822 l->list_keep = NULL; 563 823 l->top = l->pos = 0; 564 824 l->deletable = deletable; 565 825 l->callback = callback; … … listbox_new (int y, int x, int height, int width, gboolean deletable, lcback_fn 571 831 572 832 /* --------------------------------------------------------------------------------------------- */ 573 833 834 /* 835 * If the list is filtered it replaces from the back list (list_keep). It returns whether such 836 * change occurred – FALSE means that the list was already unfiltered. 837 */ 838 gboolean 839 listbox_ensure_unfiltered_state (WListbox * l) 840 { 841 gboolean ret = FALSE; 842 if (listbox_is_filter_state (l)) 843 ret = listbox_set_to_normal_state (l); 844 return ret; 845 } 846 847 gboolean 848 listbox_conditionally_enable_multi_search (WListbox * l) 849 { 850 int start_with_multi_search_active; 851 852 /* Option of starting the listbox with MultiSearch pre-activated. */ 853 start_with_multi_search_active = 854 mc_config_get_bool (mc_global.main_config, CONFIG_APP_SECTION, 855 "multi_search_active_by_default", 1); 856 857 /* CK_MultiSearch toggles the state. */ 858 if (start_with_multi_search_active) 859 send_message (l, NULL, MSG_ACTION, CK_MultiSearch, NULL); 860 861 return start_with_multi_search_active; 862 } 863 864 /* --------------------------------------------------------------------------------------------- */ 865 574 866 /** 575 867 * Finds item by its label. 576 868 */ … … listbox_select_entry (WListbox * l, int dest) 673 965 if (l->pos - l->top >= lines) 674 966 l->top = l->pos - lines + 1; 675 967 } 968 /* 969 * Set the virtual position, i.e.: a position in the initial, unfiltered list if the 970 * same element would be selected. 971 */ 972 if (listbox_is_filter_state (l)) 973 { 974 WLEntry *e = listbox_get_nth_item (l, l->pos); 975 if (e) 976 l->virtual_pos = e->index; 977 else 978 l->virtual_pos = -1; 979 } 980 else 981 { 982 l->virtual_pos = pos; 983 } 676 984 return; 677 985 } 678 986 } … … listbox_remove_list (WListbox * l) 802 1110 803 1111 /* --------------------------------------------------------------------------------------------- */ 804 1112 1113 /* 1114 * Initializes the listbox elements with their position index. This allows to alter (filter, in 1115 * particular) the listbox elements order and still get the original index (when selecting an 1116 * element). 1117 */ 1118 void 1119 listbox_init_indices (WListbox * l) 1120 { 1121 int index = 0; 1122 g_queue_foreach (l->list, listbox_foreach_apply_index, &index); 1123 } 1124 1125 /* --------------------------------------------------------------------------------------------- */ 1126 805 1127 char * 806 1128 listbox_add_item (WListbox * l, listbox_append_t pos, int hotkey, const char *text, void *data, 807 1129 gboolean free_data) … … listbox_add_item (WListbox * l, listbox_append_t pos, int hotkey, const char *te 815 1137 return NULL; 816 1138 817 1139 entry = g_new (WLEntry, 1); 1140 entry->index = -1; /* Will be initialized when switching to the filter state */ 818 1141 entry->text = g_strdup (text); 1142 entry->free_text = 1; 819 1143 entry->data = data; 820 1144 entry->free_data = free_data; 821 1145 entry->hotkey = hotkey; -
lib/widget/listbox.h
diff --git a/lib/widget/listbox.h b/lib/widget/listbox.h index 8b2236eff..0d58d8998 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..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 -
src/editor/editcmd_dialogs.c
diff --git a/src/editor/editcmd_dialogs.c b/src/editor/editcmd_dialogs.c index 8b3634f23..8fe98f875 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..5e6ee40e5 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/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 };