Ticket #4165: 0001-Add-MultiSearch-command-a-multi-term-AND-conjugated-.patch

File 0001-Add-MultiSearch-command-a-multi-term-AND-conjugated-.patch, 37.3 KB (added by psprint, 17 months ago)
  • lib/keybind.c

    From 7d412193dd93d16998b196cd09cf0187b22f08e9 Mon Sep 17 00:00:00 2001
    From: Sebastian Gniazdowski <sgniazdowski@gmail.com>
    Date: Sat, 2 Jan 2021 15:54:32 -0600
    Subject: [PATCH] =?UTF-8?q?Add=20MultiSearch=20command=20=E2=80=93=20a=20m?=
     =?UTF-8?q?ulti=20term=20AND-conjugated=20filtering=20of=20any=20listbox.?=
    MIME-Version: 1.0
    Content-Type: text/plain; charset=UTF-8
    Content-Transfer-Encoding: 8bit
    
    ---
     lib/keybind.c                |   1 +
     lib/keybind.h                |   1 +
     lib/widget/group.c           |  11 +-
     lib/widget/history.c         |  74 +++++----
     lib/widget/input.c           | 106 +++++++++---
     lib/widget/input.h           |   2 +
     lib/widget/listbox-window.c  |  13 +-
     lib/widget/listbox.c         | 304 ++++++++++++++++++++++++++++++++++-
     lib/widget/listbox.h         |   7 +
     lib/widget/widget-common.c   |   2 +
     lib/widget/widget-common.h   |  10 +-
     src/editor/choosesyntax.c    |   6 +
     src/editor/editcmd_dialogs.c |   6 +
     src/editor/editwidget.c      |   3 +
     src/editor/spell_dialogs.c   |   6 +
     src/filemanager/listmode.c   |   6 +
     src/keybind-defaults.c       |   5 +-
     src/selcodepage.c            |   6 +
     src/usermenu.c               |   6 +
     19 files changed, 511 insertions(+), 64 deletions(-)
    
    diff --git a/lib/keybind.c b/lib/keybind.c
    index abd44d3e2..df3cbf110 100644
    a b static name_keymap_t command_names[] = { 
    9393    ADD_KEYMAP_NAME (SearchContinue), 
    9494    ADD_KEYMAP_NAME (Replace), 
    9595    ADD_KEYMAP_NAME (ReplaceContinue), 
     96    ADD_KEYMAP_NAME (MultiSearch), 
    9697    ADD_KEYMAP_NAME (Help), 
    9798    ADD_KEYMAP_NAME (Shell), 
    9899    ADD_KEYMAP_NAME (Edit), 
  • lib/keybind.h

    diff --git a/lib/keybind.h b/lib/keybind.h
    index 9638bd651..96e3500ab 100644
    a b enum 
    8282    CK_SearchContinue, 
    8383    CK_Replace, 
    8484    CK_ReplaceContinue, 
     85    CK_MultiSearch, 
    8586    CK_SearchStop, 
    8687    CK_Help, 
    8788    CK_Edit, 
  • lib/widget/group.c

    diff --git a/lib/widget/group.c b/lib/widget/group.c
    index f8e318bd9..c06fbc58e 100644
    a b group_add_widget_autopos (WGroup * g, void *w, widget_pos_flags_t pos_flags, con 
    749749 * @param w Widget object 
    750750 */ 
    751751void 
    752 group_remove_widget (void *w) 
     752group_remove_widget (void *wid) 
    753753{ 
     754    Widget *w = WIDGET(wid); 
    754755    WGroup *g; 
    755756    GList *d; 
    756757 
    757758    /* Don't accept NULL widget. This shouldn't happen */ 
    758759    assert (w != NULL); 
    759760 
    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; 
    761766 
    762767    d = g_list_find (g->widgets, w); 
    763768    if (d == g->current) 
    group_remove_widget (void *w) 
    774779        group_select_current_widget (g); 
    775780    } 
    776781 
    777     WIDGET (w)->owner = NULL; 
     782    w->owner = NULL; 
    778783} 
    779784 
    780785/* --------------------------------------------------------------------------------------------- */ 
  • lib/widget/history.c

    diff --git a/lib/widget/history.c b/lib/widget/history.c
    index 775d02b1b..e0e673442 100644
    a b typedef struct 
    6666/*** file scope functions ************************************************************************/ 
    6767 
    6868static cb_ret_t 
    69 history_dlg_reposition (WDialog * dlg_head) 
     69history_dlg_reposition (WDialog * dlg_head, WRect *resize) 
    7070{ 
    71     history_dlg_data *data; 
    72     int x = 0, y, he, wi; 
    7371    WRect r; 
    7472 
    75     /* guard checks */ 
    76     if ((dlg_head == NULL) || (dlg_head->data == NULL)) 
    77         return MSG_NOT_HANDLED; 
     73    if (!resize) { 
     74        history_dlg_data *data; 
     75        int x = 0, y, he, wi; 
    7876 
    79     data = (history_dlg_data *) dlg_head->data; 
     77        /* guard checks */ 
     78        if ((dlg_head == NULL) || (dlg_head->data == NULL)) 
     79            return MSG_NOT_HANDLED; 
    8080 
    81     y = data->y; 
    82     he = data->count + 2; 
     81        data = (history_dlg_data *) dlg_head->data; 
    8382 
    84     if (he <= y || y > (LINES - 6)) 
    85     { 
    86         he = MIN (he, y - 1); 
    87         y -= he; 
    88     } 
    89     else 
    90     { 
    91         y++; 
    92         he = MIN (he, LINES - y); 
     83        y = data->y; 
     84        he = data->count + 2; 
     85 
     86        if (he <= y || y > (LINES - 6)) 
     87        { 
     88            he = MIN (he, y - 1); 
     89            y -= he; 
     90        } 
     91        else 
     92        { 
     93            y++; 
     94            he = MIN (he, LINES - y); 
     95        } 
     96 
     97        if (data->x > 2) 
     98            x = data->x - 2; 
     99 
     100        wi = data->max_width + 4; 
     101 
     102        if ((wi + x) > COLS) 
     103        { 
     104            wi = MIN (wi, COLS); 
     105            x = COLS - wi; 
     106        } 
     107        rect_init (&r, y, x, he, wi); 
     108    } else { 
     109        /* A resize from some other code (currently from the listbox filter). */ 
     110        r = *resize; 
    93111    } 
    94112 
    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); 
    107113 
    108114    return dlg_default_callback (WIDGET (dlg_head), NULL, MSG_RESIZE, 0, &r); 
    109115} 
    history_dlg_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, v 
    116122    switch (msg) 
    117123    { 
    118124    case MSG_RESIZE: 
    119         return history_dlg_reposition (DIALOG (w)); 
     125        return history_dlg_reposition (DIALOG (w), data); 
    120126 
    121127    case MSG_NOTIFY: 
    122128        { 
    history_show (history_descriptor_t * hd) 
    257263            listbox_select_entry (hd->listbox, hd->current); 
    258264    } 
    259265 
     266    /* Option of starting the listbox with MultiSearch pre-activated. */ 
     267    listbox_conditionally_enable_multi_search(hd->listbox); 
     268 
    260269    dlg_ret = dlg_run (query_dlg); 
    261270    if (dlg_ret != B_CANCEL) 
    262271    { 
    history_show (history_descriptor_t * hd) 
    278287        hd->text = g_strdup (q); 
    279288    } 
    280289 
     290    /* If needed, restore normal listbox state, with no backlist (list_keep). */ 
     291    listbox_ensure_unfiltered_state(hd->listbox); 
     292 
    281293    /* get modified history from dialog */ 
    282294    z = NULL; 
    283295    for (hi = listbox_get_first_link (hd->listbox); hi != NULL; hi = g_list_next (hi)) 
  • lib/widget/input.c

    diff --git a/lib/widget/input.c b/lib/widget/input.c
    index b5cec7e6b..7dbb94f4c 100644
    a b input_execute_cmd (WInput * in, long command) 
    827827        break; 
    828828    } 
    829829 
     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    } 
    830851    return res; 
    831852} 
    832853 
    input_save_history (const gchar * event_group_name, const gchar * event_name, 
    871892    (void) event_group_name; 
    872893    (void) event_name; 
    873894 
    874     if (!in->is_password && (DIALOG (WIDGET (in)->owner)->ret_value != B_CANCEL)) 
     895    if (!in->is_password && WIDGET (in)->owner && (DIALOG (WIDGET (in)->owner)->ret_value != B_CANCEL)) 
    875896    { 
    876897        ev_history_load_save_t *ev = (ev_history_load_save_t *) data; 
    877898 
    input_mouse_callback (Widget * w, mouse_msg_t msg, mouse_event_t * event) 
    970991    } 
    971992} 
    972993 
     994/* --------------------------------------------------------------------------------------------- */ 
     995 
     996static 
     997void listbox_unregister_history_events_cb(gpointer wid_ptr) 
     998{ 
     999    Widget *w = WIDGET(wid_ptr); 
     1000    WDialog *h = DIALOG(w->owner); 
     1001 
     1002    /* unsubscribe from "history_load" event */ 
     1003    mc_event_del (h->event_group, MCEVENT_HISTORY_LOAD, input_load_history, w); 
     1004    /* unsubscribe from "history_save" event */ 
     1005    mc_event_del (h->event_group, MCEVENT_HISTORY_SAVE, input_save_history, w); 
     1006} 
     1007 
    9731008/* --------------------------------------------------------------------------------------------- */ 
    9741009/*** public functions ****************************************************************************/ 
    9751010/* --------------------------------------------------------------------------------------------- */ 
    input_new (int y, int x, const int *colors, int width, const char *def_text, 
    10281063    if ((histname != NULL) && (*histname != '\0')) 
    10291064        in->history.name = g_strdup (histname); 
    10301065    /* history will be loaded later */ 
    1031  
    10321066    in->label = NULL; 
     1067    in->forward_listbox = NULL; 
    10331068 
    10341069    return in; 
    10351070} 
    input_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *d 
    10501085        mc_event_add (h->event_group, MCEVENT_HISTORY_LOAD, input_load_history, w, NULL); 
    10511086        /* subscribe to "history_save" event */ 
    10521087        mc_event_add (h->event_group, MCEVENT_HISTORY_SAVE, input_save_history, w, NULL); 
     1088        /* unregister (via the func) the events in case of removal from dialog */ 
     1089        w->pre_unlink_func = listbox_unregister_history_events_cb; 
    10531090        if (in->label != NULL) 
    10541091            widget_set_state (WIDGET (in->label), WST_DISABLED, widget_get_state (w, WST_DISABLED)); 
    10551092        return MSG_HANDLED; 
    input_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *d 
    10631100            return v; 
    10641101        } 
    10651102 
    1066         /* Keys we want others to handle */ 
    1067         if (parm == KEY_UP || parm == KEY_DOWN || parm == ESC_CHAR 
    1068             || parm == KEY_F (10) || parm == '\n') 
    1069             return MSG_NOT_HANDLED; 
    1070  
    1071         /* When pasting multiline text, insert literal Enter */ 
    1072         if ((parm & ~KEY_M_MASK) == '\n') 
    1073         { 
    1074             quote = TRUE; 
    1075             v = input_handle_char (in, '\n'); 
    1076             quote = FALSE; 
    1077             return v; 
     1103        /* If not forwarding the keys to a paired listbox forward_listbox… */ 
     1104        if (!in->forward_listbox || parm == ESC_CHAR || parm == '\n') { 
     1105            /* Keys we want others to handle, if not forwarding them to a paired listbox */ 
     1106            if (parm == KEY_UP || parm == KEY_DOWN || parm == ESC_CHAR 
     1107                || parm == KEY_F (10) || parm == '\n') 
     1108                return MSG_NOT_HANDLED; 
     1109            /* When pasting multiline text, insert literal Enter */ 
     1110            if ((parm & ~KEY_M_MASK) == '\n') 
     1111            { 
     1112                quote = TRUE; 
     1113                v = input_handle_char (in, '\n'); 
     1114                quote = FALSE; 
     1115                return v; 
     1116            } 
    10781117        } 
    10791118 
    10801119        return input_handle_char (in, parm); 
    input_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *d 
    10971136        return MSG_HANDLED; 
    10981137 
    10991138    case MSG_DESTROY: 
    1100         /* unsubscribe from "history_load" event */ 
    1101         mc_event_del (h->event_group, MCEVENT_HISTORY_LOAD, input_load_history, w); 
    1102         /* unsubscribe from "history_save" event */ 
    1103         mc_event_del (h->event_group, MCEVENT_HISTORY_SAVE, input_save_history, w); 
     1139        /* …only, if there is an owner WGroup. */ 
     1140        if(h) 
     1141        { 
     1142            /* unsubscribe from "history_load" event */ 
     1143            mc_event_del (h->event_group, MCEVENT_HISTORY_LOAD, input_load_history, w); 
     1144            /* unsubscribe from "history_save" event */ 
     1145            mc_event_del (h->event_group, MCEVENT_HISTORY_SAVE, input_save_history, w); 
     1146        } 
    11041147        input_destroy (in); 
    11051148        return MSG_HANDLED; 
    11061149 
    input_handle_char (WInput * in, int key) 
    11401183    command = widget_lookup_key (WIDGET (in), key); 
    11411184    if (command == CK_IgnoreKey) 
    11421185    { 
    1143         if (key > 255) 
    1144             return MSG_NOT_HANDLED; 
    1145         if (in->first) 
    1146             port_region_marked_for_delete (in); 
    1147         input_complete_free (in); 
    1148         v = insert_char (in, key); 
     1186        /* Should we forward keys to a paired listbox? */ 
     1187        if (in->forward_listbox) 
     1188            command = widget_lookup_key (WIDGET (in->forward_listbox), key); 
     1189        if (command == CK_IgnoreKey) 
     1190        { 
     1191            if (key > 255) 
     1192                return MSG_NOT_HANDLED; 
     1193            if (in->first) 
     1194                port_region_marked_for_delete (in); 
     1195            input_complete_free (in); 
     1196            v = insert_char (in, key); 
     1197        } else { 
     1198            /* Forward the listbox command to the paired listbox. */ 
     1199            send_message (WIDGET(in->forward_listbox), NULL, MSG_KEY, key, NULL); 
     1200            send_message (WIDGET(in->forward_listbox), NULL, MSG_DRAW, 0, NULL); 
     1201            return MSG_HANDLED; 
     1202        } 
    11491203    } 
    11501204    else 
    11511205    { 
    input_handle_char (WInput * in, int key) 
    11581212    } 
    11591213 
    11601214    input_update (in, TRUE); 
     1215 
     1216    /* Signal any filtering listbox that the query has been updated. */ 
     1217    if (in->forward_listbox) 
     1218        send_message(WIDGET(in->forward_listbox), NULL, MSG_UPDATE_LIST, key, in->buffer); 
    11611219    return v; 
    11621220} 
    11631221 
  • lib/widget/input.h

    diff --git a/lib/widget/input.h b/lib/widget/input.h
    index a753e6160..eaea031d0 100644
    a b typedef struct 
    7070        GList *current;         /* current history item */ 
    7171        gboolean changed;       /* the history has changed */ 
    7272    } history; 
     73    WListbox *forward_listbox;  /* a listbox that should have some of the actions forwarded to */ 
     74 
    7375} WInput; 
    7476 
    7577/*** global variables defined in .c file *********************************************************/ 
  • lib/widget/listbox-window.c

    diff --git a/lib/widget/listbox-window.c b/lib/widget/listbox-window.c
    index 44548b485..42a38ac4e 100644
    a b run_listbox (Listbox * l) 
    130130{ 
    131131    int val = -1; 
    132132 
     133    /* Option of starting the listbox with MultiSearch activated. */ 
     134    listbox_conditionally_enable_multi_search(l->list); 
     135 
    133136    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 
    135143    dlg_destroy (l->dlg); 
    136144    g_free (l); 
    137145    return val; 
    run_listbox_with_data (Listbox * l, const void *select) 
    167175        } 
    168176    } 
    169177 
     178    /* If needed, restore normal listbox state, with no back-list (list_keep). */ 
     179    listbox_ensure_unfiltered_state(l->list); 
     180 
    170181    dlg_destroy (l->dlg); 
    171182    g_free (l); 
    172183    return val; 
  • lib/widget/listbox.c

    diff --git a/lib/widget/listbox.c b/lib/widget/listbox.c
    index e20c1a82d..2155fd1fe 100644
    a b listbox_entry_free (void *data) 
    7777{ 
    7878    WLEntry *e = data; 
    7979 
    80     g_free (e->text); 
     80    if (e->free_text) 
     81        g_free (e->text); 
    8182    if (e->free_data) 
    8283        g_free (e->data); 
    8384    g_free (e); 
    listbox_entry_free (void *data) 
    8586 
    8687/* --------------------------------------------------------------------------------------------- */ 
    8788 
     89static WLEntry* listbox_shallow_copy_entry(WLEntry *src, gboolean take_ownership) 
     90{ 
     91        WLEntry *copy; 
     92        copy = g_new(WLEntry, 1); 
     93        *copy = *src; 
     94 
     95        /* Who has the ownership of the data? */ 
     96        src->free_text  = src->free_text && !take_ownership; 
     97        src->free_data  = src->free_data && !take_ownership; 
     98        copy->free_text  = copy->free_text && take_ownership; 
     99        copy->free_data  = copy->free_data && take_ownership; 
     100 
     101        return copy; 
     102} 
     103 
     104/* --------------------------------------------------------------------------------------------- */ 
     105 
     106/* 
     107 * Sets the listbox into ·filter· state. In this state, there's a separate copy of all list 
     108 * entries, while the original (and displayed) field ->list is being a filtered version of the 
     109 * full copy. 
     110 */ 
     111static gboolean 
     112listbox_set_to_filter_state (WListbox * l) 
     113{ 
     114    GList *le; 
     115 
     116    /* The listbox is already in filter state? */ 
     117    if (WIDGET(l)->state & WST_FILTER) { 
     118        /* Return doing no change, just signal the error. */ 
     119        return FALSE; 
     120    } 
     121 
     122    /* Mark the new state. */ 
     123    WIDGET(l)->set_state(WIDGET(l), WST_FILTER, 1); 
     124 
     125    /* No list copy when entering filter mode. */ 
     126    g_assert(!l->list_keep); 
     127    l->list_keep = g_queue_new(); 
     128 
     129    /* Skip empty lists. */ 
     130    if (listbox_is_empty (l)) 
     131        return TRUE; 
     132 
     133    /* 
     134     * Remember the original position in the list in the field. It'll be used to determine the 
     135     * virtual_pos field. 
     136     */ 
     137    listbox_init_indices(l); 
     138 
     139    /* Perform a shallow copy of the original list. */ 
     140    for (le = g_queue_peek_head_link (l->list); le != NULL; le = g_list_next (le)) 
     141    { 
     142        WLEntry *copy = listbox_shallow_copy_entry(LENTRY (le->data), TRUE); 
     143        g_queue_push_tail(l->list_keep, copy); 
     144    } 
     145 
     146    return TRUE; 
     147} 
     148 
     149/* --------------------------------------------------------------------------------------------- */ 
     150 
     151/* Restores original elements of the list (from l->list_keep) and turns off the WST_FILTER state. */ 
     152static gboolean 
     153listbox_set_to_normal_state (WListbox * l) 
     154{ 
     155    /* The listbox is already in non-filter state? */ 
     156    if ((WIDGET(l)->state & WST_FILTER) == 0) { 
     157        /* Return doing no change, just signal the error. */ 
     158        return FALSE; 
     159    } 
     160 
     161    /* The keep-list must be allocated (even if it's empty). */ 
     162    g_assert(l->list_keep); 
     163 
     164    /* Mark the new state. */ 
     165    WIDGET(l)->set_state(WIDGET(l), WST_FILTER, 0); 
     166 
     167    /* Release the filtered list and replace it with the original, complete list. */ 
     168    g_queue_free_full(l->list, g_free); 
     169    l->list = g_steal_pointer(&l->list_keep); 
     170 
     171    return TRUE; 
     172} 
     173 
     174/* --------------------------------------------------------------------------------------------- */ 
     175 
     176/* Return TRUE if given listbox is in WST_FILTER state. */ 
     177static gboolean 
     178listbox_is_filter_state(WListbox *l) { 
     179    return ((WIDGET(l)->state & WST_FILTER) != 0); 
     180} 
     181 
     182/* --------------------------------------------------------------------------------------------- */ 
     183 
     184static 
     185void listbox_filter_list(WListbox * l, const char *text) 
     186{ 
     187    int i, size; 
     188    GList *le; 
     189    g_auto(GStrv) query_terms = NULL; 
     190 
     191    /* Remove the list and allocate a new one. */ 
     192    if (l->list) 
     193        g_queue_free_full(l->list, g_free); 
     194    l->list = g_queue_new(); 
     195 
     196    /* Split the query into space delimeted strings. */ 
     197    query_terms = g_strsplit(text, " ", 10); 
     198 
     199    /* 
     200     * Get the size of the listbox and iterate over it testing each element against ·all· words in  
     201     * query_terms. 
     202     */ 
     203    size = g_queue_get_length(l->list_keep); 
     204    le = g_queue_peek_head_link (l->list_keep); 
     205    for (i = 0; i<size; i++, le = g_list_next (le)) 
     206    { 
     207        WLEntry *e = LENTRY (le->data); 
     208        gboolean match = TRUE; 
     209 
     210        /* Test the query against the list entry. */ 
     211        for (gchar **p=query_terms; *p; p++) { 
     212            if (**p && !strcasestr(e->text, *p)) { 
     213                match = FALSE; 
     214                break; 
     215            } 
     216        } 
     217 
     218        /* If all the terms matched, then add the element to the list. */ 
     219        if (match) 
     220            g_queue_push_tail(l->list, listbox_shallow_copy_entry(e, FALSE)); 
     221    } 
     222    if (listbox_is_empty(l)) { 
     223        listbox_add_item(l, LISTBOX_APPEND_AT_END, 0, "<no search results>", NULL, FALSE); 
     224        LENTRY(g_queue_peek_head_link (l->list)->data)->index = -1; 
     225    } 
     226    size = g_queue_get_length(l->list); 
     227    if (l->pos >= size) 
     228        listbox_select_entry (l, size - 1); 
     229    else 
     230        listbox_select_entry (l, l->pos); 
     231} 
     232 
     233/* --------------------------------------------------------------------------------------------- */ 
     234 
    88235static void 
    89236listbox_drawscroll (WListbox * l) 
    90237{ 
    listbox_back_n (WListbox * l, int n) 
    267414 
    268415/* --------------------------------------------------------------------------------------------- */ 
    269416 
     417static void 
     418listbox_make_one_line_room(WListbox *l, int should_add_free_room) { 
     419    Widget *w = WIDGET (l), *owner = WIDGET (WIDGET(w)->owner); 
     420    WRect r_dialog, r_listbox; 
     421    int new_dialog_height, new_listbox_height, take_give_from_to_owner = 1; 
     422 
     423    /* IF the enlarged dialog won't fit the screen, don't resize it but the listbox instead. */ 
     424    if (LINES <= owner->lines+2) 
     425        take_give_from_to_owner = 0; 
     426     
     427    // Increase the height of the dialog by 1, so that the new input fits. 
     428    if (should_add_free_room) { 
     429        new_dialog_height = owner->lines + take_give_from_to_owner; 
     430        new_listbox_height = w->lines + (-1+take_give_from_to_owner); 
     431    } else { 
     432        new_dialog_height = owner->lines - take_give_from_to_owner; 
     433        new_listbox_height = w->lines - (-1+take_give_from_to_owner); 
     434    } 
     435    rect_init (&r_dialog, owner->y, owner->x, new_dialog_height, owner->cols); 
     436    rect_init (&r_listbox, w->y, w->x, new_listbox_height, w->cols); 
     437    send_message (w, NULL, MSG_RESIZE, 0, &r_listbox); 
     438    send_message (owner, NULL, MSG_RESIZE, 0, &r_dialog); 
     439} 
     440 
     441/* --------------------------------------------------------------------------------------------- */ 
     442 
     443static void 
     444listbox_show_multi_search_input(WListbox *l) { 
     445    Widget *w = WIDGET(l), *owner = WIDGET(WIDGET(l)->owner); 
     446    WInput *multi_search_in; 
     447    int distance_y = owner->y + owner->lines - (w->y + w->lines) + 1; 
     448    int distance_x = w->cols > 40 ? 5 : 1, small = w->cols <= 15 ? 1 : 0; 
     449 
     450    listbox_make_one_line_room(l,1); 
     451    multi_search_in = input_new (owner->lines - distance_y, distance_x, 
     452        input_colors, w->cols-2-distance_x+small, "", 
     453            "multi_search", INPUT_COMPLETE_NONE); 
     454    multi_search_in->forward_listbox = l; 
     455    group_add_widget_autopos (GROUP(owner), multi_search_in, WPOS_KEEP_TOP | WPOS_CENTER_HORZ , NULL); 
     456 
     457    repaint_screen(); 
     458    send_message (l, NULL, MSG_DRAW, 0, NULL); 
     459    send_message (multi_search_in, NULL, MSG_DRAW, 0, NULL); 
     460} 
     461 
     462/* --------------------------------------------------------------------------------------------- */ 
     463 
     464static void 
     465listbox_hide_multi_search_widget(WListbox *l) { 
     466    Widget *in; 
     467    in = WIDGET(WIDGET(l)->owner)->find_by_type(WIDGET(WIDGET(l)->owner), input_callback); 
     468    if (in) { 
     469        group_remove_widget(in); 
     470        listbox_make_one_line_room(l,0); 
     471        group_select_next_widget(WIDGET(l)->owner); 
     472        send_message (l, NULL, MSG_DRAW, 0, NULL); 
     473        widget_destroy(in); 
     474    } 
     475    repaint_screen(); 
     476} 
     477 
     478/* --------------------------------------------------------------------------------------------- */ 
     479 
    270480static cb_ret_t 
    271481listbox_execute_cmd (WListbox * l, long command) 
    272482{ 
    listbox_execute_cmd (WListbox * l, long command) 
    320530                              D_ERROR, 2, _("&Yes"), _("&No")) == 0)) 
    321531            listbox_remove_list (l); 
    322532        break; 
     533    case CK_MultiSearch: 
     534        /* Toggle the multi term searching of any listbox. */ 
     535        if (listbox_is_filter_state(l)) { 
     536            /* Remove the input widget from the dialog. */ 
     537            listbox_hide_multi_search_widget(l); 
     538            /* Restore original (unfiltered) listbox contents. */ 
     539            ret = listbox_set_to_normal_state(l); 
     540        } else { 
     541            /* Add input widget for the filter query at the bottom of the dialog window. */ 
     542            listbox_show_multi_search_input(l); 
     543            /* … and then turn on the filter state. */ 
     544            ret = listbox_set_to_filter_state(l); 
     545        } 
     546        break; 
    323547    case CK_View: 
    324548    case CK_Edit: 
    325549    case CK_Enter: 
    listbox_key (WListbox * l, int key) 
    358582 
    359583/* --------------------------------------------------------------------------------------------- */ 
    360584 
     585/* When called via g_queue_foreach it assigns the index field with an incremented int. */ 
     586static void 
     587listbox_foreach_apply_index (gpointer data, 
     588          gpointer user_data) 
     589{ 
     590    WLEntry *e = data; 
     591    int *cur_idx = user_data; 
     592    e->index = *cur_idx; 
     593    *cur_idx = *cur_idx + 1; 
     594} 
     595 
     596/* --------------------------------------------------------------------------------------------- */ 
     597 
    361598/* Listbox item adding function */ 
    362599static inline void 
    363600listbox_append_item (WListbox * l, WLEntry * e, listbox_append_t pos) 
    listbox_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void 
    482719        widget_gotoyx (l, l->cursor_y, 0); 
    483720        return MSG_HANDLED; 
    484721 
     722    case MSG_UPDATE_LIST: 
     723        listbox_filter_list(l, (char *) data); 
     724        repaint_screen(); 
     725        return MSG_HANDLED; 
     726 
    485727    case MSG_DRAW: 
    486728        listbox_draw (l, widget_get_state (w, WST_FOCUSED)); 
    487729        return MSG_HANDLED; 
    listbox_new (int y, int x, int height, int width, gboolean deletable, lcback_fn 
    556798    l = g_new (WListbox, 1); 
    557799    w = WIDGET (l); 
    558800    widget_init (w, y, x, height, width, listbox_callback, listbox_mouse_callback); 
    559     w->options |= WOP_SELECTABLE | WOP_WANT_HOTKEY; 
     801    w->options |= WOP_SELECTABLE; 
    560802    w->keymap = listbox_map; 
    561803 
    562804    l->list = NULL; 
     805    l->list_keep = NULL; 
    563806    l->top = l->pos = 0; 
    564807    l->deletable = deletable; 
    565808    l->callback = callback; 
    listbox_new (int y, int x, int height, int width, gboolean deletable, lcback_fn 
    571814 
    572815/* --------------------------------------------------------------------------------------------- */ 
    573816 
     817/* 
     818 * If the list is filtered it replaces from the back list (list_keep). It returns whether such  
     819 * change occurred – FALSE means that the list was already unfiltered. 
     820 */ 
     821gboolean listbox_ensure_unfiltered_state(WListbox *l) 
     822{ 
     823    gboolean ret = FALSE; 
     824    if (listbox_is_filter_state(l)) 
     825        ret = listbox_set_to_normal_state(l); 
     826    return ret; 
     827} 
     828 
     829gboolean listbox_conditionally_enable_multi_search(WListbox *l) 
     830{ 
     831    int start_with_multi_search_active; 
     832 
     833    /* Option of starting the listbox with MultiSearch pre-activated. */ 
     834    start_with_multi_search_active = 
     835        mc_config_get_bool (mc_global.main_config, CONFIG_APP_SECTION, 
     836                            "multi_search_active_by_default", 1); 
     837 
     838    /* CK_MultiSearch toggles the state. */ 
     839    if (start_with_multi_search_active) 
     840        send_message(l, NULL, MSG_ACTION, CK_MultiSearch, NULL); 
     841 
     842    return start_with_multi_search_active; 
     843} 
     844 
     845/* --------------------------------------------------------------------------------------------- */ 
     846 
    574847/** 
    575848 * Finds item by its label. 
    576849 */ 
    listbox_select_entry (WListbox * l, int dest) 
    673946                if (l->pos - l->top >= lines) 
    674947                    l->top = l->pos - lines + 1; 
    675948            } 
     949            /* 
     950             * Set the virtual position, i.e.: a position in the initial, unfiltered list if the  
     951             * same element would be selected. 
     952             */ 
     953            if (listbox_is_filter_state(l)) { 
     954                WLEntry *e = listbox_get_nth_item(l, l->pos); 
     955                if (e) 
     956                    l->virtual_pos = e->index; 
     957                else 
     958                    l->virtual_pos = -1; 
     959            } else { 
     960                l->virtual_pos = pos; 
     961            } 
    676962            return; 
    677963        } 
    678964    } 
    listbox_remove_list (WListbox * l) 
    8021088 
    8031089/* --------------------------------------------------------------------------------------------- */ 
    8041090 
     1091/* 
     1092 * Initializes the listbox elements with their position index. This allows to alter (filter, in 
     1093 * particular) the listbox elements order and still get the original index (when selecting an 
     1094 * element). 
     1095 */ 
     1096void listbox_init_indices(WListbox *l) { 
     1097    int index = 0; 
     1098    g_queue_foreach(l->list, listbox_foreach_apply_index, &index); 
     1099} 
     1100 
     1101/* --------------------------------------------------------------------------------------------- */ 
     1102 
    8051103char * 
    8061104listbox_add_item (WListbox * l, listbox_append_t pos, int hotkey, const char *text, void *data, 
    8071105                  gboolean free_data) 
    listbox_add_item (WListbox * l, listbox_append_t pos, int hotkey, const char *te 
    8151113        return NULL; 
    8161114 
    8171115    entry = g_new (WLEntry, 1); 
     1116    entry->index = -1; /* Will be initialized when switching to the filter state */ 
    8181117    entry->text = g_strdup (text); 
     1118    entry->free_text = 1; 
    8191119    entry->data = data; 
    8201120    entry->free_data = free_data; 
    8211121    entry->hotkey = hotkey; 
  • lib/widget/listbox.h

    diff --git a/lib/widget/listbox.h b/lib/widget/listbox.h
    index 8b2236eff..cc853d0ee 100644
    a b typedef lcback_ret_t (*lcback_fn) (struct WListbox * l); 
    3535 
    3636typedef struct WLEntry 
    3737{ 
     38    int index;                  /* The location in the list (useful when it's filtered) */ 
    3839    char *text;                 /* Text to display */ 
     40    gboolean free_text;         /* Whether to free the text on entry's removal */ 
    3941    int hotkey; 
    4042    void *data;                 /* Client information */ 
    4143    gboolean free_data;         /* Whether to free the data on entry's removal */ 
    typedef struct WListbox 
    4547{ 
    4648    Widget widget; 
    4749    GQueue *list;               /* Pointer to the list of WLEntry */ 
     50    GQueue *list_keep;          /* Unfiltered list (used in the WST_FILTER state). */ 
    4851    int pos;                    /* The current element displayed */ 
     52    int virtual_pos;            /* The initial index of the current element, works also for filtered listbox */ 
    4953    int top;                    /* The first element displayed */ 
    5054    gboolean allow_duplicates;  /* Do we allow duplicates on the list? */ 
    5155    gboolean scrollbar;         /* Draw a scrollbar? */ 
    extern const global_keymap_t *listbox_map; 
    6165/*** declarations of public functions ************************************************************/ 
    6266 
    6367WListbox *listbox_new (int y, int x, int height, int width, gboolean deletable, lcback_fn callback); 
     68gboolean listbox_ensure_unfiltered_state(WListbox *l); 
     69gboolean listbox_conditionally_enable_multi_search(WListbox *l); 
    6470int listbox_search_text (WListbox * l, const char *text); 
    6571int listbox_search_data (WListbox * l, const void *data); 
    6672void listbox_select_first (WListbox * l); 
    void listbox_remove_current (WListbox * l); 
    7480gboolean listbox_is_empty (const WListbox * l); 
    7581void listbox_set_list (WListbox * l, GQueue * list); 
    7682void listbox_remove_list (WListbox * l); 
     83void listbox_init_indices(WListbox *l); 
    7784char *listbox_add_item (WListbox * l, listbox_append_t pos, int hotkey, const char *text, 
    7885                        void *data, gboolean free_data); 
    7986 
  • 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, 
    338338    w->find_by_type = widget_default_find_by_type; 
    339339    w->find_by_id = widget_default_find_by_id; 
    340340 
     341    w->pre_unlink_func = NULL; 
     342 
    341343    w->set_state = widget_default_set_state; 
    342344    w->get_colors = widget_default_get_colors; 
    343345} 
  • lib/widget/widget-common.h

    diff --git a/lib/widget/widget-common.h b/lib/widget/widget-common.h
    index a20bcd671..fa3e329ae 100644
    a b typedef enum 
    4343    MSG_ACTION,                 /* Send to widget to handle command */ 
    4444    MSG_NOTIFY,                 /* Typically sent to dialog to inform it of state-change 
    4545                                 * of listboxes, check- and radiobuttons. */ 
     46    MSG_UPDATE_LIST,            /* Sent to listboxes to request the list regeneration (filtering) 
     47                                 * in the MultiSearch mode. */ 
    4648    MSG_CURSOR,                 /* Sent to widget to position the cursor */ 
    4749    MSG_IDLE,                   /* The idle state is active */ 
    4850    MSG_RESIZE,                 /* Screen size has changed */ 
    typedef enum 
    8890    WST_CONSTRUCT = (1 << 15),  /* Widget has been constructed but not run yet */ 
    8991    WST_ACTIVE = (1 << 16),     /* Dialog is visible and active */ 
    9092    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 */ 
    9296} widget_state_t; 
    9397 
    9498/* Flags for widget repositioning on dialog resize */ 
    struct Widget 
    165169    cb_ret_t (*set_state) (Widget * w, widget_state_t state, gboolean enable); 
    166170    /* *INDENT-ON* */ 
    167171 
     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 
    168176    const int *(*get_colors) (const Widget * w); 
    169177}; 
    170178 
  • src/editor/choosesyntax.c

    diff --git a/src/editor/choosesyntax.c b/src/editor/choosesyntax.c
    index 09b6d75d5..015012a99 100644
    a b exec_edit_syntax_dialog (const GPtrArray * names, const char *current_syntax) 
    8787            listbox_select_entry (syntaxlist->list, i + N_DFLT_ENTRIES); 
    8888    } 
    8989 
     90    /* 
     91     * Initialize the `index` fields in the WLEntry elements so that it's possible to get 
     92     * the correct selected element index even when listbox is filtered (MultiSearch). 
     93     */ 
     94    listbox_init_indices(syntaxlist->list); 
     95 
    9096    return run_listbox (syntaxlist); 
    9197} 
    9298 
  • src/editor/editcmd_dialogs.c

    diff --git a/src/editor/editcmd_dialogs.c b/src/editor/editcmd_dialogs.c
    index 8b3634f23..217a2ea07 100644
    a b editcmd_dialog_completion_show (const WEdit * edit, int max_len, GString ** comp 
    394394        listbox_add_item (compl_list, LISTBOX_APPEND_AT_END, 0, (char *) compl[i]->str, NULL, 
    395395                          FALSE); 
    396396 
     397    /* Option to start in the MultiSearch state. */ 
     398    listbox_conditionally_enable_multi_search(compl_list); 
     399 
    397400    /* pop up the dialog and apply the chosen completion */ 
    398401    if (dlg_run (compl_dlg) == B_ENTER) 
    399402    { 
    editcmd_dialog_completion_show (const WEdit * edit, int max_len, GString ** comp 
    401404        curr = g_strdup (curr); 
    402405    } 
    403406 
     407    /* If needed, restore normal listbox state, with no back list (list_keep). */ 
     408    listbox_ensure_unfiltered_state(compl_list); 
     409 
    404410    /* destroy dialog before return */ 
    405411    dlg_destroy (compl_dlg); 
    406412 
  • src/editor/editwidget.c

    diff --git a/src/editor/editwidget.c b/src/editor/editwidget.c
    index 18ac00e66..9835503a1 100644
    a b edit_window_list (const WDialog * h) 
    327327            g_free (fname); 
    328328        } 
    329329 
     330    /* Option of starting the listbox with MultiSearch pre-activated. */ 
     331    listbox_conditionally_enable_multi_search(listbox->list); 
     332 
    330333    selected = run_listbox_with_data (listbox, g->current->data); 
    331334    if (selected != NULL) 
    332335        widget_select (WIDGET (selected)); 
  • src/editor/spell_dialogs.c

    diff --git a/src/editor/spell_dialogs.c b/src/editor/spell_dialogs.c
    index 392f8e4ec..8a4e93c62 100644
    a b spell_dialog_lang_list_show (GArray * languages) 
    174174    for (i = 0; i < languages->len; i++) 
    175175        LISTBOX_APPEND_TEXT (lang_list, 0, g_array_index (languages, char *, i), NULL, FALSE); 
    176176 
     177    /* 
     178     * Initialize the `index` fields in the WLEntry elements so that it's possible to get 
     179     * the correct selected element index even when listbox is filtered (MultiSearch). 
     180     */ 
     181    listbox_init_indices(lang_list->list); 
     182 
    177183    res = run_listbox (lang_list); 
    178184    if (res >= 0) 
    179185        selected_lang = g_strdup (g_array_index (languages, char *, (unsigned int) res)); 
  • src/filemanager/listmode.c

    diff --git a/src/filemanager/listmode.c b/src/filemanager/listmode.c
    index 0aaa0e91f..8954045c9 100644
    a b select_new_item (void) 
    124124                          FALSE); 
    125125    } 
    126126 
     127    /* 
     128     * Initialize the `index` fields in the WLEntry elements so that it's possible to get 
     129     * the correct selected element index even when listbox is filtered (MultiSearch). 
     130     */ 
     131    listbox_init_indices(listbox->list); 
     132 
    127133    i = run_listbox (mylistbox); 
    128134    if (i >= 0) 
    129135        ret = g_strdup (possible_items[i]); 
  • src/keybind-defaults.c

    diff --git a/src/keybind-defaults.c b/src/keybind-defaults.c
    index c423e6be4..f77c173e1 100644
    a b static const global_keymap_ini_t default_listbox_keymap[] = { 
    293293    {"Bottom", "end; alt-gt; c1"}, 
    294294    {"PageUp", "pgup; alt-v"}, 
    295295    {"PageDown", "pgdn; ctrl-v"}, 
    296     {"Delete", "delete; d"}, 
    297     {"Clear", "shift-delete; shift-d"}, 
     296    {"Delete", "delete"}, 
     297    {"Clear", "shift-delete"}, 
    298298    {"View", "f3"}, 
    299299    {"Edit", "f4"}, 
     300    {"MultiSearch", "alt-space; ctrl-space"}, 
    300301    {"Enter", "enter"}, 
    301302    {NULL, NULL} 
    302303}; 
  • src/selcodepage.c

    diff --git a/src/selcodepage.c b/src/selcodepage.c
    index eba9d25bb..9eeba53dd 100644
    a b select_charset (int center_y, int center_x, int current_charset, gboolean seldis 
    109109 
    110110    listbox_select_entry (listbox->list, i); 
    111111 
     112    /* 
     113     * Initialize the `index` fields in the WLEntry elements so that it's possible to get 
     114     * the correct selected element index even when listbox is filtered (MultiSearch). 
     115     */ 
     116    listbox_init_indices(listbox->list); 
     117 
    112118    listbox_result = run_listbox (listbox); 
    113119 
    114120    if (listbox_result < 0) 
  • src/usermenu.c

    diff --git a/src/usermenu.c b/src/usermenu.c
    index 698244220..b9d6f8777 100644
    a b user_menu_cmd (const WEdit * edit_widget, const char *menu_file, int selected_en 
    11241124            /* Select the default entry */ 
    11251125            listbox_select_entry (listbox->list, selected); 
    11261126 
     1127            /* 
     1128             * Initialize the `index` fields in the WLEntry elements so that it's possible to get 
     1129             * the correct selected element index even when listbox is filtered (MultiSearch). 
     1130             */ 
     1131            listbox_init_indices(listbox->list); 
     1132 
    11271133            selected = run_listbox (listbox); 
    11281134        } 
    11291135        if (selected >= 0)