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

File 0001-Add-MultiSearch-command-a-multi-term-AND-conjugated-v2.2.patch, 34.1 KB (added by psprint, 20 months ago)

make indent

  • lib/keybind.c

    From dc4e06b7adf219fe00e870650d33972aba2bd591 Mon Sep 17 00:00:00 2001
    From: Sebastian Gniazdowski <sgniazdowski@gmail.com>
    Date: Sun, 3 Jan 2021 04:57:58 -0600
    Subject: [PATCH] =?UTF-8?q?Add=20MultiSearch=20command=20=E2=80=93=20a=20m?=
     =?UTF-8?q?ulti=20term=20AND-conjugated=20filtering=20of=20any=20listbox.?=
    MIME-Version: 1.0
    Content-Type: text/plain; charset=UTF-8
    Content-Transfer-Encoding: 8bit
    
    ---
     lib/keybind.c                |   1 +
     lib/keybind.h                |   1 +
     lib/widget/group.c           |  11 +-
     lib/widget/history.c         |  75 ++++----
     lib/widget/input.c           | 108 +++++++++---
     lib/widget/input.h           |   2 +
     lib/widget/listbox-window.c  |  13 +-
     lib/widget/listbox.c         | 328 ++++++++++++++++++++++++++++++++++-
     lib/widget/listbox.h         |   7 +
     lib/widget/widget-common.c   |   2 +
     lib/widget/widget-common.h   |  10 +-
     src/editor/editcmd_dialogs.c |   6 +
     src/editor/editwidget.c      |   3 +
     src/keybind-defaults.c       |   5 +-
     14 files changed, 510 insertions(+), 62 deletions(-)
    
    diff --git a/lib/keybind.c b/lib/keybind.c
    index abd44d3e2..df3cbf110 100644
    a b static name_keymap_t command_names[] = { 
    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..238f7ab1b 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..7cd165e21 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; 
    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) 
    8574    { 
    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); 
    88109    } 
    89110    else 
    90111    { 
    91         y++; 
    92         he = MIN (he, LINES - y); 
     112        /* A resize from some other code (currently from the listbox filter). */ 
     113        r = *resize; 
    93114    } 
    94115 
    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); 
    107116 
    108117    return dlg_default_callback (WIDGET (dlg_head), NULL, MSG_RESIZE, 0, &r); 
    109118} 
    history_dlg_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, v 
    116125    switch (msg) 
    117126    { 
    118127    case MSG_RESIZE: 
    119         return history_dlg_reposition (DIALOG (w)); 
     128        return history_dlg_reposition (DIALOG (w), data); 
    120129 
    121130    case MSG_NOTIFY: 
    122131        { 
    history_show (history_descriptor_t * hd) 
    257266            listbox_select_entry (hd->listbox, hd->current); 
    258267    } 
    259268 
     269    /* Option of starting the listbox with MultiSearch pre-activated. */ 
     270    listbox_conditionally_enable_multi_search (hd->listbox); 
     271 
    260272    dlg_ret = dlg_run (query_dlg); 
    261273    if (dlg_ret != B_CANCEL) 
    262274    { 
    history_show (history_descriptor_t * hd) 
    278290        hd->text = g_strdup (q); 
    279291    } 
    280292 
     293    /* If needed, restore normal listbox state, with no backlist (list_keep). */ 
     294    listbox_ensure_unfiltered_state (hd->listbox); 
     295 
    281296    /* get modified history from dialog */ 
    282297    z = NULL; 
    283298    for (hi = listbox_get_first_link (hd->listbox); hi != NULL; hi = g_list_next (hi)) 
  • lib/widget/input.c

    diff --git a/lib/widget/input.c b/lib/widget/input.c
    index b5cec7e6b..625b394a9 100644
    a b input_execute_cmd (WInput * in, long command) 
    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 
     896        && (DIALOG (WIDGET (in)->owner)->ret_value != B_CANCEL)) 
    875897    { 
    876898        ev_history_load_save_t *ev = (ev_history_load_save_t *) data; 
    877899 
    input_mouse_callback (Widget * w, mouse_msg_t msg, mouse_event_t * event) 
    970992    } 
    971993} 
    972994 
     995/* --------------------------------------------------------------------------------------------- */ 
     996 
     997static void 
     998listbox_unregister_history_events_cb (gpointer wid_ptr) 
     999{ 
     1000    Widget *w = WIDGET (wid_ptr); 
     1001    WDialog *h = DIALOG (w->owner); 
     1002 
     1003    /* unsubscribe from "history_load" event */ 
     1004    mc_event_del (h->event_group, MCEVENT_HISTORY_LOAD, input_load_history, w); 
     1005    /* unsubscribe from "history_save" event */ 
     1006    mc_event_del (h->event_group, MCEVENT_HISTORY_SAVE, input_save_history, w); 
     1007} 
     1008 
    9731009/* --------------------------------------------------------------------------------------------- */ 
    9741010/*** public functions ****************************************************************************/ 
    9751011/* --------------------------------------------------------------------------------------------- */ 
    input_new (int y, int x, const int *colors, int width, const char *def_text, 
    10281064    if ((histname != NULL) && (*histname != '\0')) 
    10291065        in->history.name = g_strdup (histname); 
    10301066    /* history will be loaded later */ 
    1031  
    10321067    in->label = NULL; 
     1068    in->forward_listbox = NULL; 
    10331069 
    10341070    return in; 
    10351071} 
    input_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *d 
    10501086        mc_event_add (h->event_group, MCEVENT_HISTORY_LOAD, input_load_history, w, NULL); 
    10511087        /* subscribe to "history_save" event */ 
    10521088        mc_event_add (h->event_group, MCEVENT_HISTORY_SAVE, input_save_history, w, NULL); 
     1089        /* unregister (via the func) the events in case of removal from dialog */ 
     1090        w->pre_unlink_func = listbox_unregister_history_events_cb; 
    10531091        if (in->label != NULL) 
    10541092            widget_set_state (WIDGET (in->label), WST_DISABLED, widget_get_state (w, WST_DISABLED)); 
    10551093        return MSG_HANDLED; 
    input_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *d 
    10631101            return v; 
    10641102        } 
    10651103 
    1066         /* Keys we want others to handle */ 
    1067         if (parm == KEY_UP || parm == KEY_DOWN || parm == ESC_CHAR 
    1068             || parm == KEY_F (10) || parm == '\n') 
    1069             return MSG_NOT_HANDLED; 
    1070  
    1071         /* When pasting multiline text, insert literal Enter */ 
    1072         if ((parm & ~KEY_M_MASK) == '\n') 
     1104        /* If not forwarding the keys to a paired listbox forward_listbox… */ 
     1105        if (!in->forward_listbox || parm == ESC_CHAR || parm == '\n') 
    10731106        { 
    1074             quote = TRUE; 
    1075             v = input_handle_char (in, '\n'); 
    1076             quote = FALSE; 
    1077             return v; 
     1107            /* Keys we want others to handle, if not forwarding them to a paired listbox */ 
     1108            if (parm == KEY_UP || parm == KEY_DOWN || parm == ESC_CHAR 
     1109                || parm == KEY_F (10) || parm == '\n') 
     1110                return MSG_NOT_HANDLED; 
     1111            /* When pasting multiline text, insert literal Enter */ 
     1112            if ((parm & ~KEY_M_MASK) == '\n') 
     1113            { 
     1114                quote = TRUE; 
     1115                v = input_handle_char (in, '\n'); 
     1116                quote = FALSE; 
     1117                return v; 
     1118            } 
    10781119        } 
    10791120 
    10801121        return input_handle_char (in, parm); 
    input_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *d 
    10971138        return MSG_HANDLED; 
    10981139 
    10991140    case MSG_DESTROY: 
    1100         /* unsubscribe from "history_load" event */ 
    1101         mc_event_del (h->event_group, MCEVENT_HISTORY_LOAD, input_load_history, w); 
    1102         /* unsubscribe from "history_save" event */ 
    1103         mc_event_del (h->event_group, MCEVENT_HISTORY_SAVE, input_save_history, w); 
     1141        /* …only, if there is an owner WGroup. */ 
     1142        if (h) 
     1143        { 
     1144            /* unsubscribe from "history_load" event */ 
     1145            mc_event_del (h->event_group, MCEVENT_HISTORY_LOAD, input_load_history, w); 
     1146            /* unsubscribe from "history_save" event */ 
     1147            mc_event_del (h->event_group, MCEVENT_HISTORY_SAVE, input_save_history, w); 
     1148        } 
    11041149        input_destroy (in); 
    11051150        return MSG_HANDLED; 
    11061151 
    input_handle_char (WInput * in, int key) 
    11401185    command = widget_lookup_key (WIDGET (in), key); 
    11411186    if (command == CK_IgnoreKey) 
    11421187    { 
    1143         if (key > 255) 
    1144             return MSG_NOT_HANDLED; 
    1145         if (in->first) 
    1146             port_region_marked_for_delete (in); 
    1147         input_complete_free (in); 
    1148         v = insert_char (in, key); 
     1188        /* Should we forward keys to a paired listbox? */ 
     1189        if (in->forward_listbox) 
     1190            command = widget_lookup_key (WIDGET (in->forward_listbox), key); 
     1191        if (command == CK_IgnoreKey) 
     1192        { 
     1193            if (key > 255) 
     1194                return MSG_NOT_HANDLED; 
     1195            if (in->first) 
     1196                port_region_marked_for_delete (in); 
     1197            input_complete_free (in); 
     1198            v = insert_char (in, key); 
     1199        } 
     1200        else 
     1201        { 
     1202            /* Forward the listbox command to the paired listbox. */ 
     1203            send_message (WIDGET (in->forward_listbox), NULL, MSG_KEY, key, NULL); 
     1204            send_message (WIDGET (in->forward_listbox), NULL, MSG_DRAW, 0, NULL); 
     1205            return MSG_HANDLED; 
     1206        } 
    11491207    } 
    11501208    else 
    11511209    { 
    input_handle_char (WInput * in, int key) 
    11581216    } 
    11591217 
    11601218    input_update (in, TRUE); 
     1219 
     1220    /* Signal any filtering listbox that the query has been updated. */ 
     1221    if (in->forward_listbox) 
     1222        send_message (WIDGET (in->forward_listbox), NULL, MSG_UPDATE_LIST, key, in->buffer); 
    11611223    return v; 
    11621224} 
    11631225 
  • 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..c242b04e9 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..c71655acb 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 * 
     90listbox_shallow_copy_entry (WLEntry * src, gboolean take_ownership) 
     91{ 
     92    WLEntry *copy; 
     93    copy = g_new (WLEntry, 1); 
     94    *copy = *src; 
     95 
     96    /* Who has the ownership of the data? */ 
     97    src->free_text = src->free_text && !take_ownership; 
     98    src->free_data = src->free_data && !take_ownership; 
     99    copy->free_text = copy->free_text && take_ownership; 
     100    copy->free_data = copy->free_data && take_ownership; 
     101 
     102    return copy; 
     103} 
     104 
     105/* --------------------------------------------------------------------------------------------- */ 
     106 
     107/* 
     108 * Sets the listbox into ·filter· state. In this state, there's a separate copy of all list 
     109 * entries, while the original (and displayed) field ->list is being a filtered version of the 
     110 * full copy. 
     111 */ 
     112static gboolean 
     113listbox_set_to_filter_state (WListbox * l) 
     114{ 
     115    GList *le; 
     116 
     117    /* The listbox is already in filter state? */ 
     118    if (WIDGET (l)->state & WST_FILTER) 
     119    { 
     120        /* Return doing no change, just signal the error. */ 
     121        return FALSE; 
     122    } 
     123 
     124    /* Mark the new state. */ 
     125    WIDGET (l)->set_state (WIDGET (l), WST_FILTER, 1); 
     126 
     127    /* No list copy when entering filter mode. */ 
     128    g_assert (!l->list_keep); 
     129    l->list_keep = g_queue_new (); 
     130 
     131    /* Skip empty lists. */ 
     132    if (listbox_is_empty (l)) 
     133        return TRUE; 
     134 
     135    /* 
     136     * Remember the original position in the list in the field. It'll be used to determine the 
     137     * virtual_pos field. 
     138     */ 
     139    listbox_init_indices (l); 
     140 
     141    /* Perform a shallow copy of the original list. */ 
     142    for (le = g_queue_peek_head_link (l->list); le != NULL; le = g_list_next (le)) 
     143    { 
     144        WLEntry *copy = listbox_shallow_copy_entry (LENTRY (le->data), TRUE); 
     145        g_queue_push_tail (l->list_keep, copy); 
     146    } 
     147 
     148    return TRUE; 
     149} 
     150 
     151/* --------------------------------------------------------------------------------------------- */ 
     152 
     153/* Restores original elements of the list (from l->list_keep) and turns off the WST_FILTER state. */ 
     154static gboolean 
     155listbox_set_to_normal_state (WListbox * l) 
     156{ 
     157    /* The listbox is already in non-filter state? */ 
     158    if ((WIDGET (l)->state & WST_FILTER) == 0) 
     159    { 
     160        /* Return doing no change, just signal the error. */ 
     161        return FALSE; 
     162    } 
     163 
     164    /* The keep-list must be allocated (even if it's empty). */ 
     165    g_assert (l->list_keep); 
     166 
     167    /* Mark the new state. */ 
     168    WIDGET (l)->set_state (WIDGET (l), WST_FILTER, 0); 
     169 
     170    /* Release the filtered list and replace it with the original, complete list. */ 
     171    g_queue_free_full (l->list, g_free); 
     172    l->list = g_steal_pointer (&l->list_keep); 
     173 
     174    return TRUE; 
     175} 
     176 
     177/* --------------------------------------------------------------------------------------------- */ 
     178 
     179/* Return TRUE if given listbox is in WST_FILTER state. */ 
     180static gboolean 
     181listbox_is_filter_state (WListbox * l) 
     182{ 
     183    return ((WIDGET (l)->state & WST_FILTER) != 0); 
     184} 
     185 
     186/* --------------------------------------------------------------------------------------------- */ 
     187 
     188static void 
     189listbox_filter_list (WListbox * l, const char *text) 
     190{ 
     191    int i, size; 
     192    GList *le; 
     193    g_auto (GStrv) query_terms = NULL; 
     194 
     195    /* Remove the list and allocate a new one. */ 
     196    if (l->list) 
     197        g_queue_free_full (l->list, g_free); 
     198    l->list = g_queue_new (); 
     199 
     200    /* Split the query into space delimeted strings. */ 
     201    query_terms = g_strsplit (text, " ", 10); 
     202 
     203    /* 
     204     * Get the size of the listbox and iterate over it testing each element against ·all· words in  
     205     * query_terms. 
     206     */ 
     207    size = g_queue_get_length (l->list_keep); 
     208    le = g_queue_peek_head_link (l->list_keep); 
     209    for (i = 0; i < size; i++, le = g_list_next (le)) 
     210    { 
     211        WLEntry *e = LENTRY (le->data); 
     212        gboolean match = TRUE; 
     213 
     214        /* Test the query against the list entry. */ 
     215        for (gchar ** p = query_terms; *p; p++) 
     216        { 
     217            if (**p && !strcasestr (e->text, *p)) 
     218            { 
     219                match = FALSE; 
     220                break; 
     221            } 
     222        } 
     223 
     224        /* If all the terms matched, then add the element to the list. */ 
     225        if (match) 
     226            g_queue_push_tail (l->list, listbox_shallow_copy_entry (e, FALSE)); 
     227    } 
     228    if (listbox_is_empty (l)) 
     229    { 
     230        listbox_add_item (l, LISTBOX_APPEND_AT_END, 0, "<no search results>", NULL, FALSE); 
     231        LENTRY (g_queue_peek_head_link (l->list)->data)->index = -1; 
     232    } 
     233    size = g_queue_get_length (l->list); 
     234    if (l->pos >= size) 
     235        listbox_select_entry (l, size - 1); 
     236    else 
     237        listbox_select_entry (l, l->pos); 
     238} 
     239 
     240/* --------------------------------------------------------------------------------------------- */ 
     241 
    88242static void 
    89243listbox_drawscroll (WListbox * l) 
    90244{ 
    listbox_back_n (WListbox * l, int n) 
    267421 
    268422/* --------------------------------------------------------------------------------------------- */ 
    269423 
     424static void 
     425listbox_make_one_line_room (WListbox * l, int should_add_free_room) 
     426{ 
     427    Widget *w = WIDGET (l), *owner = WIDGET (WIDGET (w)->owner); 
     428    WRect r_dialog, r_listbox; 
     429    int new_dialog_height, new_listbox_height, take_give_from_to_owner = 1; 
     430 
     431    /* IF the enlarged dialog won't fit the screen, don't resize it but the listbox instead. */ 
     432    if (LINES <= owner->lines + 2) 
     433        take_give_from_to_owner = 0; 
     434 
     435    // Increase the height of the dialog by 1, so that the new input fits. 
     436    if (should_add_free_room) 
     437    { 
     438        new_dialog_height = owner->lines + take_give_from_to_owner; 
     439        new_listbox_height = w->lines + (-1 + take_give_from_to_owner); 
     440    } 
     441    else 
     442    { 
     443        new_dialog_height = owner->lines - take_give_from_to_owner; 
     444        new_listbox_height = w->lines - (-1 + take_give_from_to_owner); 
     445    } 
     446    rect_init (&r_dialog, owner->y, owner->x, new_dialog_height, owner->cols); 
     447    rect_init (&r_listbox, w->y, w->x, new_listbox_height, w->cols); 
     448    send_message (w, NULL, MSG_RESIZE, 0, &r_listbox); 
     449    send_message (owner, NULL, MSG_RESIZE, 0, &r_dialog); 
     450} 
     451 
     452/* --------------------------------------------------------------------------------------------- */ 
     453 
     454static void 
     455listbox_show_multi_search_input (WListbox * l) 
     456{ 
     457    Widget *w = WIDGET (l), *owner = WIDGET (WIDGET (l)->owner); 
     458    WInput *multi_search_in; 
     459    int distance_y = owner->y + owner->lines - (w->y + w->lines) + 1; 
     460    int distance_x = w->cols > 40 ? 5 : 1, small = w->cols <= 15 ? 1 : 0; 
     461 
     462    listbox_make_one_line_room (l, 1); 
     463    multi_search_in = input_new (owner->lines - distance_y, distance_x, 
     464                                 input_colors, w->cols - 2 - distance_x + small, "", 
     465                                 "multi_search", INPUT_COMPLETE_NONE); 
     466    multi_search_in->forward_listbox = l; 
     467    group_add_widget_autopos (GROUP (owner), multi_search_in, WPOS_KEEP_TOP | WPOS_CENTER_HORZ, 
     468                              NULL); 
     469 
     470    repaint_screen (); 
     471    send_message (l, NULL, MSG_DRAW, 0, NULL); 
     472    send_message (multi_search_in, NULL, MSG_DRAW, 0, NULL); 
     473} 
     474 
     475/* --------------------------------------------------------------------------------------------- */ 
     476 
     477static void 
     478listbox_hide_multi_search_widget (WListbox * l) 
     479{ 
     480    Widget *in; 
     481    in = WIDGET (WIDGET (l)->owner)->find_by_type (WIDGET (WIDGET (l)->owner), input_callback); 
     482    if (in) 
     483    { 
     484        group_remove_widget (in); 
     485        listbox_make_one_line_room (l, 0); 
     486        group_select_next_widget (WIDGET (l)->owner); 
     487        send_message (l, NULL, MSG_DRAW, 0, NULL); 
     488        widget_destroy (in); 
     489    } 
     490    repaint_screen (); 
     491} 
     492 
     493/* --------------------------------------------------------------------------------------------- */ 
     494 
    270495static cb_ret_t 
    271496listbox_execute_cmd (WListbox * l, long command) 
    272497{ 
    listbox_execute_cmd (WListbox * l, long command) 
    320545                              D_ERROR, 2, _("&Yes"), _("&No")) == 0)) 
    321546            listbox_remove_list (l); 
    322547        break; 
     548    case CK_MultiSearch: 
     549        /* Toggle the multi term searching of any listbox. */ 
     550        if (listbox_is_filter_state (l)) 
     551        { 
     552            /* Remove the input widget from the dialog. */ 
     553            listbox_hide_multi_search_widget (l); 
     554            /* Restore original (unfiltered) listbox contents. */ 
     555            ret = listbox_set_to_normal_state (l); 
     556        } 
     557        else 
     558        { 
     559            /* Add input widget for the filter query at the bottom of the dialog window. */ 
     560            listbox_show_multi_search_input (l); 
     561            /* … and then turn on the filter state. */ 
     562            ret = listbox_set_to_filter_state (l); 
     563        } 
     564        break; 
    323565    case CK_View: 
    324566    case CK_Edit: 
    325567    case CK_Enter: 
    listbox_key (WListbox * l, int key) 
    358600 
    359601/* --------------------------------------------------------------------------------------------- */ 
    360602 
     603/* When called via g_queue_foreach it assigns the index field with an incremented int. */ 
     604static void 
     605listbox_foreach_apply_index (gpointer data, gpointer user_data) 
     606{ 
     607    WLEntry *e = data; 
     608    int *cur_idx = user_data; 
     609    e->index = *cur_idx; 
     610    *cur_idx = *cur_idx + 1; 
     611} 
     612 
     613/* --------------------------------------------------------------------------------------------- */ 
     614 
    361615/* Listbox item adding function */ 
    362616static inline void 
    363617listbox_append_item (WListbox * l, WLEntry * e, listbox_append_t pos) 
    listbox_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void 
    482736        widget_gotoyx (l, l->cursor_y, 0); 
    483737        return MSG_HANDLED; 
    484738 
     739    case MSG_UPDATE_LIST: 
     740        listbox_filter_list (l, (char *) data); 
     741        repaint_screen (); 
     742        return MSG_HANDLED; 
     743 
    485744    case MSG_DRAW: 
    486745        listbox_draw (l, widget_get_state (w, WST_FOCUSED)); 
    487746        return MSG_HANDLED; 
    listbox_new (int y, int x, int height, int width, gboolean deletable, lcback_fn 
    556815    l = g_new (WListbox, 1); 
    557816    w = WIDGET (l); 
    558817    widget_init (w, y, x, height, width, listbox_callback, listbox_mouse_callback); 
    559     w->options |= WOP_SELECTABLE | WOP_WANT_HOTKEY; 
     818    w->options |= WOP_SELECTABLE; 
    560819    w->keymap = listbox_map; 
    561820 
    562821    l->list = NULL; 
     822    l->list_keep = NULL; 
    563823    l->top = l->pos = 0; 
    564824    l->deletable = deletable; 
    565825    l->callback = callback; 
    listbox_new (int y, int x, int height, int width, gboolean deletable, lcback_fn 
    571831 
    572832/* --------------------------------------------------------------------------------------------- */ 
    573833 
     834/* 
     835 * If the list is filtered it replaces from the back list (list_keep). It returns whether such  
     836 * change occurred – FALSE means that the list was already unfiltered. 
     837 */ 
     838gboolean 
     839listbox_ensure_unfiltered_state (WListbox * l) 
     840{ 
     841    gboolean ret = FALSE; 
     842    if (listbox_is_filter_state (l)) 
     843        ret = listbox_set_to_normal_state (l); 
     844    return ret; 
     845} 
     846 
     847gboolean 
     848listbox_conditionally_enable_multi_search (WListbox * l) 
     849{ 
     850    int start_with_multi_search_active; 
     851 
     852    /* Option of starting the listbox with MultiSearch pre-activated. */ 
     853    start_with_multi_search_active = 
     854        mc_config_get_bool (mc_global.main_config, CONFIG_APP_SECTION, 
     855                            "multi_search_active_by_default", 1); 
     856 
     857    /* CK_MultiSearch toggles the state. */ 
     858    if (start_with_multi_search_active) 
     859        send_message (l, NULL, MSG_ACTION, CK_MultiSearch, NULL); 
     860 
     861    return start_with_multi_search_active; 
     862} 
     863 
     864/* --------------------------------------------------------------------------------------------- */ 
     865 
    574866/** 
    575867 * Finds item by its label. 
    576868 */ 
    listbox_select_entry (WListbox * l, int dest) 
    673965                if (l->pos - l->top >= lines) 
    674966                    l->top = l->pos - lines + 1; 
    675967            } 
     968            /* 
     969             * Set the virtual position, i.e.: a position in the initial, unfiltered list if the  
     970             * same element would be selected. 
     971             */ 
     972            if (listbox_is_filter_state (l)) 
     973            { 
     974                WLEntry *e = listbox_get_nth_item (l, l->pos); 
     975                if (e) 
     976                    l->virtual_pos = e->index; 
     977                else 
     978                    l->virtual_pos = -1; 
     979            } 
     980            else 
     981            { 
     982                l->virtual_pos = pos; 
     983            } 
    676984            return; 
    677985        } 
    678986    } 
    listbox_remove_list (WListbox * l) 
    8021110 
    8031111/* --------------------------------------------------------------------------------------------- */ 
    8041112 
     1113/* 
     1114 * Initializes the listbox elements with their position index. This allows to alter (filter, in 
     1115 * particular) the listbox elements order and still get the original index (when selecting an 
     1116 * element). 
     1117 */ 
     1118void 
     1119listbox_init_indices (WListbox * l) 
     1120{ 
     1121    int index = 0; 
     1122    g_queue_foreach (l->list, listbox_foreach_apply_index, &index); 
     1123} 
     1124 
     1125/* --------------------------------------------------------------------------------------------- */ 
     1126 
    8051127char * 
    8061128listbox_add_item (WListbox * l, listbox_append_t pos, int hotkey, const char *text, void *data, 
    8071129                  gboolean free_data) 
    listbox_add_item (WListbox * l, listbox_append_t pos, int hotkey, const char *te 
    8151137        return NULL; 
    8161138 
    8171139    entry = g_new (WLEntry, 1); 
     1140    entry->index = -1;          /* Will be initialized when switching to the filter state */ 
    8181141    entry->text = g_strdup (text); 
     1142    entry->free_text = 1; 
    8191143    entry->data = data; 
    8201144    entry->free_data = free_data; 
    8211145    entry->hotkey = hotkey; 
  • lib/widget/listbox.h

    diff --git a/lib/widget/listbox.h b/lib/widget/listbox.h
    index 8b2236eff..0d58d8998 100644
    a b typedef lcback_ret_t (*lcback_fn) (struct WListbox * l); 
    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..c58bddeae 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/editcmd_dialogs.c

    diff --git a/src/editor/editcmd_dialogs.c b/src/editor/editcmd_dialogs.c
    index 8b3634f23..8fe98f875 100644
    a b editcmd_dialog_completion_show (const WEdit * edit, int max_len, GString ** comp 
    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..5e6ee40e5 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/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};