Ticket #4196: InstructionStation_v3.5.patch

File InstructionStation_v3.5.patch, 73.0 KB (added by psprint, 4 years ago)

Cleanup + #ifdefs for GLib 2.30

  • lib/global.h

    From 4394ab2b59eeafe30dca36c561f94a177d4e8462 Mon Sep 17 00:00:00 2001
    From: Sebastian Gniazdowski <sgniazdowski@gmail.com>
    Date: Wed, 10 Feb 2021 13:41:35 -0600
    Subject: =?UTF-8?q?Instruction=20Station=20=E2=80=93=20a=20CLI=20window=20?=
     =?UTF-8?q?for=20MCEdit?=
    MIME-Version: 1.0
    Content-Type: text/plain; charset=UTF-8
    Content-Transfer-Encoding: 8bit
    
    ---
     lib/global.h               |   3 +
     lib/keybind.c              |   1 +
     lib/keybind.h              |   1 +
     lib/tty/key.c              |  10 +-
     lib/widget/wtools.c        |  40 +-
     lib/widget/wtools.h        |   4 +
     misc/mc.default.keymap     |   1 +
     misc/mc.emacs.keymap       |   1 +
     src/editor/Makefile.am     |   1 +
     src/editor/edit-impl.h     |  24 +-
     src/editor/edit.c          |  27 +-
     src/editor/editcmd.c       |  41 +-
     src/editor/editdraw.c      |   1 -
     src/editor/editmenu.c      |   3 +
     src/editor/editwidget.c    | 592 ++++++++++++------------
     src/editor/editwidget.h    |   3 +
     src/editor/instr_station.c | 897 +++++++++++++++++++++++++++++++++++++
     src/editor/instr_station.h |  92 ++++
     src/keybind-defaults.c     |   1 +
     19 files changed, 1432 insertions(+), 311 deletions(-)
     create mode 100644 src/editor/instr_station.c
     create mode 100644 src/editor/instr_station.h
    
    diff --git a/lib/global.h b/lib/global.h
    index f1a3e702c..1f76061c6 100644
    a b  
    103103#include "lib/logging.h" 
    104104#endif 
    105105 
     106#define has_flag(x,y) (((x) & (y)) != 0) 
     107#define set_flag_in(x,y) ((x) |= (y)) 
     108 
    106109/* Just for keeping Your's brains from invention a proper size of the buffer :-) */ 
    107110#define BUF_10K 10240L 
    108111#define BUF_8K  8192L 
  • lib/keybind.c

    diff --git a/lib/keybind.c b/lib/keybind.c
    index 65ac5f15c..2f9d3cf29 100644
    a b static name_keymap_t command_names_start[] = { 
    300300    ADD_KEYMAP_NAME (EditMail), 
    301301    ADD_KEYMAP_NAME (ParagraphFormat), 
    302302    ADD_KEYMAP_NAME (MatchBracket), 
     303    ADD_KEYMAP_NAME (InstructionStation), 
    303304    ADD_KEYMAP_NAME (ExternalCommand), 
    304305    ADD_KEYMAP_NAME (MacroStartRecord), 
    305306    ADD_KEYMAP_NAME (MacroStopRecord), 
  • lib/keybind.h

    diff --git a/lib/keybind.h b/lib/keybind.h
    index 4e954ade2..18c7d95e4 100644
    a b enum 
    324324    CK_SyntaxOnOff, 
    325325    CK_SyntaxChoose, 
    326326    CK_InsertLiteral, 
     327    CK_InstructionStation, 
    327328    CK_ExternalCommand, 
    328329    CK_Date, 
    329330    CK_EditMail, 
  • lib/tty/key.c

    diff --git a/lib/tty/key.c b/lib/tty/key.c
    index 58a2f018b..b55fb8f07 100644
    a b tty_get_event (struct Gpm_Event *event, gboolean redo_event, gboolean block) 
    20312031            time_out.tv_sec = 0; 
    20322032            time_out.tv_usec = 0; 
    20332033        } 
    2034  
     2034        else if (time_addr == NULL) 
     2035        { 
     2036            /* Standard timeout to call GLib main loop */ 
     2037            time_addr = &time_out; 
     2038            time_addr->tv_sec = 0; 
     2039            time_addr->tv_usec = 200000; 
     2040        } 
    20352041        tty_enable_interrupt_key (); 
    20362042        flag = select (nfd, &select_set, NULL, NULL, time_addr); 
    20372043        tty_disable_interrupt_key (); 
    tty_get_event (struct Gpm_Event *event, gboolean redo_event, gboolean block) 
    20432049         */ 
    20442050        if (flag == 0) 
    20452051        { 
     2052            /* Provide CPU time to GLib main loop's default context */ 
     2053            g_main_context_iteration (NULL, FALSE); 
    20462054            if (redo_event) 
    20472055                return EV_MOUSE; 
    20482056            if (!block || tty_got_winch ()) 
  • lib/widget/wtools.c

    diff --git a/lib/widget/wtools.c b/lib/widget/wtools.c
    index 94ace3167..aa2678b83 100644
    a b bg_message (int dummy, int *flags, char *title, const char *text) 
    193193static char * 
    194194fg_input_dialog_help (const char *header, const char *text, const char *help, 
    195195                      const char *history_name, const char *def_text, gboolean strip_password, 
    196                       input_complete_t completion_flags) 
     196                      input_complete_t completion_flags, quick_widget_t * add_w) 
    197197{ 
    198198    char *p_text; 
    199199    char histname[64] = "inp|"; 
    fg_input_dialog_help (const char *header, const char *text, const char *help, 
    218218    } 
    219219 
    220220    { 
    221         quick_widget_t quick_widgets[] = { 
     221        int i = 1, norm_i = 0, add_i = 0; 
     222        quick_widget_t quick_widgets[15] = { 
    222223            /* *INDENT-OFF* */ 
    223224            QUICK_LABELED_INPUT (p_text, input_label_above, def_text, histname, &my_str, 
    224                                  NULL, is_passwd, strip_password, completion_flags), 
    225             QUICK_BUTTONS_OK_CANCEL, 
    226             QUICK_END 
     225                                 NULL, is_passwd, strip_password, completion_flags) 
    227226            /* *INDENT-ON* */ 
    228227        }; 
    229  
     228        quick_widget_t press_end[] = { QUICK_BUTTONS_OK_CANCEL, QUICK_END }; 
    230229        quick_dialog_t qdlg = { 
    231230            -1, -1, COLS / 2, header, 
    232231            help, quick_widgets, NULL, NULL 
    233232        }; 
    234233 
     234        /* Handling of additional widgets optionally passed via last argument `add_w` */ 
     235 
     236        /* Copy/append any extra widgets (max 10) */ 
     237        while (add_w != NULL && add_w[add_i].widget_type != quick_end && i < 15) 
     238            quick_widgets[i++] = add_w[add_i++]; 
     239 
     240        /* Copy normal buttons */ 
     241        while (norm_i < sizeof (press_end) / sizeof (quick_widget_t) && i < 15) 
     242            quick_widgets[i++] = press_end[norm_i++]; 
     243 
    235244        ret = quick_dialog (&qdlg); 
    236245    } 
    237246 
    input_dialog_help (const char *header, const char *text, const char *help, 
    543552        { 
    544553            void *p; 
    545554            char *(*f) (const char *, const char *, const char *, const char *, const char *, 
    546                         gboolean, input_complete_t); 
     555                        gboolean, input_complete_t, quick_widget_t * add_w); 
    547556        } func; 
    548557        func.f = fg_input_dialog_help; 
    549         return wtools_parent_call_string (func.p, 7, 
     558        return wtools_parent_call_string (func.p, 8, 
    550559                                          strlen (header), header, strlen (text), 
    551560                                          text, strlen (help), help, 
    552561                                          strlen (history_name), history_name, 
    553562                                          strlen (def_text), def_text, 
    554563                                          sizeof (gboolean), strip_password, 
    555                                           sizeof (input_complete_t), completion_flags); 
     564                                          sizeof (input_complete_t), completion_flags, 
     565                                          sizeof (quick_widget_t *), NULL); 
    556566    } 
    557567    else 
    558568#endif /* ENABLE_BACKGROUND */ 
    559569        return fg_input_dialog_help (header, text, help, history_name, def_text, strip_password, 
    560                                      completion_flags); 
     570                                     completion_flags, NULL); 
    561571} 
    562572 
    563573/* --------------------------------------------------------------------------------------------- */ 
    input_dialog (const char *header, const char *text, const char *history_name, co 
    573583 
    574584/* --------------------------------------------------------------------------------------------- */ 
    575585 
     586char * 
     587input_dialog_ext (const char *header, const char *text, const char *history_name, 
     588                  const char *def_text, input_complete_t completion_flags, quick_widget_t * add_w) 
     589{ 
     590    return fg_input_dialog_help (header, text, "[Input Line Keys]", history_name, def_text, FALSE, 
     591                                 completion_flags, add_w); 
     592} 
     593 
     594/* --------------------------------------------------------------------------------------------- */ 
     595 
    576596char * 
    577597input_expand_dialog (const char *header, const char *text, 
    578598                     const char *history_name, const char *def_text, 
  • lib/widget/wtools.h

    diff --git a/lib/widget/wtools.h b/lib/widget/wtools.h
    index b1f7a9b47..9511aed46 100644
    a b void display_postponed_messages (void); 
    7676char *input_dialog (const char *header, const char *text, 
    7777                    const char *history_name, const char *def_text, 
    7878                    input_complete_t completion_flags); 
     79/* The input dialogs */ 
     80char *input_dialog_ext (const char *header, const char *text, 
     81                        const char *history_name, const char *def_text, 
     82                        input_complete_t completion_flags, quick_widget_t * add_w); 
    7983char *input_dialog_help (const char *header, const char *text, const char *help, 
    8084                         const char *history_name, const char *def_text, gboolean strip_password, 
    8185                         input_complete_t completion_flags); 
  • misc/mc.default.keymap

    diff --git a/misc/mc.default.keymap b/misc/mc.default.keymap
    index 73b4c0eab..a7269d07f 100644
    a b Sort = alt-t 
    352352Mail = alt-m 
    353353ParagraphFormat = alt-p 
    354354MatchBracket = alt-b 
     355InstructionStation = alt-i 
    355356ExternalCommand = alt-u 
    356357UserMenu = f11 
    357358Menu = f9 
  • misc/mc.emacs.keymap

    diff --git a/misc/mc.emacs.keymap b/misc/mc.emacs.keymap
    index 955929444..98412b75f 100644
    a b Sort = alt-t 
    351351# Mail = 
    352352ParagraphFormat = alt-p 
    353353# MatchBracket = 
     354InstructionStation = alt-i 
    354355ExternalCommand = alt-u 
    355356UserMenu = f11 
    356357Menu = f9 
  • src/editor/Makefile.am

    diff --git a/src/editor/Makefile.am b/src/editor/Makefile.am
    index 235ed76af..0ac4b4af7 100644
    a b libedit_la_SOURCES = \ 
    1818        editmenu.c \ 
    1919        editoptions.c \ 
    2020        editwidget.c editwidget.h \ 
     21        instr_station.c instr_station.h \ 
    2122        etags.c etags.h \ 
    2223        format.c \ 
    2324        syntax.c 
  • src/editor/edit-impl.h

    diff --git a/src/editor/edit-impl.h b/src/editor/edit-impl.h
    index 881ef549b..3d35e0a7e 100644
    a b typedef enum 
    8888    LB_MAC 
    8989} LineBreaks; 
    9090 
     91typedef enum 
     92{ 
     93    EDIT_DO_INIT_BASE_CLASS = 1 << 0, 
     94 
     95} edit_init_flags_t; 
     96 
     97typedef enum instr_stn_flags 
     98{ 
     99    INSTR_STN_NO_FLAGS = 0, 
     100    INSTR_STN_NO_STDIN = 1 << 17, 
     101    INSTR_STN_NO_PROMPT = 1 << 18 
     102} instr_stn_flags_t; 
     103 
    91104typedef enum 
    92105{ 
    93106    EDIT_QUICK_SAVE = 0, 
    extern char *edit_window_state_char; 
    146159extern char *edit_window_close_char; 
    147160 
    148161/*** declarations of public functions ************************************************************/ 
     162cb_ret_t edit_dialog_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data); 
     163cb_ret_t edit_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data); 
     164cb_ret_t edit_dialog_command_execute (WDialog * h, long command, void *data); 
     165 
     166void edit_mouse_callback (Widget * w, mouse_msg_t msg, mouse_event_t * event); 
    149167 
    150168gboolean edit_add_window (WDialog * h, int y, int x, int lines, int cols, 
    151169                          const vfs_path_t * f, long fline); 
    void edit_move_to_prev_col (WEdit * edit, off_t p); 
    169187long edit_get_col (const WEdit * edit); 
    170188void edit_update_curs_row (WEdit * edit); 
    171189void edit_update_curs_col (WEdit * edit); 
     190gboolean edit_newline_end_check_only (const WEdit * edit); 
    172191void edit_find_bracket (WEdit * edit); 
    173192gboolean edit_reload_line (WEdit * edit, const vfs_path_t * filename_vpath, long line); 
    174193void edit_set_codeset (WEdit * edit); 
    void edit_delete_line (WEdit * edit); 
    180199 
    181200int edit_delete (WEdit * edit, gboolean byte_delete); 
    182201int edit_backspace (WEdit * edit, gboolean byte_delete); 
     202void edit_insert_string (WEdit * edit, char *str_text, gsize len); 
    183203void edit_insert (WEdit * edit, int c); 
    184204void edit_insert_over (WEdit * edit); 
    185205void edit_cursor_move (WEdit * edit, off_t increment); 
    char *edit_get_write_filter (const vfs_path_t * write_name_vpath, 
    193213gboolean edit_save_confirm_cmd (WEdit * edit); 
    194214gboolean edit_save_as_cmd (WEdit * edit); 
    195215WEdit *edit_init (WEdit * edit, int y, int x, int lines, int cols, 
    196                   const vfs_path_t * filename_vpath, long line); 
     216                  const vfs_path_t * filename_vpath, long line, edit_init_flags_t flags); 
    197217gboolean edit_clean (WEdit * edit); 
    198218gboolean edit_ok_to_exit (WEdit * edit); 
    199219file_suitable_rank_t edit_check_file_suitable (const vfs_path_t * fs_path); 
    void edit_center_display (WEdit * e, long diff); 
    249269void edit_move_display (WEdit * e, long line); 
    250270void edit_word_wrap (WEdit * edit); 
    251271int edit_sort_cmd (WEdit * edit); 
     272gboolean edit_add_instr_stn_window (WDialog * h, instr_stn_flags_t flags, void *data); 
     273 
    252274int edit_ext_cmd (WEdit * edit); 
    253275 
    254276int edit_store_macro_cmd (WEdit * edit); 
  • src/editor/edit.c

    diff --git a/src/editor/edit.c b/src/editor/edit.c
    index 8477939aa..ea193a8de 100644
    a b edit_modification (WEdit * edit) 
    652652{ 
    653653    edit->caches_valid = FALSE; 
    654654 
    655     /* raise lock when file modified */ 
    656     if (!edit->modified && !edit->delete_file) 
     655    /* raise lock when file is about to be modified */ 
     656    if (edit->filename_vpath && !edit->modified && !edit->delete_file) 
    657657        edit->locked = lock_file (edit->filename_vpath); 
    658658    edit->modified = 1; 
    659659} 
    edit_insert_file (WEdit * edit, const vfs_path_t * filename_vpath) 
    21062106 
    21072107WEdit * 
    21082108edit_init (WEdit * edit, int y, int x, int lines, int cols, const vfs_path_t * filename_vpath, 
    2109            long line) 
     2109           long line, edit_init_flags_t flags) 
    21102110{ 
    21112111    gboolean to_free = FALSE; 
    21122112 
    edit_init (WEdit * edit, int y, int x, int lines, int cols, const vfs_path_t * f 
    21282128        edit->fullscreen = fullscreen; 
    21292129        edit->loc_prev = loc_prev; 
    21302130    } 
    2131     else 
     2131    if (has_flag (flags, EDIT_DO_INIT_BASE_CLASS) || edit == NULL) 
    21322132    { 
    21332133        Widget *w; 
    2134         edit = g_malloc0 (sizeof (WEdit)); 
    2135         to_free = TRUE; 
     2134        if (edit == NULL) 
     2135        { 
     2136            edit = g_malloc0 (sizeof (WEdit)); 
     2137            to_free = TRUE; 
     2138        } 
    21362139 
    21372140        w = WIDGET (edit); 
    21382141        widget_init (w, y, x, lines, cols, NULL, NULL); 
    edit_reload_line (WEdit * edit, const vfs_path_t * filename_vpath, long line) 
    22712274    e->fullscreen = edit->fullscreen; 
    22722275    e->loc_prev = edit->loc_prev; 
    22732276 
    2274     if (edit_init (e, w->y, w->x, w->lines, w->cols, filename_vpath, line) == NULL) 
     2277    if (edit_init (e, w->y, w->x, w->lines, w->cols, filename_vpath, line, 0) == NULL) 
    22752278    { 
    22762279        g_free (e); 
    22772280        return FALSE; 
    edit_push_redo_action (WEdit * edit, long c) 
    25282531        edit->redo_stack_bottom = edit->redo_stack_pointer = 0; 
    25292532} 
    25302533 
     2534/* --------------------------------------------------------------------------------------------- */ 
     2535 
     2536void 
     2537edit_insert_string (WEdit * edit, char *str_text, gsize len) 
     2538{ 
     2539    size_t i; 
     2540    for (i = 0; i < len; i++) 
     2541        edit_insert (edit, str_text[i]); 
     2542} 
     2543 
    25312544/* --------------------------------------------------------------------------------------------- */ 
    25322545/** 
    25332546   Basic low level single character buffer alterations and movements at the cursor. 
  • src/editor/editcmd.c

    diff --git a/src/editor/editcmd.c b/src/editor/editcmd.c
    index be58f3db7..17d2d6b2f 100644
    a b edit_complete_word_insert_recoded_completion (WEdit * edit, char *completion, gs 
    15731573/*** public functions ****************************************************************************/ 
    15741574/* --------------------------------------------------------------------------------------------- */ 
    15751575 
     1576gboolean 
     1577edit_newline_end_check_only (const WEdit * e) 
     1578{ 
     1579    const edit_buffer_t *buf = &e->buffer; 
     1580    return (buf->size > 0 && edit_buffer_get_byte (buf, buf->size - 1) == '\n'); 
     1581} 
     1582 
     1583/* --------------------------------------------------------------------------------------------- */ 
     1584 
    15761585void 
    15771586edit_refresh_cmd (void) 
    15781587{ 
    edit_close_cmd (WEdit * edit) 
    22982307        group_remove_widget (w); 
    22992308        widget_destroy (w); 
    23002309 
    2301         if (edit_widget_is_editor (CONST_WIDGET (g->current->data))) 
     2310        if (edit_widget_is_editor (CONST_WIDGET (g->current->data)) 
     2311            || edit_widget_is_cli (CONST_WIDGET (g->current->data))) 
    23022312            edit = (WEdit *) (g->current->data); 
    23032313        else 
    23042314        { 
     2315            /* Look for a file window or a CLI window */ 
    23052316            edit = find_editor (DIALOG (g)); 
     2317            if (!edit) 
     2318                edit = find_cli (DIALOG (g)); 
    23062319            if (edit != NULL) 
    23072320                widget_select (w); 
    23082321        } 
    edit_ext_cmd (WEdit * edit) 
    32723285{ 
    32733286    char *exp, *tmp, *tmp_edit_temp_file; 
    32743287    int e; 
     3288    gboolean run_in_cli = FALSE; 
     3289 
     3290    quick_widget_t chbox[3] = { QUICK_SEPARATOR (TRUE), 
     3291        QUICK_CHECKBOX ("&Open in CLI window", &run_in_cli, NULL), 
     3292        QUICK_END 
     3293    }; 
    32753294 
    32763295    exp = 
    3277         input_dialog (_("Paste output of external command"), 
    3278                       _("Enter shell command(s):"), MC_HISTORY_EDIT_PASTE_EXTCMD, INPUT_LAST_TEXT, 
    3279                       INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_VARIABLES | INPUT_COMPLETE_USERNAMES 
    3280                       | INPUT_COMPLETE_HOSTNAMES | INPUT_COMPLETE_CD | INPUT_COMPLETE_COMMANDS | 
    3281                       INPUT_COMPLETE_SHELL_ESC); 
     3296        input_dialog_ext (_("Paste output of external command"), 
     3297                          _("Enter shell command(s):"), MC_HISTORY_EDIT_PASTE_EXTCMD, 
     3298                          INPUT_LAST_TEXT, 
     3299                          INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_VARIABLES | 
     3300                          INPUT_COMPLETE_USERNAMES | INPUT_COMPLETE_HOSTNAMES | INPUT_COMPLETE_CD | 
     3301                          INPUT_COMPLETE_COMMANDS | INPUT_COMPLETE_SHELL_ESC, chbox); 
    32823302 
    32833303    if (!exp) 
    32843304        return 1; 
    32853305 
     3306    /* Should start a CLI window with the command ? */ 
     3307    if (run_in_cli) 
     3308    { 
     3309        edit_add_instr_stn_window (DIALOG (WIDGET (edit)->owner), 
     3310                                   INSTR_STN_NO_STDIN | INSTR_STN_NO_PROMPT, exp); 
     3311        g_free (exp); 
     3312        return 0; 
     3313    } 
     3314 
    32863315    tmp_edit_temp_file = mc_config_get_full_path (EDIT_HOME_TEMP_FILE); 
    32873316    tmp = g_strconcat (exp, " > ", tmp_edit_temp_file, (char *) NULL); 
    32883317    g_free (tmp_edit_temp_file); 
  • src/editor/editdraw.c

    diff --git a/src/editor/editdraw.c b/src/editor/editdraw.c
    index 61bbdcbee..80495dcd0 100644
    a b edit_draw_this_line (WEdit * edit, off_t b, long row, long start_col, long end_c 
    820820    } 
    821821 
    822822    p->ch = 0; 
    823  
    824823    print_to_widget (edit, row, start_col, start_col_real, end_col, line, line_stat, book_mark); 
    825824} 
    826825 
  • src/editor/editmenu.c

    diff --git a/src/editor/editmenu.c b/src/editor/editmenu.c
    index c20e9fb7e..0c8e60801 100644
    a b create_window_menu (void) 
    238238    entries = g_list_prepend (entries, menu_entry_create (_("&Next"), CK_WindowNext)); 
    239239    entries = g_list_prepend (entries, menu_entry_create (_("&Previous"), CK_WindowPrev)); 
    240240    entries = g_list_prepend (entries, menu_entry_create (_("&List..."), CK_WindowList)); 
     241    entries = 
     242        g_list_prepend (entries, 
     243                        menu_entry_create (_("Instr&uction Station"), CK_InstructionStation)); 
    241244 
    242245    return g_list_reverse (entries); 
    243246} 
  • src/editor/editwidget.c

    diff --git a/src/editor/editwidget.c b/src/editor/editwidget.c
    index e32541cd9..c26a7d009 100644
    a b  
    6868#ifdef HAVE_ASPELL 
    6969#include "spell.h" 
    7070#endif 
     71#include "src/editor/instr_station.h" 
    7172 
    7273/*** global variables ****************************************************************************/ 
    7374 
    static unsigned int edit_dlg_init_refcounter = 0; 
    8687 
    8788/*** file scope functions ************************************************************************/ 
    8889 
    89 static cb_ret_t edit_dialog_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, 
    90                                       void *data); 
    91  
    9290/* --------------------------------------------------------------------------------------------- */ 
    9391 
    9492static char * 
    edit_get_title (const WDialog * h, size_t len) 
    634632    return g_strconcat (_("Edit: "), modified, file_label, (char *) NULL); 
    635633} 
    636634 
    637 /* --------------------------------------------------------------------------------------------- */ 
    638  
    639 static cb_ret_t 
    640 edit_dialog_command_execute (WDialog * h, long command) 
    641 { 
    642     WGroup *g = GROUP (h); 
    643     Widget *wh = WIDGET (h); 
    644     cb_ret_t ret = MSG_HANDLED; 
    645  
    646     switch (command) 
    647     { 
    648     case CK_EditNew: 
    649         edit_add_window (h, wh->y + 1, wh->x, wh->lines - 2, wh->cols, NULL, 0); 
    650         break; 
    651     case CK_EditFile: 
    652         edit_load_cmd (h, NULL); 
    653         break; 
    654     case CK_OtherFile: 
    655         { 
    656             WEdit *e = (WEdit *) g->current->data; 
    657             gboolean retflag = FALSE; 
    658  
    659             if (e != NULL && edit_widget_is_editor (CONST_WIDGET (e))) 
    660             { 
    661                 retflag = edit_compute_other_file_vfs_path (e); 
    662                 if (retflag) 
    663                     retflag = edit_switch_to_file (h, e->otherfile_vpath); 
    664             } 
    665             if (!retflag) 
    666                 ret = MSG_NOT_HANDLED; 
    667         } 
    668         break; 
    669     case CK_History: 
    670         edit_load_file_from_history (h); 
    671         break; 
    672     case CK_EditSyntaxFile: 
    673         edit_load_syntax_file (h); 
    674         break; 
    675     case CK_EditUserMenu: 
    676         edit_load_menu_file (h); 
    677         break; 
    678     case CK_Close: 
    679         /* if there are no opened files anymore, close MC editor */ 
    680         if (edit_widget_is_editor (CONST_WIDGET (g->current->data)) && 
    681             edit_close_cmd ((WEdit *) g->current->data) && find_editor (h) == NULL) 
    682             dlg_stop (h); 
    683         break; 
    684     case CK_Help: 
    685         edit_help (); 
    686         /* edit->force |= REDRAW_COMPLETELY; */ 
    687         break; 
    688     case CK_Menu: 
    689         edit_menu_cmd (h); 
    690         break; 
    691     case CK_Quit: 
    692     case CK_Cancel: 
    693         /* don't close editor due to SIGINT, but stop move/resize window */ 
    694         { 
    695             Widget *w = WIDGET (g->current->data); 
    696  
    697             if (edit_widget_is_editor (w) && ((WEdit *) w)->drag_state != MCEDIT_DRAG_NONE) 
    698                 edit_restore_size ((WEdit *) w); 
    699             else if (command == CK_Quit) 
    700                 dlg_stop (h); 
    701         } 
    702         break; 
    703     case CK_About: 
    704         edit_about (); 
    705         break; 
    706     case CK_SyntaxOnOff: 
    707         edit_syntax_onoff_cmd (h); 
    708         break; 
    709     case CK_ShowTabTws: 
    710         edit_show_tabs_tws_cmd (h); 
    711         break; 
    712     case CK_ShowMargin: 
    713         edit_show_margin_cmd (h); 
    714         break; 
    715     case CK_ShowNumbers: 
    716         edit_show_numbers_cmd (h); 
    717         break; 
    718     case CK_Refresh: 
    719         edit_refresh_cmd (); 
    720         break; 
    721     case CK_Shell: 
    722         toggle_subshell (); 
    723         break; 
    724     case CK_LearnKeys: 
    725         learn_keys (); 
    726         break; 
    727     case CK_WindowMove: 
    728     case CK_WindowResize: 
    729         if (edit_widget_is_editor (CONST_WIDGET (g->current->data))) 
    730             edit_handle_move_resize ((WEdit *) g->current->data, command); 
    731         break; 
    732     case CK_WindowList: 
    733         edit_window_list (h); 
    734         break; 
    735     case CK_WindowCascade: 
    736         edit_window_cascade (h); 
    737         break; 
    738     case CK_WindowTile: 
    739         edit_window_tile (h); 
    740         break; 
    741     case CK_WindowNext: 
    742         group_select_next_widget (g); 
    743         break; 
    744     case CK_WindowPrev: 
    745         group_select_prev_widget (g); 
    746         break; 
    747     case CK_Options: 
    748         edit_options_dialog (h); 
    749         break; 
    750     case CK_OptionsSaveMode: 
    751         edit_save_mode_cmd (); 
    752         break; 
    753     case CK_SaveSetup: 
    754         save_setup_cmd (); 
    755         break; 
    756     default: 
    757         ret = MSG_NOT_HANDLED; 
    758         break; 
    759     } 
    760  
    761     return ret; 
    762 } 
    763  
    764635/* --------------------------------------------------------------------------------------------- */ 
    765636/* 
    766637 * Translate the keycode into either 'command' or 'char_for_insertion'. 
    edit_update_cursor (WEdit * edit, const mouse_event_t * event) 
    1018889    return done; 
    1019890} 
    1020891 
    1021 /* --------------------------------------------------------------------------------------------- */ 
    1022 /** Callback for the edit dialog */ 
    1023  
    1024 static cb_ret_t 
    1025 edit_dialog_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data) 
    1026 { 
    1027     WGroup *g = GROUP (w); 
    1028     WDialog *h = DIALOG (w); 
    1029  
    1030     switch (msg) 
    1031     { 
    1032     case MSG_INIT: 
    1033         edit_dlg_init (); 
    1034         return MSG_HANDLED; 
    1035  
    1036     case MSG_RESIZE: 
    1037         dlg_default_callback (w, NULL, MSG_RESIZE, 0, NULL); 
    1038         menubar_arrange (find_menubar (h)); 
    1039         return MSG_HANDLED; 
    1040  
    1041     case MSG_ACTION: 
    1042         { 
    1043             /* Handle shortcuts, menu, and buttonbar. */ 
    1044  
    1045             cb_ret_t result; 
    1046  
    1047             result = edit_dialog_command_execute (h, parm); 
    1048  
    1049             /* We forward any commands coming from the menu, and which haven't been 
    1050                handled by the dialog, to the focused WEdit window. */ 
    1051             if (result == MSG_NOT_HANDLED && sender == WIDGET (find_menubar (h))) 
    1052                 result = send_message (g->current->data, NULL, MSG_ACTION, parm, NULL); 
    1053  
    1054             return result; 
    1055         } 
    1056  
    1057     case MSG_KEY: 
    1058         { 
    1059             Widget *we = WIDGET (g->current->data); 
    1060             cb_ret_t ret = MSG_NOT_HANDLED; 
    1061  
    1062             if (edit_widget_is_editor (we)) 
    1063             { 
    1064                 gboolean ext_mode; 
    1065                 long command; 
    1066  
    1067                 /* keep and then extmod flag */ 
    1068                 ext_mode = we->ext_mode; 
    1069                 command = widget_lookup_key (we, parm); 
    1070                 we->ext_mode = ext_mode; 
    1071  
    1072                 if (command == CK_IgnoreKey) 
    1073                     we->ext_mode = FALSE; 
    1074                 else 
    1075                 { 
    1076                     ret = edit_dialog_command_execute (h, command); 
    1077                     /* if command was not handled, keep the extended mode 
    1078                        for the further key processing */ 
    1079                     if (ret == MSG_HANDLED) 
    1080                         we->ext_mode = FALSE; 
    1081                 } 
    1082             } 
    1083  
    1084             /* 
    1085              * Due to the "end of bracket" escape the editor sees input with is_idle() == false 
    1086              * (expects more characters) and hence doesn't yet refresh the screen, but then 
    1087              * no further characters arrive (there's only an "end of bracket" which is swallowed 
    1088              * by tty_get_event()), so you end up with a screen that's not refreshed after pasting. 
    1089              * So let's trigger an IDLE signal. 
    1090              */ 
    1091             if (!is_idle ()) 
    1092                 widget_idle (w, TRUE); 
    1093             return ret; 
    1094         } 
    1095  
    1096         /* hardcoded menu hotkeys (see edit_drop_hotkey_menu) */ 
    1097     case MSG_UNHANDLED_KEY: 
    1098         return edit_drop_hotkey_menu (h, parm) ? MSG_HANDLED : MSG_NOT_HANDLED; 
    1099  
    1100     case MSG_VALIDATE: 
    1101         edit_quit (h); 
    1102         return MSG_HANDLED; 
    1103  
    1104     case MSG_END: 
    1105         edit_dlg_deinit (); 
    1106         return MSG_HANDLED; 
    1107  
    1108     case MSG_IDLE: 
    1109         widget_idle (w, FALSE); 
    1110         return send_message (g->current->data, NULL, MSG_IDLE, 0, NULL); 
    1111  
    1112     default: 
    1113         return dlg_default_callback (w, sender, msg, parm, data); 
    1114     } 
    1115 } 
    1116  
    1117892/* --------------------------------------------------------------------------------------------- */ 
    1118893 
    1119894/** 
    edit_dialog_bg_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm 
    1208983 
    1209984/* --------------------------------------------------------------------------------------------- */ 
    1210985 
    1211 static cb_ret_t 
     986/** 
     987 * Handle move/resize mouse events. 
     988 */ 
     989static void 
     990edit_mouse_handle_move_resize (Widget * w, mouse_msg_t msg, mouse_event_t * event) 
     991{ 
     992    WEdit *edit = (WEdit *) (w); 
     993    Widget *h = WIDGET (w->owner); 
     994    int global_x, global_y; 
     995 
     996    if (msg == MSG_MOUSE_UP) 
     997    { 
     998        /* Exit move/resize mode. */ 
     999        edit_execute_cmd (edit, CK_Enter, -1); 
     1000        edit_update_screen (edit);      /* Paint the buttonbar over our possibly overlapping frame. */ 
     1001        return; 
     1002    } 
     1003 
     1004    if (msg != MSG_MOUSE_DRAG) 
     1005        /** 
     1006         * We ignore any other events. Specifically, MSG_MOUSE_DOWN. 
     1007         * 
     1008         * When the move/resize is initiated by the menu, we let the user 
     1009         * stop it by clicking with the mouse. Which is why we don't want 
     1010         * a mouse down to affect the window. 
     1011         */ 
     1012        return; 
     1013 
     1014    /* Convert point to global coordinates for easier calculations. */ 
     1015    global_x = event->x + w->x; 
     1016    global_y = event->y + w->y; 
     1017 
     1018    /* Clamp the point to the dialog's client area. */ 
     1019    global_y = CLAMP (global_y, h->y + 1, h->y + h->lines - 2); /* Status line, buttonbar */ 
     1020    global_x = CLAMP (global_x, h->x, h->x + h->cols - 1);      /* Currently a no-op, as the dialog has no left/right margins. */ 
     1021 
     1022    if (edit->drag_state == MCEDIT_DRAG_MOVE) 
     1023    { 
     1024        w->y = global_y; 
     1025        w->x = global_x - edit->drag_state_start; 
     1026    } 
     1027    else if (edit->drag_state == MCEDIT_DRAG_RESIZE) 
     1028    { 
     1029        w->lines = MAX (WINDOW_MIN_LINES, global_y - w->y + 1); 
     1030        w->cols = MAX (WINDOW_MIN_COLS, global_x - w->x + 1); 
     1031    } 
     1032 
     1033    edit->force |= REDRAW_COMPLETELY;   /* Not really needed as WEdit's MSG_DRAW already does this. */ 
     1034 
     1035    /* We draw the whole dialog because dragging/resizing exposes area beneath. */ 
     1036    widget_draw (WIDGET (w->owner)); 
     1037} 
     1038 
     1039/* --------------------------------------------------------------------------------------------- */ 
     1040/*** public functions ****************************************************************************/ 
     1041/* --------------------------------------------------------------------------------------------- */ 
     1042 
     1043cb_ret_t 
     1044edit_dialog_command_execute (WDialog * h, long command, void *data) 
     1045{ 
     1046    WGroup *g = GROUP (h); 
     1047    Widget *wh = WIDGET (h); 
     1048    cb_ret_t ret = MSG_HANDLED; 
     1049 
     1050    switch (command) 
     1051    { 
     1052    case CK_EditNew: 
     1053        edit_add_window (h, wh->y + 1, wh->x, wh->lines - 2, wh->cols, NULL, 0); 
     1054        break; 
     1055    case CK_EditFile: 
     1056        edit_load_cmd (h, NULL); 
     1057        break; 
     1058    case CK_OtherFile: 
     1059        { 
     1060            WEdit *e = (WEdit *) g->current->data; 
     1061            gboolean retflag = FALSE; 
     1062 
     1063            if (e != NULL && edit_widget_is_editor (CONST_WIDGET (e))) 
     1064            { 
     1065                retflag = edit_compute_other_file_vfs_path (e); 
     1066                if (retflag) 
     1067                    retflag = edit_switch_to_file (h, e->otherfile_vpath); 
     1068            } 
     1069            if (!retflag) 
     1070                ret = MSG_NOT_HANDLED; 
     1071        } 
     1072        break; 
     1073    case CK_History: 
     1074        edit_load_file_from_history (h); 
     1075        break; 
     1076    case CK_EditSyntaxFile: 
     1077        edit_load_syntax_file (h); 
     1078        break; 
     1079    case CK_EditUserMenu: 
     1080        edit_load_menu_file (h); 
     1081        break; 
     1082    case CK_Close: 
     1083        /* if there are no opened files anymore, close MC editor */ 
     1084        if ((edit_widget_is_editor (CONST_WIDGET (g->current->data)) || 
     1085             edit_widget_is_cli (CONST_WIDGET (g->current->data))) && 
     1086            edit_close_cmd ((WEdit *) g->current->data) && find_editor (h) == NULL) 
     1087            dlg_stop (h); 
     1088        break; 
     1089    case CK_Help: 
     1090        edit_help (); 
     1091        /* edit->force |= REDRAW_COMPLETELY; */ 
     1092        break; 
     1093    case CK_Menu: 
     1094        edit_menu_cmd (h); 
     1095        break; 
     1096    case CK_Quit: 
     1097    case CK_Cancel: 
     1098        /* don't close editor due to SIGINT, but stop move/resize window */ 
     1099        { 
     1100            Widget *w = WIDGET (g->current->data); 
     1101 
     1102            if (edit_widget_is_editor (w) && ((WEdit *) w)->drag_state != MCEDIT_DRAG_NONE) 
     1103                edit_restore_size ((WEdit *) w); 
     1104            else if (command == CK_Quit) 
     1105                dlg_stop (h); 
     1106        } 
     1107        break; 
     1108    case CK_About: 
     1109        edit_about (); 
     1110        break; 
     1111    case CK_SyntaxOnOff: 
     1112        edit_syntax_onoff_cmd (h); 
     1113        break; 
     1114    case CK_ShowTabTws: 
     1115        edit_show_tabs_tws_cmd (h); 
     1116        break; 
     1117    case CK_ShowMargin: 
     1118        edit_show_margin_cmd (h); 
     1119        break; 
     1120    case CK_ShowNumbers: 
     1121        edit_show_numbers_cmd (h); 
     1122        break; 
     1123    case CK_Refresh: 
     1124        edit_refresh_cmd (); 
     1125        break; 
     1126    case CK_InstructionStation: 
     1127        edit_add_instr_stn_window (h, INSTR_STN_NO_FLAGS, data); 
     1128        break; 
     1129    case CK_Shell: 
     1130        toggle_subshell (); 
     1131        break; 
     1132    case CK_LearnKeys: 
     1133        learn_keys (); 
     1134        break; 
     1135    case CK_WindowMove: 
     1136    case CK_WindowResize: 
     1137        if (edit_widget_is_editor (CONST_WIDGET (g->current->data))) 
     1138            edit_handle_move_resize ((WEdit *) g->current->data, command); 
     1139        break; 
     1140    case CK_WindowList: 
     1141        edit_window_list (h); 
     1142        break; 
     1143    case CK_WindowCascade: 
     1144        edit_window_cascade (h); 
     1145        break; 
     1146    case CK_WindowTile: 
     1147        edit_window_tile (h); 
     1148        break; 
     1149    case CK_WindowNext: 
     1150        group_select_next_widget (g); 
     1151        break; 
     1152    case CK_WindowPrev: 
     1153        group_select_prev_widget (g); 
     1154        break; 
     1155    case CK_Options: 
     1156        edit_options_dialog (h); 
     1157        break; 
     1158    case CK_OptionsSaveMode: 
     1159        edit_save_mode_cmd (); 
     1160        break; 
     1161    case CK_SaveSetup: 
     1162        save_setup_cmd (); 
     1163        break; 
     1164    default: 
     1165        ret = MSG_NOT_HANDLED; 
     1166        break; 
     1167    } 
     1168 
     1169    return ret; 
     1170} 
     1171 
     1172/* --------------------------------------------------------------------------------------------- */ 
     1173/** Callback for the edit dialog */ 
     1174 
     1175cb_ret_t 
     1176edit_dialog_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data) 
     1177{ 
     1178    WGroup *g = GROUP (w); 
     1179    WDialog *h = DIALOG (w); 
     1180 
     1181    switch (msg) 
     1182    { 
     1183    case MSG_INIT: 
     1184        edit_dlg_init (); 
     1185        return MSG_HANDLED; 
     1186 
     1187    case MSG_RESIZE: 
     1188        dlg_default_callback (w, NULL, MSG_RESIZE, 0, NULL); 
     1189        menubar_arrange (find_menubar (h)); 
     1190        return MSG_HANDLED; 
     1191 
     1192    case MSG_ACTION: 
     1193        { 
     1194            /* Handle shortcuts, menu, and buttonbar. */ 
     1195 
     1196            cb_ret_t result; 
     1197 
     1198            result = edit_dialog_command_execute (h, parm, data); 
     1199 
     1200            /* We forward any commands coming from the menu, and which haven't been 
     1201               handled by the dialog, to the focused WEdit window. */ 
     1202            if (result == MSG_NOT_HANDLED && sender == WIDGET (find_menubar (h))) 
     1203                result = send_message (g->current->data, NULL, MSG_ACTION, parm, NULL); 
     1204 
     1205            return result; 
     1206        } 
     1207 
     1208    case MSG_KEY: 
     1209        { 
     1210            Widget *we = WIDGET (g->current->data); 
     1211            cb_ret_t ret = MSG_NOT_HANDLED; 
     1212 
     1213            if (edit_widget_is_editor (we)) 
     1214            { 
     1215                gboolean ext_mode; 
     1216                long command; 
     1217 
     1218                /* keep and then extmod flag */ 
     1219                ext_mode = we->ext_mode; 
     1220                command = widget_lookup_key (we, parm); 
     1221                we->ext_mode = ext_mode; 
     1222 
     1223                if (command == CK_IgnoreKey) 
     1224                    we->ext_mode = FALSE; 
     1225                else 
     1226                { 
     1227                    ret = edit_dialog_command_execute (h, command, data); 
     1228                    /* if command was not handled, keep the extended mode 
     1229                       for the further key processing */ 
     1230                    if (ret == MSG_HANDLED) 
     1231                        we->ext_mode = FALSE; 
     1232                } 
     1233            } 
     1234 
     1235            /* 
     1236             * Due to the "end of bracket" escape the editor sees input with is_idle() == false 
     1237             * (expects more characters) and hence doesn't yet refresh the screen, but then 
     1238             * no further characters arrive (there's only an "end of bracket" which is swallowed 
     1239             * by tty_get_event()), so you end up with a screen that's not refreshed after pasting. 
     1240             * So let's trigger an IDLE signal. 
     1241             */ 
     1242            if (!is_idle ()) 
     1243                widget_idle (w, TRUE); 
     1244            return ret; 
     1245        } 
     1246 
     1247        /* hardcoded menu hotkeys (see edit_drop_hotkey_menu) */ 
     1248    case MSG_UNHANDLED_KEY: 
     1249        return edit_drop_hotkey_menu (h, parm) ? MSG_HANDLED : MSG_NOT_HANDLED; 
     1250 
     1251    case MSG_VALIDATE: 
     1252        edit_quit (h); 
     1253        return MSG_HANDLED; 
     1254 
     1255    case MSG_END: 
     1256        edit_dlg_deinit (); 
     1257        return MSG_HANDLED; 
     1258 
     1259    case MSG_IDLE: 
     1260        widget_idle (w, FALSE); 
     1261        return send_message (g->current->data, NULL, MSG_IDLE, 0, NULL); 
     1262 
     1263    default: 
     1264        return dlg_default_callback (w, sender, msg, parm, data); 
     1265    } 
     1266} 
     1267 
     1268/* --------------------------------------------------------------------------------------------- */ 
     1269 
     1270cb_ret_t 
    12121271edit_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data) 
    12131272{ 
    12141273    WEdit *e = (WEdit *) w; 
    edit_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *da 
    12781337 
    12791338/* --------------------------------------------------------------------------------------------- */ 
    12801339 
    1281 /** 
    1282  * Handle move/resize mouse events. 
    1283  */ 
    1284 static void 
    1285 edit_mouse_handle_move_resize (Widget * w, mouse_msg_t msg, mouse_event_t * event) 
    1286 { 
    1287     WEdit *edit = (WEdit *) (w); 
    1288     Widget *h = WIDGET (w->owner); 
    1289     int global_x, global_y; 
    1290  
    1291     if (msg == MSG_MOUSE_UP) 
    1292     { 
    1293         /* Exit move/resize mode. */ 
    1294         edit_execute_cmd (edit, CK_Enter, -1); 
    1295         edit_update_screen (edit);      /* Paint the buttonbar over our possibly overlapping frame. */ 
    1296         return; 
    1297     } 
    1298  
    1299     if (msg != MSG_MOUSE_DRAG) 
    1300         /** 
    1301          * We ignore any other events. Specifically, MSG_MOUSE_DOWN. 
    1302          * 
    1303          * When the move/resize is initiated by the menu, we let the user 
    1304          * stop it by clicking with the mouse. Which is why we don't want 
    1305          * a mouse down to affect the window. 
    1306          */ 
    1307         return; 
    1308  
    1309     /* Convert point to global coordinates for easier calculations. */ 
    1310     global_x = event->x + w->x; 
    1311     global_y = event->y + w->y; 
    1312  
    1313     /* Clamp the point to the dialog's client area. */ 
    1314     global_y = CLAMP (global_y, h->y + 1, h->y + h->lines - 2); /* Status line, buttonbar */ 
    1315     global_x = CLAMP (global_x, h->x, h->x + h->cols - 1);      /* Currently a no-op, as the dialog has no left/right margins. */ 
    1316  
    1317     if (edit->drag_state == MCEDIT_DRAG_MOVE) 
    1318     { 
    1319         w->y = global_y; 
    1320         w->x = global_x - edit->drag_state_start; 
    1321     } 
    1322     else if (edit->drag_state == MCEDIT_DRAG_RESIZE) 
    1323     { 
    1324         w->lines = MAX (WINDOW_MIN_LINES, global_y - w->y + 1); 
    1325         w->cols = MAX (WINDOW_MIN_COLS, global_x - w->x + 1); 
    1326     } 
    1327  
    1328     edit->force |= REDRAW_COMPLETELY;   /* Not really needed as WEdit's MSG_DRAW already does this. */ 
    1329  
    1330     /* We draw the whole dialog because dragging/resizing exposes area beneath. */ 
    1331     widget_draw (WIDGET (w->owner)); 
    1332 } 
    1333  
    1334 /* --------------------------------------------------------------------------------------------- */ 
    1335  
    13361340/** 
    13371341 * Handle mouse events of editor window 
    13381342 * 
    edit_mouse_handle_move_resize (Widget * w, mouse_msg_t msg, mouse_event_t * even 
    13401344 * @param msg mouse event message 
    13411345 * @param event mouse event data 
    13421346 */ 
    1343 static void 
     1347void 
    13441348edit_mouse_callback (Widget * w, mouse_msg_t msg, mouse_event_t * event) 
    13451349{ 
    13461350    WEdit *edit = (WEdit *) w; 
    edit_mouse_callback (Widget * w, mouse_msg_t msg, mouse_event_t * event) 
    14551459} 
    14561460 
    14571461/* --------------------------------------------------------------------------------------------- */ 
    1458 /*** public functions ****************************************************************************/ 
     1462/* Creates a CLI special window, windowed (not fullscreen) */ 
     1463 
     1464gboolean 
     1465edit_add_instr_stn_window (WDialog * h, instr_stn_flags_t flags, void *data) 
     1466{ 
     1467    WInstructionStation *ip; 
     1468 
     1469    /* Note passing of data - ability to alter program that's run in this station */ 
     1470    ip = instr_stn_create (3, 10, 17, 80, flags, (const char *) data); 
     1471 
     1472    if (ip == NULL) 
     1473        return FALSE; 
     1474 
     1475    /* Add CLI-extended editor window (extending WEdit) to front dialog */ 
     1476    group_add_widget_autopos (GROUP (h), ip, WPOS_KEEP_ALL, NULL); 
     1477    widget_draw (WIDGET (h)); 
     1478    return TRUE; 
     1479} 
     1480 
    14591481/* --------------------------------------------------------------------------------------------- */ 
    14601482 
    14611483gboolean 
    edit_add_window (WDialog * h, int y, int x, int lines, int cols, const vfs_path_ 
    16861708    WEdit *edit; 
    16871709    Widget *w; 
    16881710 
    1689     edit = edit_init (NULL, y, x, lines, cols, f, fline); 
     1711    edit = edit_init (NULL, y, x, lines, cols, f, fline, 0); 
    16901712    if (edit == NULL) 
    16911713        return FALSE; 
    16921714 
  • src/editor/editwidget.h

    diff --git a/src/editor/editwidget.h b/src/editor/editwidget.h
    index b4b10692e..a80057393 100644
    a b  
    1313 
    1414/*** typedefs(not structures) and defined constants **********************************************/ 
    1515 
     16#define EDIT(x) ((WEdit *)(x)) 
     17#define CONST_EDIT(x) ((const WEdit *)(x)) 
     18 
    1619#define N_LINE_CACHES 32 
    1720 
    1821/*** enums ***************************************************************************************/ 
  • new file src/editor/instr_station.c

    diff --git a/src/editor/instr_station.c b/src/editor/instr_station.c
    new file mode 100644
    index 000000000..fe0f7d8d9
    - +  
     1/* 
     2   Implementation of a CLI window for MCEdit. 
     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 instr_station.c 
     27 *  \brief Implementation of a CLI special window for MCEdit. 
     28 *  \author Sebastian Gniazdowski 
     29 *  \date 2021 
     30 * 
     31 *  Such window runs a set up program (/bin/bash by default) allowing to 
     32 *  provide an input to it and read its output. 
     33 */ 
     34 
     35#include <config.h> 
     36 
     37#include "lib/global.h" 
     38 
     39#include <sys/types.h> 
     40#include <sys/wait.h> 
     41 
     42#include "src/editor/editwidget.h" 
     43 
     44#include "lib/sub-util.h" 
     45#include "lib/tty/key.h" 
     46#include "lib/widget.h" 
     47#include "src/editor/instr_station.h" 
     48 
     49/*** global variables ****************************************************************************/ 
     50 
     51/*** file scope macro definitions ****************************************************************/ 
     52 
     53/*** file scope type declarations ****************************************************************/ 
     54 
     55typedef enum instr_stn_io_type 
     56{ 
     57    STDIN = 0, 
     58    STDOUT, 
     59    STDERR 
     60} instr_stn_io_type_t; 
     61 
     62/*** file scope variables ************************************************************************/ 
     63 
     64static gboolean alt_prog_first_run = TRUE; 
     65 
     66/*** file scope functions ************************************************************************/ 
     67/* --------------------------------------------------------------------------------------------- */ 
     68 
     69static char * 
     70instr_stn_get_instruction_text (WInstructionStation * ip) 
     71{ 
     72    char *ret_str; 
     73    GString *instr; 
     74    off_t bol, eol, begin_instr; 
     75    long size; 
     76    int idx, byte; 
     77 
     78    /* Calculate offset of text after prompt */ 
     79    bol = edit_buffer_get_current_bol (&EDIT (ip)->buffer); 
     80    eol = edit_buffer_get_current_eol (&EDIT (ip)->buffer); 
     81    begin_instr = bol + ip->prompt_span; 
     82 
     83    /* Is there anything entered? */ 
     84    size = ((long) eol) - ((long) begin_instr); 
     85    if (size <= 0) 
     86        return NULL; 
     87 
     88    /* Allocate expected size string and fill it */ 
     89    instr = g_string_sized_new (size + 2); 
     90    for (idx = 0; idx < size; idx++) 
     91    { 
     92        byte = edit_buffer_get_byte (&EDIT (ip)->buffer, begin_instr + idx); 
     93        g_string_append_c (instr, byte); 
     94    } 
     95 
     96    /* Append new line if needed */ 
     97    if (instr->str[instr->len - 1] != '\n') 
     98        g_string_append_c (instr, '\n'); 
     99 
     100    /* Return char buffer */ 
     101    ret_str = instr->str; 
     102    g_string_free (instr, FALSE); 
     103    return ret_str; 
     104} 
     105 
     106/* --------------------------------------------------------------------------------------------- */ 
     107 
     108static gboolean 
     109instr_stn_release (WInstructionStation * ip, gboolean free_all) 
     110{ 
     111    /* Already relased? */ 
     112    if (ip == NULL) 
     113    { 
     114        if (free_all) 
     115            return TRUE; 
     116        else 
     117            return FALSE; 
     118    } 
     119 
     120    /* Already cleaned up (for a restart of program) ? */ 
     121    if (ip->finalized && !free_all) 
     122        return FALSE; 
     123    ip->finalized = TRUE; 
     124 
     125    /* Close process watcher first, suppressing *program_ended_cb() callback */ 
     126    if (ip->proc_src_id != 0) 
     127    { 
     128        GSource *src; 
     129        src = g_main_context_find_source_by_id (NULL, ip->proc_src_id); 
     130        if (src != NULL) 
     131            g_source_destroy (src); 
     132        ip->proc_src_id = 0; 
     133    } 
     134 
     135    /* Release the pipes, channels, etc. */ 
     136    for (int i = 0; i <= STDERR; i++) 
     137    { 
     138        /* Source */ 
     139        if (ip->io[i].src_id != 0) 
     140        { 
     141            GSource *src; 
     142            src = g_main_context_find_source_by_id (NULL, ip->io[i].src_id); 
     143            if (src != NULL) 
     144                g_source_destroy (src); 
     145            ip->io[i].src_id = 0; 
     146        } 
     147        /* Channel */ 
     148        if (ip->io[i].ch != NULL) 
     149        { 
     150            g_io_channel_unref (ip->io[i].ch); 
     151            ip->io[i].ch = NULL; 
     152        } 
     153        /* Pipe */ 
     154        if (ip->io[i].fd >= 0) 
     155        { 
     156            close (ip->io[i].fd); 
     157            ip->io[i].fd = -1; 
     158        } 
     159    } 
     160 
     161    /* Remove prompt guard recurring timeout callback function */ 
     162    if (ip->prompt_timeout_id != 0) 
     163    { 
     164        g_source_remove (ip->prompt_timeout_id); 
     165        ip->prompt_timeout_id = 0; 
     166    } 
     167 
     168    MC_PTR_FREE (ip->instruction); 
     169    /* Release prompt */ 
     170    MC_PTR_FREE (ip->prompt); 
     171    /* Release error prompt */ 
     172    MC_PTR_FREE (ip->eprompt); 
     173 
     174    /* Clear prompt information */ 
     175    ip->prompt_span = 0; 
     176    ip->eprompt_span = 0; 
     177 
     178    /* Set various indicator flags to some inactive state */ 
     179    ip->io_complete = TRUE; 
     180    ip->first_response = TRUE; 
     181    ip->before_first_prompt = TRUE; 
     182    ip->prompt_shown = FALSE; 
     183    ip->flags = INSTR_STN_NO_FLAGS; 
     184 
     185    /* Clear any GError */ 
     186    if (ip->error != NULL) 
     187    { 
     188        g_error_free (ip->error); 
     189        ip->error = NULL; 
     190    } 
     191 
     192    /* Release space occupied by WInstructionStation object? */ 
     193    if (free_all) 
     194    { 
     195        MC_PTR_FREE (ip->program); 
     196        /* Free main object */ 
     197        g_free (ip); 
     198    } 
     199    else 
     200        /* Value is still accessible in prev_program field */ 
     201        ip->program = NULL; 
     202 
     203    return TRUE; 
     204} 
     205 
     206/* --------------------------------------------------------------------------------------------- */ 
     207 
     208static cb_ret_t 
     209instr_stn_run_novel_state_id (WInstructionStation * ip, long novel_state_id, void *data) 
     210{ 
     211    WGroup *owner = WIDGET (ip)->owner; 
     212    cb_ret_t ret = MSG_NOT_HANDLED; 
     213 
     214    switch (novel_state_id) 
     215    { 
     216    case CK_BackSpace: 
     217        if (ip->cur_col <= ip->prompt_span) 
     218            ret = MSG_HANDLED; 
     219        else 
     220        { 
     221            EDIT (ip)->force |= REDRAW_PAGE; 
     222            edit_update_screen (EDIT (ip)); 
     223        } 
     224        break; 
     225    case CK_Enter: 
     226    case CK_Return: 
     227        if (EDIT (ip)->buffer.curs_line == ip->cur_line) 
     228        { 
     229            ip->instruction = instr_stn_get_instruction_text (ip); 
     230            ip->io_complete = FALSE; 
     231            /* Should dispatch to write callback which will send instruction */ 
     232            g_main_context_iteration (NULL, 0); 
     233            repaint_screen (); 
     234        } 
     235        ret = MSG_HANDLED; 
     236        break; 
     237        /* InstructionStation activity invoked when focus is at a station window behaves differently */ 
     238    case CK_InstructionStation: 
     239        { 
     240            /* Save needed information */ 
     241            char *program_save; 
     242 
     243            /* End previous program */ 
     244            if (kill (ip->process_id, 15) == -1) 
     245                mc_log ("Problem sending TERM signal to %d: %s.", ip->process_id, 
     246                        g_strerror (errno)); 
     247            else 
     248                mc_log ("Ending process %d.", ip->process_id); 
     249            if (!ip->proc_support) 
     250                waitpid (ip->process_id, NULL, 0); 
     251 
     252            /* Allow process watcher callbacks to be called */ 
     253            g_usleep (150000); 
     254            g_main_context_iteration (NULL, FALSE); 
     255            /* Allow the process end message to flash noticeably long */ 
     256            g_usleep (200000); 
     257 
     258            /* Clean WInstructionStation object */ 
     259            instr_stn_release (ip, FALSE); 
     260            /* ...and reinitialize it */ 
     261            program_save = data != NULL ? g_strdup (data) : ip->prev_program; 
     262            if (instr_stn_init (ip, -1, -1, -1, -1, ip->prev_flags, program_save) == NULL) 
     263            { 
     264                /* Init failed -> close faulty station window */ 
     265                ret = 
     266                    edit_dialog_callback (WIDGET (owner), WIDGET (ip), MSG_ACTION, CK_Close, NULL); 
     267                if (ret == MSG_HANDLED) 
     268                    /* Update pointer in case of some future code like widget_draw(ip), etc. */ 
     269                    ip = INSTR_STATION (owner->current->data); 
     270            } 
     271            else 
     272            { 
     273                repaint_screen (); 
     274                ret = MSG_HANDLED; 
     275            } 
     276            g_free (program_save); 
     277        } 
     278        break; 
     279    default: 
     280        break; 
     281    } 
     282    return ret; 
     283} 
     284 
     285/* --------------------------------------------------------------------------------------------- */ 
     286 
     287static void 
     288instr_stn_show_prompt (WInstructionStation * ip, gboolean is_ok) 
     289{ 
     290    char *sel_prompt = is_ok ? ip->prompt : ip->eprompt; 
     291    int sel_prompt_span = is_ok ? ip->prompt_span : ip->eprompt_span; 
     292 
     293    if (has_flag (ip->flags, INSTR_STN_NO_PROMPT)) 
     294        return; 
     295 
     296    /* Print prompt */ 
     297    if (!edit_newline_end_check_only (EDIT (ip)) && !ip->before_first_prompt) 
     298    { 
     299        edit_insert (EDIT (ip), '\n'); 
     300        ip->cur_line += 1; 
     301    } 
     302    else 
     303        ip->before_first_prompt = FALSE; 
     304    edit_insert_string (EDIT (ip), sel_prompt, sel_prompt_span); 
     305 
     306    /* Raise an indicator flag */ 
     307    ip->prompt_shown = TRUE; 
     308 
     309    /* Set cursor position to reflect state */ 
     310    ip->cur_col = sel_prompt_span; 
     311} 
     312 
     313/* --------------------------------------------------------------------------------------------- */ 
     314/* Remove prompt from buffer (basically clear current line). */ 
     315 
     316static void 
     317instr_stn_clear_prompt (WInstructionStation * ip) 
     318{ 
     319    off_t bol, eol, idx; 
     320 
     321    if (has_flag (ip->flags, INSTR_STN_NO_PROMPT)) 
     322        return; 
     323 
     324    eol = edit_buffer_get_current_eol (&EDIT (ip)->buffer); 
     325    bol = edit_buffer_get_current_bol (&EDIT (ip)->buffer); 
     326    for (idx = 0; idx < eol - bol; idx++) 
     327        edit_backspace (EDIT (ip), TRUE); 
     328    ip->prompt_shown = FALSE; 
     329} 
     330 
     331/* --------------------------------------------------------------------------------------------- */ 
     332 
     333static gboolean 
     334prompt_drawing_timeout_callback (gpointer data) 
     335{ 
     336    WInstructionStation *ip = INSTR_STATION (data); 
     337    guint64 cur_time; 
     338    cur_time = g_get_real_time (); 
     339    /* Draw prompt after 0.5 seconds of no output from program */ 
     340    if (cur_time - ip->read_time > 500000 && !ip->prompt_shown) 
     341    { 
     342        instr_stn_show_prompt (ip, TRUE); 
     343        repaint_screen (); 
     344    } 
     345    return TRUE; 
     346} 
     347 
     348/* --------------------------------------------------------------------------------------------- */ 
     349 
     350#ifdef GLIB_VERSION_2_40 
     351 
     352static void 
     353program_ended_callback (GPid pid, gint exit_code, gpointer user_data) 
     354{ 
     355    WInstructionStation *ip = INSTR_STATION (user_data); 
     356    char *msg; 
     357    gboolean st_ret; 
     358 
     359    /* Release object leaving it uninitialized (no freeing of main object pointer) */ 
     360    instr_stn_release (ip, FALSE); 
     361 
     362    g_spawn_close_pid (pid); 
     363 
     364    /* Examine exit code of closed program */ 
     365#ifdef GLIB_VERSION_2_34 
     366    st_ret = g_spawn_check_exit_status (exit_code, NULL); 
     367#else 
     368    st_ret = (exit_code == 0); 
     369#endif 
     370 
     371    /* Choose message basing on exit code */ 
     372    if (st_ret) 
     373        msg = g_strdup (_("\nProgram closed.\n")); 
     374    else 
     375        msg = g_strdup_printf (_("\nProgram closed abnormally (exit code: %d).\n"), exit_code); 
     376 
     377    /* Insert/print message to buffer and to mc.log */ 
     378    edit_insert_string (EDIT (ip), msg, strlen (msg)); 
     379    mc_log ("%s", msg); 
     380 
     381    /* Release message upon using/printing it */ 
     382    g_free (msg); 
     383    repaint_screen (); 
     384} 
     385 
     386#endif 
     387 
     388/* --------------------------------------------------------------------------------------------- */ 
     389 
     390static gboolean 
     391helper_configure_src_and_ch (GIOChannel ** ch, gint fd, guint * src_id, gboolean write, 
     392                             GIOFunc cb_fun, gpointer data, const char *name) 
     393{ 
     394    /* Initialize the output variables */ 
     395    *ch = NULL; 
     396    *src_id = 0; 
     397 
     398    *ch = g_io_channel_unix_new (fd); 
     399 
     400    /* Channel created? */ 
     401    if (*ch == NULL) 
     402        goto cleanup_and_err; 
     403 
     404    /* Automatic shutdown of channel */ 
     405    g_io_channel_set_close_on_unref (*ch, TRUE); 
     406 
     407    /* Trim down buffering on this channel */ 
     408    g_io_channel_set_buffer_size (*ch, 5); 
     409 
     410    /* Attempt to set non-blocking flag on channel */ 
     411    if (g_io_channel_set_flags (*ch, G_IO_FLAG_NONBLOCK, NULL) != G_IO_STATUS_NORMAL) 
     412        mc_log ("Problem setting a channel to non-blocking state (channel flags: 0x%x)", 
     413                g_io_channel_get_flags (*ch)); 
     414 
     415    /* Create GSource */ 
     416    *src_id = 
     417        g_io_add_watch (*ch, (write ? G_IO_OUT : G_IO_IN) | G_IO_HUP | G_IO_ERR, cb_fun, data); 
     418 
     419    /* Source created OK? */ 
     420    if (*src_id == 0) 
     421        goto cleanup_and_err; 
     422 
     423    /* Configure the sources */ 
     424    g_source_set_name_by_id (*src_id, name); 
     425 
     426    /* Return true */ 
     427    return TRUE; 
     428 
     429  cleanup_and_err: 
     430    if (*src_id != 0) 
     431    { 
     432        GSource *src; 
     433        src = g_main_context_find_source_by_id (NULL, *src_id); 
     434        if (src != NULL) 
     435            /* Triggers also unref of wrapped channel */ 
     436            g_source_destroy (src); 
     437    } 
     438    if (*ch != NULL) 
     439        g_io_channel_unref (*ch); 
     440    *ch = NULL; 
     441    *src_id = 0; 
     442 
     443    return FALSE; 
     444} 
     445 
     446/* --------------------------------------------------------------------------------------------- */ 
     447 
     448static gboolean 
     449helper_read_all (GIOChannel * channel, char *buf, gsize * out_bytes_read_in_len) 
     450{ 
     451    gsize len = *out_bytes_read_in_len; 
     452    gsize count, retries = 10; 
     453    GIOStatus stat; 
     454 
     455    *out_bytes_read_in_len = 0; 
     456 
     457    while (*out_bytes_read_in_len < len) 
     458    { 
     459        count = 0; 
     460        stat = 
     461            g_io_channel_read_chars (channel, buf + *out_bytes_read_in_len, 
     462                                     len - *out_bytes_read_in_len, &count, NULL); 
     463        *out_bytes_read_in_len += count; 
     464 
     465        /* Should end the reading ? */ 
     466        if (stat == G_IO_STATUS_ERROR || (stat == G_IO_STATUS_NORMAL && count == 0) || 
     467            stat == G_IO_STATUS_EOF) 
     468            return (stat != G_IO_STATUS_ERROR); 
     469        else if (stat == G_IO_STATUS_AGAIN && retries-- == 0) 
     470            /* Exhausted retries - there must be no data to read - return OK */ 
     471            return TRUE; 
     472    } 
     473 
     474    return TRUE; 
     475} 
     476 
     477/* --------------------------------------------------------------------------------------------- */ 
     478 
     479static gboolean 
     480helper_write_all (GIOChannel * channel, char *buf, gsize * out_bytes_written_in_len) 
     481{ 
     482    gsize len = *out_bytes_written_in_len; 
     483    gsize count; 
     484    GIOError err; 
     485 
     486    *out_bytes_written_in_len = 0; 
     487 
     488    while (*out_bytes_written_in_len < len) 
     489    { 
     490        count = 0; 
     491        err = 
     492            g_io_channel_write (channel, buf + *out_bytes_written_in_len, 
     493                                len - *out_bytes_written_in_len, &count); 
     494        *out_bytes_written_in_len += count; 
     495 
     496        if (err && err != G_IO_ERROR_AGAIN) 
     497            return FALSE; 
     498    } 
     499 
     500    return TRUE; 
     501} 
     502 
     503/* --------------------------------------------------------------------------------------------- */ 
     504 
     505static gboolean 
     506stdout_read_callback (GIOChannel * source, GIOCondition condition, gpointer data) 
     507{ 
     508    WInstructionStation *ip = INSTR_STATION (data); 
     509    char buf[2048]; 
     510    gsize count = 2047; 
     511    gboolean r_ret; 
     512 
     513    /* Active only when a recognized event of interest occurs */ 
     514    if ((condition & (G_IO_IN | G_IO_HUP | G_IO_ERR)) == 0) 
     515        return TRUE; 
     516    else 
     517        ip->io_complete = FALSE; 
     518 
     519    /* Repeat read of 2KiB-1 maximum bytes until 0 bytes are being read */ 
     520    do 
     521    { 
     522        r_ret = helper_read_all (source, buf, &count); 
     523        buf[count] = '\0'; 
     524        if (count == 0 && !r_ret) 
     525        { 
     526            mc_log ("Error while reading program's output: %s", g_strerror (errno)); 
     527        } 
     528        else 
     529        { 
     530            ip->read_time = g_get_real_time (); 
     531            buf[count] = '\0'; 
     532            if (count > 0) 
     533            { 
     534                gsize idx = 0, nl_count = 0; 
     535 
     536                /* Count number of read lines */ 
     537                char *p = buf; 
     538                while (idx++ < count) 
     539                    if (*p++ == '\n') 
     540                        nl_count++; 
     541 
     542                /* Move to next line after prompt */ 
     543                if (ip->first_response) 
     544                { 
     545                    edit_insert (EDIT (ip), '\n'); 
     546                    nl_count++; 
     547                    ip->first_response = FALSE; 
     548                    ip->prompt_shown = FALSE; 
     549                } 
     550                else if (ip->prompt_shown) 
     551                    instr_stn_clear_prompt (ip); 
     552 
     553                /* Print whole buffer into file's window  */ 
     554                edit_insert_string (EDIT (ip), buf, count); 
     555 
     556                /* Increase the row pointer */ 
     557                ip->cur_line += nl_count; 
     558            } 
     559            else if (!ip->prompt_shown) 
     560                instr_stn_show_prompt (ip, r_ret); 
     561 
     562            repaint_screen (); 
     563        } 
     564    } 
     565    while (count != 0); 
     566 
     567    ip->io_complete = r_ret; 
     568    return condition != G_IO_HUP; 
     569} 
     570 
     571/* --------------------------------------------------------------------------------------------- */ 
     572 
     573static gboolean 
     574stdin_write_callback (GIOChannel * source, GIOCondition condition, gpointer data) 
     575{ 
     576    WInstructionStation *ip = INSTR_STATION (data); 
     577    gsize count; 
     578    gboolean ret = TRUE; 
     579 
     580    /* Any instruction to send? */ 
     581    if (ip->instruction == NULL) 
     582        return ret; 
     583    else 
     584        ip->io_complete = FALSE; 
     585 
     586    if ((condition & G_IO_OUT) == 0) 
     587        return TRUE; 
     588 
     589    errno = 0; 
     590    count = strlen (ip->instruction); 
     591    ret = helper_write_all (source, ip->instruction, &count); 
     592    if (!ret) 
     593    { 
     594        mc_log ("Error during sending instruction to program: %s", g_strerror (errno)); 
     595    } 
     596    else 
     597    { 
     598        ip->first_response = TRUE; 
     599        MC_PTR_FREE (ip->instruction); 
     600        if (ip->prompt_timeout_id == 0) 
     601            ip->prompt_timeout_id = g_timeout_add (200, prompt_drawing_timeout_callback, ip); 
     602    } 
     603 
     604    g_io_channel_flush (ip->io[STDIN].ch, NULL); 
     605    ip->io_complete = TRUE; 
     606 
     607    return ret; 
     608} 
     609 
     610/* --------------------------------------------------------------------------------------------- */ 
     611 
     612/* --------------------------------------------------------------------------------------------- */ 
     613/*** public functions ****************************************************************************/ 
     614/* --------------------------------------------------------------------------------------------- */ 
     615 
     616WInstructionStation * 
     617instr_stn_create (int y, int x, int lines, int cols, instr_stn_flags_t flags, const char *program) 
     618{ 
     619    WInstructionStation *ip, *iret; 
     620 
     621    ip = g_new0 (WInstructionStation, 1); 
     622    if (ip == NULL) 
     623        return NULL; 
     624 
     625    iret = instr_stn_init (ip, y, x, lines, cols, flags, program); 
     626    if (iret == NULL) 
     627        MC_PTR_FREE (ip); 
     628 
     629    return ip; 
     630} 
     631 
     632/* --------------------------------------------------------------------------------------------- */ 
     633/* Initializes a preallocated object */ 
     634 
     635WInstructionStation * 
     636instr_stn_init (WInstructionStation * ip, int y, int x, int lines, int cols, 
     637                instr_stn_flags_t flags, const char *program) 
     638{ 
     639    WEdit *iret; 
     640    WGroup *owner_save = NULL; 
     641    gboolean tmp_ret = FALSE; 
     642    char **program_cline; 
     643    int rounds = 0; 
     644 
     645    if (ip == NULL) 
     646        goto cleanup_and_err; 
     647 
     648    /* Handle special -1 values indicating reuse of old values */ 
     649    if (ip->finalized) 
     650    { 
     651        y = (y == -1) ? WIDGET (ip)->y : y; 
     652        x = (x == -1) ? WIDGET (ip)->x : x; 
     653        lines = (lines == -1) ? WIDGET (ip)->lines : lines; 
     654        cols = (cols == -1) ? WIDGET (ip)->cols : cols; 
     655        /* Save owner */ 
     656        owner_save = WIDGET (ip)->owner; 
     657        ip->finalized = FALSE; 
     658    } 
     659 
     660    /* Initialize base object (WEdit) */ 
     661    iret = edit_init (EDIT (ip), y, x, lines, cols, NULL, 0, EDIT_DO_INIT_BASE_CLASS); 
     662    if (iret == NULL) 
     663        goto cleanup_and_err; 
     664 
     665    /* Restore owner field value */ 
     666    WIDGET (ip)->owner = owner_save; 
     667    WIDGET (ip)->callback = instr_stn_callback; 
     668    WIDGET (ip)->mouse_callback = edit_mouse_callback; 
     669    EDIT (ip)->fullscreen = 0; 
     670 
     671    ip->io[0].name = g_strdup ("Std-In (WInstructionStation)"); 
     672    ip->io[1].name = g_strdup ("Std-Out (WInstructionStation)"); 
     673    ip->io[2].name = g_strdup ("Std-Error (WInstructionStation)"); 
     674    ip->io[0].fd = ip->io[1].fd = ip->io[2].fd = -1; 
     675 
     676    ip->before_first_prompt = TRUE; 
     677    ip->prompt = g_strdup ("[guest@localhost]# "); 
     678    ip->prompt_span = strlen (ip->prompt); 
     679    ip->eprompt = g_strdup ("[guest@localhost]! "); 
     680    ip->eprompt_span = strlen (ip->eprompt); 
     681    ip->cur_col = 0; 
     682    ip->cur_line = 0; 
     683    ip->flags = flags; 
     684    ip->prev_flags = flags; 
     685 
     686    /* Set initial program (most probably a shell, like Bash) */ 
     687    if (program != NULL) 
     688        ip->program = g_strdup (program); 
     689    else 
     690        /* Fallback instruction if no program given */ 
     691        ip->program = g_strdup ("bash"); 
     692 
     693    while (++rounds <= 2 && !tmp_ret) 
     694    { 
     695        int argc = 0; 
     696        if (ip->error != NULL) 
     697        { 
     698            g_error_free (ip->error); 
     699            ip->error = NULL; 
     700        } 
     701        tmp_ret = g_shell_parse_argv (ip->program, &argc, &program_cline, &ip->error); 
     702        if (tmp_ret) 
     703        { 
     704            tmp_ret = g_spawn_async_with_pipes (NULL, program_cline, 
     705                                                NULL, G_SPAWN_SEARCH_PATH | 
     706                                                G_SPAWN_DO_NOT_REAP_CHILD | 
     707                                                G_SPAWN_LEAVE_DESCRIPTORS_OPEN, 
     708                                                NULL, NULL, &ip->process_id, 
     709                                                has_flag (flags, 
     710                                                          INSTR_STN_NO_STDIN) ? NULL : 
     711                                                &ip->io[STDIN].fd, &ip->io[STDOUT].fd, 
     712                                                &ip->io[STDERR].fd, &ip->error); 
     713            g_strfreev (program_cline); 
     714        } 
     715        if (!tmp_ret) 
     716        { 
     717            char *new_program, *msg_text; 
     718            mc_log ("Creating CLI process [instruction:%s] failed: %s", 
     719                    program, ip->error ? ip->error->message : "<no error message>"); 
     720 
     721            /* Create a transatable, parametrized message */ 
     722            msg_text = g_strdup_printf ("%s%s:", _("The specified program has failed to run.\n" 
     723                                                   "Enter a new full path or a program name"), 
     724                                        (rounds >= 2 ? _(" (last try)") : "")); 
     725 
     726            /* Display message asking for a new program */ 
     727            new_program = input_expand_dialog (_("Provide an alternate program to run"), 
     728                                               msg_text, "instruction-station", 
     729                                               alt_prog_first_run ? ip->program : INPUT_LAST_TEXT, 
     730                                               INPUT_COMPLETE_COMMANDS | INPUT_COMPLETE_FILENAMES | 
     731                                               INPUT_COMPLETE_VARIABLES); 
     732            g_free (msg_text); 
     733            alt_prog_first_run = FALSE; 
     734 
     735            /* Obtained a new program? */ 
     736            if (new_program != NULL) 
     737            { 
     738                g_free (ip->program); 
     739                ip->program = new_program; 
     740            } 
     741        } 
     742    } 
     743 
     744    /* If no program to run has been found, then exit. */ 
     745    if (!tmp_ret) 
     746    { 
     747        message (D_ERROR, _("InstructionStation startup failed"), 
     748                 _("Failed to initialize the CLI window\n(reason: %s)."), 
     749                 ip->error ? ip->error->message : "<no error message>"); 
     750        goto cleanup_and_err; 
     751    } 
     752 
     753    ip->prev_program = ip->program; 
     754 
     755    for (int i = 0; i <= STDERR; i++) 
     756    { 
     757        if (has_flag (flags, INSTR_STN_NO_STDIN) && i == STDIN) 
     758        { 
     759            /* There is no standard input pipe for program */ 
     760            ip->io[i].fd = -1; 
     761            continue; 
     762        } 
     763 
     764        if (helper_configure_src_and_ch (&ip->io[i].ch, ip->io[i].fd, &ip->io[i].src_id, 
     765                                         (i == STDIN ? TRUE : FALSE), 
     766                                         (i == STDIN ? stdin_write_callback : stdout_read_callback), 
     767                                         ip, ip->io[i].name) == FALSE) 
     768            goto cleanup_and_err; 
     769    } 
     770 
     771#ifdef GLIB_VERSION_2_40 
     772    /* Set a process watcher and restarter for the program */ 
     773    ip->proc_src_id = g_child_watch_add (ip->process_id, program_ended_callback, ip); 
     774    ip->proc_support = TRUE; 
     775#endif 
     776 
     777    /* Display initial prompt */ 
     778    instr_stn_show_prompt (ip, TRUE); 
     779    ip->io_complete = TRUE; 
     780 
     781    return ip; 
     782 
     783  cleanup_and_err: 
     784 
     785    instr_stn_release (ip, TRUE); 
     786    return NULL; 
     787} 
     788 
     789/* --------------------------------------------------------------------------------------------- */ 
     790 
     791WEdit * 
     792find_cli (const WDialog * h) 
     793{ 
     794    const WGroup *g = CONST_GROUP (h); 
     795 
     796    if (edit_widget_is_cli (CONST_WIDGET (g->current->data))) 
     797        return (WEdit *) g->current->data; 
     798    return (WEdit *) widget_find_by_type (CONST_WIDGET (h), instr_stn_callback); 
     799} 
     800 
     801/* --------------------------------------------------------------------------------------------- */ 
     802/* Check if widget is a WInstructionStation class type. */ 
     803 
     804gboolean 
     805edit_widget_is_cli (const Widget * w) 
     806{ 
     807    return (w != NULL && w->callback == instr_stn_callback); 
     808} 
     809 
     810/* --------------------------------------------------------------------------------------------- */ 
     811 
     812cb_ret_t 
     813instr_stn_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data) 
     814{ 
     815    cb_ret_t ret = MSG_NOT_HANDLED; 
     816    WInstructionStation *ip = INSTR_STATION (w); 
     817    WGroup *owner = w->owner; 
     818 
     819    switch (msg) 
     820    { 
     821 
     822    case MSG_KEY: 
     823        { 
     824            gboolean ext_mode; 
     825            long novel_state_id; 
     826 
     827            /* keep and then extmod flag */ 
     828            ext_mode = w->ext_mode; 
     829            novel_state_id = widget_lookup_key (w, parm); 
     830            w->ext_mode = ext_mode; 
     831 
     832            if (novel_state_id == CK_IgnoreKey) 
     833                w->ext_mode = FALSE; 
     834            else 
     835            { 
     836                /* Treat Station action specially when run from within a station window */ 
     837                if (novel_state_id != CK_InstructionStation) 
     838                    ret = edit_dialog_command_execute (DIALOG (owner), novel_state_id, data); 
     839                if (ret == MSG_NOT_HANDLED) 
     840                    ret = instr_stn_run_novel_state_id (ip, novel_state_id, data); 
     841                /* 
     842                 * If activity was not handled, keep the extended mode 
     843                 * for the further key processing, checking if the 
     844                 * window is still open */ 
     845                if (novel_state_id != CK_Close) 
     846                { 
     847                    if (ret == MSG_HANDLED) 
     848                        w->ext_mode = FALSE; 
     849                } 
     850                else if (ret == MSG_HANDLED) 
     851                    /* Window closed -> update `w` variable */ 
     852                    w = WIDGET (owner->current->data); 
     853            } 
     854 
     855            /* 
     856             * Due to the "end of bracket" escape the editor sees input with is_idle() == false 
     857             * (expects more characters) and hence doesn't yet refresh the screen, but then 
     858             * no further characters arrive (there's only an "end of bracket" which is swallowed 
     859             * by tty_get_event()), so you end up with a screen that's not refreshed after pasting. 
     860             * So let's trigger an IDLE signal. 
     861             */ 
     862            if (!is_idle ()) 
     863                widget_idle (w, TRUE); 
     864        } 
     865        break; 
     866    case MSG_ACTION: 
     867        ret = instr_stn_run_novel_state_id (ip, parm, data); 
     868        break; 
     869    case MSG_DESTROY: 
     870        kill (ip->process_id, 15); 
     871        /* Have to wait directly (no GLib support for watchers) ? */ 
     872        if (!ip->proc_support) 
     873            waitpid (ip->process_id, NULL, 0); 
     874 
     875        /* 
     876         * Message will be passed on to WEdit callback which will g_free(), so passing FALSE (i.e.: 
     877         * requesting cleanup of WInstructionStation object without full release of its main 
     878         * pointer) */ 
     879        instr_stn_release (ip, FALSE); 
     880        /* Not restarting the station, so release program string backup field */ 
     881        g_free (ip->prev_program); 
     882        break; 
     883    default: 
     884        break; 
     885    } 
     886 
     887    /* Redirect message to base object (i.e.: editor window «» WEdit) if needed */ 
     888    if (ret == MSG_NOT_HANDLED) 
     889    { 
     890        ret = edit_callback (w, sender, msg, parm, data); 
     891        if (msg != MSG_DESTROY) 
     892            ip->cur_col = EDIT (w)->curs_col; 
     893    } 
     894    return ret; 
     895} 
     896 
     897/* --------------------------------------------------------------------------------------------- */ 
  • new file src/editor/instr_station.h

    diff --git a/src/editor/instr_station.h b/src/editor/instr_station.h
    new file mode 100644
    index 000000000..0f05900c1
    - +  
     1#ifndef MC__INSTR_STATION_H 
     2#define MC__INSTR_STATION_H 
     3 
     4/*** typedefs(not structures) and defined constants **********************************************/ 
     5 
     6#define INSTR_STATION(x) ((WInstructionStation *)x) 
     7#define CONST_INSTR_STATION(x) ((const WInstructionStation *)x) 
     8 
     9/*** enums ***************************************************************************************/ 
     10 
     11/*** structures declarations (and typedefs of structures)*****************************************/ 
     12 
     13typedef struct 
     14{ 
     15    WEdit base; 
     16 
     17    /* Configuration flags */ 
     18    instr_stn_flags_t flags; 
     19    /* Save of flags for use upon finalization of this object */ 
     20    instr_stn_flags_t prev_flags; 
     21 
     22    int cur_line;               /* Current line where prompt is */ 
     23    int cur_col;                /* Current column where prompt ends */ 
     24    int prompt_span;            /* Length of prompt in bytes */ 
     25    int eprompt_span;           /* Length of error prompt */ 
     26 
     27    /* Complete command that's being run in this station */ 
     28    char *program; 
     29    /* Saved program string from before calling instr_stn_release(...,FALSE) (i.e.: finalization) */ 
     30    char *prev_program; 
     31    /* OK prompt */ 
     32    char *prompt; 
     33    /* Failure prompt */ 
     34    char *eprompt; 
     35    /* The current instruction to send (filled for the writer GSource's pseudo thread) */ 
     36    char *instruction; 
     37 
     38    /* An indicator of whether the prompt to show is the first one */ 
     39    gboolean before_first_prompt; 
     40    /* An indicator of waiting for an initial response from program */ 
     41    gboolean first_response; 
     42    /* An indicator of the prompt being printed or not */ 
     43    gboolean prompt_shown; 
     44    /* ID of prompt drawing GLib's timeout recurring function */ 
     45    guint prompt_timeout_id; 
     46 
     47    /* Process ID of the program */ 
     48    GPid process_id; 
     49 
     50    /* Pipes to and from the program – series of fd+channel+GSource collections */ 
     51    struct 
     52    { 
     53        gint fd; 
     54        GIOChannel *ch; 
     55        guint src_id; 
     56        char *name; 
     57    } io[3]; 
     58 
     59    /* Time of last non empty read of data from program */ 
     60    guint64 read_time; 
     61 
     62    /* GSource id of program process watcher */ 
     63    guint proc_src_id; 
     64 
     65    /* A flag indicating whether there is (can be) a process watcher running */ 
     66    gboolean proc_support; 
     67 
     68    /* A GError placeholder */ 
     69    GError *error; 
     70 
     71    /* Indicator of an idle state – set when there's no data to send and read */ 
     72    gboolean io_complete; 
     73 
     74    /* Indicator of instr_release being already called on WInstructionStation object */ 
     75    gboolean finalized; 
     76} WInstructionStation; 
     77 
     78/*** global variables defined in .c file *********************************************************/ 
     79 
     80/*** declarations of public functions ************************************************************/ 
     81 
     82WInstructionStation *instr_stn_create (int y, int x, int lines, int cols, instr_stn_flags_t flags, 
     83                                       const char *program); 
     84WInstructionStation *instr_stn_init (WInstructionStation * ip, int y, int x, int lines, int cols, 
     85                                     instr_stn_flags_t flags, const char *program); 
     86WEdit *find_cli (const WDialog * h); 
     87gboolean edit_widget_is_cli (const Widget * w); 
     88cb_ret_t instr_stn_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data); 
     89 
     90/*** inline functions ****************************************************************************/ 
     91 
     92#endif /* MC__INSTR_STATION_H */ 
  • src/keybind-defaults.c

    diff --git a/src/keybind-defaults.c b/src/keybind-defaults.c
    index db483382a..c0cede558 100644
    a b static const global_keymap_ini_t default_editor_keymap[] = { 
    478478    {"FileNext", "alt-plus"}, 
    479479    {"Sort", "alt-t"}, 
    480480    {"Mail", "alt-m"}, 
     481    {"InstructionStation", "alt-i"}, 
    481482    {"ExternalCommand", "alt-u"}, 
    482483    {"WindowMove", "ctrl-alt-e"}, 
    483484    {"WindowResize", "ctrl-alt-s"},