Ticket #4196: InstructionStation.patch

File InstructionStation.patch, 42.1 KB (added by psprint, 4 years ago)
  • lib/global.h

    From 221429752ef6bc253101aa52b4f6e66e8a864e48 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?Instructions=20Station=20=E2=80=93=20a=20CLI=20window?=
     =?UTF-8?q?=20for=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/Makefile.am     |   1 +
     lib/widget/instr_station.c | 663 +++++++++++++++++++++++++++++++++++++
     lib/widget/instr_station.h |  81 +++++
     misc/mc.default.keymap     |   1 +
     misc/mc.emacs.keymap       |   1 +
     src/editor/edit-impl.h     |  12 +-
     src/editor/edit.c          |  27 +-
     src/editor/editcmd.c       |   9 +
     src/editor/editdraw.c      |   1 -
     src/editor/editmenu.c      |   3 +
     src/editor/editwidget.c    | 139 ++++----
     src/editor/editwidget.h    |   3 +
     src/history.h              |   1 +
     src/keybind-defaults.c     |   1 +
     18 files changed, 888 insertions(+), 70 deletions(-)
     create mode 100644 lib/widget/instr_station.c
     create mode 100644 lib/widget/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 ()) 
  • lib/widget/Makefile.am

    diff --git a/lib/widget/Makefile.am b/lib/widget/Makefile.am
    index 2d8434391..2500af671 100644
    a b libmcwidget_la_SOURCES = \ 
    88        check.c check.h \ 
    99        dialog.c dialog.h \ 
    1010        dialog-switch.c dialog-switch.h \ 
     11        instr_station.c instr_station.h \ 
    1112        frame.c frame.h \ 
    1213        gauge.c gauge.h \ 
    1314        group.c group.h \ 
  • new file lib/widget/instr_station.c

    diff --git a/lib/widget/instr_station.c b/lib/widget/instr_station.c
    new file mode 100644
    index 000000000..1dfb453dc
    - +  
     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 <glib/gstdio.h>        /* g_close() */ 
     40#include <fcntl.h> 
     41#include <sys/types.h> 
     42#include <sys/stat.h> 
     43 
     44#include "lib/tty/key.h" 
     45#include "lib/widget.h" 
     46#include "lib/widget/instr_station.h" 
     47#include "src/history.h" 
     48 
     49/*** global variables ****************************************************************************/ 
     50 
     51/*** file scope macro definitions ****************************************************************/ 
     52 
     53/*** file scope type declarations ****************************************************************/ 
     54 
     55/*** file scope variables ************************************************************************/ 
     56 
     57static gboolean alt_prog_first_run = TRUE; 
     58 
     59/*** file scope functions ************************************************************************/ 
     60/* --------------------------------------------------------------------------------------------- */ 
     61 
     62static char * 
     63instr_stat_get_instruction_text (WInstructionStation * ip) 
     64{ 
     65    char *ret_str; 
     66    GString *instr; 
     67    off_t bol, eol, begin_instr; 
     68    long size; 
     69    int idx, byte; 
     70 
     71    /* Calculate offset of text after prompt */ 
     72    bol = edit_buffer_get_current_bol (&EDIT (ip)->buffer); 
     73    eol = edit_buffer_get_current_eol (&EDIT (ip)->buffer); 
     74    begin_instr = bol + ip->prompt_span; 
     75 
     76    /* Is there anything entered? */ 
     77    size = ((long) eol) - ((long) begin_instr); 
     78    if (size <= 0) 
     79        return NULL; 
     80 
     81    /* Allocate expected size string and fill it */ 
     82    instr = g_string_sized_new (size + 2); 
     83    for (idx = 0; idx < size; idx++) 
     84    { 
     85        byte = edit_buffer_get_byte (&EDIT (ip)->buffer, begin_instr + idx); 
     86        g_string_append_c (instr, byte); 
     87    } 
     88 
     89    /* Append new line if needed */ 
     90    if (instr->str[instr->len - 1] != '\n') 
     91        g_string_append_c (instr, '\n'); 
     92 
     93    /* Return char buffer */ 
     94    ret_str = instr->str; 
     95    g_string_free (instr, FALSE); 
     96    return ret_str; 
     97} 
     98 
     99/* --------------------------------------------------------------------------------------------- */ 
     100 
     101static cb_ret_t 
     102instr_stat_run_activity (WInstructionStation * ip, long activity) 
     103{ 
     104    cb_ret_t ret = MSG_NOT_HANDLED; 
     105    switch (activity) 
     106    { 
     107    case CK_BackSpace: 
     108        if (ip->cur_col <= ip->prompt_span) 
     109        { 
     110            ret = MSG_HANDLED; 
     111        } 
     112        else 
     113        { 
     114            EDIT (ip)->force |= REDRAW_PAGE; 
     115            edit_update_screen (EDIT (ip)); 
     116        } 
     117        break; 
     118    case CK_Enter: 
     119    case CK_Return: 
     120        if (EDIT (ip)->buffer.curs_line == ip->cur_line) 
     121        { 
     122            ip->instruction = instr_stat_get_instruction_text (ip); 
     123            /* Should dispatch to write callback which will send instruction */ 
     124            g_main_context_iteration (NULL, 0); 
     125            EDIT (ip)->force |= REDRAW_PAGE; 
     126            edit_update_screen (EDIT (ip)); 
     127        } 
     128        ret = MSG_HANDLED; 
     129        break; 
     130    default: 
     131        break; 
     132    } 
     133    return ret; 
     134} 
     135 
     136/* --------------------------------------------------------------------------------------------- */ 
     137 
     138static gboolean 
     139instr_release (WInstructionStation * ip, gboolean free_all) 
     140{ 
     141    if (ip->finalizing) 
     142        return FALSE; 
     143    ip->finalizing = TRUE; 
     144 
     145    /* Close process watcher first, suppressing *program_ended_cb() callback */ 
     146    if (ip->proc_src_id != 0) 
     147    { 
     148        GSource *src; 
     149        src = g_main_context_find_source_by_id (NULL, ip->proc_src_id); 
     150        if (src != NULL) 
     151            g_source_destroy (src); 
     152        ip->proc_src_id = 0; 
     153    } 
     154 
     155    /* Release the pipes, channels, etc. */ 
     156    for (int i = 0; i <= STDERR; i++) 
     157    { 
     158        if (ip->io[i].src_id != 0) 
     159        { 
     160            GSource *src; 
     161            src = g_main_context_find_source_by_id (NULL, ip->io[i].src_id); 
     162            if (src != NULL) 
     163                g_source_destroy (src); 
     164            ip->io[i].src_id = 0; 
     165        } 
     166        if (ip->io[i].ch != NULL) 
     167        { 
     168            g_io_channel_unref (ip->io[i].ch); 
     169            ip->io[i].ch = NULL; 
     170        } 
     171        if (ip->io[i].fd >= 0) 
     172        { 
     173            close (ip->io[i].fd); 
     174            ip->io[i].fd = -1; 
     175        } 
     176    } 
     177 
     178    if (free_all) 
     179    { 
     180        if (ip->program != NULL) 
     181        { 
     182            g_free (ip->program); 
     183            ip->program = NULL; 
     184        } 
     185 
     186        /* Release prompt */ 
     187        g_free (ip->prompt); 
     188        /* Release error prompt */ 
     189        g_free (ip->eprompt); 
     190 
     191        /* Free main object */ 
     192        g_free (ip); 
     193    } 
     194    return TRUE; 
     195} 
     196 
     197/* --------------------------------------------------------------------------------------------- */ 
     198 
     199static void 
     200helper_program_ended_cb (GPid pid, gint status, gpointer user_data) 
     201{ 
     202    WInstructionStation *ip = INSTR_STATION (user_data); 
     203    char *msg; 
     204    mc_log ("Program %d ended with status: %d (%s)", pid, status, 
     205            g_spawn_check_exit_status (status, NULL) ? "normally" : "abnormally"); 
     206 
     207    instr_release (ip, FALSE); 
     208    g_spawn_close_pid (pid); 
     209    msg = _("\nProgram closed."); 
     210    edit_insert_string (EDIT (ip), msg, strlen (msg)); 
     211} 
     212 
     213/* --------------------------------------------------------------------------------------------- */ 
     214 
     215static gboolean 
     216instr_stat_update_cursor (WInstructionStation * ip, int y, int x) 
     217{ 
     218    WEdit *edit = EDIT (ip); 
     219 
     220    x += option_line_state_width; 
     221 
     222    edit->prev_col = x - edit->start_col - option_line_state_width; 
     223 
     224    if (y > edit->curs_row) 
     225        edit_move_down (edit, y - edit->curs_row, FALSE); 
     226    else if (y < edit->curs_row) 
     227        edit_move_up (edit, edit->curs_row - y, FALSE); 
     228    else 
     229        edit_move_to_prev_col (edit, edit_buffer_get_current_bol (&edit->buffer)); 
     230 
     231    return TRUE; 
     232} 
     233 
     234/* --------------------------------------------------------------------------------------------- */ 
     235 
     236static void 
     237instr_stat_show_prompt (WInstructionStation * ip, gboolean is_ok) 
     238{ 
     239    char *sel_prompt = is_ok ? ip->prompt : ip->eprompt; 
     240    int sel_prompt_span = is_ok ? ip->prompt_span : ip->eprompt_span; 
     241 
     242    /* Print prompt */ 
     243    if (!edit_newline_end_check_only (EDIT (ip))) 
     244    { 
     245        edit_insert (EDIT (ip), '\n'); 
     246        ip->cur_line += 1; 
     247    } 
     248    edit_insert_string (EDIT (ip), sel_prompt, sel_prompt_span); 
     249 
     250    /* Raise an indicator flag */ 
     251    ip->prompt_shown = TRUE; 
     252 
     253    /* Set cursor position to reflect state */ 
     254    ip->cur_col = sel_prompt_span; 
     255} 
     256 
     257/* --------------------------------------------------------------------------------------------- */ 
     258 
     259static gboolean 
     260helper_timeout_cb (gpointer data) 
     261{ 
     262    WInstructionStation *ip = INSTR_STATION (data); 
     263    guint64 cur_time; 
     264    cur_time = g_get_real_time (); 
     265    if (cur_time - ip->read_time > 500000) 
     266    { 
     267        instr_stat_show_prompt (ip, TRUE); 
     268        repaint_screen (); 
     269        return FALSE; 
     270    } 
     271    return TRUE; 
     272} 
     273 
     274/* --------------------------------------------------------------------------------------------- */ 
     275 
     276static gboolean 
     277helper_configure_src_and_ch (GIOChannel ** ch, gint fd, guint * src_id, gboolean write, 
     278                             GIOFunc cb_fun, gpointer data, const char *name) 
     279{ 
     280    /* Initialize the output variables */ 
     281    *ch = NULL; 
     282    *src_id = 0; 
     283 
     284    *ch = g_io_channel_unix_new (fd); 
     285 
     286    /* Channel created? */ 
     287    if (*ch == NULL) 
     288        goto cleanup_and_err; 
     289 
     290    /* Automatic shutdown of channel */ 
     291    g_io_channel_set_close_on_unref (*ch, TRUE); 
     292 
     293    /* Trim down buffering on this channel */ 
     294    g_io_channel_set_buffer_size (*ch, 5); 
     295 
     296    /* Attempt to set non-blocking flag on channel */ 
     297    if (g_io_channel_set_flags (*ch, G_IO_FLAG_NONBLOCK, NULL) != G_IO_STATUS_NORMAL) 
     298        mc_log ("Problem setting a channel to non-blocking state (channel flags: 0x%x)", 
     299                g_io_channel_get_flags (*ch)); 
     300 
     301    /* Create GSource */ 
     302    *src_id = g_io_add_watch (*ch, (write ? G_IO_OUT : G_IO_IN) | G_IO_HUP, cb_fun, data); 
     303 
     304    /* Source created OK? */ 
     305    if (*src_id == 0) 
     306        goto cleanup_and_err; 
     307 
     308    /* Configure the sources */ 
     309    g_source_set_name_by_id (*src_id, name); 
     310 
     311    /* Return true */ 
     312    return TRUE; 
     313 
     314  cleanup_and_err: 
     315    if (*src_id != 0) 
     316    { 
     317        GSource *src; 
     318        src = g_main_context_find_source_by_id (NULL, *src_id); 
     319        if (src != NULL) 
     320            /* Triggers also unref of wrapped channel */ 
     321            g_source_unref (src); 
     322    } 
     323    if (*ch != NULL) 
     324        g_io_channel_unref (*ch); 
     325    *ch = NULL; 
     326    *src_id = 0; 
     327 
     328    return FALSE; 
     329} 
     330 
     331/* --------------------------------------------------------------------------------------------- */ 
     332 
     333static gboolean 
     334helper_read_all (GIOChannel * channel, char *buf, gsize * out_bytes_read_in_len) 
     335{ 
     336    gsize len = *out_bytes_read_in_len; 
     337    gsize count, retries = 10; 
     338    GIOStatus stat; 
     339 
     340    *out_bytes_read_in_len = 0; 
     341 
     342    while (*out_bytes_read_in_len < len) 
     343    { 
     344        count = 0; 
     345        stat = 
     346            g_io_channel_read_chars (channel, buf + *out_bytes_read_in_len, 
     347                                     len - *out_bytes_read_in_len, &count, NULL); 
     348        *out_bytes_read_in_len += count; 
     349 
     350        /* Should end the reading ? */ 
     351        if (stat == G_IO_STATUS_ERROR || (stat == G_IO_STATUS_NORMAL && count == 0) || 
     352            stat == G_IO_STATUS_EOF) 
     353            return (stat != G_IO_STATUS_ERROR); 
     354        else if (stat == G_IO_STATUS_AGAIN && retries-- == 0) 
     355            /* Exhausted retries - there must be no data to read - return OK */ 
     356            return TRUE; 
     357    } 
     358 
     359    return TRUE; 
     360} 
     361 
     362/* --------------------------------------------------------------------------------------------- */ 
     363 
     364static gboolean 
     365helper_write_all (GIOChannel * channel, char *buf, gsize * out_bytes_written_in_len) 
     366{ 
     367    gsize len = *out_bytes_written_in_len; 
     368    gsize count; 
     369    GIOError err; 
     370 
     371    *out_bytes_written_in_len = 0; 
     372 
     373    while (*out_bytes_written_in_len < len) 
     374    { 
     375        count = 0; 
     376        err = 
     377            g_io_channel_write (channel, buf + *out_bytes_written_in_len, 
     378                                len - *out_bytes_written_in_len, &count); 
     379        *out_bytes_written_in_len += count; 
     380 
     381        if (err && err != G_IO_ERROR_AGAIN) 
     382            return FALSE; 
     383    } 
     384 
     385    return TRUE; 
     386} 
     387 
     388/* --------------------------------------------------------------------------------------------- */ 
     389 
     390static gboolean 
     391stdout_read_callback (GIOChannel * source, GIOCondition condition, gpointer data) 
     392{ 
     393    WInstructionStation *ip = INSTR_STATION (data); 
     394    char buf[2048]; 
     395    gsize count = 2047; 
     396    gboolean r_ret, first_response = ip->prompt_shown; 
     397 
     398    if ((condition & G_IO_IN) == 0) 
     399        return TRUE; 
     400 
     401    r_ret = helper_read_all (source, buf, &count); 
     402    buf[count] = '\0'; 
     403    if (count == 0) 
     404    { 
     405        mc_log ("Error while reading program's output: %s", g_strerror (errno)); 
     406    } 
     407    else 
     408    { 
     409        ip->read_time = g_get_real_time (); 
     410        buf[count] = '\0'; 
     411        if (count > 0) 
     412        { 
     413            gsize idx = 0, nl_count = 0; 
     414            char *p = buf; 
     415            while (idx++ < count) 
     416                if (*p++ == '\n') 
     417                    nl_count++; 
     418 
     419            /* Move to next line after prompt */ 
     420            if (first_response) 
     421            { 
     422                edit_insert (EDIT (ip), '\n'); 
     423                nl_count++; 
     424            } 
     425 
     426            /* Print whole buffer into file's window  */ 
     427            edit_insert_string (EDIT (ip), buf, count); 
     428 
     429            /* Increase the row pointer */ 
     430            ip->cur_line += nl_count; 
     431 
     432            /* Mark a lack of prompt display */ 
     433            ip->prompt_shown = FALSE; 
     434        } 
     435        else if (!ip->prompt_shown) 
     436            instr_stat_show_prompt (ip, r_ret); 
     437 
     438        repaint_screen (); 
     439    } 
     440 
     441    return TRUE; 
     442} 
     443 
     444/* --------------------------------------------------------------------------------------------- */ 
     445 
     446static gboolean 
     447stdin_write_callback (GIOChannel * source, GIOCondition condition, gpointer data) 
     448{ 
     449    WInstructionStation *ip = INSTR_STATION (data); 
     450    gsize count; 
     451    gboolean ret = TRUE; 
     452 
     453    /* Any instruction to send? */ 
     454    if (ip->instruction == NULL) 
     455        return ret; 
     456 
     457    if ((condition & G_IO_OUT) == 0) 
     458        return TRUE; 
     459 
     460    errno = 0; 
     461    count = strlen (ip->instruction); 
     462    ret = helper_write_all (ip->io[STDIN].ch, ip->instruction, &count); 
     463    if (!ret) 
     464    { 
     465        mc_log ("Error during sending instruction to program: %s", g_strerror (errno)); 
     466    } 
     467    else 
     468    { 
     469        g_free (ip->instruction); 
     470        ip->instruction = NULL; 
     471        g_timeout_add (200, helper_timeout_cb, ip); 
     472    } 
     473 
     474    g_io_channel_flush (ip->io[STDIN].ch, NULL); 
     475 
     476    return ret; 
     477} 
     478 
     479/* --------------------------------------------------------------------------------------------- */ 
     480 
     481/* --------------------------------------------------------------------------------------------- */ 
     482/*** public functions ****************************************************************************/ 
     483/* --------------------------------------------------------------------------------------------- */ 
     484 
     485WInstructionStation * 
     486instr_stat_create (int y, int x, int lines, int cols, const char *program) 
     487{ 
     488    WInstructionStation *ip; 
     489    WEdit *iret; 
     490    gboolean proc_ret = FALSE; 
     491    char *program_cline[2]; 
     492    int rounds = 0; 
     493 
     494    ip = g_new0 (WInstructionStation, 1); 
     495    if (ip == NULL) 
     496        return NULL; 
     497 
     498    iret = edit_init (EDIT (ip), y, x, lines, cols, NULL, 0, EDIT_DO_INIT_BASE_CLASS); 
     499    if (iret == NULL) 
     500        goto cleanup_and_err; 
     501 
     502    WIDGET (ip)->callback = instr_stat_callback; 
     503    WIDGET (ip)->mouse_callback = edit_mouse_callback; 
     504    EDIT (ip)->fullscreen = 0; 
     505 
     506    ip->io[0].name = g_strdup ("Std-In (WInstructionStation)"); 
     507    ip->io[1].name = g_strdup ("Std-Out (WInstructionStation)"); 
     508    ip->io[2].name = g_strdup ("Std-Error (WInstructionStation)"); 
     509    ip->io[0].fd = ip->io[1].fd = ip->io[2].fd = -1; 
     510 
     511    ip->prompt = g_strdup ("[guest@localhost]# "); 
     512    ip->prompt_span = strlen (ip->prompt); 
     513    ip->eprompt = g_strdup ("[guest@localhost]! "); 
     514    ip->eprompt_span = strlen (ip->eprompt); 
     515 
     516    /* Set initial program (most probably a shell, like Bash) */ 
     517    if (program != NULL) 
     518        ip->program = g_strdup (program); 
     519    else 
     520        /* Fallback instruction if no program given */ 
     521        ip->program = g_strdup ("bash++__"); 
     522 
     523    while (++rounds <= 2 && !proc_ret) 
     524    { 
     525        if (ip->error != NULL) 
     526        { 
     527            g_error_free (ip->error); 
     528            ip->error = NULL; 
     529        } 
     530        program_cline[0] = ip->program; 
     531        program_cline[1] = NULL; 
     532        proc_ret = g_spawn_async_with_pipes (NULL, program_cline, 
     533                                             NULL, G_SPAWN_SEARCH_PATH | 
     534                                             G_SPAWN_DO_NOT_REAP_CHILD | 
     535                                             G_SPAWN_LEAVE_DESCRIPTORS_OPEN, 
     536                                             NULL, NULL, &ip->proc_id, &ip->io[STDIN].fd, 
     537                                             &ip->io[STDOUT].fd, &ip->io[STDERR].fd, &ip->error); 
     538 
     539        if (!proc_ret) 
     540        { 
     541            char *new_program, *msg_text; 
     542            mc_log ("Creating the background process [program:%s] failed: %s", 
     543                    program, ip->error ? ip->error->message : "<no error message>"); 
     544 
     545            /* Create a transatable, parametrized message */ 
     546            msg_text = g_strdup_printf ("%s%s:", _("The specified program has failed to run.\n" 
     547                                                   "Enter a new full path or a program name"), 
     548                                        (rounds >= 2 ? _(" (last try)") : "")); 
     549 
     550            /* Display message asking for a new program */ 
     551            new_program = input_expand_dialog (_("Provide an alternate program to run"), 
     552                                               msg_text, MC_HISTORY_EDIT_INSTR_STATION_PATH, 
     553                                               alt_prog_first_run ? ip->program : INPUT_LAST_TEXT, 
     554                                               INPUT_COMPLETE_COMMANDS | INPUT_COMPLETE_FILENAMES | 
     555                                               INPUT_COMPLETE_VARIABLES); 
     556 
     557            alt_prog_first_run = FALSE; 
     558 
     559            /* Obtained a new program? */ 
     560            if (new_program != NULL) 
     561            { 
     562                g_free (ip->program); 
     563                ip->program = new_program; 
     564            } 
     565        } 
     566    } 
     567 
     568    /* If no program to run has been found, then exit. */ 
     569    if (!proc_ret) 
     570    { 
     571        message (D_ERROR, _("InstructionStation startup failed"), 
     572                 _("Failed to initialize the CLI window.")); 
     573        goto cleanup_and_err; 
     574    } 
     575 
     576    /* Display initial prompt */ 
     577    instr_stat_show_prompt (ip, TRUE); 
     578 
     579    for (int i = 0; i <= STDERR; i++) 
     580    { 
     581        if (helper_configure_src_and_ch (&ip->io[i].ch, ip->io[i].fd, &ip->io[i].src_id, 
     582                                         (i == STDIN ? TRUE : FALSE), 
     583                                         (i == STDIN ? stdin_write_callback : stdout_read_callback), 
     584                                         ip, ip->io[i].name) == FALSE) 
     585            goto cleanup_and_err; 
     586    } 
     587    /* Set a process watcher and restarter for the program */ 
     588    ip->proc_src_id = g_child_watch_add (ip->proc_id, helper_program_ended_cb, ip); 
     589 
     590    return ip; 
     591 
     592  cleanup_and_err: 
     593 
     594    instr_release (ip, TRUE); 
     595    return NULL; 
     596} 
     597 
     598/* --------------------------------------------------------------------------------------------- */ 
     599 
     600cb_ret_t 
     601instr_stat_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data) 
     602{ 
     603    cb_ret_t ret = MSG_NOT_HANDLED; 
     604    WInstructionStation *ip = INSTR_STATION (w); 
     605    WEdit *e = EDIT (w); 
     606 
     607    switch (msg) 
     608    { 
     609 
     610    case MSG_KEY: 
     611        g_main_context_iteration (NULL, 0); 
     612        { 
     613            gboolean ext_mode; 
     614            long activity; 
     615 
     616            /* keep and then extmod flag */ 
     617            ext_mode = w->ext_mode; 
     618            activity = widget_lookup_key (w, parm); 
     619            w->ext_mode = ext_mode; 
     620 
     621            if (activity == CK_IgnoreKey) 
     622                w->ext_mode = FALSE; 
     623            else 
     624            { 
     625                ret = instr_stat_run_activity (ip, activity); 
     626                /* if activity was not handled, keep the extended mode 
     627                   for the further key processing */ 
     628                if (ret == MSG_HANDLED) 
     629                    w->ext_mode = FALSE; 
     630            } 
     631 
     632            /* 
     633             * Due to the "end of bracket" escape the editor sees input with is_idle() == false 
     634             * (expects more characters) and hence doesn't yet refresh the screen, but then 
     635             * no further characters arrive (there's only an "end of bracket" which is swallowed 
     636             * by tty_get_event()), so you end up with a screen that's not refreshed after pasting. 
     637             * So let's trigger an IDLE signal. 
     638             */ 
     639            if (!is_idle ()) 
     640                widget_idle (w, TRUE); 
     641        } 
     642        break; 
     643    case MSG_ACTION: 
     644        ret = instr_stat_run_activity (ip, parm); 
     645        break; 
     646    case MSG_IDLE: 
     647        edit_update_screen (e); 
     648        break; 
     649 
     650 
     651    default: 
     652        break; 
     653    } 
     654 
     655    if (ret == MSG_NOT_HANDLED) 
     656    { 
     657        ret = edit_callback (w, sender, msg, parm, data); 
     658        ip->cur_col = EDIT (w)->curs_col; 
     659    } 
     660    return ret; 
     661} 
     662 
     663/* --------------------------------------------------------------------------------------------- */ 
  • new file lib/widget/instr_station.h

    diff --git a/lib/widget/instr_station.h b/lib/widget/instr_station.h
    new file mode 100644
    index 000000000..0daf8281f
    - +  
     1#ifndef MC__INSTR_STATION_H 
     2#define MC__INSTR_STATION_H 
     3 
     4#include <gio/gio.h> 
     5 
     6#include "src/editor/editwidget.h" 
     7 
     8/*** typedefs(not structures) and defined constants **********************************************/ 
     9 
     10#define INSTR_STATION(x) ((WInstructionStation *)x) 
     11#define CONST_INSTR_STATION(x) ((const WInstructionStation *)x) 
     12 
     13/*** enums ***************************************************************************************/ 
     14 
     15typedef enum instr_stat_io_type 
     16{ 
     17    STDIN = 0, 
     18    STDOUT, 
     19    STDERR 
     20} instr_stat_io_type_t; 
     21 
     22/*** structures declarations (and typedefs of structures)*****************************************/ 
     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 the prompt being printed after running an instruction */ 
     42    gboolean prompt_shown; 
     43 
     44    /* Process ID of the program */ 
     45    GPid proc_id; 
     46 
     47    /* A mark whether process ID is ready to be released */ 
     48    gboolean program_working; 
     49 
     50    /* Pipes to and from the program */ 
     51    struct 
     52    { 
     53        gint fd; 
     54        GIOChannel *ch; 
     55        guint src_id; 
     56        char *name; 
     57    } io[3]; 
     58 
     59    /* Time of last read of data from program */ 
     60    guint64 read_time; 
     61 
     62    /* GSource id (tag) of program process watcher */ 
     63    guint proc_src_id; 
     64 
     65    /* A GError placeholder */ 
     66    GError *error; 
     67 
     68    gboolean io_complete; 
     69    gboolean finalizing; 
     70} WInstructionStation; 
     71 
     72/*** global variables defined in .c file *********************************************************/ 
     73 
     74/*** declarations of public functions ************************************************************/ 
     75 
     76WInstructionStation *instr_stat_create (int y, int x, int lines, int cols, const char *program); 
     77cb_ret_t instr_stat_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data); 
     78 
     79/*** inline functions ****************************************************************************/ 
     80 
     81#endif /* MC__INSTR_STATION_H */ 
  • 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/edit-impl.h

    diff --git a/src/editor/edit-impl.h b/src/editor/edit-impl.h
    index 881ef549b..5bc88ec12 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_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data); 
     156void edit_mouse_callback (Widget * w, mouse_msg_t msg, mouse_event_t * event); 
    149157 
    150158gboolean edit_add_window (WDialog * h, int y, int x, int lines, int cols, 
    151159                          const vfs_path_t * f, long fline); 
    void edit_move_to_prev_col (WEdit * edit, off_t p); 
    169177long edit_get_col (const WEdit * edit); 
    170178void edit_update_curs_row (WEdit * edit); 
    171179void edit_update_curs_col (WEdit * edit); 
     180gboolean edit_newline_end_check_only (const WEdit * edit); 
    172181void edit_find_bracket (WEdit * edit); 
    173182gboolean edit_reload_line (WEdit * edit, const vfs_path_t * filename_vpath, long line); 
    174183void edit_set_codeset (WEdit * edit); 
    void edit_delete_line (WEdit * edit); 
    180189 
    181190int edit_delete (WEdit * edit, gboolean byte_delete); 
    182191int edit_backspace (WEdit * edit, gboolean byte_delete); 
     192void edit_insert_string (WEdit * edit, char *str_text, gsize len); 
    183193void edit_insert (WEdit * edit, int c); 
    184194void edit_insert_over (WEdit * edit); 
    185195void edit_cursor_move (WEdit * edit, off_t increment); 
    char *edit_get_write_filter (const vfs_path_t * write_name_vpath, 
    193203gboolean edit_save_confirm_cmd (WEdit * edit); 
    194204gboolean edit_save_as_cmd (WEdit * edit); 
    195205WEdit *edit_init (WEdit * edit, int y, int x, int lines, int cols, 
    196                   const vfs_path_t * filename_vpath, long line); 
     206                  const vfs_path_t * filename_vpath, long line, edit_init_flags_t flags); 
    197207gboolean edit_clean (WEdit * edit); 
    198208gboolean edit_ok_to_exit (WEdit * edit); 
    199209file_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..bafc65332 100644
    a b  
    6868#ifdef HAVE_ASPELL 
    6969#include "spell.h" 
    7070#endif 
     71#include "lib/widget/instr_station.h" 
    7172 
    7273/*** global variables ****************************************************************************/ 
    7374 
    static unsigned int edit_dlg_init_refcounter = 0; 
    8990static cb_ret_t edit_dialog_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, 
    9091                                      void *data); 
    9192 
     93/* --------------------------------------------------------------------------------------------- */ 
     94/* Creates a CLI special window, windowed (not fullscreen) */ 
     95 
     96static gboolean 
     97edit_add_instr_stat_window (WDialog * h) 
     98{ 
     99    WInstructionStation *ip; 
     100    ip = instr_stat_create (3, 10, 17, 80, "/bin/bash"); 
     101    if (ip == NULL) 
     102        return FALSE; 
     103    group_add_widget_autopos (GROUP (h), ip, WPOS_KEEP_ALL, NULL); 
     104    widget_draw (WIDGET (h)); 
     105    return TRUE; 
     106} 
     107 
    92108/* --------------------------------------------------------------------------------------------- */ 
    93109 
    94110static char * 
    edit_dialog_command_execute (WDialog * h, long command) 
    718734    case CK_Refresh: 
    719735        edit_refresh_cmd (); 
    720736        break; 
     737    case CK_InstructionStation: 
     738        edit_add_instr_stat_window (h); 
     739        break; 
    721740    case CK_Shell: 
    722741        toggle_subshell (); 
    723742        break; 
    edit_dialog_bg_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm 
    12081227 
    12091228/* --------------------------------------------------------------------------------------------- */ 
    12101229 
    1211 static cb_ret_t 
     1230/** 
     1231 * Handle move/resize mouse events. 
     1232 */ 
     1233static void 
     1234edit_mouse_handle_move_resize (Widget * w, mouse_msg_t msg, mouse_event_t * event) 
     1235{ 
     1236    WEdit *edit = (WEdit *) (w); 
     1237    Widget *h = WIDGET (w->owner); 
     1238    int global_x, global_y; 
     1239 
     1240    if (msg == MSG_MOUSE_UP) 
     1241    { 
     1242        /* Exit move/resize mode. */ 
     1243        edit_execute_cmd (edit, CK_Enter, -1); 
     1244        edit_update_screen (edit);      /* Paint the buttonbar over our possibly overlapping frame. */ 
     1245        return; 
     1246    } 
     1247 
     1248    if (msg != MSG_MOUSE_DRAG) 
     1249        /** 
     1250         * We ignore any other events. Specifically, MSG_MOUSE_DOWN. 
     1251         * 
     1252         * When the move/resize is initiated by the menu, we let the user 
     1253         * stop it by clicking with the mouse. Which is why we don't want 
     1254         * a mouse down to affect the window. 
     1255         */ 
     1256        return; 
     1257 
     1258    /* Convert point to global coordinates for easier calculations. */ 
     1259    global_x = event->x + w->x; 
     1260    global_y = event->y + w->y; 
     1261 
     1262    /* Clamp the point to the dialog's client area. */ 
     1263    global_y = CLAMP (global_y, h->y + 1, h->y + h->lines - 2); /* Status line, buttonbar */ 
     1264    global_x = CLAMP (global_x, h->x, h->x + h->cols - 1);      /* Currently a no-op, as the dialog has no left/right margins. */ 
     1265 
     1266    if (edit->drag_state == MCEDIT_DRAG_MOVE) 
     1267    { 
     1268        w->y = global_y; 
     1269        w->x = global_x - edit->drag_state_start; 
     1270    } 
     1271    else if (edit->drag_state == MCEDIT_DRAG_RESIZE) 
     1272    { 
     1273        w->lines = MAX (WINDOW_MIN_LINES, global_y - w->y + 1); 
     1274        w->cols = MAX (WINDOW_MIN_COLS, global_x - w->x + 1); 
     1275    } 
     1276 
     1277    edit->force |= REDRAW_COMPLETELY;   /* Not really needed as WEdit's MSG_DRAW already does this. */ 
     1278 
     1279    /* We draw the whole dialog because dragging/resizing exposes area beneath. */ 
     1280    widget_draw (WIDGET (w->owner)); 
     1281} 
     1282 
     1283/* --------------------------------------------------------------------------------------------- */ 
     1284/*** public functions ****************************************************************************/ 
     1285/* --------------------------------------------------------------------------------------------- */ 
     1286 
     1287cb_ret_t 
    12121288edit_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data) 
    12131289{ 
    12141290    WEdit *e = (WEdit *) w; 
    edit_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *da 
    12781354 
    12791355/* --------------------------------------------------------------------------------------------- */ 
    12801356 
    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  
    13361357/** 
    13371358 * Handle mouse events of editor window 
    13381359 * 
    edit_mouse_handle_move_resize (Widget * w, mouse_msg_t msg, mouse_event_t * even 
    13401361 * @param msg mouse event message 
    13411362 * @param event mouse event data 
    13421363 */ 
    1343 static void 
     1364void 
    13441365edit_mouse_callback (Widget * w, mouse_msg_t msg, mouse_event_t * event) 
    13451366{ 
    13461367    WEdit *edit = (WEdit *) w; 
    edit_mouse_callback (Widget * w, mouse_msg_t msg, mouse_event_t * event) 
    14541475    } 
    14551476} 
    14561477 
    1457 /* --------------------------------------------------------------------------------------------- */ 
    1458 /*** public functions ****************************************************************************/ 
    14591478/* --------------------------------------------------------------------------------------------- */ 
    14601479 
    14611480gboolean 
    edit_add_window (WDialog * h, int y, int x, int lines, int cols, const vfs_path_ 
    16861705    WEdit *edit; 
    16871706    Widget *w; 
    16881707 
    1689     edit = edit_init (NULL, y, x, lines, cols, f, fline); 
     1708    edit = edit_init (NULL, y, x, lines, cols, f, fline, 0); 
    16901709    if (edit == NULL) 
    16911710        return FALSE; 
    16921711 
  • 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 ***************************************************************************************/ 
  • 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"},