Ticket #4196: InstructionStation_v2.patch

File InstructionStation_v2.patch, 55.5 KB (added by psprint, 4 years ago)

improved the patch

  • lib/global.h

    From 78ef35f42a19d5549fa8733c25bce3103c572ae7 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 | 675 +++++++++++++++++++++++++++++++++++++
     lib/widget/instr_station.h |  82 +++++
     misc/mc.default.keymap     |   1 +
     misc/mc.emacs.keymap       |   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/history.h              |   1 +
     src/keybind-defaults.c     |   1 +
     18 files changed, 1124 insertions(+), 292 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..eacfc4d50
    - +  
     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/* Check if widget is an WInstructionStation class. */ 
     600 
     601gboolean 
     602edit_widget_is_cli (const Widget * w) 
     603{ 
     604    return (w != NULL && w->callback == instr_stat_callback); 
     605} 
     606 
     607/* --------------------------------------------------------------------------------------------- */ 
     608 
     609cb_ret_t 
     610instr_stat_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data) 
     611{ 
     612    cb_ret_t ret = MSG_NOT_HANDLED; 
     613    WInstructionStation *ip = INSTR_STATION (w); 
     614    WEdit *e = EDIT (w); 
     615 
     616    switch (msg) 
     617    { 
     618 
     619    case MSG_KEY: 
     620        { 
     621            gboolean ext_mode; 
     622            long activity; 
     623 
     624            /* keep and then extmod flag */ 
     625            ext_mode = w->ext_mode; 
     626            activity = widget_lookup_key (w, parm); 
     627            w->ext_mode = ext_mode; 
     628 
     629            if (activity == CK_IgnoreKey) 
     630                w->ext_mode = FALSE; 
     631            else 
     632            { 
     633                ret = edit_dialog_command_execute (DIALOG(w->owner), activity); 
     634                if (ret == MSG_NOT_HANDLED) 
     635                    ret = instr_stat_run_activity (ip, activity); 
     636                /* if activity was not handled, keep the extended mode 
     637                   for the further key processing */ 
     638                if (ret == MSG_HANDLED) 
     639                    w->ext_mode = FALSE; 
     640            } 
     641 
     642            /* 
     643             * Due to the "end of bracket" escape the editor sees input with is_idle() == false 
     644             * (expects more characters) and hence doesn't yet refresh the screen, but then 
     645             * no further characters arrive (there's only an "end of bracket" which is swallowed 
     646             * by tty_get_event()), so you end up with a screen that's not refreshed after pasting. 
     647             * So let's trigger an IDLE signal. 
     648             */ 
     649            if (!is_idle ()) 
     650                widget_idle (w, TRUE); 
     651        } 
     652        break; 
     653    case MSG_ACTION: 
     654        ret = instr_stat_run_activity (ip, parm); 
     655        break; 
     656    case MSG_IDLE: 
     657        edit_update_screen (e); 
     658        break; 
     659    case MSG_DESTROY: 
     660        instr_release(ip, FALSE); 
     661        break; 
     662    default: 
     663        break; 
     664    } 
     665 
     666    /* Route to editor window */ 
     667    if (ret == MSG_NOT_HANDLED) 
     668    { 
     669        ret = edit_callback (w, sender, msg, parm, data); 
     670        ip->cur_col = EDIT (w)->curs_col; 
     671    } 
     672    return ret; 
     673} 
     674 
     675/* --------------------------------------------------------------------------------------------- */ 
  • 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..d744d7b6e
    - +  
     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); 
     77gboolean edit_widget_is_cli (const Widget * w); 
     78cb_ret_t instr_stat_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data); 
     79 
     80/*** inline functions ****************************************************************************/ 
     81 
     82#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..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..41ac2917a 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; 
    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 ***************************************************************************************/ 
  • 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"},