Ticket #4165: 0001-MultiSearch-an-AND-chained-filtering-of-any-listbox_V3.patch

File 0001-MultiSearch-an-AND-chained-filtering-of-any-listbox_V3.patch, 88.2 KB (added by psprint, 16 months ago)

sub-classing

  • lib/keybind.c

    From c2b6774eb040ef9b97494e9c844fbf68d94963d0 Mon Sep 17 00:00:00 2001
    From: Sebastian Gniazdowski <sgniazdowski@gmail.com>
    Date: Thu, 14 Jan 2021 09:27:39 -0600
    Subject: [PATCH] =?UTF-8?q?MultiSearch=20=E2=80=93=20an=20AND-chained=20au?=
     =?UTF-8?q?tomatic=20filtering=20of=20any=20listbox=20in=20MC?=
    MIME-Version: 1.0
    Content-Type: text/plain; charset=UTF-8
    Content-Transfer-Encoding: 8bit
    
    ---
     lib/keybind.c                  |   1 +
     lib/keybind.h                  |   1 +
     lib/strutil.h                  |  30 ++-
     lib/strutil/strutil.c          |  32 +--
     lib/strutil/strutil8bit.c      |   4 +-
     lib/strutil/strutilascii.c     |   4 +-
     lib/strutil/strutilutf8.c      |  34 ++-
     lib/widget.h                   |   2 +
     lib/widget/Makefile.am         |   2 +
     lib/widget/dialog-switch.c     |   3 +-
     lib/widget/filtering_listbox.c | 420 +++++++++++++++++++++++++++++++++
     lib/widget/filtering_listbox.h |  48 ++++
     lib/widget/forwarding_input.c  | 167 +++++++++++++
     lib/widget/forwarding_input.h  |  37 +++
     lib/widget/group.c             |  11 +-
     lib/widget/history.c           | 100 ++++----
     lib/widget/history.h           |   4 +-
     lib/widget/input.c             |  64 +++--
     lib/widget/input.h             |   1 +
     lib/widget/input_complete.c    | 141 +++++++----
     lib/widget/listbox-window.c    |  32 ++-
     lib/widget/listbox-window.h    |   4 +-
     lib/widget/listbox.c           |  51 +++-
     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   |  17 +-
     src/editor/editwidget.c        |   8 +-
     src/file_history.c             |   3 +-
     src/help.c                     |   3 +-
     src/keybind-defaults.c         |   5 +-
     src/selcodepage.c              |   2 +-
     src/usermenu.c                 |  48 ++--
     34 files changed, 1102 insertions(+), 196 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[] = { 
    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/strutil.h

    diff --git a/lib/strutil.h b/lib/strutil.h
    index a091c25aa..667f7df2e 100644
    a b struct str_class 
    110110    gboolean (*is_valid_string) (const char *); 
    111111      /*I*/ int (*is_valid_char) (const char *, size_t); 
    112112      /*I*/ void (*cnext_char) (const char **); 
    113     void (*cprev_char) (const char **); 
     113    void (*cprev_char) (const char **, const char *begin); 
    114114    void (*cnext_char_safe) (const char **); 
    115       /*I*/ void (*cprev_char_safe) (const char **); 
     115      /*I*/ void (*cprev_char_safe) (const char **, const char *begin); 
    116116      /*I*/ int (*cnext_noncomb_char) (const char **text); 
    117117      /*I*/ int (*cprev_noncomb_char) (const char **text, const char *begin); 
    118118      /*I*/ gboolean (*char_isspace) (const char *); 
    int str_is_valid_char (const char *ch, size_t size); 
    262262char *str_get_next_char (char *text); 
    263263const char *str_cget_next_char (const char *text); 
    264264 
    265 /* return previous characters before text, do not call on the start of strings 
     265/* return previous characters before text 
    266266 */ 
    267 char *str_get_prev_char (char *text); 
    268 const char *str_cget_prev_char (const char *text); 
     267char *str_get_prev_char (char *text, const char *begin); 
     268const char *str_cget_prev_char (const char *text, const char *begin); 
    269269 
    270270/* set text to next characters, do not call on the end of string 
    271271 */ 
    272272void str_next_char (char **text); 
    273273void str_cnext_char (const char **text); 
    274274 
    275 /* set text to previous characters, do not call on the start of strings 
     275/* set text to previous characters 
    276276 */ 
    277 void str_prev_char (char **text); 
    278 void str_cprev_char (const char **text); 
     277void str_prev_char (char **text, const char *begin); 
     278void str_cprev_char (const char **text, const char *begin); 
    279279 
    280280/* return next characters after text, do not call on the end of string 
    281281 * works with invalid string  
    void str_cprev_char (const char **text); 
    284284char *str_get_next_char_safe (char *text); 
    285285const char *str_cget_next_char_safe (const char *text); 
    286286 
    287 /* return previous characters before text, do not call on the start of strings 
    288  * works with invalid string  
     287/* return previous characters before text, works with invalid string 
    289288 * I 
    290289 */ 
    291 char *str_get_prev_char_safe (char *text); 
    292 const char *str_cget_prev_char_safe (const char *text); 
     290char *str_get_prev_char_safe (char *text, const char *begin); 
     291const char *str_cget_prev_char_safe (const char *text, const char *begin); 
    293292 
    294293/* set text to next characters, do not call on the end of string 
    295294 * works with invalid string  
    const char *str_cget_prev_char_safe (const char *text); 
    298297void str_next_char_safe (char **text); 
    299298void str_cnext_char_safe (const char **text); 
    300299 
    301 /* set text to previous characters, do not call on the start of strings 
    302  * works with invalid string  
     300/* set text to previous characters, works with invalid string 
    303301 * I 
    304302 */ 
    305 void str_prev_char_safe (char **text); 
    306 void str_cprev_char_safe (const char **text); 
     303void str_prev_char_safe (char **text, const char *begin); 
     304void str_cprev_char_safe (const char **text, const char *begin); 
    307305 
    308306/* set text to next noncombining characters, check the end of text 
    309307 * return how many characters was skipped 
  • lib/strutil/strutil.c

    diff --git a/lib/strutil/strutil.c b/lib/strutil/strutil.c
    index cf11d00d8..937256260 100644
    a b str_cnext_char (const char **text) 
    507507/* --------------------------------------------------------------------------------------------- */ 
    508508 
    509509char * 
    510 str_get_prev_char (char *text) 
     510str_get_prev_char (char *text, const char *begin) 
    511511{ 
    512     used_class.cprev_char ((const char **) &text); 
     512    used_class.cprev_char ((const char **) &text, begin); 
    513513    return text; 
    514514} 
    515515 
    516516/* --------------------------------------------------------------------------------------------- */ 
    517517 
    518518const char * 
    519 str_cget_prev_char (const char *text) 
     519str_cget_prev_char (const char *text, const char *begin) 
    520520{ 
    521     used_class.cprev_char (&text); 
     521    used_class.cprev_char (&text, begin); 
    522522    return text; 
    523523} 
    524524 
    525525/* --------------------------------------------------------------------------------------------- */ 
    526526 
    527527void 
    528 str_prev_char (char **text) 
     528str_prev_char (char **text, const char *begin) 
    529529{ 
    530     used_class.cprev_char ((const char **) text); 
     530    used_class.cprev_char ((const char **) text, begin); 
    531531} 
    532532 
    533533/* --------------------------------------------------------------------------------------------- */ 
    534534 
    535535void 
    536 str_cprev_char (const char **text) 
     536str_cprev_char (const char **text, const char *begin) 
    537537{ 
    538     used_class.cprev_char (text); 
     538    used_class.cprev_char (text, begin); 
    539539} 
    540540 
    541541/* --------------------------------------------------------------------------------------------- */ 
    str_cnext_char_safe (const char **text) 
    575575/* --------------------------------------------------------------------------------------------- */ 
    576576 
    577577char * 
    578 str_get_prev_char_safe (char *text) 
     578str_get_prev_char_safe (char *text, const char *begin) 
    579579{ 
    580     used_class.cprev_char_safe ((const char **) &text); 
     580    used_class.cprev_char_safe ((const char **) &text, begin); 
    581581    return text; 
    582582} 
    583583 
    584584/* --------------------------------------------------------------------------------------------- */ 
    585585 
    586586const char * 
    587 str_cget_prev_char_safe (const char *text) 
     587str_cget_prev_char_safe (const char *text, const char *begin) 
    588588{ 
    589     used_class.cprev_char_safe (&text); 
     589    used_class.cprev_char_safe (&text, begin); 
    590590    return text; 
    591591} 
    592592 
    593593/* --------------------------------------------------------------------------------------------- */ 
    594594 
    595595void 
    596 str_prev_char_safe (char **text) 
     596str_prev_char_safe (char **text, const char *begin) 
    597597{ 
    598     used_class.cprev_char_safe ((const char **) text); 
     598    used_class.cprev_char_safe ((const char **) text, begin); 
    599599} 
    600600 
    601601/* --------------------------------------------------------------------------------------------- */ 
    602602 
    603603void 
    604 str_cprev_char_safe (const char **text) 
     604str_cprev_char_safe (const char **text, const char *begin) 
    605605{ 
    606     used_class.cprev_char_safe (text); 
     606    used_class.cprev_char_safe (text, begin); 
    607607} 
    608608 
    609609/* --------------------------------------------------------------------------------------------- */ 
  • lib/strutil/strutil8bit.c

    diff --git a/lib/strutil/strutil8bit.c b/lib/strutil/strutil8bit.c
    index fa7bbf6a4..755a66ddb 100644
    a b str_8bit_cnext_char (const char **text) 
    112112/* --------------------------------------------------------------------------------------------- */ 
    113113 
    114114static void 
    115 str_8bit_cprev_char (const char **text) 
     115str_8bit_cprev_char (const char **text, const char *begin) 
    116116{ 
    117117    (*text)--; 
     118    if (*text < begin) 
     119        *text = NULL; 
    118120} 
    119121 
    120122/* --------------------------------------------------------------------------------------------- */ 
  • lib/strutil/strutilascii.c

    diff --git a/lib/strutil/strutilascii.c b/lib/strutil/strutilascii.c
    index 7a585303e..a18d34bd7 100644
    a b str_ascii_cnext_char (const char **text) 
    8585/* --------------------------------------------------------------------------------------------- */ 
    8686 
    8787static void 
    88 str_ascii_cprev_char (const char **text) 
     88str_ascii_cprev_char (const char **text, const char *begin) 
    8989{ 
    9090    (*text)--; 
     91    if (*text < begin) 
     92        *text = NULL; 
    9193} 
    9294 
    9395/* --------------------------------------------------------------------------------------------- */ 
  • lib/strutil/strutilutf8.c

    diff --git a/lib/strutil/strutilutf8.c b/lib/strutil/strutilutf8.c
    index 5ac0015e6..37a24be62 100644
    a b str_utf8_cnext_char (const char **text) 
    117117/* --------------------------------------------------------------------------------------------- */ 
    118118 
    119119static void 
    120 str_utf8_cprev_char (const char **text) 
     120str_utf8_cprev_char (const char **text, const char *begin) 
    121121{ 
    122     (*text) = g_utf8_prev_char (*text); 
     122    (*text) = g_utf8_find_prev_char (begin, *text); 
    123123} 
    124124 
    125125/* --------------------------------------------------------------------------------------------- */ 
    str_utf8_cnext_char_safe (const char **text) 
    136136/* --------------------------------------------------------------------------------------------- */ 
    137137 
    138138static void 
    139 str_utf8_cprev_char_safe (const char **text) 
     139str_utf8_cprev_char_safe (const char **text, const char *begin) 
    140140{ 
    141141    const char *result, *t; 
    142142 
    143     result = g_utf8_prev_char (*text); 
     143    result = g_utf8_find_prev_char (begin, *text); 
     144    /* Beginning of the string reached (if NULL)? */ 
     145    if (!result) 
     146    { 
     147        *text = NULL; 
     148        return; 
     149    } 
     150 
    144151    t = result; 
    145152    str_utf8_cnext_char_safe (&t); 
    146153    if (t == *text) 
    147154        (*text) = result; 
    148155    else 
    149156        (*text)--; 
     157 
     158    /* Additional check to ensure no buffer underruns. */ 
     159    if (*text && *text < begin) 
     160        *text = NULL; 
    150161} 
    151162 
    152163/* --------------------------------------------------------------------------------------------- */ 
    str_utf8_cprev_noncomb_char (const char **text, const char *begin) 
    262273 
    263274    while ((*text) != begin) 
    264275    { 
    265         str_utf8_cprev_char_safe (text); 
     276        str_utf8_cprev_char_safe (text, begin); 
    266277        count++; 
    267278        if (!str_utf8_iscombiningmark (*text)) 
    268279            break; 
    str_utf8_trunc (const char *text, int width) 
    889900} 
    890901 
    891902/* --------------------------------------------------------------------------------------------- */ 
     903/* Converts a character index into a byte offset. */ 
    892904 
    893905static int 
    894906str_utf8_offset_to_pos (const char *text, size_t length) 
    895907{ 
     908    glong size; 
     909 
    896910    if (str_utf8_is_valid_string (text)) 
     911    { 
     912        /* Limit index by string size. */ 
     913        size = g_utf8_strlen (text, -1); 
     914        if (size < (glong) length) 
     915            length = (size_t) size; 
    897916        return g_utf8_offset_to_pointer (text, length) - text; 
     917    } 
    898918    else 
    899919    { 
    900920        int result; 
    str_utf8_offset_to_pos (const char *text, size_t length) 
    902922 
    903923        buffer = g_string_new (text); 
    904924        str_utf8_fix_string (buffer->str); 
     925        /* Limit index by string size. */ 
     926        size = g_utf8_strlen (buffer->str, -1); 
     927        if (size < (glong) length) 
     928            length = (size_t) size; 
    905929        result = g_utf8_offset_to_pointer (buffer->str, length) - buffer->str; 
    906930        g_string_free (buffer, TRUE); 
    907931        return result; 
  • lib/widget.h

    diff --git a/lib/widget.h b/lib/widget.h
    index e3bb5cac2..cfe556891 100644
    a b typedef struct WGroup WGroup; 
    3030#include "lib/widget/groupbox.h" 
    3131#include "lib/widget/label.h" 
    3232#include "lib/widget/listbox.h" 
     33#include "lib/widget/filtering_listbox.h" 
    3334#include "lib/widget/menu.h" 
    3435#include "lib/widget/radio.h" 
    3536#include "lib/widget/input.h" 
     37#include "lib/widget/forwarding_input.h" 
    3638#include "lib/widget/listbox-window.h" 
    3739#include "lib/widget/quick.h" 
    3840#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 = \ 
    1616        history.c history.h \ 
    1717        input.c input.h \ 
    1818        input_complete.c \ 
     19        forwarding_input.c forwarding_input.h \ 
    1920        listbox-window.c listbox-window.h \ 
    2021        listbox.c listbox.h \ 
     22        filtering_listbox.c filtering_listbox.h \ 
    2123        label.c label.h \ 
    2224        menu.c menu.h \ 
    2325        mouse.c mouse.h \ 
  • lib/widget/dialog-switch.c

    diff --git a/lib/widget/dialog-switch.c b/lib/widget/dialog-switch.c
    index 62ab23ce0..d01d1328d 100644
    a b dialog_switch_list (void) 
    241241        else 
    242242            title = g_strdup (""); 
    243243 
    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); 
    245246 
    246247        g_free (title); 
    247248    } 
  • 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..937c3047f
    - +  
     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 
     49static WLEntry * 
     50filt_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 
     67static void 
     68filt_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    /* IF the enlarged dialog won't fit the screen, don't resize it but the listbox instead. */ 
     76    if ((sl->resize_strategy == FILT_LIST_DIALOG_AUTO_RESIZE && LINES <= owner->lines + 2) || 
     77        sl->resize_strategy == FILT_LIST_KEEP_DIALOG_SIZE) 
     78        take_give_from_to_owner = 0; 
     79 
     80    // Increase the height of the dialog by 1, so that the new input fits. 
     81    if (should_add_free_room) 
     82    { 
     83        new_dialog_height = owner->lines + take_give_from_to_owner; 
     84        new_listbox_height = w->lines + (-1 + take_give_from_to_owner); 
     85        new_dialog_ypos = owner->y - take_give_from_to_owner; 
     86    } 
     87    else 
     88    { 
     89        new_dialog_height = owner->lines - take_give_from_to_owner; 
     90        new_listbox_height = w->lines - (-1 + take_give_from_to_owner); 
     91        new_dialog_ypos = owner->y + take_give_from_to_owner; 
     92    } 
     93    rect_init (&r_dialog, new_dialog_ypos, owner->x, new_dialog_height, owner->cols); 
     94    rect_init (&r_listbox, w->y, w->x, new_listbox_height, w->cols); 
     95    send_message (w, NULL, MSG_RESIZE, 0, &r_listbox); 
     96    send_message (owner, NULL, MSG_RESIZE, 0, &r_dialog); 
     97} 
     98 
     99/* --------------------------------------------------------------------------------------------- */ 
     100 
     101static void 
     102filt_listbox_show_multi_search_widget (WFilteringListbox * sl) 
     103{ 
     104    WListbox *l = LISTBOX (sl); 
     105    Widget *w = WIDGET (l), *owner = WIDGET (WIDGET (l)->owner); 
     106    WForwardingInput *multi_search_in; 
     107    int distance_y = owner->y + owner->lines - (w->y + w->lines) + 1; 
     108    int distance_x = w->cols > 40 ? 5 : 1, small = w->cols <= 15 ? 1 : 0; 
     109 
     110    filt_listbox_make_one_line_room (sl, 1); 
     111    multi_search_in = forwarding_input_new (owner->lines - distance_y, distance_x, 
     112                                            input_colors, w->cols - 2 - distance_x + small, "", 
     113                                            "multi_search", INPUT_COMPLETE_NONE, w); 
     114    group_add_widget_autopos (GROUP (owner), multi_search_in, WPOS_KEEP_TOP | WPOS_CENTER_HORZ, 
     115                              NULL); 
     116 
     117    repaint_screen (); 
     118    send_message (w, NULL, MSG_DRAW, 0, NULL); 
     119    send_message (WIDGET (multi_search_in), NULL, MSG_DRAW, 0, NULL); 
     120} 
     121 
     122/* --------------------------------------------------------------------------------------------- */ 
     123 
     124static void 
     125filt_listbox_hide_multi_search_widget (WFilteringListbox * sl) 
     126{ 
     127    WListbox *l = LISTBOX (sl); 
     128    Widget *in; 
     129    in = WIDGET (WIDGET (l)->owner)->find_by_type (WIDGET (WIDGET (l)->owner), forw_input_callback); 
     130    if (in) 
     131    { 
     132        group_remove_widget (in); 
     133        filt_listbox_make_one_line_room (sl, 0); 
     134        group_select_next_widget (WIDGET (l)->owner); 
     135        send_message (l, NULL, MSG_DRAW, 0, NULL); 
     136        widget_destroy (in); 
     137    } 
     138    repaint_screen (); 
     139} 
     140 
     141/* --------------------------------------------------------------------------------------------- */ 
     142 
     143/* Return TRUE if given listbox is in WST_FILTER state. */ 
     144static gboolean 
     145filt_listbox_is_filter_state (WFilteringListbox * sl) 
     146{ 
     147    return ((WIDGET (sl)->state & WST_FILTER) != 0); 
     148} 
     149 
     150/* --------------------------------------------------------------------------------------------- */ 
     151 
     152static void 
     153filt_listbox_filter_list (WFilteringListbox * sl, const char *text) 
     154{ 
     155    WListbox *l = LISTBOX (sl); 
     156    int i, size; 
     157    GList *le; 
     158    g_auto (GStrv) query_terms = NULL; 
     159 
     160    /* Remove the list and allocate a new one. */ 
     161    if (l->list) 
     162        g_queue_free_full (l->list, g_free); 
     163    l->list = g_queue_new (); 
     164 
     165    /* Split the query into space delimeted strings. */ 
     166    query_terms = g_strsplit (text, " ", 10); 
     167 
     168    /* 
     169     * Get the size of the listbox and iterate over it testing each element against ·all· words in  
     170     * query_terms. 
     171     */ 
     172    size = g_queue_get_length (sl->list_keep); 
     173    le = g_queue_peek_head_link (sl->list_keep); 
     174    for (i = 0; i < size; i++, le = g_list_next (le)) 
     175    { 
     176        WLEntry *e = LENTRY (le->data); 
     177        gboolean match = TRUE; 
     178 
     179        /* Test the query against the list entry. */ 
     180        for (gchar ** p = query_terms; *p; p++) 
     181        { 
     182            if (**p && !strcasestr (e->text, *p)) 
     183            { 
     184                match = FALSE; 
     185                break; 
     186            } 
     187        } 
     188 
     189        /* If all the terms matched, then add the element to the list. */ 
     190        if (match) 
     191            g_queue_push_tail (l->list, filt_listbox_shallow_copy_entry (e, FALSE)); 
     192    } 
     193    if (listbox_is_empty (l)) 
     194    { 
     195        listbox_add_item (l, LISTBOX_APPEND_AT_END, 0, "<no search results>", NULL, FALSE); 
     196        LENTRY (g_queue_peek_head_link (l->list)->data)->index = -2; 
     197    } 
     198    size = g_queue_get_length (l->list); 
     199    if (l->pos >= size) 
     200        listbox_select_entry (l, size - 1); 
     201    else 
     202        listbox_select_entry (l, l->pos); 
     203} 
     204 
     205/* --------------------------------------------------------------------------------------------- */ 
     206 
     207/* Restores original elements of the list (from sl->list_keep) and turns off the WST_FILTER state. */ 
     208static gboolean 
     209filt_listbox_set_to_normal_state (WFilteringListbox * sl) 
     210{ 
     211    WListbox *l = LISTBOX (sl); 
     212    /* The listbox is already in non-filter state? */ 
     213    if ((WIDGET (l)->state & WST_FILTER) == 0) 
     214    { 
     215        /* Return doing no change, just signal the error. */ 
     216        return FALSE; 
     217    } 
     218 
     219    /* The keep-list must be allocated (even if it's empty). */ 
     220    g_assert (sl->list_keep); 
     221 
     222    /* Mark the new state. */ 
     223    WIDGET (l)->set_state (WIDGET (l), WST_FILTER, 0); 
     224 
     225    /* Release the filtered list and replace it with the original, complete list. */ 
     226    g_queue_free_full (l->list, g_free); 
     227    l->list = sl->list_keep; 
     228    sl->list_keep = NULL; 
     229    return TRUE; 
     230} 
     231 
     232/* --------------------------------------------------------------------------------------------- */ 
     233 
     234/* 
     235 * Sets the listbox into ·filter· state. In this state, there's a separate copy of all list 
     236 * entries, while the original (and displayed) field ->list is being a filtered version of the 
     237 * full copy. 
     238 */ 
     239static gboolean 
     240filt_listbox_set_to_filter_state (WFilteringListbox * sl) 
     241{ 
     242    WListbox *l = LISTBOX (sl); 
     243    GList *le; 
     244 
     245    /* The listbox is already in filter state? */ 
     246    if (WIDGET (l)->state & WST_FILTER) 
     247    { 
     248        /* Return doing no change, just signal the error. */ 
     249        return FALSE; 
     250    } 
     251 
     252    /* Mark the new state. */ 
     253    WIDGET (l)->set_state (WIDGET (l), WST_FILTER, 1); 
     254 
     255    /* No list copy when entering filter mode. */ 
     256    g_assert (!sl->list_keep); 
     257    sl->list_keep = g_queue_new (); 
     258 
     259    /* Skip empty lists. */ 
     260    if (listbox_is_empty (l)) 
     261        return TRUE; 
     262 
     263    /* 
     264     * Remember the original position in the list in the field. It'll be used to determine the 
     265     * virtual_pos field. 
     266     */ 
     267    listbox_init_indices (l); 
     268 
     269    /* Perform a shallow copy of the original list. */ 
     270    for (le = g_queue_peek_head_link (l->list); le != NULL; le = g_list_next (le)) 
     271    { 
     272        WLEntry *copy = filt_listbox_shallow_copy_entry (LENTRY (le->data), TRUE); 
     273        g_queue_push_tail (sl->list_keep, copy); 
     274    } 
     275 
     276    return TRUE; 
     277} 
     278 
     279/* --------------------------------------------------------------------------------------------- */ 
     280 
     281/* 
     282 * If the list is filtered it replaces from the back list (list_keep). It returns whether such  
     283 * change occurred – FALSE means that the list was already unfiltered. 
     284 */ 
     285gboolean 
     286filt_listbox_ensure_unfiltered_state (WFilteringListbox * sl) 
     287{ 
     288    gboolean ret = FALSE; 
     289    if (filt_listbox_is_filter_state (sl)) 
     290        ret = filt_listbox_set_to_normal_state (sl); 
     291    return ret; 
     292} 
     293 
     294/* --------------------------------------------------------------------------------------------- */ 
     295 
     296gboolean 
     297filt_listbox_conditionally_enable_multi_search_init (WFilteringListbox * sl) 
     298{ 
     299    int start_with_multi_search_active; 
     300 
     301    /* Option of starting the listbox with MultiSearch pre-activated. */ 
     302    start_with_multi_search_active = 
     303        mc_config_get_bool (mc_global.main_config, CONFIG_APP_SECTION, 
     304                            "multi_search_active_by_default", 1); 
     305 
     306    /* CK_MultiSearch toggles the state. */ 
     307    if (start_with_multi_search_active) 
     308        send_message (WIDGET (sl), NULL, MSG_ACTION, CK_MultiSearch, NULL); 
     309    else 
     310        /* Only init embedded position indices. */ 
     311        listbox_init_indices (LISTBOX (sl)); 
     312 
     313    return start_with_multi_search_active; 
     314} 
     315 
     316/* --------------------------------------------------------------------------------------------- */ 
     317/*** public functions ****************************************************************************/ 
     318/* --------------------------------------------------------------------------------------------- */ 
     319 
     320WFilteringListbox * 
     321filtering_listbox_new (int y, int x, int height, int width, 
     322                       gboolean deletable, lcback_fn callback, 
     323                       filt_listbox_resize_strategy_t resize) 
     324{ 
     325    WListbox *base; 
     326    WFilteringListbox *object; 
     327    Widget *w; 
     328 
     329    /* Allocate memory for the object body. */ 
     330    object = g_new (WFilteringListbox, 1); 
     331 
     332    /* Forward the call to construct the inherited object. */ 
     333    base = listbox_new (y, x, height, width, deletable, callback); 
     334 
     335    /* Shallow copy and shallow release. */ 
     336    object->base = *base; 
     337    g_free (base); 
     338 
     339    /* Alter fields of base class. */ 
     340    w = WIDGET (object); 
     341    object->base_callback = w->callback;        /* Save original callback function */ 
     342    w->callback = filt_listbox_callback;        /* Set custom callback handler */ 
     343 
     344    /* Set extending fields of this class. */ 
     345    object->list_keep = NULL;   /* No back buffer at startup */ 
     346    object->resize_strategy = resize;   /* Save resize strategy */ 
     347 
     348    return object; 
     349} 
     350 
     351/* --------------------------------------------------------------------------------------------- */ 
     352 
     353cb_ret_t 
     354filt_listbox_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data) 
     355{ 
     356    WFilteringListbox *sl = FILT_LISTBOX (w);   /* s* – from `screen`, a "screened" listbox */ 
     357    cb_ret_t ret = MSG_NOT_HANDLED; 
     358    long activity; 
     359 
     360    switch (msg) 
     361    { 
     362    case MSG_ACTION: 
     363        if (parm == CK_MultiSearch) 
     364        { 
     365            gboolean retval; 
     366            /* Toggle the multi term searching of any listbox. */ 
     367            if (filt_listbox_is_filter_state (sl)) 
     368            { 
     369                /* Remove the input widget from the dialog. */ 
     370                filt_listbox_hide_multi_search_widget (sl); 
     371                /* Restore original (unfiltered) listbox contents. */ 
     372                retval = filt_listbox_set_to_normal_state (sl); 
     373            } 
     374            else 
     375            { 
     376                /* Add input widget for the filter query at the bottom of the dialog window. */ 
     377                filt_listbox_show_multi_search_widget (sl); 
     378                /* … and then turn on the filter state. */ 
     379                retval = filt_listbox_set_to_filter_state (sl); 
     380            } 
     381            if (!retval) 
     382                message (D_ERROR | D_CENTER, MSG_ERROR, 
     383                         "An internal error #3 occurred (filtered listbox support)."); 
     384 
     385            ret = MSG_HANDLED; 
     386        } 
     387        break; 
     388 
     389    case MSG_KEY: 
     390        activity = widget_lookup_key (WIDGET (sl), parm); 
     391        if (activity == CK_MultiSearch) 
     392            ret = send_message (w, NULL, MSG_ACTION, CK_MultiSearch, NULL); 
     393        break; 
     394 
     395    case MSG_UPDATE_LIST: 
     396        if (widget_get_state (w, WST_FILTER)) 
     397        { 
     398            filt_listbox_filter_list (sl, (char *) data); 
     399            repaint_screen (); 
     400            ret = MSG_HANDLED; 
     401        } 
     402        widget_draw (w); 
     403        break; 
     404 
     405    case MSG_DESTROY: 
     406        filt_listbox_ensure_unfiltered_state (sl); 
     407        /* ret is unhandled → the message will be forwarded to base class. */ 
     408        break; 
     409    default: 
     410        break; 
     411    } 
     412 
     413    /* Forward action to base class in case it's not yet handled. */ 
     414    if (ret == MSG_NOT_HANDLED) 
     415        ret = sl->base_callback (w, sender, msg, parm, data); 
     416 
     417    return ret; 
     418} 
     419 
     420/* --------------------------------------------------------------------------------------------- */ 
  • 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..8cf2e72df
    - +  
     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 
     14typedef 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 
     23typedef struct WFilteringListbox_s 
     24{ 
     25    WListbox base; 
     26    widget_cb_fn base_callback; /* Saved callback of base class */ 
     27 
     28    /* Fields for new logic. */ 
     29    GQueue *list_keep;          /* Unfiltered list (used in  WST_FILTER state). */ 
     30    filt_listbox_resize_strategy_t resize_strategy; 
     31} WFilteringListbox; 
     32 
     33/*** global variables defined in .c file *********************************************************/ 
     34 
     35/*** declarations of public functions ************************************************************/ 
     36 
     37WFilteringListbox *filtering_listbox_new (int y, int x, int height, int width, 
     38                                          gboolean deletable, lcback_fn callback, 
     39                                          filt_listbox_resize_strategy_t resize); 
     40gboolean filt_listbox_ensure_unfiltered_state (WFilteringListbox * l); 
     41gboolean filt_listbox_conditionally_enable_multi_search_init (WFilteringListbox * l); 
     42void filt_listbox_select_entry (WFilteringListbox * sl, int dest); 
     43cb_ret_t filt_listbox_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, 
     44                                void *data); 
     45 
     46/*** inline functions ****************************************************************************/ 
     47 
     48#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..b3a983814
    - +  
     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 
     55WForwardingInput * 
     56forwarding_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 
     86cb_ret_t 
     87forw_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 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 
     116cb_ret_t 
     117forw_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            /* send_message (WIDGET (in->forward_listbox), NULL, MSG_DRAW, 0, NULL); */ 
     145        } 
     146    } 
     147 
     148    /* 
     149     * If not handled yet, then send the key to the base object for general recognition (if 
     150     * not already done that). 
     151     */ 
     152 
     153    if (!sent_to_base && ret == MSG_NOT_HANDLED) 
     154    { 
     155        ret = in->base_callback (WIDGET (in), WIDGET (in), MSG_KEY, key, NULL); 
     156        sent_to_base = TRUE;    /* unused */ 
     157    } 
     158 
     159    /* Send update signal to paired widget ·if· input's text has changed. */ 
     160    if (in->forward_to_widget && g_strcmp0 (str_cp, in->base.buffer)) 
     161        send_message (WIDGET (in->forward_to_widget), NULL, MSG_UPDATE_LIST, key, in->base.buffer); 
     162    g_free (str_cp); 
     163 
     164    return ret; 
     165} 
     166 
     167/* --------------------------------------------------------------------------------------------- */ 
  • 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 
     10typedef 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 
     27WForwardingInput *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 
     32cb_ret_t forw_input_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data); 
     33cb_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..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..159d6d475 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_create_item (history_descriptor_t * hd, void *data) 
    158167    width = str_term_width1 (text); 
    159168    hd->max_width = MAX (width, hd->max_width); 
    160169 
    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); 
    162171} 
    163172 
    164173/* --------------------------------------------------------------------------------------------- */ 
    history_descriptor_init (history_descriptor_t * hd, int y, int x, GList * histor 
    190199    hd->action = CK_IgnoreKey; 
    191200    hd->text = NULL; 
    192201    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); 
    194203    /* in most cases history list contains string only and no any other data */ 
    195204    hd->create = history_create_item; 
    196205    hd->release = history_release_item; 
    history_show (history_descriptor_t * hd) 
    205214    GList *z, *hi; 
    206215    size_t count; 
    207216    WDialog *query_dlg; 
     217    WListbox *lw; 
    208218    history_dlg_data hist_data; 
    209219    int dlg_ret; 
    210220 
    history_show (history_descriptor_t * hd) 
    217227        hd->create (hd, z->data); 
    218228    /* after this, the order of history items is following: recent at begin, oldest at end */ 
    219229 
    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); 
    221233 
    222234    hist_data.y = hd->y; 
    223235    hist_data.x = hd->x; 
    history_show (history_descriptor_t * hd) 
    244256    { 
    245257        /* history is above base widget -- revert order to place recent item at bottom */ 
    246258        /* revert history direction */ 
    247         g_queue_reverse (hd->listbox->list); 
     259        g_queue_reverse (lw->list); 
    248260        if (hd->current < 0 || (size_t) hd->current >= count) 
    249             listbox_select_last (hd->listbox); 
     261            listbox_select_last (lw); 
    250262        else 
    251             listbox_select_entry (hd->listbox, count - 1 - (size_t) hd->current); 
     263            listbox_select_entry (lw, count - 1 - (size_t) hd->current); 
    252264    } 
    253265    else 
    254266    { 
    255267        /* history is below base widget -- keep order to place recent item on top  */ 
    256268        if (hd->current > 0) 
    257             listbox_select_entry (hd->listbox, hd->current); 
     269            listbox_select_entry (lw, hd->current); 
    258270    } 
    259271 
     272    /* Option of starting the listbox with MultiSearch pre-activated. */ 
     273    filt_listbox_conditionally_enable_multi_search_init (hd->listbox); 
     274 
    260275    dlg_ret = dlg_run (query_dlg); 
    261276    if (dlg_ret != B_CANCEL) 
    262277    { 
    history_show (history_descriptor_t * hd) 
    274289            hd->action = CK_Enter; 
    275290        } 
    276291 
    277         listbox_get_current (hd->listbox, &q, NULL); 
    278         hd->text = g_strdup (q); 
     292        listbox_get_current (lw, &q, NULL); 
     293        /* Can still be 0 if special entry "<no search results>" will be selected. */ 
     294        if (q) 
     295            hd->text = g_strdup (q); 
    279296    } 
    280297 
     298    /* If needed, restore normal listbox state, with no backlist (list_keep). */ 
     299    filt_listbox_ensure_unfiltered_state (hd->listbox); 
     300 
    281301    /* get modified history from dialog */ 
    282302    z = NULL; 
    283     for (hi = listbox_get_first_link (hd->listbox); hi != NULL; hi = g_list_next (hi)) 
     303    for (hi = listbox_get_first_link (lw); hi != NULL; hi = g_list_next (hi)) 
    284304        /* history is being reverted here again */ 
    285305        z = g_list_prepend (z, hd->release (hd, LENTRY (hi->data))); 
    286306 
  • lib/widget/history.h

    diff --git a/lib/widget/history.h b/lib/widget/history.h
    index 9c4b403d1..23f4d403d 100644
    a b  
    1111/* forward declarations */ 
    1212struct history_descriptor_t; 
    1313struct WLEntry; 
    14 struct WListbox; 
     14typedef struct WFilteringListbox_s WFilteringListbox; 
    1515 
    1616typedef void (*history_create_item_func) (struct history_descriptor_t * hd, void *data); 
    1717typedef void *(*history_release_item_func) (struct history_descriptor_t * hd, struct WLEntry * le); 
    typedef struct history_descriptor_t 
    3030    char *text;                 /**< return text of selected item */ 
    3131 
    3232    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 */ 
    3434 
    3535    history_create_item_func create;    /**< function to create item of @list */ 
    3636    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 b5cec7e6b..9b88e4ff8 100644
    a b backward_word (WInput * in) 
    425425{ 
    426426    const char *p; 
    427427 
     428    /* p is limited by strlen. */ 
    428429    p = in->buffer + str_offset_to_pos (in->buffer, in->point); 
    429430 
    430     while (p != in->buffer) 
     431    while (p && p != in->buffer) 
    431432    { 
    432433        const char *p_tmp; 
    433434 
    434435        p_tmp = p; 
    435         str_cprev_char (&p); 
    436         if (!str_isspace (p) && !str_ispunct (p)) 
     436        str_cprev_char (&p, in->buffer); 
     437        if (p) 
    437438        { 
    438             p = p_tmp; 
    439             break; 
     439            /* 
     440             * If preceding char isn't a word boundary (↔ we're ·in· a word), then undo last move and 
     441             * break from loop. 
     442             */ 
     443 
     444            if (!str_isspace (p) && !str_ispunct (p)) 
     445            { 
     446                p = p_tmp; 
     447                break; 
     448            } 
     449            in->point--; 
    440450        } 
    441         in->point--; 
    442451    } 
    443     while (p != in->buffer) 
     452 
     453    /* Further skip the ·word· chars, if not outside the string. */ 
     454    while (p && p != in->buffer) 
    444455    { 
    445         str_cprev_char (&p); 
    446         if (str_isspace (p) || str_ispunct (p)) 
     456        str_cprev_char (&p, in->buffer); 
     457        /* Stop when a word boundary char is detected or when the string ends. */ 
     458        if (!p || str_isspace (p) || str_ispunct (p)) 
    447459            break; 
    448460 
    449461        in->point--; 
    input_save_history (const gchar * event_group_name, const gchar * event_name, 
    871883    (void) event_group_name; 
    872884    (void) event_name; 
    873885 
    874     if (!in->is_password && (DIALOG (WIDGET (in)->owner)->ret_value != B_CANCEL)) 
     886    if (!in->is_password && WIDGET (in)->owner 
     887        && (DIALOG (WIDGET (in)->owner)->ret_value != B_CANCEL)) 
    875888    { 
    876889        ev_history_load_save_t *ev = (ev_history_load_save_t *) data; 
    877890 
    input_mouse_callback (Widget * w, mouse_msg_t msg, mouse_event_t * event) 
    970983    } 
    971984} 
    972985 
     986/* --------------------------------------------------------------------------------------------- */ 
     987 
     988static void 
     989listbox_unregister_history_events_cb (gpointer wid_ptr) 
     990{ 
     991    Widget *w = WIDGET (wid_ptr); 
     992    WDialog *h = DIALOG (w->owner); 
     993 
     994    /* unsubscribe from "history_load" event */ 
     995    mc_event_del (h->event_group, MCEVENT_HISTORY_LOAD, input_load_history, w); 
     996    /* unsubscribe from "history_save" event */ 
     997    mc_event_del (h->event_group, MCEVENT_HISTORY_SAVE, input_save_history, w); 
     998} 
     999 
    9731000/* --------------------------------------------------------------------------------------------- */ 
    9741001/*** public functions ****************************************************************************/ 
    9751002/* --------------------------------------------------------------------------------------------- */ 
    input_new (int y, int x, const int *colors, int width, const char *def_text, 
    10281055    if ((histname != NULL) && (*histname != '\0')) 
    10291056        in->history.name = g_strdup (histname); 
    10301057    /* history will be loaded later */ 
    1031  
    10321058    in->label = NULL; 
    10331059 
    10341060    return in; 
    input_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *d 
    10501076        mc_event_add (h->event_group, MCEVENT_HISTORY_LOAD, input_load_history, w, NULL); 
    10511077        /* subscribe to "history_save" event */ 
    10521078        mc_event_add (h->event_group, MCEVENT_HISTORY_SAVE, input_save_history, w, NULL); 
     1079        /* unregister (via the func) the events in case of removal from dialog */ 
     1080        w->pre_unlink_func = listbox_unregister_history_events_cb; 
    10531081        if (in->label != NULL) 
    10541082            widget_set_state (WIDGET (in->label), WST_DISABLED, widget_get_state (w, WST_DISABLED)); 
    10551083        return MSG_HANDLED; 
    input_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *d 
    10631091            return v; 
    10641092        } 
    10651093 
    1066         /* Keys we want others to handle */ 
     1094        /* Keys we want others to handle. */ 
    10671095        if (parm == KEY_UP || parm == KEY_DOWN || parm == ESC_CHAR 
    10681096            || parm == KEY_F (10) || parm == '\n') 
    10691097            return MSG_NOT_HANDLED; 
    input_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *d 
    10971125        return MSG_HANDLED; 
    10981126 
    10991127    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); 
     1128        /* …only, if there is an owner WGroup. */ 
     1129        if (h) 
     1130        { 
     1131            /* unsubscribe from "history_load" event */ 
     1132            mc_event_del (h->event_group, MCEVENT_HISTORY_LOAD, input_load_history, w); 
     1133            /* unsubscribe from "history_save" event */ 
     1134            mc_event_del (h->event_group, MCEVENT_HISTORY_SAVE, input_save_history, w); 
     1135        } 
    11041136        input_destroy (in); 
    11051137        return MSG_HANDLED; 
    11061138 
  • lib/widget/input.h

    diff --git a/lib/widget/input.h b/lib/widget/input.h
    index a753e6160..aca26d43d 100644
    a b typedef struct 
    7070        GList *current;         /* current history item */ 
    7171        gboolean changed;       /* the history has changed */ 
    7272    } history; 
     73 
    7374} WInput; 
    7475 
    7576/*** global variables defined in .c file *********************************************************/ 
  • lib/widget/input_complete.c

    diff --git a/lib/widget/input_complete.c b/lib/widget/input_complete.c
    index 59a994d62..8ddc975b5 100644
    a b try_complete_commands_prepare (try_complete_automation_state_t * state, char *te 
    843843        ti = text; 
    844844    else 
    845845    { 
    846         ti = str_get_prev_char (&text[*lc_start]); 
     846        ti = str_get_prev_char (&text[*lc_start], text); 
    847847        while (ti > text && whitespace (ti[0])) 
    848             str_prev_char (&ti); 
     848            str_prev_char (&ti, text); 
    849849    } 
    850850 
    851     if (ti == text) 
    852         state->in_command_position++; 
    853     else if (strchr (command_separator_chars, ti[0]) != NULL) 
     851    if (!ti) 
    854852    { 
    855853        state->in_command_position++; 
    856         if (ti != text) 
    857         { 
    858             int this_char, prev_char; 
     854        ti = text; 
     855    } 
    859856 
    860             /* Handle the two character tokens '>&', '<&', and '>|'. 
    861                We are not in a command position after one of these. */ 
    862             this_char = ti[0]; 
    863             prev_char = str_get_prev_char (ti)[0]; 
     857    /* Is there any more preceding text, and there's a command separator? */ 
     858    if (ti != text && strchr (command_separator_chars, ti[0]) != NULL) 
     859    { 
     860        int this_char, prev_char = '\0'; 
     861        char *prev_char_p; 
     862        state->in_command_position++; 
    864863 
    865             /* Quoted */ 
    866             if ((this_char == '&' && (prev_char == '<' || prev_char == '>')) 
    867                 || (this_char == '|' && prev_char == '>') || (ti != text 
    868                                                               && str_get_prev_char (ti)[0] == '\\')) 
    869                 state->in_command_position = 0; 
    870         } 
     864        /* Initial assumption that we're at command. */ 
     865        state->in_command_position++; 
     866 
     867        /* Handle the two character tokens '>&', '<&', and '>|'. 
     868           We are not in a command position after one of these. */ 
     869        this_char = ti[0]; 
     870        prev_char_p = str_get_prev_char (ti, text); 
     871        if (prev_char_p) 
     872            prev_char = prev_char_p[0]; 
     873 
     874        if ((this_char == '&' && (strchr ("<>", prev_char))) 
     875            || (this_char == '|' && prev_char == '>') || prev_char == '\\') 
     876            state->in_command_position = 0; 
    871877    } 
    872878} 
    873879 
    complete_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void 
    10171023{ 
    10181024    static int bl = 0; 
    10191025 
    1020     WGroup *g = GROUP (w); 
    10211026    WDialog *h = DIALOG (w); 
     1027    WFilteringListbox *slw; 
     1028    cb_ret_t ret = MSG_NOT_HANDLED; 
     1029 
     1030    /* Find the listbox in dialog's group. */ 
     1031    slw = FILT_LISTBOX (WIDGET (h)->find_by_type (WIDGET (h), filt_listbox_callback)); 
    10221032 
    10231033    switch (msg) 
    10241034    { 
    complete_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void 
    10271037        { 
    10281038        case KEY_LEFT: 
    10291039        case KEY_RIGHT: 
     1040            /* MultiSearch (list filtering) is allowing for left/right movement in query input. */ 
     1041            if (widget_get_state (WIDGET (slw), WST_FILTER)) 
     1042                break; 
    10301043            bl = 0; 
    10311044            h->ret_value = 0; 
    10321045            dlg_stop (h); 
    1033             return MSG_HANDLED; 
     1046            ret = MSG_HANDLED; 
     1047            break; 
    10341048 
    10351049        case KEY_BACKSPACE: 
     1050            /* MultiSearch is exclusive with completion widening. */ 
     1051            if (widget_get_state (WIDGET (slw), WST_FILTER)) 
     1052                break; 
    10361053            bl = 0; 
    10371054            /* exit from completion list if input line is empty */ 
    10381055            if (end == 0) 
    complete_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void 
    10431060            /* Refill the list box and start again */ 
    10441061            else if (end == min_end) 
    10451062            { 
    1046                 end = str_get_prev_char (&input->buffer[end]) - input->buffer; 
     1063                end = str_get_prev_char (&input->buffer[end], input->buffer) - input->buffer; 
     1064                if (end < 0) 
     1065                    end = 0; 
    10471066                input_handle_char (input, parm); 
    10481067                h->ret_value = B_USER; 
    10491068                dlg_stop (h); 
    complete_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void 
    10541073                int i; 
    10551074                GList *e; 
    10561075 
    1057                 new_end = str_get_prev_char (&input->buffer[end]) - input->buffer; 
     1076                new_end = str_get_prev_char (&input->buffer[end], input->buffer) - input->buffer; 
     1077                /* The buffer is empty (no previous char available)? */ 
     1078                if (new_end < 0) 
     1079                    new_end = 0; 
    10581080 
    1059                 for (i = 0, e = listbox_get_first_link (LISTBOX (g->current->data)); 
     1081                for (i = 0, e = listbox_get_first_link (LISTBOX (slw)); 
    10601082                     e != NULL; i++, e = g_list_next (e)) 
    10611083                { 
    10621084                    WLEntry *le = LENTRY (e->data); 
    10631085 
    10641086                    if (strncmp (input->buffer + start, le->text, new_end - start) == 0) 
    10651087                    { 
    1066                         listbox_select_entry (LISTBOX (g->current->data), i); 
     1088                        listbox_select_entry (LISTBOX (slw), i); 
    10671089                        end = new_end; 
    10681090                        input_handle_char (input, parm); 
    1069                         widget_draw (WIDGET (g->current->data)); 
     1091                        widget_draw (WIDGET (slw)); 
    10701092                        break; 
    10711093                    } 
    10721094                } 
    10731095            } 
    1074             return MSG_HANDLED; 
     1096            ret = MSG_HANDLED; 
     1097            break; 
    10751098 
    10761099        default: 
    10771100            if (parm < 32 || parm > 255) 
    10781101            { 
    10791102                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); 
     1103                /* CK_Complete → Is completion up to date? */ 
     1104                if ((widget_lookup_key (WIDGET (input), parm) == CK_Complete) && (end != min_end)) 
     1105                { 
     1106                    /* This means we want to refill the list box and start again. */ 
     1107                    h->ret_value = B_USER; 
     1108                    dlg_stop (h); 
     1109                } 
     1110                /* 
     1111                 * else – key will be ignored by this function, so leave ret unchanged – allow other 
     1112                 * widgets to process it. 
     1113                 */ 
    10891114            } 
    1090             else 
     1115            /* 
     1116             * Do standard live entry lookup only when not filtering list via MultiSearch – it is 
     1117             * a feature that to a great extent replaces old engine. It can be still used as MultiSearch  
     1118             * can be dynamically enabled and disabled (default by alt-space and ctrl-space). 
     1119             */ 
     1120            else if (!widget_get_state (WIDGET (slw), WST_FILTER)) 
    10911121            { 
    10921122                static char buff[MB_LEN_MAX] = ""; 
    10931123                GList *e; 
    complete_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void 
    11101140                    break; 
    11111141                } 
    11121142 
    1113                 for (i = 0, e = listbox_get_first_link (LISTBOX (g->current->data)); 
     1143                for (i = 0, e = listbox_get_first_link (LISTBOX (slw)); 
    11141144                     e != NULL; i++, e = g_list_next (e)) 
    11151145                { 
    11161146                    WLEntry *le = LENTRY (e->data); 
    complete_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void 
    11211151                        if (need_redraw == 0) 
    11221152                        { 
    11231153                            need_redraw = 1; 
    1124                             listbox_select_entry (LISTBOX (g->current->data), i); 
     1154                            listbox_select_entry (LISTBOX (slw), i); 
    11251155                            last_text = le->text; 
    11261156                        } 
    11271157                        else 
    complete_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void 
    11721202                if (need_redraw == 2) 
    11731203                { 
    11741204                    insert_text (input, last_text, low); 
    1175                     widget_draw (WIDGET (g->current->data)); 
     1205                    widget_draw (WIDGET (slw)); 
    11761206                } 
    11771207                else if (need_redraw == 1) 
    11781208                { 
    complete_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void 
    11801210                    dlg_stop (h); 
    11811211                } 
    11821212                bl = 0; 
     1213                ret = MSG_HANDLED; 
    11831214            } 
    11841215        } 
    1185         return MSG_HANDLED; 
     1216        break; 
    11861217 
    11871218    default: 
    11881219        return dlg_default_callback (w, sender, msg, parm, data); 
    11891220    } 
     1221    return ret; 
    11901222} 
    11911223 
    11921224/* --------------------------------------------------------------------------------------------- */ 
    complete_engine (WInput * in, int what_to_do) 
    12231255            int start_x, start_y; 
    12241256            char **p, *q; 
    12251257            WDialog *complete_dlg; 
    1226             WListbox *complete_list; 
     1258            WFilteringListbox *complete_list; 
    12271259 
    12281260            for (p = in->completions + 1; *p != NULL; count++, p++) 
    12291261            { 
    complete_engine (WInput * in, int what_to_do) 
    12661298            complete_dlg = 
    12671299                dlg_create (TRUE, y, x, complete_height, complete_width, WPOS_KEEP_DEFAULT, TRUE, 
    12681300                            dialog_colors, complete_callback, NULL, "[Completion]", NULL); 
    1269             complete_list = listbox_new (1, 1, h - 2, w - 2, FALSE, NULL); 
     1301            complete_list = filtering_listbox_new (1, 1, h - 2, w - 2, FALSE, NULL, 
     1302                                                   FILT_LIST_KEEP_DIALOG_SIZE); 
    12701303            group_add_widget (GROUP (complete_dlg), complete_list); 
    12711304 
    12721305            for (p = in->completions + 1; *p != NULL; p++) 
    1273                 listbox_add_item (complete_list, LISTBOX_APPEND_AT_END, 0, *p, NULL, FALSE); 
     1306                listbox_add_item (LISTBOX (complete_list), LISTBOX_APPEND_AT_END, 0, *p, NULL, 
     1307                                  FALSE); 
     1308 
     1309            /* Option of starting the listbox with MultiSearch pre-activated. */ 
     1310            filt_listbox_conditionally_enable_multi_search_init (complete_list); 
    12741311 
    12751312            i = dlg_run (complete_dlg); 
    12761313            q = NULL; 
    12771314            if (i == B_ENTER) 
    12781315            { 
    1279                 listbox_get_current (complete_list, &q, NULL); 
     1316                listbox_get_current (LISTBOX (complete_list), &q, NULL); 
    12801317                if (q != NULL) 
    12811318                    insert_text (in, q, strlen (q)); 
    12821319            } 
    12831320            if (q != NULL || end != min_end) 
    12841321                input_complete_free (in); 
     1322 
    12851323            dlg_destroy (complete_dlg); 
    12861324 
    12871325            /* B_USER if user wants to start over again */ 
    complete_engine_fill_completions (WInput * in) 
    14011439{ 
    14021440    char *s; 
    14031441    const char *word_separators; 
     1442    gboolean separator_found = FALSE; 
    14041443 
    14051444    word_separators = (in->completion_flags & INPUT_COMPLETE_SHELL_ESC) ? " \t;|<>" : "\t;|<>"; 
    14061445 
    complete_engine_fill_completions (WInput * in) 
    14161455            str_next_char (&s); 
    14171456    } 
    14181457 
    1419     for (; s >= in->buffer; str_prev_char (&s)) 
     1458    for (; s; str_prev_char (&s, in->buffer)) 
    14201459    { 
    14211460        start = s - in->buffer; 
    14221461        if (strchr (word_separators, *s) != NULL && !strutils_is_char_escaped (in->buffer, s)) 
     1462        { 
     1463            separator_found = TRUE; 
    14231464            break; 
     1465        } 
    14241466    } 
    14251467 
     1468    /* Note: str_prev_char() returns NULL if no preceding character fitting within string found. */ 
     1469    if (s < in->buffer) 
     1470        s = in->buffer; 
     1471 
    14261472    if (start < end) 
    14271473    { 
    1428         str_next_char (&s); 
     1474        if (separator_found) 
     1475            str_next_char (&s); 
     1476        if (s > in->buffer + end) 
     1477            s = in->buffer + end; 
    14291478        start = s - in->buffer; 
    14301479    } 
    14311480 
  • lib/widget/listbox-window.c

    diff --git a/lib/widget/listbox-window.c b/lib/widget/listbox-window.c
    index 44548b485..58b1e5f75 100644
    a b create_listbox_window_centered (int center_y, int center_x, int lines, int cols, 
    108108        dlg_create (TRUE, ypos, xpos, lines + space, cols + space, pos_flags, FALSE, listbox_colors, 
    109109                    NULL, NULL, help, title); 
    110110 
    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)); 
    113114 
    114115    return listbox; 
    115116} 
    create_listbox_window (int lines, int cols, const char *title, const char *help) 
    128129int 
    129130run_listbox (Listbox * l) 
    130131{ 
     132    WListbox *lw = LISTBOX (l->list); 
    131133    int val = -1; 
    132134 
     135    /* Option of starting the listbox with MultiSearch activated. */ 
     136    filt_listbox_conditionally_enable_multi_search_init (l->list); 
     137 
    133138    if (dlg_run (l->dlg) != B_CANCEL) 
    134         val = l->list->pos; 
     139    { 
     140        /* Get the virtual index first, to support filtered listboxes. */ 
     141        val = lw->virtual_pos; 
     142 
     143        /* No virtual position → get pos. */ 
     144        if (val == -1) 
     145            val = lw->pos; 
     146        else if (val == -2) 
     147            val = -1; 
     148    } 
     149 
    135150    dlg_destroy (l->dlg); 
    136151    g_free (l); 
    137152    return val; 
    run_listbox (Listbox * l) 
    149164void * 
    150165run_listbox_with_data (Listbox * l, const void *select) 
    151166{ 
     167    WListbox *lw = LISTBOX (l->list); 
    152168    void *val = NULL; 
    153169 
     170    /* Option of starting the listbox with MultiSearch activated. */ 
     171    filt_listbox_conditionally_enable_multi_search_init (l->list); 
     172 
    154173    if (select != NULL) 
    155         listbox_select_entry (l->list, listbox_search_data (l->list, select)); 
     174        listbox_select_entry (lw, listbox_search_data (lw, select)); 
    156175 
    157176    if (dlg_run (l->dlg) != B_CANCEL) 
    158177    { 
    159178        WLEntry *e; 
    160         e = listbox_get_nth_item (l->list, l->list->pos); 
    161         if (e != NULL) 
     179        e = listbox_get_nth_item (lw, lw->pos); 
     180        /* -2 means that "<no search results>" has been selected. */ 
     181        if (e != NULL && e->index != -2) 
    162182        { 
    163183            /* The assert guards against returning a soon-to-be deallocated 
    164184             * 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  
    88/*** typedefs(not structures) and defined constants **********************************************/ 
    99 
    1010#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) 
    1212 
    1313/*** enums ***************************************************************************************/ 
    1414 
     
    1717typedef struct 
    1818{ 
    1919    WDialog *dlg; 
    20     WListbox *list; 
     20    WFilteringListbox *list; 
    2121} Listbox; 
    2222 
    2323/*** global variables defined in .c file *********************************************************/ 
  • lib/widget/listbox.c

    diff --git a/lib/widget/listbox.c b/lib/widget/listbox.c
    index e20c1a82d..4c33e89b2 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_key (WListbox * l, int key) 
    358359 
    359360/* --------------------------------------------------------------------------------------------- */ 
    360361 
     362/* When called via g_queue_foreach it assigns the index field with an incremented int. */ 
     363static void 
     364listbox_foreach_apply_index (gpointer data, gpointer user_data) 
     365{ 
     366    WLEntry *e = data; 
     367    int *cur_idx = user_data; 
     368    e->index = *cur_idx; 
     369    *cur_idx = *cur_idx + 1; 
     370} 
     371 
     372/* --------------------------------------------------------------------------------------------- */ 
     373 
    361374/* Listbox item adding function */ 
    362375static inline void 
    363376listbox_append_item (WListbox * l, WLEntry * e, listbox_append_t pos) 
    listbox_new (int y, int x, int height, int width, gboolean deletable, lcback_fn 
    556569    l = g_new (WListbox, 1); 
    557570    w = WIDGET (l); 
    558571    widget_init (w, y, x, height, width, listbox_callback, listbox_mouse_callback); 
    559     w->options |= WOP_SELECTABLE | WOP_WANT_HOTKEY; 
     572    w->options |= WOP_SELECTABLE; 
    560573    w->keymap = listbox_map; 
    561574 
    562575    l->list = NULL; 
    563     l->top = l->pos = 0; 
     576    l->top = l->pos = l->virtual_pos = 0; 
    564577    l->deletable = deletable; 
    565578    l->callback = callback; 
    566579    l->allow_duplicates = TRUE; 
    listbox_select_last (WListbox * l) 
    648661void 
    649662listbox_select_entry (WListbox * l, int dest) 
    650663{ 
     664    WLEntry *e; 
    651665    GList *le; 
    652666    int pos; 
    653667    gboolean top_seen = FALSE; 
    654668 
    655669    if (listbox_is_empty (l) || dest < 0) 
     670    { 
     671        /* Reset position to a minimal one. */ 
     672        l->pos = 0; 
     673        l->virtual_pos = 0; 
    656674        return; 
     675    } 
    657676 
    658677    /* Special case */ 
    659678    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) 
    673692                if (l->pos - l->top >= lines) 
    674693                    l->top = l->pos - lines + 1; 
    675694            } 
     695            /* 
     696             * Set the virtual position, i.e.: a position in the initial, unfiltered list if the 
     697             * same element would be selected. 
     698             */ 
     699            e = LENTRY (le->data); 
     700            l->virtual_pos = e->index; 
    676701            return; 
    677702        } 
    678703    } 
    listbox_get_current (WListbox * l, char **string, void **extra) 
    701726    if (l != NULL) 
    702727        e = listbox_get_nth_item (l, l->pos); 
    703728 
    704     ok = (e != NULL); 
     729    ok = (e != NULL && e->index != -2); 
    705730 
    706731    if (string != NULL) 
    707732        *string = ok ? e->text : NULL; 
    listbox_get_nth_item (const WListbox * l, int pos) 
    720745        GList *item; 
    721746 
    722747        item = g_queue_peek_nth_link (l->list, (guint) pos); 
    723         if (item != NULL) 
     748        if (item != NULL && LENTRY (item->data)->index != -2) 
    724749            return LENTRY (item->data); 
    725750    } 
    726751 
    listbox_remove_list (WListbox * l) 
    802827 
    803828/* --------------------------------------------------------------------------------------------- */ 
    804829 
     830/* 
     831 * Initializes the listbox elements with their position index. This allows to alter (filter, in 
     832 * particular) the listbox elements order and still get the original index (when selecting an 
     833 * element). 
     834 */ 
     835void 
     836listbox_init_indices (WListbox * l) 
     837{ 
     838    int index = 0; 
     839    g_queue_foreach (l->list, listbox_foreach_apply_index, &index); 
     840} 
     841 
     842/* --------------------------------------------------------------------------------------------- */ 
     843 
    805844char * 
    806845listbox_add_item (WListbox * l, listbox_append_t pos, int hotkey, const char *text, void *data, 
    807846                  gboolean free_data) 
    listbox_add_item (WListbox * l, listbox_append_t pos, int hotkey, const char *te 
    815854        return NULL; 
    816855 
    817856    entry = g_new (WLEntry, 1); 
     857    entry->index = -1;          /* Will be initialized when switching to the filter state */ 
    818858    entry->text = g_strdup (text); 
     859    entry->free_text = 1; 
    819860    entry->data = data; 
    820861    entry->free_data = free_data; 
    821862    entry->hotkey = hotkey; 
  • lib/widget/listbox.h

    diff --git a/lib/widget/listbox.h b/lib/widget/listbox.h
    index 8b2236eff..461564f29 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 
    4648    Widget widget; 
    4749    GQueue *list;               /* Pointer to the list of WLEntry */ 
    4850    int pos;                    /* The current element displayed */ 
     51    int virtual_pos;            /* The initial index of the current element, works also for filtered listbox */ 
    4952    int top;                    /* The first element displayed */ 
    5053    gboolean allow_duplicates;  /* Do we allow duplicates on the list? */ 
    5154    gboolean scrollbar;         /* Draw a scrollbar? */ 
    extern const global_keymap_t *listbox_map; 
    6164/*** declarations of public functions ************************************************************/ 
    6265 
    6366WListbox *listbox_new (int y, int x, int height, int width, gboolean deletable, lcback_fn callback); 
     67 
    6468int listbox_search_text (WListbox * l, const char *text); 
    6569int listbox_search_data (WListbox * l, const void *data); 
    6670void listbox_select_first (WListbox * l); 
    void listbox_remove_current (WListbox * l); 
    7478gboolean listbox_is_empty (const WListbox * l); 
    7579void listbox_set_list (WListbox * l, GQueue * list); 
    7680void listbox_remove_list (WListbox * l); 
     81void listbox_init_indices (WListbox * l); 
    7782char *listbox_add_item (WListbox * l, listbox_append_t pos, int hotkey, const char *text, 
    7883                        void *data, gboolean free_data); 
    7984 
  • 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/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) 
    8484        name = g_ptr_array_index (names, i); 
    8585        LISTBOX_APPEND_TEXT (syntaxlist, 0, name, NULL, FALSE); 
    8686        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); 
    8888    } 
    8989 
    9090    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..1e8509884 100644
    a b editcmd_dialog_completion_show (const WEdit * edit, int max_len, GString ** comp 
    351351    int start_x, start_y, offset, i; 
    352352    char *curr = NULL; 
    353353    WDialog *compl_dlg; 
    354     WListbox *compl_list; 
     354    WFilteringListbox *compl_list; 
     355    WListbox *lw; 
    355356    int compl_dlg_h;            /* completion dialog height */ 
    356357    int compl_dlg_w;            /* completion dialog width */ 
    357358 
    editcmd_dialog_completion_show (const WEdit * edit, int max_len, GString ** comp 
    384385                    dialog_colors, NULL, NULL, "[Completion]", NULL); 
    385386 
    386387    /* 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_KEEP_DIALOG_SIZE); 
     390 
     391    /* Save WListbox pointer for convenience. */ 
     392    lw = LISTBOX (compl_list); 
    388393 
    389394    /* add the dialog */ 
    390395    group_add_widget (GROUP (compl_dlg), compl_list); 
    391396 
    392397    /* fill the listbox with the completions */ 
    393398    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); 
     400 
     401    /* Option to start in the MultiSearch state. */ 
     402    filt_listbox_conditionally_enable_multi_search_init (compl_list); 
    396403 
    397404    /* pop up the dialog and apply the chosen completion */ 
    398405    if (dlg_run (compl_dlg) == B_ENTER) 
    399406    { 
    400         listbox_get_current (compl_list, &curr, NULL); 
     407        listbox_get_current (lw, &curr, NULL); 
    401408        curr = g_strdup (curr); 
    402409    } 
    403410 
  • 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) 
    302302    Listbox *listbox; 
    303303    GList *w; 
    304304    WEdit *selected; 
     305    WListbox *lw; 
    305306    int i = 0; 
    306307 
    307308    lines = MIN ((size_t) (LINES * 2 / 3), dlg_num); 
    edit_window_list (const WDialog * h) 
    309310 
    310311    listbox = create_listbox_window (lines, cols, _("Open files"), "[Open files]"); 
    311312 
     313    /* Convenience variable. */ 
     314    lw = LISTBOX (listbox->list); 
     315 
    312316    for (w = g->widgets; w != NULL; w = g_list_next (w)) 
    313317        if (edit_widget_is_editor (CONST_WIDGET (w->data))) 
    314318        { 
    edit_window_list (const WDialog * h) 
    322326                    g_strdup_printf ("%c%s", e->modified ? '*' : ' ', 
    323327                                     vfs_path_as_str (e->filename_vpath)); 
    324328 
    325             listbox_add_item (listbox->list, LISTBOX_APPEND_AT_END, get_hotkey (i++), 
    326                               str_term_trim (fname, WIDGET (listbox->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); 
    327331            g_free (fname); 
    328332        } 
    329333 
  • 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) 
    155155    width = str_term_width1 (fhd->file_name); 
    156156    hd->max_width = MAX (width, hd->max_width); 
    157157 
    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); 
    159160    /* fhd->file_pos is not copied, NULLize it to prevent double free */ 
    160161    fhd->file_pos = NULL; 
    161162} 
  • src/help.c

    diff --git a/src/help.c b/src/help.c
    index f5922f086..2c437ea57 100644
    a b move_backward2 (const char *c, int lines) 
    229229    int line; 
    230230 
    231231    currentpoint = c; 
    232     for (line = 0, p = currentpoint; (*p != '\0') && ((int) (p - fdata) >= 0); str_cprev_char (&p)) 
     232    for (line = 0, p = currentpoint; p && (*p != '\0') && ((int) (p - fdata) >= 0); 
     233         str_cprev_char (&p, fdata)) 
    233234    { 
    234235        if (*p == CHAR_NODE_END) 
    235236        { 
  • 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..9ce48325b 100644
    a b select_charset (int center_y, int center_x, int current_charset, gboolean seldis 
    107107        ? ((current_charset < 0) ? codepages->len : (size_t) current_charset) 
    108108        : ((size_t) current_charset + 1); 
    109109 
    110     listbox_select_entry (listbox->list, i); 
     110    listbox_select_entry (LISTBOX (listbox->list), i); 
    111111 
    112112    listbox_result = run_listbox (listbox); 
    113113 
  • src/usermenu.c

    diff --git a/src/usermenu.c b/src/usermenu.c
    index 698244220..9fbffb0fc 100644
    a b check_patterns (char *p) 
    133133   point after argument. */ 
    134134 
    135135static char * 
    136 extract_arg (char *p, char *arg, int size) 
     136extract_arg (char *p, char *arg, const char *begin, int size) 
    137137{ 
    138138    while (*p != '\0' && whiteness (*p)) 
    139139        p++; 
    extract_arg (char *p, char *arg, int size) 
    152152        p = np; 
    153153    } 
    154154    *arg = '\0'; 
    155     if (*p == '\0' || *p == '\n') 
    156         str_prev_char (&p); 
     155    if (p && (*p == '\0' || *p == '\n')) 
     156        str_prev_char (&p, begin); 
     157    if (!p) 
     158        p = (char *) begin; 
    157159    return p; 
    158160} 
    159161 
    test_type (WPanel * panel, char *arg) 
    215217   p. Returns the point after condition. */ 
    216218 
    217219static char * 
    218 test_condition (const WEdit * edit_widget, char *p, gboolean * condition) 
     220test_condition (const WEdit * edit_widget, char *p, const char *begin, gboolean * condition) 
    219221{ 
    220222    char arg[256]; 
    221223    const mc_search_type_t search_type = easy_patterns ? MC_SEARCH_T_GLOB : MC_SEARCH_T_REGEX; 
    222224 
    223225    /* Handle one condition */ 
    224     for (; *p != '\n' && *p != '&' && *p != '|'; p++) 
     226    for (; p && *p != '\n' && *p != '&' && *p != '|'; p++) 
    225227    { 
    226228        WPanel *panel = NULL; 
    227229 
    test_condition (const WEdit * edit_widget, char *p, gboolean * condition) 
    238240        switch (*p++) 
    239241        { 
    240242        case '!': 
    241             p = test_condition (edit_widget, p, condition); 
     243            p = test_condition (edit_widget, p, begin, condition); 
    242244            *condition = !*condition; 
    243             str_prev_char (&p); 
     245            str_prev_char (&p, begin); 
    244246            break; 
    245247        case 'f':              /* file name pattern */ 
    246             p = extract_arg (p, arg, sizeof (arg)); 
     248            p = extract_arg (p, arg, begin, sizeof (arg)); 
    247249#ifdef USE_INTERNAL_EDIT 
    248250            if (edit_widget != NULL) 
    249251            { 
    test_condition (const WEdit * edit_widget, char *p, gboolean * condition) 
    267269                syntax_type = edit_get_syntax_type (edit_widget); 
    268270                if (syntax_type != NULL) 
    269271                { 
    270                     p = extract_arg (p, arg, sizeof (arg)); 
     272                    p = extract_arg (p, arg, begin, sizeof (arg)); 
    271273                    *condition = mc_search (arg, DEFAULT_CHARSET, syntax_type, MC_SEARCH_T_NORMAL); 
    272274                } 
    273275            } 
    274276#endif 
    275277            break; 
    276278        case 'd': 
    277             p = extract_arg (p, arg, sizeof (arg)); 
     279            p = extract_arg (p, arg, begin, sizeof (arg)); 
    278280            *condition = panel != NULL 
    279281                && mc_search (arg, DEFAULT_CHARSET, vfs_path_as_str (panel->cwd_vpath), 
    280282                              search_type); 
    281283            break; 
    282284        case 't': 
    283             p = extract_arg (p, arg, sizeof (arg)); 
     285            p = extract_arg (p, arg, begin, sizeof (arg)); 
    284286            *condition = panel != NULL && test_type (panel, arg); 
    285287            break; 
    286288        case 'x':              /* executable */ 
    287289            { 
    288290                struct stat status; 
    289291 
    290                 p = extract_arg (p, arg, sizeof (arg)); 
     292                p = extract_arg (p, arg, begin, sizeof (arg)); 
    291293                *condition = stat (arg, &status) == 0 && is_exe (status.st_mode); 
    292294                break; 
    293295            } 
    debug_out (char *start, char *end, gboolean condition) 
    356358   the point just before the end of line. */ 
    357359 
    358360static char * 
    359 test_line (const WEdit * edit_widget, char *p, gboolean * result) 
     361test_line (const WEdit * edit_widget, char *p, const char *begin, gboolean * result) 
    360362{ 
    361363    char operator; 
    362364 
    363365    /* Repeat till end of line */ 
    364     while (*p != '\0' && *p != '\n') 
     366    while (p && *p != '\0' && *p != '\n') 
    365367    { 
    366368        char *debug_start, *debug_end; 
    367369        gboolean condition = TRUE; 
    test_line (const WEdit * edit_widget, char *p, gboolean * result) 
    384386            break; 
    385387 
    386388        debug_start = p; 
    387         p = test_condition (edit_widget, p, &condition); 
     389        p = test_condition (edit_widget, p, begin, &condition); 
    388390        debug_end = p; 
    389391        /* Add one debug statement */ 
    390392        debug_out (debug_start, debug_end, condition); 
    test_line (const WEdit * edit_widget, char *p, gboolean * result) 
    413415    /* Report debug message */ 
    414416    debug_out (NULL, NULL, TRUE); 
    415417 
    416     if (*p == '\0' || *p == '\n') 
    417         str_prev_char (&p); 
     418    if (p && (*p == '\0' || *p == '\n')) 
     419        str_prev_char (&p, begin); 
     420    if (!p) 
     421        p = (char *) begin; 
    418422    return p; 
    419423} 
    420424 
    user_menu_cmd (const WEdit * edit_widget, const char *menu_file, int selected_en 
    10371041                if (*(p + 1) == '=') 
    10381042                { 
    10391043                    /* Combined adding and default */ 
    1040                     p = test_line (edit_widget, p + 1, &accept_entry); 
     1044                    p = test_line (edit_widget, p + 1, data, &accept_entry); 
    10411045                    if (selected == 0 && accept_entry) 
    10421046                        selected = menu_lines; 
    10431047                } 
    10441048                else 
    10451049                { 
    10461050                    /* A condition for adding the entry */ 
    1047                     p = test_line (edit_widget, p, &accept_entry); 
     1051                    p = test_line (edit_widget, p, data, &accept_entry); 
    10481052                } 
    10491053                break; 
    10501054 
    user_menu_cmd (const WEdit * edit_widget, const char *menu_file, int selected_en 
    10521056                if (*(p + 1) == '+') 
    10531057                { 
    10541058                    /* Combined adding and default */ 
    1055                     p = test_line (edit_widget, p + 1, &accept_entry); 
     1059                    p = test_line (edit_widget, p + 1, data, &accept_entry); 
    10561060                    if (selected == 0 && accept_entry) 
    10571061                        selected = menu_lines; 
    10581062                } 
    user_menu_cmd (const WEdit * edit_widget, const char *menu_file, int selected_en 
    10601064                { 
    10611065                    /* A condition for making the entry default */ 
    10621066                    i = 1; 
    1063                     p = test_line (edit_widget, p, &i); 
     1067                    p = test_line (edit_widget, p, data, &i); 
    10641068                    if (selected == 0 && i != 0) 
    10651069                        selected = menu_lines; 
    10661070                } 
    user_menu_cmd (const WEdit * edit_widget, const char *menu_file, int selected_en 
    11221126                                     extract_line (p, p + MAX_ENTRY_LEN), p, FALSE); 
    11231127            } 
    11241128            /* Select the default entry */ 
    1125             listbox_select_entry (listbox->list, selected); 
     1129            listbox_select_entry (LISTBOX (listbox->list), selected); 
    11261130 
    11271131            selected = run_listbox (listbox); 
    11281132        }