Ticket #4165: MultiSearch_V4.3_final.patch
File MultiSearch_V4.3_final.patch, 65.8 KB (added by psprint, 4 years ago) |
---|
-
lib/keybind.c
From 316c57347747bcf0af6104497c5400f189dd0873 Mon Sep 17 00:00:00 2001 From: Sebastian Gniazdowski <sgniazdowski@gmail.com> Date: Fri, 15 Jan 2021 21:56:49 -0600 Subject: =?UTF-8?q?Multi=20Search=20=E2=80=93=20an=20AND-chained=20greppin?= =?UTF-8?q?g=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.h | 2 + lib/widget/Makefile.am | 2 + lib/widget/dialog-switch.c | 3 +- lib/widget/filtering_listbox.c | 448 +++++++++++++++++++++++++++++++++ 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 | 34 ++- lib/widget/input_complete.c | 72 ++++-- lib/widget/listbox-window.c | 24 +- lib/widget/listbox-window.h | 4 +- lib/widget/listbox.c | 53 +++- lib/widget/listbox.h | 5 + lib/widget/widget-common.c | 2 + lib/widget/widget-common.h | 10 +- src/editor/choosesyntax.c | 2 +- src/editor/editcmd_dialogs.c | 14 +- src/editor/editwidget.c | 8 +- src/file_history.c | 3 +- src/keybind-defaults.c | 5 +- src/selcodepage.c | 2 +- src/usermenu.c | 2 +- 27 files changed, 957 insertions(+), 104 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/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..a4bb8286c
- + 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 /* 76 * IF the enlarged dialog won't fit the screen, don't resize it but the listbox instead. 77 * Do it also when requested if the listbox is large (for small listboxes always try to 78 * enlarge the dialog). 79 */ 80 if ((sl->resize_strategy == FILT_LIST_DIALOG_AUTO_RESIZE && LINES <= owner->lines + 2) || 81 (sl->resize_strategy == FILT_LIST_KEEP_DIALOG_SIZE && owner->lines > 7)) 82 take_give_from_to_owner = 0; 83 84 // Increase the height of the dialog by 1, so that the new input fits. 85 if (should_add_free_room) 86 { 87 new_dialog_height = owner->lines + take_give_from_to_owner; 88 new_listbox_height = w->lines + (-1 + take_give_from_to_owner); 89 new_dialog_ypos = owner->y - take_give_from_to_owner; 90 } 91 else 92 { 93 new_dialog_height = owner->lines - take_give_from_to_owner; 94 new_listbox_height = w->lines - (-1 + take_give_from_to_owner); 95 new_dialog_ypos = owner->y + take_give_from_to_owner; 96 } 97 rect_init (&r_dialog, new_dialog_ypos, owner->x, new_dialog_height, owner->cols); 98 rect_init (&r_listbox, w->y, w->x, new_listbox_height, w->cols); 99 // Doing widget_set_size_rect(w, &r_listbox) causes problems as it invokes 100 // drawing of the widget owner. 101 send_message (w, NULL, MSG_RESIZE, 0, &r_listbox); 102 send_message (owner, NULL, MSG_RESIZE, 0, &r_dialog); 103 } 104 105 /* --------------------------------------------------------------------------------------------- */ 106 107 static void 108 filt_listbox_show_multi_search_widget (WFilteringListbox * sl) 109 { 110 WListbox *l = LISTBOX (sl); 111 Widget *w = WIDGET (l), *owner = WIDGET (WIDGET (l)->owner); 112 WForwardingInput *multi_search_in; 113 int distance_y = owner->y + owner->lines - (w->y + w->lines) + 1; 114 int distance_x = w->cols > 40 ? 5 : 1, small = w->cols <= 15 ? 1 : 0; 115 116 filt_listbox_make_one_line_room (sl, 1); 117 multi_search_in = forwarding_input_new (owner->lines - distance_y, distance_x, 118 input_colors, w->cols - 2 - distance_x + small, "", 119 "multi_search", INPUT_COMPLETE_NONE, w); 120 group_add_widget_autopos (GROUP (owner), multi_search_in, WPOS_KEEP_TOP | WPOS_CENTER_HORZ, 121 NULL); 122 123 widget_draw (WIDGET (w->owner)); 124 widget_draw (w); 125 widget_draw (WIDGET (multi_search_in)); 126 } 127 128 /* --------------------------------------------------------------------------------------------- */ 129 130 static void 131 filt_listbox_hide_multi_search_widget (WFilteringListbox * sl) 132 { 133 WListbox *l = LISTBOX (sl); 134 Widget *w = WIDGET (sl); 135 Widget *in; 136 in = widget_find_by_type (WIDGET (WIDGET (l)->owner), forw_input_callback); 137 if (in != NULL) 138 { 139 group_remove_widget (in); 140 filt_listbox_make_one_line_room (sl, 0); 141 group_select_next_widget (WIDGET (l)->owner); 142 // Repainting is needed because some part of the resized dialog can be left on the 143 // background. 144 if (sl->resize_strategy != FILT_LIST_KEEP_DIALOG_SIZE || w->lines <= 7) 145 repaint_screen (); 146 widget_draw (WIDGET (w->owner)); 147 widget_draw (w); 148 widget_destroy (in); 149 } 150 } 151 152 /* --------------------------------------------------------------------------------------------- */ 153 154 /* Return TRUE if given listbox is in WST_FILTER state. */ 155 static gboolean 156 filt_listbox_is_filter_state (WFilteringListbox * sl) 157 { 158 return widget_get_state (WIDGET (sl), WST_FILTER); 159 } 160 161 /* --------------------------------------------------------------------------------------------- */ 162 163 static void 164 filt_listbox_filter_list (WFilteringListbox * sl, const char *text) 165 { 166 WListbox *l = LISTBOX (sl); 167 int i, size; 168 GList *le; 169 char **query_terms; 170 171 /* 172 * Remove the list and allocate a new one. The elements are only shallowly freed because the 173 * internal data is still used (and kept in list_keep field). 174 */ 175 if (l->list != NULL) 176 g_queue_free_full (l->list, g_free); 177 l->list = g_queue_new (); 178 179 /* Split the query into space delimeted strings. */ 180 query_terms = g_strsplit (text, " ", 10); 181 182 /* 183 * Get the size of the listbox and iterate over it testing each element against ·all· words in 184 * query_terms. 185 */ 186 size = g_queue_get_length (sl->list_keep); 187 le = g_queue_peek_head_link (sl->list_keep); 188 for (i = 0; i < size; i++, le = g_list_next (le)) 189 { 190 WLEntry *e = LENTRY (le->data); 191 gboolean match = TRUE; 192 193 /* Test the query against the list entry. */ 194 for (gchar ** p = query_terms; *p != NULL; p++) 195 { 196 if (**p != '\0' && !strcasestr (e->text, *p)) 197 { 198 match = FALSE; 199 break; 200 } 201 } 202 203 /* If all the terms matched, then add the element to the list. */ 204 if (match) 205 g_queue_push_tail (l->list, filt_listbox_shallow_copy_entry (e, FALSE)); 206 } 207 if (listbox_is_empty (l)) 208 { 209 listbox_add_item (l, LISTBOX_APPEND_AT_END, 0, "<no search results>", NULL, FALSE); 210 LENTRY (g_queue_peek_head_link (l->list)->data)->index = -2; 211 } 212 size = g_queue_get_length (l->list); 213 if (l->pos >= size) 214 listbox_select_entry (l, size - 1); 215 else 216 listbox_select_entry (l, l->pos); 217 218 g_strfreev (query_terms); 219 } 220 221 /* --------------------------------------------------------------------------------------------- */ 222 223 /* Restores original elements of the list (from sl->list_keep) and turns off the WST_FILTER state. */ 224 static gboolean 225 filt_listbox_set_to_normal_state (WFilteringListbox * sl) 226 { 227 WListbox *l = LISTBOX (sl); 228 /* The listbox is already in non-filter state? */ 229 if (!widget_get_state (WIDGET (l), WST_FILTER)) 230 { 231 /* Return doing no change, just signal the error. */ 232 return FALSE; 233 } 234 235 /* The keep-list must be allocated (even if it's empty). */ 236 g_assert (sl->list_keep != NULL); 237 238 /* Mark the new state. */ 239 widget_set_state (WIDGET (l), WST_FILTER, FALSE); 240 241 /* Release the filtered list and replace it with the original, complete list (it owns the 242 * internal data, hence the release is a shallow one). */ 243 g_queue_free_full (l->list, g_free); 244 l->list = sl->list_keep; 245 sl->list_keep = NULL; 246 return TRUE; 247 } 248 249 /* --------------------------------------------------------------------------------------------- */ 250 251 /* 252 * Sets the listbox into ·filter· state. In this state, there's a separate copy of all list 253 * entries, while the original (and displayed) field ->list is being a filtered version of the 254 * full copy. 255 */ 256 static gboolean 257 filt_listbox_set_to_filter_state (WFilteringListbox * sl) 258 { 259 WListbox *l = LISTBOX (sl); 260 GList *le; 261 262 /* The listbox is already in filter state? */ 263 if (widget_get_state (WIDGET (l), WST_FILTER)) 264 { 265 /* Return doing no change, just signal the error. */ 266 return FALSE; 267 } 268 269 /* Mark the new state. */ 270 widget_set_state (WIDGET (l), WST_FILTER, TRUE); 271 272 /* No list copy when entering filter mode. */ 273 g_assert (sl->list_keep == NULL); 274 sl->list_keep = g_queue_new (); 275 276 /* Skip empty lists. */ 277 if (listbox_is_empty (l)) 278 return TRUE; 279 280 /* 281 * Remember the original position in the list in the field. It'll be used to determine the 282 * virtual_pos field. 283 */ 284 listbox_init_indices (l); 285 286 /* Perform a shallow copy of the original list. */ 287 for (le = g_queue_peek_head_link (l->list); le != NULL; le = g_list_next (le)) 288 { 289 WLEntry *copy; 290 copy = filt_listbox_shallow_copy_entry (LENTRY (le->data), TRUE); 291 g_queue_push_tail (sl->list_keep, copy); 292 } 293 294 return TRUE; 295 } 296 297 /* --------------------------------------------------------------------------------------------- */ 298 299 /* 300 * If the list is filtered it replaces from the back list (list_keep). It returns whether such 301 * change occurred – FALSE means that the list was already unfiltered. 302 */ 303 gboolean 304 filt_listbox_ensure_unfiltered_state (WFilteringListbox * sl) 305 { 306 gboolean ret = FALSE; 307 if (filt_listbox_is_filter_state (sl)) 308 ret = filt_listbox_set_to_normal_state (sl); 309 return ret; 310 } 311 312 /* --------------------------------------------------------------------------------------------- */ 313 314 gboolean 315 filt_listbox_conditionally_enable_multi_search_init (WFilteringListbox * sl) 316 { 317 int start_with_multi_search_active; 318 319 /* Option of starting the listbox with MultiSearch pre-activated. */ 320 start_with_multi_search_active = 321 mc_config_get_bool (mc_global.main_config, CONFIG_APP_SECTION, 322 "multi_search_active_by_default", 1); 323 324 /* CK_MultiSearch toggles the state. */ 325 if (start_with_multi_search_active) 326 send_message (WIDGET (sl), NULL, MSG_ACTION, CK_MultiSearch, NULL); 327 else 328 /* Only init embedded position indices. */ 329 listbox_init_indices (LISTBOX (sl)); 330 331 return start_with_multi_search_active; 332 } 333 334 /* --------------------------------------------------------------------------------------------- */ 335 /*** public functions ****************************************************************************/ 336 /* --------------------------------------------------------------------------------------------- */ 337 338 WFilteringListbox * 339 filtering_listbox_new (int y, int x, int height, int width, 340 gboolean deletable, lcback_fn callback, 341 filt_listbox_resize_strategy_t resize) 342 { 343 WListbox *base; 344 WFilteringListbox *object; 345 Widget *w; 346 347 /* Allocate memory for the object body. */ 348 object = g_new (WFilteringListbox, 1); 349 350 /* Forward the call to construct the inherited object. */ 351 base = listbox_new (y, x, height, width, deletable, callback); 352 353 /* Shallow copy and shallow release. */ 354 object->base = *base; 355 g_free (base); 356 357 /* Alter fields of base class. */ 358 w = WIDGET (object); 359 object->base_callback = w->callback; /* Save original callback function */ 360 w->callback = filt_listbox_callback; /* Set custom callback handler */ 361 362 /* Set extending fields of this class. */ 363 object->list_keep = NULL; /* No back buffer at startup */ 364 object->resize_strategy = resize; /* Save resize strategy */ 365 object->initialized = FALSE; 366 367 return object; 368 } 369 370 /* --------------------------------------------------------------------------------------------- */ 371 372 cb_ret_t 373 filt_listbox_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data) 374 { 375 WFilteringListbox *sl = FILT_LISTBOX (w); /* s* – from `screen`, a "screened" listbox */ 376 cb_ret_t ret = MSG_NOT_HANDLED; 377 long activity; 378 379 switch (msg) 380 { 381 case MSG_INIT: 382 if (!sl->initialized) 383 { 384 filt_listbox_conditionally_enable_multi_search_init (sl); 385 sl->initialized = TRUE; 386 } 387 /* WListbox doesn't have MSG_INIT, so don't forward. */ 388 ret = MSG_HANDLED; 389 break; 390 case MSG_ACTION: 391 if (parm == CK_MultiSearch) 392 { 393 gboolean retval; 394 /* Toggle the multi term searching of any listbox. */ 395 if (filt_listbox_is_filter_state (sl)) 396 { 397 /* Remove the input widget from the dialog. */ 398 filt_listbox_hide_multi_search_widget (sl); 399 /* Restore original (unfiltered) listbox contents. */ 400 retval = filt_listbox_set_to_normal_state (sl); 401 } 402 else 403 { 404 /* Add input widget for the filter query at the bottom of the dialog window. */ 405 filt_listbox_show_multi_search_widget (sl); 406 /* … and then turn on the filter state. */ 407 retval = filt_listbox_set_to_filter_state (sl); 408 } 409 if (!retval) 410 message (D_ERROR | D_CENTER, MSG_ERROR, 411 "An internal error #3 occurred (filtered listbox support)."); 412 413 ret = MSG_HANDLED; 414 } 415 break; 416 417 case MSG_KEY: 418 activity = widget_lookup_key (WIDGET (sl), parm); 419 if (activity == CK_MultiSearch) 420 ret = send_message (w, NULL, MSG_ACTION, CK_MultiSearch, NULL); 421 break; 422 423 case MSG_UPDATE_LIST: 424 if (widget_get_state (w, WST_FILTER)) 425 { 426 filt_listbox_filter_list (sl, (char *) data); 427 ret = MSG_HANDLED; 428 } 429 widget_draw (w); 430 widget_draw (sender); 431 break; 432 433 case MSG_DESTROY: 434 filt_listbox_ensure_unfiltered_state (sl); 435 /* ret is unhandled → the message will be forwarded to base class. */ 436 break; 437 default: 438 break; 439 } 440 441 /* Forward action to base class in case it's not yet handled. */ 442 if (ret == MSG_NOT_HANDLED) 443 ret = sl->base_callback (w, sender, msg, parm, data); 444 445 return ret; 446 } 447 448 /* --------------------------------------------------------------------------------------------- */ -
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..1b0157f88
- + 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 != NULL && g_strcmp0 (str_cp, in->base.buffer) != 0) 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..f16de833f 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 != NULL) 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..2ee719ca7 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 != NULL) 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..653e870cd 100644
a b input_save_history (const gchar * event_group_name, const gchar * event_name, 871 871 (void) event_group_name; 872 872 (void) event_name; 873 873 874 if (!in->is_password && (DIALOG (WIDGET (in)->owner)->ret_value != B_CANCEL)) 874 if (!in->is_password && WIDGET (in)->owner 875 && (DIALOG (WIDGET (in)->owner)->ret_value != B_CANCEL)) 875 876 { 876 877 ev_history_load_save_t *ev = (ev_history_load_save_t *) data; 877 878 … … input_mouse_callback (Widget * w, mouse_msg_t msg, mouse_event_t * event) 970 971 } 971 972 } 972 973 974 /* --------------------------------------------------------------------------------------------- */ 975 976 static void 977 listbox_unregister_history_events_cb (gpointer wid_ptr) 978 { 979 Widget *w = WIDGET (wid_ptr); 980 WDialog *h = DIALOG (w->owner); 981 982 /* unsubscribe from "history_load" event */ 983 mc_event_del (h->event_group, MCEVENT_HISTORY_LOAD, input_load_history, w); 984 /* unsubscribe from "history_save" event */ 985 mc_event_del (h->event_group, MCEVENT_HISTORY_SAVE, input_save_history, w); 986 } 987 973 988 /* --------------------------------------------------------------------------------------------- */ 974 989 /*** public functions ****************************************************************************/ 975 990 /* --------------------------------------------------------------------------------------------- */ … … input_new (int y, int x, const int *colors, int width, const char *def_text, 1028 1043 if ((histname != NULL) && (*histname != '\0')) 1029 1044 in->history.name = g_strdup (histname); 1030 1045 /* history will be loaded later */ 1031 1032 1046 in->label = NULL; 1033 1047 1034 1048 return in; … … input_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *d 1050 1064 mc_event_add (h->event_group, MCEVENT_HISTORY_LOAD, input_load_history, w, NULL); 1051 1065 /* subscribe to "history_save" event */ 1052 1066 mc_event_add (h->event_group, MCEVENT_HISTORY_SAVE, input_save_history, w, NULL); 1067 /* unregister (via the func) the events in case of removal from dialog */ 1068 w->pre_unlink_func = listbox_unregister_history_events_cb; 1053 1069 if (in->label != NULL) 1054 1070 widget_set_state (WIDGET (in->label), WST_DISABLED, widget_get_state (w, WST_DISABLED)); 1055 1071 return MSG_HANDLED; … … input_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *d 1063 1079 return v; 1064 1080 } 1065 1081 1066 /* Keys we want others to handle */1082 /* Keys we want others to handle. */ 1067 1083 if (parm == KEY_UP || parm == KEY_DOWN || parm == ESC_CHAR 1068 1084 || parm == KEY_F (10) || parm == '\n') 1069 1085 return MSG_NOT_HANDLED; … … input_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *d 1097 1113 return MSG_HANDLED; 1098 1114 1099 1115 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); 1116 /* …only, if there is an owner WGroup. */ 1117 if (h != NULL) 1118 { 1119 /* unsubscribe from "history_load" event */ 1120 mc_event_del (h->event_group, MCEVENT_HISTORY_LOAD, input_load_history, w); 1121 /* unsubscribe from "history_save" event */ 1122 mc_event_del (h->event_group, MCEVENT_HISTORY_SAVE, input_save_history, w); 1123 } 1104 1124 input_destroy (in); 1105 1125 return MSG_HANDLED; 1106 1126 -
lib/widget/input_complete.c
diff --git a/lib/widget/input_complete.c b/lib/widget/input_complete.c index c23fe6063..7bee2ff11 100644
a b complete_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void 1017 1017 { 1018 1018 static int bl = 0; 1019 1019 1020 WGroup *g = GROUP (w);1021 1020 WDialog *h = DIALOG (w); 1021 WFilteringListbox *slw; 1022 cb_ret_t ret = MSG_NOT_HANDLED; 1023 1024 /* Find the listbox in dialog's group. */ 1025 slw = FILT_LISTBOX (WIDGET (h)->find_by_type (WIDGET (h), filt_listbox_callback)); 1022 1026 1023 1027 switch (msg) 1024 1028 { … … complete_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void 1027 1031 { 1028 1032 case KEY_LEFT: 1029 1033 case KEY_RIGHT: 1034 /* MultiSearch (list filtering) is allowing for left/right movement in query input. */ 1035 if (widget_get_state (WIDGET (slw), WST_FILTER)) 1036 break; 1030 1037 bl = 0; 1031 1038 h->ret_value = 0; 1032 1039 dlg_stop (h); 1033 return MSG_HANDLED; 1040 ret = MSG_HANDLED; 1041 break; 1034 1042 1035 1043 case KEY_BACKSPACE: 1044 /* MultiSearch is exclusive with completion widening. */ 1045 if (widget_get_state (WIDGET (slw), WST_FILTER)) 1046 break; 1036 1047 bl = 0; 1037 1048 /* exit from completion list if input line is empty */ 1038 1049 if (end == 0) … … complete_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void 1056 1067 1057 1068 new_end = str_get_prev_char (&input->buffer[end]) - input->buffer; 1058 1069 1059 for (i = 0, e = listbox_get_first_link (LISTBOX ( g->current->data));1070 for (i = 0, e = listbox_get_first_link (LISTBOX (slw)); 1060 1071 e != NULL; i++, e = g_list_next (e)) 1061 1072 { 1062 1073 WLEntry *le = LENTRY (e->data); 1063 1074 1064 1075 if (strncmp (input->buffer + start, le->text, new_end - start) == 0) 1065 1076 { 1066 listbox_select_entry (LISTBOX ( g->current->data), i);1077 listbox_select_entry (LISTBOX (slw), i); 1067 1078 end = new_end; 1068 1079 input_handle_char (input, parm); 1069 widget_draw (WIDGET ( g->current->data));1080 widget_draw (WIDGET (slw)); 1070 1081 break; 1071 1082 } 1072 1083 } 1073 1084 } 1074 return MSG_HANDLED; 1085 ret = MSG_HANDLED; 1086 break; 1075 1087 1076 1088 default: 1077 1089 if (parm < 32 || parm > 255) 1078 1090 { 1079 1091 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); 1092 /* CK_Complete → Is completion up to date? */ 1093 if ((widget_lookup_key (WIDGET (input), parm) == CK_Complete) && (end != min_end)) 1094 { 1095 /* This means we want to refill the list box and start again. */ 1096 h->ret_value = B_USER; 1097 dlg_stop (h); 1098 } 1099 /* 1100 * else – key will be ignored by this function, so leave ret unchanged – allow other 1101 * widgets to process it. 1102 */ 1089 1103 } 1090 else 1104 /* 1105 * Do standard live entry lookup only when not filtering list via MultiSearch – it is 1106 * a feature that to a great extent replaces old engine. It can be still used as MultiSearch 1107 * can be dynamically enabled and disabled (default by alt-space and ctrl-space). 1108 */ 1109 else if (!widget_get_state (WIDGET (slw), WST_FILTER)) 1091 1110 { 1092 1111 static char buff[MB_LEN_MAX] = ""; 1093 1112 GList *e; … … complete_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void 1110 1129 break; 1111 1130 } 1112 1131 1113 for (i = 0, e = listbox_get_first_link (LISTBOX ( g->current->data));1132 for (i = 0, e = listbox_get_first_link (LISTBOX (slw)); 1114 1133 e != NULL; i++, e = g_list_next (e)) 1115 1134 { 1116 1135 WLEntry *le = LENTRY (e->data); … … complete_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void 1121 1140 if (need_redraw == 0) 1122 1141 { 1123 1142 need_redraw = 1; 1124 listbox_select_entry (LISTBOX ( g->current->data), i);1143 listbox_select_entry (LISTBOX (slw), i); 1125 1144 last_text = le->text; 1126 1145 } 1127 1146 else … … complete_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void 1172 1191 if (need_redraw == 2) 1173 1192 { 1174 1193 insert_text (input, last_text, low); 1175 widget_draw (WIDGET ( g->current->data));1194 widget_draw (WIDGET (slw)); 1176 1195 } 1177 1196 else if (need_redraw == 1) 1178 1197 { … … complete_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void 1180 1199 dlg_stop (h); 1181 1200 } 1182 1201 bl = 0; 1202 ret = MSG_HANDLED; 1183 1203 } 1184 1204 } 1185 return MSG_HANDLED;1205 break; 1186 1206 1187 1207 default: 1188 1208 return dlg_default_callback (w, sender, msg, parm, data); 1189 1209 } 1210 return ret; 1190 1211 } 1191 1212 1192 1213 /* --------------------------------------------------------------------------------------------- */ … … complete_engine (WInput * in, int what_to_do) 1223 1244 int start_x, start_y; 1224 1245 char **p, *q; 1225 1246 WDialog *complete_dlg; 1226 W Listbox *complete_list;1247 WFilteringListbox *complete_list; 1227 1248 1228 1249 for (p = in->completions + 1; *p != NULL; count++, p++) 1229 1250 { … … complete_engine (WInput * in, int what_to_do) 1266 1287 complete_dlg = 1267 1288 dlg_create (TRUE, y, x, complete_height, complete_width, WPOS_KEEP_DEFAULT, TRUE, 1268 1289 dialog_colors, complete_callback, NULL, "[Completion]", NULL); 1269 complete_list = listbox_new (1, 1, h - 2, w - 2, FALSE, NULL); 1290 complete_list = filtering_listbox_new (1, 1, h - 2, w - 2, FALSE, NULL, 1291 FILT_LIST_KEEP_DIALOG_SIZE); 1270 1292 group_add_widget (GROUP (complete_dlg), complete_list); 1271 1293 1272 1294 for (p = in->completions + 1; *p != NULL; p++) 1273 listbox_add_item (complete_list, LISTBOX_APPEND_AT_END, 0, *p, NULL, FALSE); 1295 listbox_add_item (LISTBOX (complete_list), LISTBOX_APPEND_AT_END, 0, *p, NULL, 1296 FALSE); 1274 1297 1275 1298 i = dlg_run (complete_dlg); 1276 1299 q = NULL; 1277 1300 if (i == B_ENTER) 1278 1301 { 1279 listbox_get_current ( complete_list, &q, NULL);1302 listbox_get_current (LISTBOX (complete_list), &q, NULL); 1280 1303 if (q != NULL) 1281 1304 insert_text (in, q, strlen (q)); 1282 1305 } 1283 1306 if (q != NULL || end != min_end) 1284 1307 input_complete_free (in); 1308 1285 1309 dlg_destroy (complete_dlg); 1286 1310 1287 1311 /* B_USER if user wants to start over again */ -
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..942dad7a9 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 369 /* Set the index and increment it. */ 370 e->index = *cur_idx; 371 *cur_idx = *cur_idx + 1; 372 } 373 374 /* --------------------------------------------------------------------------------------------- */ 375 361 376 /* Listbox item adding function */ 362 377 static inline void 363 378 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 571 l = g_new (WListbox, 1); 557 572 w = WIDGET (l); 558 573 widget_init (w, y, x, height, width, listbox_callback, listbox_mouse_callback); 559 w->options |= WOP_SELECTABLE | WOP_WANT_HOTKEY;574 w->options |= WOP_SELECTABLE; 560 575 w->keymap = listbox_map; 561 576 562 577 l->list = NULL; 563 l->top = l->pos = 0;578 l->top = l->pos = l->virtual_pos = 0; 564 579 l->deletable = deletable; 565 580 l->callback = callback; 566 581 l->allow_duplicates = TRUE; … … listbox_select_last (WListbox * l) 648 663 void 649 664 listbox_select_entry (WListbox * l, int dest) 650 665 { 666 WLEntry *e; 651 667 GList *le; 652 668 int pos; 653 669 gboolean top_seen = FALSE; 654 670 655 671 if (listbox_is_empty (l) || dest < 0) 672 { 673 /* Reset position to a minimal one. */ 674 l->pos = 0; 675 l->virtual_pos = 0; 656 676 return; 677 } 657 678 658 679 /* Special case */ 659 680 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 694 if (l->pos - l->top >= lines) 674 695 l->top = l->pos - lines + 1; 675 696 } 697 /* 698 * Set the virtual position, i.e.: a position in the initial, unfiltered list if the 699 * same element would be selected. 700 */ 701 e = LENTRY (le->data); 702 l->virtual_pos = e->index; 676 703 return; 677 704 } 678 705 } … … listbox_get_current (WListbox * l, char **string, void **extra) 701 728 if (l != NULL) 702 729 e = listbox_get_nth_item (l, l->pos); 703 730 704 ok = (e != NULL );731 ok = (e != NULL && e->index != -2); 705 732 706 733 if (string != NULL) 707 734 *string = ok ? e->text : NULL; … … listbox_get_nth_item (const WListbox * l, int pos) 720 747 GList *item; 721 748 722 749 item = g_queue_peek_nth_link (l->list, (guint) pos); 723 if (item != NULL )750 if (item != NULL && LENTRY (item->data)->index != -2) 724 751 return LENTRY (item->data); 725 752 } 726 753 … … listbox_remove_list (WListbox * l) 802 829 803 830 /* --------------------------------------------------------------------------------------------- */ 804 831 832 /* 833 * Initializes the listbox elements with their position index. This allows to alter (filter, in 834 * particular) the listbox elements order and still get the original index (when selecting an 835 * element). 836 */ 837 void 838 listbox_init_indices (WListbox * l) 839 { 840 int index = 0; 841 g_queue_foreach (l->list, listbox_foreach_apply_index, &index); 842 } 843 844 /* --------------------------------------------------------------------------------------------- */ 845 805 846 char * 806 847 listbox_add_item (WListbox * l, listbox_append_t pos, int hotkey, const char *text, void *data, 807 848 gboolean free_data) … … listbox_add_item (WListbox * l, listbox_append_t pos, int hotkey, const char *te 815 856 return NULL; 816 857 817 858 entry = g_new (WLEntry, 1); 859 entry->index = -1; /* Will be initialized when switching to the filter state */ 818 860 entry->text = g_strdup (text); 861 entry->free_text = 1; 819 862 entry->data = data; 820 863 entry->free_data = free_data; 821 864 entry->hotkey = hotkey; -
lib/widget/listbox.h
diff --git a/lib/widget/listbox.h b/lib/widget/listbox.h index 8b2236eff..53b3e0f45 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 (used 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 -
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..2bfb71900 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_DIALOG_AUTO_RESIZE); 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/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..ba10634ab 100644
a b user_menu_cmd (const WEdit * edit_widget, const char *menu_file, int selected_en 1122 1122 extract_line (p, p + MAX_ENTRY_LEN), p, FALSE); 1123 1123 } 1124 1124 /* Select the default entry */ 1125 listbox_select_entry ( listbox->list, selected);1125 listbox_select_entry (LISTBOX (listbox->list), selected); 1126 1126 1127 1127 selected = run_listbox (listbox); 1128 1128 }