Ticket #4196: InstructionStation_v3.2.patch

File InstructionStation_v3.2.patch, 55.6 KB (added by psprint, 3 years ago)
  • lib/global.h

    From 3a101ba6fc4d0b800e56d8208c9581ce8dbc923b 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 +-
     misc/mc.default.keymap     |   1 +
     misc/mc.emacs.keymap       |   1 +
     src/editor/Makefile.am     |   1 +
     src/editor/edit-impl.h     |  15 +-
     src/editor/edit.c          |  27 +-
     src/editor/editcmd.c       |   9 +
     src/editor/editdraw.c      |   1 -
     src/editor/editmenu.c      |   3 +
     src/editor/editwidget.c    | 581 ++++++++++++++++----------------
     src/editor/editwidget.h    |   3 +
     src/editor/instr_station.c | 668 +++++++++++++++++++++++++++++++++++++
     src/editor/instr_station.h |  86 +++++
     src/history.h              |   1 +
     src/keybind-defaults.c     |   1 +
     18 files changed, 1121 insertions(+), 292 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..cec17801a 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, 0); 
    20462054            if (redo_event) 
    20472055                return EV_MOUSE; 
    20482056            if (!block || tty_got_winch ()) 
  • 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..eedd98952 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 
    9197typedef enum 
    9298{ 
    9399    EDIT_QUICK_SAVE = 0, 
    extern char *edit_window_state_char; 
    146152extern char *edit_window_close_char; 
    147153 
    148154/*** declarations of public functions ************************************************************/ 
     155cb_ret_t edit_dialog_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data); 
     156cb_ret_t edit_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data); 
     157cb_ret_t edit_dialog_command_execute (WDialog * h, long command); 
     158 
     159void edit_mouse_callback (Widget * w, mouse_msg_t msg, mouse_event_t * event); 
    149160 
    150161gboolean edit_add_window (WDialog * h, int y, int x, int lines, int cols, 
    151162                          const vfs_path_t * f, long fline); 
    void edit_move_to_prev_col (WEdit * edit, off_t p); 
    169180long edit_get_col (const WEdit * edit); 
    170181void edit_update_curs_row (WEdit * edit); 
    171182void edit_update_curs_col (WEdit * edit); 
     183gboolean edit_newline_end_check_only (const WEdit * edit); 
    172184void edit_find_bracket (WEdit * edit); 
    173185gboolean edit_reload_line (WEdit * edit, const vfs_path_t * filename_vpath, long line); 
    174186void edit_set_codeset (WEdit * edit); 
    void edit_delete_line (WEdit * edit); 
    180192 
    181193int edit_delete (WEdit * edit, gboolean byte_delete); 
    182194int edit_backspace (WEdit * edit, gboolean byte_delete); 
     195void edit_insert_string (WEdit * edit, char *str_text, gsize len); 
    183196void edit_insert (WEdit * edit, int c); 
    184197void edit_insert_over (WEdit * edit); 
    185198void edit_cursor_move (WEdit * edit, off_t increment); 
    char *edit_get_write_filter (const vfs_path_t * write_name_vpath, 
    193206gboolean edit_save_confirm_cmd (WEdit * edit); 
    194207gboolean edit_save_as_cmd (WEdit * edit); 
    195208WEdit *edit_init (WEdit * edit, int y, int x, int lines, int cols, 
    196                   const vfs_path_t * filename_vpath, long line); 
     209                  const vfs_path_t * filename_vpath, long line, edit_init_flags_t flags); 
    197210gboolean edit_clean (WEdit * edit); 
    198211gboolean edit_ok_to_exit (WEdit * edit); 
    199212file_suitable_rank_t edit_check_file_suitable (const vfs_path_t * fs_path); 
  • 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..7318aebd5 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{ 
  • 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..bbc4e4af8 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); 
     90/* --------------------------------------------------------------------------------------------- */ 
     91/* Creates a CLI special window, windowed (not fullscreen) */ 
     92 
     93static gboolean 
     94edit_add_instr_stat_window (WDialog * h) 
     95{ 
     96    WInstructionStation *ip; 
     97    ip = instr_stat_create (3, 10, 17, 80, "/bin/bash"); 
     98    if (ip == NULL) 
     99        return FALSE; 
     100    group_add_widget_autopos (GROUP (h), ip, WPOS_KEEP_ALL, NULL); 
     101    widget_draw (WIDGET (h)); 
     102    return TRUE; 
     103} 
    91104 
    92105/* --------------------------------------------------------------------------------------------- */ 
    93106 
    edit_get_title (const WDialog * h, size_t len) 
    634647    return g_strconcat (_("Edit: "), modified, file_label, (char *) NULL); 
    635648} 
    636649 
    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  
    764650/* --------------------------------------------------------------------------------------------- */ 
    765651/* 
    766652 * Translate the keycode into either 'command' or 'char_for_insertion'. 
    edit_update_cursor (WEdit * edit, const mouse_event_t * event) 
    1019905} 
    1020906 
    1021907/* --------------------------------------------------------------------------------------------- */ 
    1022 /** Callback for the edit dialog */ 
     908 
     909/** 
     910 * Handle mouse events of editor screen. 
     911 * 
     912 * @param w Widget object (the editor) 
     913 * @param msg mouse event message 
     914 * @param event mouse event data 
     915 */ 
     916static void 
     917edit_dialog_mouse_callback (Widget * w, mouse_msg_t msg, mouse_event_t * event) 
     918{ 
     919    gboolean unhandled = TRUE; 
     920 
     921    if (msg == MSG_MOUSE_DOWN && event->y == 0) 
     922    { 
     923        WGroup *g = GROUP (w); 
     924        WDialog *h = DIALOG (w); 
     925        WMenuBar *b; 
     926 
     927        b = find_menubar (h); 
     928 
     929        if (!widget_get_state (WIDGET (b), WST_FOCUSED)) 
     930        { 
     931            /* menubar */ 
     932 
     933            GList *l; 
     934            GList *top = NULL; 
     935            int x; 
     936 
     937            /* Try find top fullscreen window */ 
     938            for (l = g->widgets; l != NULL; l = g_list_next (l)) 
     939                if (edit_widget_is_editor (CONST_WIDGET (l->data)) 
     940                    && ((WEdit *) l->data)->fullscreen) 
     941                    top = l; 
     942 
     943            /* Handle fullscreen/close buttons in the top line */ 
     944            x = w->cols - 6; 
     945 
     946            if (top != NULL && event->x >= x) 
     947            { 
     948                WEdit *e = (WEdit *) top->data; 
     949 
     950                if (top != g->current) 
     951                { 
     952                    /* Window is not active. Activate it */ 
     953                    widget_select (WIDGET (e)); 
     954                } 
     955 
     956                /* Handle buttons */ 
     957                if (event->x - x <= 2) 
     958                    edit_toggle_fullscreen (e); 
     959                else 
     960                    send_message (h, NULL, MSG_ACTION, CK_Close, NULL); 
     961 
     962                unhandled = FALSE; 
     963            } 
     964 
     965            if (unhandled) 
     966                menubar_activate (b, drop_menus, -1); 
     967        } 
     968    } 
     969 
     970    /* Continue handling of unhandled event in window or menu */ 
     971    event->result.abort = unhandled; 
     972} 
     973 
     974/* --------------------------------------------------------------------------------------------- */ 
    1023975 
    1024976static cb_ret_t 
     977edit_dialog_bg_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data) 
     978{ 
     979    switch (msg) 
     980    { 
     981    case MSG_INIT: 
     982        { 
     983            Widget *wo = WIDGET (w->owner); 
     984 
     985            w->y = wo->y + 1; 
     986            w->x = wo->x; 
     987            w->lines = wo->lines - 2; 
     988            w->cols = wo->cols; 
     989            w->pos_flags |= WPOS_KEEP_ALL; 
     990 
     991            return MSG_HANDLED; 
     992        } 
     993 
     994    default: 
     995        return background_callback (w, sender, msg, parm, data); 
     996    } 
     997} 
     998 
     999/* --------------------------------------------------------------------------------------------- */ 
     1000 
     1001/** 
     1002 * Handle move/resize mouse events. 
     1003 */ 
     1004static void 
     1005edit_mouse_handle_move_resize (Widget * w, mouse_msg_t msg, mouse_event_t * event) 
     1006{ 
     1007    WEdit *edit = (WEdit *) (w); 
     1008    Widget *h = WIDGET (w->owner); 
     1009    int global_x, global_y; 
     1010 
     1011    if (msg == MSG_MOUSE_UP) 
     1012    { 
     1013        /* Exit move/resize mode. */ 
     1014        edit_execute_cmd (edit, CK_Enter, -1); 
     1015        edit_update_screen (edit);      /* Paint the buttonbar over our possibly overlapping frame. */ 
     1016        return; 
     1017    } 
     1018 
     1019    if (msg != MSG_MOUSE_DRAG) 
     1020        /** 
     1021         * We ignore any other events. Specifically, MSG_MOUSE_DOWN. 
     1022         * 
     1023         * When the move/resize is initiated by the menu, we let the user 
     1024         * stop it by clicking with the mouse. Which is why we don't want 
     1025         * a mouse down to affect the window. 
     1026         */ 
     1027        return; 
     1028 
     1029    /* Convert point to global coordinates for easier calculations. */ 
     1030    global_x = event->x + w->x; 
     1031    global_y = event->y + w->y; 
     1032 
     1033    /* Clamp the point to the dialog's client area. */ 
     1034    global_y = CLAMP (global_y, h->y + 1, h->y + h->lines - 2); /* Status line, buttonbar */ 
     1035    global_x = CLAMP (global_x, h->x, h->x + h->cols - 1);      /* Currently a no-op, as the dialog has no left/right margins. */ 
     1036 
     1037    if (edit->drag_state == MCEDIT_DRAG_MOVE) 
     1038    { 
     1039        w->y = global_y; 
     1040        w->x = global_x - edit->drag_state_start; 
     1041    } 
     1042    else if (edit->drag_state == MCEDIT_DRAG_RESIZE) 
     1043    { 
     1044        w->lines = MAX (WINDOW_MIN_LINES, global_y - w->y + 1); 
     1045        w->cols = MAX (WINDOW_MIN_COLS, global_x - w->x + 1); 
     1046    } 
     1047 
     1048    edit->force |= REDRAW_COMPLETELY;   /* Not really needed as WEdit's MSG_DRAW already does this. */ 
     1049 
     1050    /* We draw the whole dialog because dragging/resizing exposes area beneath. */ 
     1051    widget_draw (WIDGET (w->owner)); 
     1052} 
     1053 
     1054/* --------------------------------------------------------------------------------------------- */ 
     1055/*** public functions ****************************************************************************/ 
     1056/* --------------------------------------------------------------------------------------------- */ 
     1057 
     1058cb_ret_t 
     1059edit_dialog_command_execute (WDialog * h, long command) 
     1060{ 
     1061    WGroup *g = GROUP (h); 
     1062    Widget *wh = WIDGET (h); 
     1063    cb_ret_t ret = MSG_HANDLED; 
     1064 
     1065    switch (command) 
     1066    { 
     1067    case CK_EditNew: 
     1068        edit_add_window (h, wh->y + 1, wh->x, wh->lines - 2, wh->cols, NULL, 0); 
     1069        break; 
     1070    case CK_EditFile: 
     1071        edit_load_cmd (h, NULL); 
     1072        break; 
     1073    case CK_OtherFile: 
     1074        { 
     1075            WEdit *e = (WEdit *) g->current->data; 
     1076            gboolean retflag = FALSE; 
     1077 
     1078            if (e != NULL && edit_widget_is_editor (CONST_WIDGET (e))) 
     1079            { 
     1080                retflag = edit_compute_other_file_vfs_path (e); 
     1081                if (retflag) 
     1082                    retflag = edit_switch_to_file (h, e->otherfile_vpath); 
     1083            } 
     1084            if (!retflag) 
     1085                ret = MSG_NOT_HANDLED; 
     1086        } 
     1087        break; 
     1088    case CK_History: 
     1089        edit_load_file_from_history (h); 
     1090        break; 
     1091    case CK_EditSyntaxFile: 
     1092        edit_load_syntax_file (h); 
     1093        break; 
     1094    case CK_EditUserMenu: 
     1095        edit_load_menu_file (h); 
     1096        break; 
     1097    case CK_Close: 
     1098        /* if there are no opened files anymore, close MC editor */ 
     1099        if ((edit_widget_is_editor (CONST_WIDGET (g->current->data)) || 
     1100            edit_widget_is_cli(CONST_WIDGET(g->current->data))) && 
     1101            edit_close_cmd ((WEdit *) g->current->data) && find_editor (h) == NULL) 
     1102            dlg_stop (h); 
     1103        break; 
     1104    case CK_Help: 
     1105        edit_help (); 
     1106        /* edit->force |= REDRAW_COMPLETELY; */ 
     1107        break; 
     1108    case CK_Menu: 
     1109        edit_menu_cmd (h); 
     1110        break; 
     1111    case CK_Quit: 
     1112    case CK_Cancel: 
     1113        /* don't close editor due to SIGINT, but stop move/resize window */ 
     1114        { 
     1115            Widget *w = WIDGET (g->current->data); 
     1116 
     1117            if (edit_widget_is_editor (w) && ((WEdit *) w)->drag_state != MCEDIT_DRAG_NONE) 
     1118                edit_restore_size ((WEdit *) w); 
     1119            else if (command == CK_Quit) 
     1120                dlg_stop (h); 
     1121        } 
     1122        break; 
     1123    case CK_About: 
     1124        edit_about (); 
     1125        break; 
     1126    case CK_SyntaxOnOff: 
     1127        edit_syntax_onoff_cmd (h); 
     1128        break; 
     1129    case CK_ShowTabTws: 
     1130        edit_show_tabs_tws_cmd (h); 
     1131        break; 
     1132    case CK_ShowMargin: 
     1133        edit_show_margin_cmd (h); 
     1134        break; 
     1135    case CK_ShowNumbers: 
     1136        edit_show_numbers_cmd (h); 
     1137        break; 
     1138    case CK_Refresh: 
     1139        edit_refresh_cmd (); 
     1140        break; 
     1141    case CK_InstructionStation: 
     1142        edit_add_instr_stat_window (h); 
     1143        break; 
     1144    case CK_Shell: 
     1145        toggle_subshell (); 
     1146        break; 
     1147    case CK_LearnKeys: 
     1148        learn_keys (); 
     1149        break; 
     1150    case CK_WindowMove: 
     1151    case CK_WindowResize: 
     1152        if (edit_widget_is_editor (CONST_WIDGET (g->current->data))) 
     1153            edit_handle_move_resize ((WEdit *) g->current->data, command); 
     1154        break; 
     1155    case CK_WindowList: 
     1156        edit_window_list (h); 
     1157        break; 
     1158    case CK_WindowCascade: 
     1159        edit_window_cascade (h); 
     1160        break; 
     1161    case CK_WindowTile: 
     1162        edit_window_tile (h); 
     1163        break; 
     1164    case CK_WindowNext: 
     1165        group_select_next_widget (g); 
     1166        break; 
     1167    case CK_WindowPrev: 
     1168        group_select_prev_widget (g); 
     1169        break; 
     1170    case CK_Options: 
     1171        edit_options_dialog (h); 
     1172        break; 
     1173    case CK_OptionsSaveMode: 
     1174        edit_save_mode_cmd (); 
     1175        break; 
     1176    case CK_SaveSetup: 
     1177        save_setup_cmd (); 
     1178        break; 
     1179    default: 
     1180        ret = MSG_NOT_HANDLED; 
     1181        break; 
     1182    } 
     1183 
     1184    return ret; 
     1185} 
     1186 
     1187/* --------------------------------------------------------------------------------------------- */ 
     1188/** Callback for the edit dialog */ 
     1189 
     1190cb_ret_t 
    10251191edit_dialog_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data) 
    10261192{ 
    10271193    WGroup *g = GROUP (w); 
    edit_dialog_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, v 
    11161282 
    11171283/* --------------------------------------------------------------------------------------------- */ 
    11181284 
    1119 /** 
    1120  * Handle mouse events of editor screen. 
    1121  * 
    1122  * @param w Widget object (the editor) 
    1123  * @param msg mouse event message 
    1124  * @param event mouse event data 
    1125  */ 
    1126 static void 
    1127 edit_dialog_mouse_callback (Widget * w, mouse_msg_t msg, mouse_event_t * event) 
    1128 { 
    1129     gboolean unhandled = TRUE; 
    1130  
    1131     if (msg == MSG_MOUSE_DOWN && event->y == 0) 
    1132     { 
    1133         WGroup *g = GROUP (w); 
    1134         WDialog *h = DIALOG (w); 
    1135         WMenuBar *b; 
    1136  
    1137         b = find_menubar (h); 
    1138  
    1139         if (!widget_get_state (WIDGET (b), WST_FOCUSED)) 
    1140         { 
    1141             /* menubar */ 
    1142  
    1143             GList *l; 
    1144             GList *top = NULL; 
    1145             int x; 
    1146  
    1147             /* Try find top fullscreen window */ 
    1148             for (l = g->widgets; l != NULL; l = g_list_next (l)) 
    1149                 if (edit_widget_is_editor (CONST_WIDGET (l->data)) 
    1150                     && ((WEdit *) l->data)->fullscreen) 
    1151                     top = l; 
    1152  
    1153             /* Handle fullscreen/close buttons in the top line */ 
    1154             x = w->cols - 6; 
    1155  
    1156             if (top != NULL && event->x >= x) 
    1157             { 
    1158                 WEdit *e = (WEdit *) top->data; 
    1159  
    1160                 if (top != g->current) 
    1161                 { 
    1162                     /* Window is not active. Activate it */ 
    1163                     widget_select (WIDGET (e)); 
    1164                 } 
    1165  
    1166                 /* Handle buttons */ 
    1167                 if (event->x - x <= 2) 
    1168                     edit_toggle_fullscreen (e); 
    1169                 else 
    1170                     send_message (h, NULL, MSG_ACTION, CK_Close, NULL); 
    1171  
    1172                 unhandled = FALSE; 
    1173             } 
    1174  
    1175             if (unhandled) 
    1176                 menubar_activate (b, drop_menus, -1); 
    1177         } 
    1178     } 
    1179  
    1180     /* Continue handling of unhandled event in window or menu */ 
    1181     event->result.abort = unhandled; 
    1182 } 
    1183  
    1184 /* --------------------------------------------------------------------------------------------- */ 
    1185  
    1186 static cb_ret_t 
    1187 edit_dialog_bg_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data) 
    1188 { 
    1189     switch (msg) 
    1190     { 
    1191     case MSG_INIT: 
    1192         { 
    1193             Widget *wo = WIDGET (w->owner); 
    1194  
    1195             w->y = wo->y + 1; 
    1196             w->x = wo->x; 
    1197             w->lines = wo->lines - 2; 
    1198             w->cols = wo->cols; 
    1199             w->pos_flags |= WPOS_KEEP_ALL; 
    1200  
    1201             return MSG_HANDLED; 
    1202         } 
    1203  
    1204     default: 
    1205         return background_callback (w, sender, msg, parm, data); 
    1206     } 
    1207 } 
    1208  
    1209 /* --------------------------------------------------------------------------------------------- */ 
    1210  
    1211 static cb_ret_t 
     1285cb_ret_t 
    12121286edit_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data) 
    12131287{ 
    12141288    WEdit *e = (WEdit *) w; 
    edit_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *da 
    12781352 
    12791353/* --------------------------------------------------------------------------------------------- */ 
    12801354 
    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  
    13361355/** 
    13371356 * Handle mouse events of editor window 
    13381357 * 
    edit_mouse_handle_move_resize (Widget * w, mouse_msg_t msg, mouse_event_t * even 
    13401359 * @param msg mouse event message 
    13411360 * @param event mouse event data 
    13421361 */ 
    1343 static void 
     1362void 
    13441363edit_mouse_callback (Widget * w, mouse_msg_t msg, mouse_event_t * event) 
    13451364{ 
    13461365    WEdit *edit = (WEdit *) w; 
    edit_mouse_callback (Widget * w, mouse_msg_t msg, mouse_event_t * event) 
    14541473    } 
    14551474} 
    14561475 
    1457 /* --------------------------------------------------------------------------------------------- */ 
    1458 /*** public functions ****************************************************************************/ 
    14591476/* --------------------------------------------------------------------------------------------- */ 
    14601477 
    14611478gboolean 
    edit_add_window (WDialog * h, int y, int x, int lines, int cols, const vfs_path_ 
    16861703    WEdit *edit; 
    16871704    Widget *w; 
    16881705 
    1689     edit = edit_init (NULL, y, x, lines, cols, f, fline); 
     1706    edit = edit_init (NULL, y, x, lines, cols, f, fline, 0); 
    16901707    if (edit == NULL) 
    16911708        return FALSE; 
    16921709 
  • 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..dcbb8e5e3
    - +  
     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 "src/editor/editwidget.h" 
     40 
     41#include "lib/util.h" 
     42#include "lib/tty/key.h" 
     43#include "lib/widget.h" 
     44#include "src/editor/instr_station.h" 
     45 
     46/*** global variables ****************************************************************************/ 
     47 
     48/*** file scope macro definitions ****************************************************************/ 
     49 
     50/*** file scope type declarations ****************************************************************/ 
     51 
     52/*** file scope variables ************************************************************************/ 
     53 
     54static gboolean alt_prog_first_run = TRUE; 
     55 
     56/*** file scope functions ************************************************************************/ 
     57/* --------------------------------------------------------------------------------------------- */ 
     58 
     59static char * 
     60instr_stat_get_instruction_text (WInstructionStation * ip) 
     61{ 
     62    char *ret_str; 
     63    GString *instr; 
     64    off_t bol, eol, begin_instr; 
     65    long size; 
     66    int idx, byte; 
     67 
     68    /* Calculate offset of text after prompt */ 
     69    bol = edit_buffer_get_current_bol (&EDIT (ip)->buffer); 
     70    eol = edit_buffer_get_current_eol (&EDIT (ip)->buffer); 
     71    begin_instr = bol + ip->prompt_span; 
     72 
     73    /* Is there anything entered? */ 
     74    size = ((long) eol) - ((long) begin_instr); 
     75    if (size <= 0) 
     76        return NULL; 
     77 
     78    /* Allocate expected size string and fill it */ 
     79    instr = g_string_sized_new (size + 2); 
     80    for (idx = 0; idx < size; idx++) 
     81    { 
     82        byte = edit_buffer_get_byte (&EDIT (ip)->buffer, begin_instr + idx); 
     83        g_string_append_c (instr, byte); 
     84    } 
     85 
     86    /* Append new line if needed */ 
     87    if (instr->str[instr->len - 1] != '\n') 
     88        g_string_append_c (instr, '\n'); 
     89 
     90    /* Return char buffer */ 
     91    ret_str = instr->str; 
     92    g_string_free (instr, FALSE); 
     93    return ret_str; 
     94} 
     95 
     96/* --------------------------------------------------------------------------------------------- */ 
     97 
     98static cb_ret_t 
     99instr_stat_run_activity (WInstructionStation * ip, long activity) 
     100{ 
     101    cb_ret_t ret = MSG_NOT_HANDLED; 
     102    switch (activity) 
     103    { 
     104    case CK_BackSpace: 
     105        if (ip->cur_col <= ip->prompt_span) 
     106        { 
     107            ret = MSG_HANDLED; 
     108        } 
     109        else 
     110        { 
     111            EDIT (ip)->force |= REDRAW_PAGE; 
     112            edit_update_screen (EDIT (ip)); 
     113        } 
     114        break; 
     115    case CK_Enter: 
     116    case CK_Return: 
     117        if (EDIT (ip)->buffer.curs_line == ip->cur_line) 
     118        { 
     119            ip->instruction = instr_stat_get_instruction_text (ip); 
     120            /* Should dispatch to write callback which will send instruction */ 
     121            g_main_context_iteration (NULL, 0); 
     122            EDIT (ip)->force |= REDRAW_PAGE; 
     123            edit_update_screen (EDIT (ip)); 
     124        } 
     125        ret = MSG_HANDLED; 
     126        break; 
     127    default: 
     128        break; 
     129    } 
     130    return ret; 
     131} 
     132 
     133/* --------------------------------------------------------------------------------------------- */ 
     134 
     135static gboolean 
     136instr_release (WInstructionStation * ip, gboolean free_all) 
     137{ 
     138    if (ip->finalizing) 
     139        return FALSE; 
     140    ip->finalizing = TRUE; 
     141 
     142    /* Close process watcher first, suppressing *program_ended_cb() callback */ 
     143    if (ip->proc_src_id != 0) 
     144    { 
     145        GSource *src; 
     146        src = g_main_context_find_source_by_id (NULL, ip->proc_src_id); 
     147        if (src != NULL) 
     148            g_source_destroy (src); 
     149        ip->proc_src_id = 0; 
     150    } 
     151 
     152    /* Release the pipes, channels, etc. */ 
     153    for (int i = 0; i <= STDERR; i++) 
     154    { 
     155        if (ip->io[i].src_id != 0) 
     156        { 
     157            GSource *src; 
     158            src = g_main_context_find_source_by_id (NULL, ip->io[i].src_id); 
     159            if (src != NULL) 
     160                g_source_destroy (src); 
     161            ip->io[i].src_id = 0; 
     162        } 
     163        if (ip->io[i].ch != NULL) 
     164        { 
     165            g_io_channel_unref (ip->io[i].ch); 
     166            ip->io[i].ch = NULL; 
     167        } 
     168        if (ip->io[i].fd >= 0) 
     169        { 
     170            close (ip->io[i].fd); 
     171            ip->io[i].fd = -1; 
     172        } 
     173    } 
     174 
     175    if (ip->prompt_timeout_id != 0) 
     176    { 
     177        g_source_remove(ip->prompt_timeout_id); 
     178        ip->prompt_timeout_id = 0; 
     179    } 
     180 
     181    if (free_all) 
     182    { 
     183        MC_PTR_FREE (ip->program); 
     184        /* Release prompt */ 
     185        MC_PTR_FREE (ip->prompt); 
     186        /* Release error prompt */ 
     187        MC_PTR_FREE (ip->eprompt); 
     188 
     189        /* Free main object */ 
     190        g_free (ip); 
     191    } 
     192    return TRUE; 
     193} 
     194 
     195/* --------------------------------------------------------------------------------------------- */ 
     196 
     197static void 
     198helper_program_ended_cb (GPid pid, gint status, gpointer user_data) 
     199{ 
     200    WInstructionStation *ip = INSTR_STATION (user_data); 
     201    char *msg; 
     202    mc_log ("Program %d ended with status: %d (%s)", pid, status, 
     203            g_spawn_check_exit_status (status, NULL) ? "normally" : "abnormally"); 
     204 
     205    instr_release (ip, FALSE); 
     206    g_spawn_close_pid (pid); 
     207    msg = _("\nProgram closed.\n"); 
     208    edit_insert_string (EDIT (ip), msg, strlen (msg)); 
     209    repaint_screen(); 
     210} 
     211 
     212/* --------------------------------------------------------------------------------------------- */ 
     213 
     214static void 
     215instr_stat_show_prompt (WInstructionStation * ip, gboolean is_ok) 
     216{ 
     217    char *sel_prompt = is_ok ? ip->prompt : ip->eprompt; 
     218    int sel_prompt_span = is_ok ? ip->prompt_span : ip->eprompt_span; 
     219 
     220    /* Print prompt */ 
     221    if (!edit_newline_end_check_only (EDIT (ip))) 
     222    { 
     223        edit_insert (EDIT (ip), '\n'); 
     224        ip->cur_line += 1; 
     225    } 
     226    edit_insert_string (EDIT (ip), sel_prompt, sel_prompt_span); 
     227 
     228    /* Raise an indicator flag */ 
     229    ip->prompt_shown = TRUE; 
     230 
     231    /* Set cursor position to reflect state */ 
     232    ip->cur_col = sel_prompt_span; 
     233} 
     234 
     235/* --------------------------------------------------------------------------------------------- */ 
     236 
     237static void 
     238instr_stat_clear_prompt (WInstructionStation * ip) 
     239{ 
     240    off_t bol, eol, idx; 
     241    eol = edit_buffer_get_current_eol(&EDIT(ip)->buffer); 
     242    bol = edit_buffer_get_current_bol(&EDIT(ip)->buffer); 
     243    for (idx=0; idx < eol - bol; idx ++) 
     244        edit_backspace(EDIT(ip), TRUE); 
     245    ip->prompt_shown = FALSE; 
     246} 
     247 
     248/* --------------------------------------------------------------------------------------------- */ 
     249 
     250static gboolean 
     251helper_timeout_cb (gpointer data) 
     252{ 
     253    WInstructionStation *ip = INSTR_STATION (data); 
     254    guint64 cur_time; 
     255    cur_time = g_get_real_time (); 
     256    /* Draw prompt after 0.5 seconds of no output from program */ 
     257    if (cur_time - ip->read_time > 500000 && !ip->prompt_shown) 
     258    { 
     259        instr_stat_show_prompt (ip, TRUE); 
     260        repaint_screen(); 
     261    } 
     262    return TRUE; 
     263} 
     264 
     265/* --------------------------------------------------------------------------------------------- */ 
     266 
     267static gboolean 
     268helper_configure_src_and_ch (GIOChannel ** ch, gint fd, guint * src_id, gboolean write, 
     269                             GIOFunc cb_fun, gpointer data, const char *name) 
     270{ 
     271    /* Initialize the output variables */ 
     272    *ch = NULL; 
     273    *src_id = 0; 
     274 
     275    *ch = g_io_channel_unix_new (fd); 
     276 
     277    /* Channel created? */ 
     278    if (*ch == NULL) 
     279        goto cleanup_and_err; 
     280 
     281    /* Automatic shutdown of channel */ 
     282    g_io_channel_set_close_on_unref (*ch, TRUE); 
     283 
     284    /* Trim down buffering on this channel */ 
     285    g_io_channel_set_buffer_size (*ch, 5); 
     286 
     287    /* Attempt to set non-blocking flag on channel */ 
     288    if (g_io_channel_set_flags (*ch, G_IO_FLAG_NONBLOCK, NULL) != G_IO_STATUS_NORMAL) 
     289        mc_log ("Problem setting a channel to non-blocking state (channel flags: 0x%x)", 
     290                g_io_channel_get_flags (*ch)); 
     291 
     292    /* Create GSource */ 
     293    *src_id = g_io_add_watch (*ch, (write ? G_IO_OUT : G_IO_IN) | G_IO_HUP, cb_fun, data); 
     294 
     295    /* Source created OK? */ 
     296    if (*src_id == 0) 
     297        goto cleanup_and_err; 
     298 
     299    /* Configure the sources */ 
     300    g_source_set_name_by_id (*src_id, name); 
     301 
     302    /* Return true */ 
     303    return TRUE; 
     304 
     305  cleanup_and_err: 
     306    if (*src_id != 0) 
     307    { 
     308        GSource *src; 
     309        src = g_main_context_find_source_by_id (NULL, *src_id); 
     310        if (src != NULL) 
     311            /* Triggers also unref of wrapped channel */ 
     312            g_source_destroy (src); 
     313    } 
     314    if (*ch != NULL) 
     315        g_io_channel_unref (*ch); 
     316    *ch = NULL; 
     317    *src_id = 0; 
     318 
     319    return FALSE; 
     320} 
     321 
     322/* --------------------------------------------------------------------------------------------- */ 
     323 
     324static gboolean 
     325helper_read_all (GIOChannel * channel, char *buf, gsize * out_bytes_read_in_len) 
     326{ 
     327    gsize len = *out_bytes_read_in_len; 
     328    gsize count, retries = 10; 
     329    GIOStatus stat; 
     330 
     331    *out_bytes_read_in_len = 0; 
     332 
     333    while (*out_bytes_read_in_len < len) 
     334    { 
     335        count = 0; 
     336        stat = 
     337            g_io_channel_read_chars (channel, buf + *out_bytes_read_in_len, 
     338                                     len - *out_bytes_read_in_len, &count, NULL); 
     339        *out_bytes_read_in_len += count; 
     340 
     341        /* Should end the reading ? */ 
     342        if (stat == G_IO_STATUS_ERROR || (stat == G_IO_STATUS_NORMAL && count == 0) || 
     343            stat == G_IO_STATUS_EOF) 
     344            return (stat != G_IO_STATUS_ERROR); 
     345        else if (stat == G_IO_STATUS_AGAIN && retries-- == 0) 
     346            /* Exhausted retries - there must be no data to read - return OK */ 
     347            return TRUE; 
     348    } 
     349 
     350    return TRUE; 
     351} 
     352 
     353/* --------------------------------------------------------------------------------------------- */ 
     354 
     355static gboolean 
     356helper_write_all (GIOChannel * channel, char *buf, gsize * out_bytes_written_in_len) 
     357{ 
     358    gsize len = *out_bytes_written_in_len; 
     359    gsize count; 
     360    GIOError err; 
     361 
     362    *out_bytes_written_in_len = 0; 
     363 
     364    while (*out_bytes_written_in_len < len) 
     365    { 
     366        count = 0; 
     367        err = 
     368            g_io_channel_write (channel, buf + *out_bytes_written_in_len, 
     369                                len - *out_bytes_written_in_len, &count); 
     370        *out_bytes_written_in_len += count; 
     371 
     372        if (err && err != G_IO_ERROR_AGAIN) 
     373            return FALSE; 
     374    } 
     375 
     376    return TRUE; 
     377} 
     378 
     379/* --------------------------------------------------------------------------------------------- */ 
     380 
     381static gboolean 
     382stdout_read_callback (GIOChannel * source, GIOCondition condition, gpointer data) 
     383{ 
     384    WInstructionStation *ip = INSTR_STATION (data); 
     385    char buf[2048]; 
     386    gsize count = 2047; 
     387    gboolean r_ret; 
     388 
     389    if ((condition & G_IO_IN) == 0) 
     390        return TRUE; 
     391 
     392    r_ret = helper_read_all (source, buf, &count); 
     393    buf[count] = '\0'; 
     394    if (count == 0) 
     395    { 
     396        mc_log ("Error while reading program's output: %s", g_strerror (errno)); 
     397    } 
     398    else 
     399    { 
     400        ip->read_time = g_get_real_time (); 
     401        buf[count] = '\0'; 
     402        if (count > 0) 
     403        { 
     404            gsize idx = 0, nl_count = 0; 
     405            char *p = buf; 
     406            while (idx++ < count) 
     407                if (*p++ == '\n') 
     408                    nl_count++; 
     409 
     410            /* Move to next line after prompt */ 
     411            if (ip->first_response) 
     412            { 
     413                edit_insert (EDIT (ip), '\n'); 
     414                nl_count++; 
     415                ip->first_response = FALSE; 
     416                ip->prompt_shown = FALSE; 
     417            } 
     418            else if(ip->prompt_shown) 
     419                instr_stat_clear_prompt(ip); 
     420 
     421            /* Print whole buffer into file's window  */ 
     422            edit_insert_string (EDIT (ip), buf, count); 
     423 
     424            /* Increase the row pointer */ 
     425            ip->cur_line += nl_count; 
     426        } 
     427        else if (!ip->prompt_shown) 
     428            instr_stat_show_prompt (ip, r_ret); 
     429 
     430        repaint_screen (); 
     431    } 
     432 
     433    return TRUE; 
     434} 
     435 
     436/* --------------------------------------------------------------------------------------------- */ 
     437 
     438static gboolean 
     439stdin_write_callback (GIOChannel * source, GIOCondition condition, gpointer data) 
     440{ 
     441    WInstructionStation *ip = INSTR_STATION (data); 
     442    gsize count; 
     443    gboolean ret = TRUE; 
     444 
     445    /* Any instruction to send? */ 
     446    if (ip->instruction == NULL) 
     447        return ret; 
     448 
     449    if ((condition & G_IO_OUT) == 0) 
     450        return TRUE; 
     451 
     452    errno = 0; 
     453    count = strlen (ip->instruction); 
     454    ret = helper_write_all (ip->io[STDIN].ch, ip->instruction, &count); 
     455    if (!ret) 
     456    { 
     457        mc_log ("Error during sending instruction to program: %s", g_strerror (errno)); 
     458    } 
     459    else 
     460    { 
     461        ip->first_response = TRUE; 
     462        MC_PTR_FREE (ip->instruction); 
     463        if (ip->prompt_timeout_id == 0) 
     464            ip->prompt_timeout_id = g_timeout_add (200, helper_timeout_cb, ip); 
     465    } 
     466 
     467    g_io_channel_flush (ip->io[STDIN].ch, NULL); 
     468 
     469    return ret; 
     470} 
     471 
     472/* --------------------------------------------------------------------------------------------- */ 
     473 
     474/* --------------------------------------------------------------------------------------------- */ 
     475/*** public functions ****************************************************************************/ 
     476/* --------------------------------------------------------------------------------------------- */ 
     477 
     478WInstructionStation * 
     479instr_stat_create (int y, int x, int lines, int cols, const char *program) 
     480{ 
     481    WInstructionStation *ip; 
     482    WEdit *iret; 
     483    gboolean proc_ret = FALSE; 
     484    char *program_cline[2]; 
     485    int rounds = 0; 
     486 
     487    ip = g_new0 (WInstructionStation, 1); 
     488    if (ip == NULL) 
     489        return NULL; 
     490 
     491    iret = edit_init (EDIT (ip), y, x, lines, cols, NULL, 0, EDIT_DO_INIT_BASE_CLASS); 
     492    if (iret == NULL) 
     493        goto cleanup_and_err; 
     494 
     495    WIDGET (ip)->callback = instr_stat_callback; 
     496    WIDGET (ip)->mouse_callback = edit_mouse_callback; 
     497    EDIT (ip)->fullscreen = 0; 
     498 
     499    ip->io[0].name = g_strdup ("Std-In (WInstructionStation)"); 
     500    ip->io[1].name = g_strdup ("Std-Out (WInstructionStation)"); 
     501    ip->io[2].name = g_strdup ("Std-Error (WInstructionStation)"); 
     502    ip->io[0].fd = ip->io[1].fd = ip->io[2].fd = -1; 
     503 
     504    ip->prompt = g_strdup ("[guest@localhost]# "); 
     505    ip->prompt_span = strlen (ip->prompt); 
     506    ip->eprompt = g_strdup ("[guest@localhost]! "); 
     507    ip->eprompt_span = strlen (ip->eprompt); 
     508 
     509    /* Set initial program (most probably a shell, like Bash) */ 
     510    if (program != NULL) 
     511        ip->program = g_strdup (program); 
     512    else 
     513        /* Fallback instruction if no program given */ 
     514        ip->program = g_strdup ("bash"); 
     515 
     516    while (++rounds <= 2 && !proc_ret) 
     517    { 
     518        if (ip->error != NULL) 
     519        { 
     520            g_error_free (ip->error); 
     521            ip->error = NULL; 
     522        } 
     523        program_cline[0] = ip->program; 
     524        program_cline[1] = NULL; 
     525        proc_ret = g_spawn_async_with_pipes (NULL, program_cline, 
     526                                             NULL, G_SPAWN_SEARCH_PATH | 
     527                                             G_SPAWN_DO_NOT_REAP_CHILD | 
     528                                             G_SPAWN_LEAVE_DESCRIPTORS_OPEN, 
     529                                             NULL, NULL, &ip->proc_id, &ip->io[STDIN].fd, 
     530                                             &ip->io[STDOUT].fd, &ip->io[STDERR].fd, &ip->error); 
     531 
     532        if (!proc_ret) 
     533        { 
     534            char *new_program, *msg_text; 
     535            mc_log ("Creating the background process [program:%s] failed: %s", 
     536                    program, ip->error ? ip->error->message : "<no error message>"); 
     537 
     538            /* Create a transatable, parametrized message */ 
     539            msg_text = g_strdup_printf ("%s%s:", _("The specified program has failed to run.\n" 
     540                                                   "Enter a new full path or a program name"), 
     541                                        (rounds >= 2 ? _(" (last try)") : "")); 
     542 
     543            /* Display message asking for a new program */ 
     544            new_program = input_expand_dialog (_("Provide an alternate program to run"), 
     545                                               msg_text, "instruction-station", 
     546                                               alt_prog_first_run ? ip->program : INPUT_LAST_TEXT, 
     547                                               INPUT_COMPLETE_COMMANDS | INPUT_COMPLETE_FILENAMES | 
     548                                               INPUT_COMPLETE_VARIABLES); 
     549            g_free(msg_text); 
     550            alt_prog_first_run = FALSE; 
     551 
     552            /* Obtained a new program? */ 
     553            if (new_program != NULL) 
     554            { 
     555                g_free (ip->program); 
     556                ip->program = new_program; 
     557            } 
     558        } 
     559    } 
     560 
     561    /* If no program to run has been found, then exit. */ 
     562    if (!proc_ret) 
     563    { 
     564        message (D_ERROR, _("InstructionStation startup failed"), 
     565                 _("Failed to initialize the CLI window.")); 
     566        goto cleanup_and_err; 
     567    } 
     568 
     569    /* Display initial prompt */ 
     570    instr_stat_show_prompt (ip, TRUE); 
     571 
     572    for (int i = 0; i <= STDERR; i++) 
     573    { 
     574        if (helper_configure_src_and_ch (&ip->io[i].ch, ip->io[i].fd, &ip->io[i].src_id, 
     575                                         (i == STDIN ? TRUE : FALSE), 
     576                                         (i == STDIN ? stdin_write_callback : stdout_read_callback), 
     577                                         ip, ip->io[i].name) == FALSE) 
     578            goto cleanup_and_err; 
     579    } 
     580    /* Set a process watcher and restarter for the program */ 
     581    ip->proc_src_id = g_child_watch_add (ip->proc_id, helper_program_ended_cb, ip); 
     582 
     583    return ip; 
     584 
     585  cleanup_and_err: 
     586 
     587    instr_release (ip, TRUE); 
     588    return NULL; 
     589} 
     590 
     591/* --------------------------------------------------------------------------------------------- */ 
     592/* Check if widget is an WInstructionStation class. */ 
     593 
     594gboolean 
     595edit_widget_is_cli (const Widget * w) 
     596{ 
     597    return (w != NULL && w->callback == instr_stat_callback); 
     598} 
     599 
     600/* --------------------------------------------------------------------------------------------- */ 
     601 
     602cb_ret_t 
     603instr_stat_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data) 
     604{ 
     605    cb_ret_t ret = MSG_NOT_HANDLED; 
     606    WInstructionStation *ip = INSTR_STATION (w); 
     607    WEdit *e = EDIT (w); 
     608 
     609    switch (msg) 
     610    { 
     611 
     612    case MSG_KEY: 
     613        { 
     614            gboolean ext_mode; 
     615            long activity; 
     616 
     617            /* keep and then extmod flag */ 
     618            ext_mode = w->ext_mode; 
     619            activity = widget_lookup_key (w, parm); 
     620            w->ext_mode = ext_mode; 
     621 
     622            if (activity == CK_IgnoreKey) 
     623                w->ext_mode = FALSE; 
     624            else 
     625            { 
     626                ret = edit_dialog_command_execute (DIALOG(w->owner), activity); 
     627                if (ret == MSG_NOT_HANDLED) 
     628                    ret = instr_stat_run_activity (ip, activity); 
     629                /* if activity was not handled, keep the extended mode 
     630                   for the further key processing */ 
     631                if (ret == MSG_HANDLED) 
     632                    w->ext_mode = FALSE; 
     633            } 
     634 
     635            /* 
     636             * Due to the "end of bracket" escape the editor sees input with is_idle() == false 
     637             * (expects more characters) and hence doesn't yet refresh the screen, but then 
     638             * no further characters arrive (there's only an "end of bracket" which is swallowed 
     639             * by tty_get_event()), so you end up with a screen that's not refreshed after pasting. 
     640             * So let's trigger an IDLE signal. 
     641             */ 
     642            if (!is_idle ()) 
     643                widget_idle (w, TRUE); 
     644        } 
     645        break; 
     646    case MSG_ACTION: 
     647        ret = instr_stat_run_activity (ip, parm); 
     648        break; 
     649    case MSG_IDLE: 
     650        edit_update_screen (e); 
     651        break; 
     652    case MSG_DESTROY: 
     653        instr_release(ip, FALSE); 
     654        break; 
     655    default: 
     656        break; 
     657    } 
     658 
     659    /* Route to editor window */ 
     660    if (ret == MSG_NOT_HANDLED) 
     661    { 
     662        ret = edit_callback (w, sender, msg, parm, data); 
     663        ip->cur_col = EDIT (w)->curs_col; 
     664    } 
     665    return ret; 
     666} 
     667 
     668/* --------------------------------------------------------------------------------------------- */ 
  • 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..8b5d31cb0
    - +  
     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 
     11typedef enum instr_stat_io_type 
     12{ 
     13    STDIN = 0, 
     14    STDOUT, 
     15    STDERR 
     16} instr_stat_io_type_t; 
     17 
     18/*** structures declarations (and typedefs of structures)*****************************************/ 
     19 
     20/* Forward declare the subclassed editor widget */ 
     21struct WEdit; 
     22typedef struct WEdit WEdit; 
     23 
     24typedef struct 
     25{ 
     26    WEdit base; 
     27 
     28    int cur_line; 
     29    int cur_col; 
     30    int prompt_span; 
     31    int eprompt_span; 
     32 
     33    /*  Shell (or other program) that is being run in station */ 
     34    char *program; 
     35    /* The prompt (a simulation) */ 
     36    char *prompt; 
     37    char *eprompt; 
     38    /* The current instruction to send */ 
     39    char *instruction; 
     40 
     41    /* An indicator of waiting for an initial response from program */ 
     42    gboolean first_response; 
     43    /* An indicator of the prompt being printed after running an instruction */ 
     44    gboolean prompt_shown; 
     45    /* ID of prompt drawing timeout */ 
     46    guint prompt_timeout_id; 
     47 
     48    /* Process ID of the program */ 
     49    GPid proc_id; 
     50 
     51    /* A mark whether process ID is ready to be released */ 
     52    gboolean program_working; 
     53 
     54    /* Pipes to and from the program */ 
     55    struct 
     56    { 
     57        gint fd; 
     58        GIOChannel *ch; 
     59        guint src_id; 
     60        char *name; 
     61    } io[3]; 
     62 
     63    /* Time of last read of data from program */ 
     64    guint64 read_time; 
     65 
     66    /* GSource id (tag) of program process watcher */ 
     67    guint proc_src_id; 
     68 
     69    /* A GError placeholder */ 
     70    GError *error; 
     71 
     72    gboolean io_complete; 
     73    gboolean finalizing; 
     74} WInstructionStation; 
     75 
     76/*** global variables defined in .c file *********************************************************/ 
     77 
     78/*** declarations of public functions ************************************************************/ 
     79 
     80WInstructionStation *instr_stat_create (int y, int x, int lines, int cols, const char *program); 
     81gboolean edit_widget_is_cli (const Widget * w); 
     82cb_ret_t instr_stat_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data); 
     83 
     84/*** inline functions ****************************************************************************/ 
     85 
     86#endif /* MC__INSTR_STATION_H */ 
  • src/history.h

    diff --git a/src/history.h b/src/history.h
    index 7a8d73fa2..738d75c66 100644
    a b  
    1717#define MC_HISTORY_EDIT_SORT          "mc.edit.sort" 
    1818#define MC_HISTORY_EDIT_PASTE_EXTCMD  "mc.edit.paste-extcmd" 
    1919#define MC_HISTORY_EDIT_REPEAT        "mc.edit.repeat-action" 
     20#define MC_HISTORY_EDIT_INSTR_STATION_PATH "mc.edit.instr-panel-path" 
    2021 
    2122#define MC_HISTORY_FM_VIEW_FILE       "mc.fm.view-file" 
    2223#define MC_HISTORY_FM_MKDIR           "mc.fm.mkdir" 
  • 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"},