Ticket #4196: InstructionStation_v3.9.patch
File InstructionStation_v3.9.patch, 81.6 KB (added by psprint, 4 years ago) |
---|
-
Makefile.am
From 8a0a1b7ffc4b9570adc71497117d50e9ed8e75e6 Mon Sep 17 00:00:00 2001 From: Sebastian Gniazdowski <sgniazdowski@gmail.com> Date: Wed, 17 Feb 2021 15:02:15 -0600 Subject: A simple CLI window for MCEdit --- Makefile.am | 49 +- lib/global.h | 3 + lib/keybind.c | 1 + lib/keybind.h | 1 + lib/tty/key.c | 10 +- lib/widget/wtools.c | 40 +- lib/widget/wtools.h | 4 + misc/mc.default.keymap | 1 + misc/mc.emacs.keymap | 1 + src/editor/Makefile.am | 1 + src/editor/edit-impl.h | 28 +- src/editor/edit.c | 139 +++--- src/editor/editcmd.c | 58 ++- src/editor/editdraw.c | 1 - src/editor/editmenu.c | 3 + src/editor/editwidget.c | 550 ++++++++++---------- src/editor/editwidget.h | 3 + src/editor/instr_station.c | 994 +++++++++++++++++++++++++++++++++++++ src/editor/instr_station.h | 95 ++++ src/keybind-defaults.c | 1 + 20 files changed, 1633 insertions(+), 350 deletions(-) create mode 100644 src/editor/instr_station.c create mode 100644 src/editor/instr_station.h diff --git a/Makefile.am b/Makefile.am index f86f6ed38..196a474af 100644
a b CONFIG_STATUS_DEPENDENCIES = $(top_srcdir)/version.h 25 25 cppcheck-portability \ 26 26 cppcheck-style \ 27 27 cppcheck-warning \ 28 cppcheck-all 28 cppcheck-all \ 29 tags \ 30 tags-emacs \ 31 tags-vim 29 32 33 # Invoke a ctags utility to generate Emacs and Vim's format tag file. 34 # The meaning of the flags: 35 # - `p` kind enables function prototypes, 36 # - `x` kind enables extern and forward variable declarations, 37 # - `S` field enables function signatures, 38 # - `properties` field enables static, volatile, etc. properties, 39 # - `macrodef` field enables macro definitions. 40 # 41 # Generate two formats at once: Emacs and Vim's. 42 tags: tags-emacs tags-vim 43 44 # Build only the Emacs-style `TAGS` file. 45 tags-emacs: 46 @if type ctags >/dev/null 2>&1; then \ 47 if ctags --version | grep >/dev/null 2>&1 "Universal Ctags"; then \ 48 ctags -e -R --languages=C --langmap=C:.h.c --c-kinds=+px \ 49 --fields=+S --fields-c=+"{properties}" --fields-c=+"{macrodef}" \ 50 --extras=+qr; \ 51 else \ 52 ctags -e -R --languages=C --langmap=C:.h.c --c-kinds=+px \ 53 --fields=+S; \ 54 fi; \ 55 printf "Created the Emacs \`TAGS\` file.\\n"; \ 56 else \ 57 printf 'Error: Please install a Ctags (e.g.: either the Exuberant or Universal %b' \ 58 'version) utility first.\n'; \ 59 fi 60 61 # Build only the Vim-style `tags` file. 62 tags-vim: 63 @if type ctags >/dev/null 2>&1; then \ 64 if ctags --version | grep >/dev/null 2>&1 "Universal Ctags"; then \ 65 ctags -R --languages=C --langmap=C:.h.c --c-kinds=+px \ 66 --fields=+S --fields-c=+"{properties}" --fields-c=+"{macrodef}" \ 67 --extras=+qr; \ 68 else \ 69 ctags -R --languages=C --langmap=C:.h.c --c-kinds=+px \ 70 --fields=+S; \ 71 fi; \ 72 printf "Created the Vim's style \`tags\` file.\\n"; \ 73 else \ 74 printf 'Error: Please install a Ctags (e.g.: either the Exuberant or Universal %b' \ 75 'version) utility first.\n'; \ 76 fi 30 77 31 78 update-version: 32 79 @if test -x $(top_srcdir)/maint/utils/version.sh; then \ -
lib/global.h
diff --git a/lib/global.h b/lib/global.h index c60063faa..05d88cbb4 100644
a b 103 103 #include "lib/logging.h" 104 104 #endif 105 105 106 #define has_flag(x,y) (((x) & (y)) != 0) 107 #define set_flag_in(x,y) ((x) |= (y)) 108 106 109 /* Just for keeping Your's brains from invention a proper size of the buffer :-) */ 107 110 #define BUF_10K 10240L 108 111 #define BUF_8K 8192L -
lib/keybind.c
diff --git a/lib/keybind.c b/lib/keybind.c index abd44d3e2..40e25466c 100644
a b static name_keymap_t command_names[] = { 290 290 ADD_KEYMAP_NAME (EditMail), 291 291 ADD_KEYMAP_NAME (ParagraphFormat), 292 292 ADD_KEYMAP_NAME (MatchBracket), 293 ADD_KEYMAP_NAME (InstructionStation), 293 294 ADD_KEYMAP_NAME (ExternalCommand), 294 295 ADD_KEYMAP_NAME (MacroStartRecord), 295 296 ADD_KEYMAP_NAME (MacroStopRecord), -
lib/keybind.h
diff --git a/lib/keybind.h b/lib/keybind.h index af019df09..e99181ec6 100644
a b enum 314 314 CK_SyntaxOnOff, 315 315 CK_SyntaxChoose, 316 316 CK_InsertLiteral, 317 CK_InstructionStation, 317 318 CK_ExternalCommand, 318 319 CK_Date, 319 320 CK_EditMail, -
lib/tty/key.c
diff --git a/lib/tty/key.c b/lib/tty/key.c index 4abfc71d7..8876fbf82 100644
a b tty_get_event (struct Gpm_Event *event, gboolean redo_event, gboolean block) 2031 2031 time_out.tv_sec = 0; 2032 2032 time_out.tv_usec = 0; 2033 2033 } 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 } 2035 2041 tty_enable_interrupt_key (); 2036 2042 flag = select (nfd, &select_set, NULL, NULL, time_addr); 2037 2043 tty_disable_interrupt_key (); … … tty_get_event (struct Gpm_Event *event, gboolean redo_event, gboolean block) 2043 2049 */ 2044 2050 if (flag == 0) 2045 2051 { 2052 /* Provide CPU time to GLib main loop's default context */ 2053 g_main_context_iteration (NULL, FALSE); 2046 2054 if (redo_event) 2047 2055 return EV_MOUSE; 2048 2056 if (!block || tty_got_winch ()) -
lib/widget/wtools.c
diff --git a/lib/widget/wtools.c b/lib/widget/wtools.c index 5c5dc4487..8c34d0c82 100644
a b bg_message (int dummy, int *flags, char *title, const char *text) 184 184 static char * 185 185 fg_input_dialog_help (const char *header, const char *text, const char *help, 186 186 const char *history_name, const char *def_text, gboolean strip_password, 187 input_complete_t completion_flags )187 input_complete_t completion_flags, quick_widget_t * add_w) 188 188 { 189 189 char *p_text; 190 190 char histname[64] = "inp|"; … … fg_input_dialog_help (const char *header, const char *text, const char *help, 209 209 } 210 210 211 211 { 212 quick_widget_t quick_widgets[] = { 212 guint i = 1, norm_i = 0, add_i = 0; 213 quick_widget_t quick_widgets[15] = { 213 214 /* *INDENT-OFF* */ 214 215 QUICK_LABELED_INPUT (p_text, input_label_above, def_text, histname, &my_str, 215 NULL, is_passwd, strip_password, completion_flags), 216 QUICK_BUTTONS_OK_CANCEL, 217 QUICK_END 216 NULL, is_passwd, strip_password, completion_flags) 218 217 /* *INDENT-ON* */ 219 218 }; 220 219 quick_widget_t press_end[] = { QUICK_BUTTONS_OK_CANCEL, QUICK_END }; 221 220 quick_dialog_t qdlg = { 222 221 -1, -1, COLS / 2, header, 223 222 help, quick_widgets, NULL, NULL 224 223 }; 225 224 225 /* Handling of additional widgets optionally passed via last argument `add_w` */ 226 227 /* Copy/append any extra widgets (max 10) */ 228 while (add_w != NULL && add_w[add_i].widget_type != quick_end && i < 15) 229 quick_widgets[i++] = add_w[add_i++]; 230 231 /* Copy normal buttons */ 232 while (norm_i < sizeof (press_end) / sizeof (quick_widget_t) && i < 15) 233 quick_widgets[i++] = press_end[norm_i++]; 234 226 235 ret = quick_dialog (&qdlg); 227 236 } 228 237 … … input_dialog_help (const char *header, const char *text, const char *help, 479 488 { 480 489 void *p; 481 490 char *(*f) (const char *, const char *, const char *, const char *, const char *, 482 gboolean, input_complete_t );491 gboolean, input_complete_t, quick_widget_t * add_w); 483 492 } func; 484 493 func.f = fg_input_dialog_help; 485 return wtools_parent_call_string (func.p, 7,494 return wtools_parent_call_string (func.p, 8, 486 495 strlen (header), header, strlen (text), 487 496 text, strlen (help), help, 488 497 strlen (history_name), history_name, 489 498 strlen (def_text), def_text, 490 499 sizeof (gboolean), strip_password, 491 sizeof (input_complete_t), completion_flags); 500 sizeof (input_complete_t), completion_flags, 501 sizeof (quick_widget_t *), NULL); 492 502 } 493 503 else 494 504 #endif /* ENABLE_BACKGROUND */ 495 505 return fg_input_dialog_help (header, text, help, history_name, def_text, strip_password, 496 completion_flags );506 completion_flags, NULL); 497 507 } 498 508 499 509 /* --------------------------------------------------------------------------------------------- */ … … input_dialog (const char *header, const char *text, const char *history_name, co 509 519 510 520 /* --------------------------------------------------------------------------------------------- */ 511 521 522 char * 523 input_dialog_ext (const char *header, const char *text, const char *history_name, 524 const char *def_text, input_complete_t completion_flags, quick_widget_t * add_w) 525 { 526 return fg_input_dialog_help (header, text, "[Input Line Keys]", history_name, def_text, FALSE, 527 completion_flags, add_w); 528 } 529 530 /* --------------------------------------------------------------------------------------------- */ 531 512 532 char * 513 533 input_expand_dialog (const char *header, const char *text, 514 534 const char *history_name, const char *def_text, -
lib/widget/wtools.h
diff --git a/lib/widget/wtools.h b/lib/widget/wtools.h index cd0bc3253..d5a39971b 100644
a b struct simple_status_msg_t 65 65 char *input_dialog (const char *header, const char *text, 66 66 const char *history_name, const char *def_text, 67 67 input_complete_t completion_flags); 68 /* The input dialogs */ 69 char *input_dialog_ext (const char *header, const char *text, 70 const char *history_name, const char *def_text, 71 input_complete_t completion_flags, quick_widget_t * add_w); 68 72 char *input_dialog_help (const char *header, const char *text, const char *help, 69 73 const char *history_name, const char *def_text, gboolean strip_password, 70 74 input_complete_t completion_flags); -
misc/mc.default.keymap
diff --git a/misc/mc.default.keymap b/misc/mc.default.keymap index 2931ddd0a..05beca13f 100644
a b Sort = alt-t 350 350 Mail = alt-m 351 351 ParagraphFormat = alt-p 352 352 MatchBracket = alt-b 353 InstructionStation = alt-s 353 354 ExternalCommand = alt-u 354 355 UserMenu = f11 355 356 Menu = f9 -
misc/mc.emacs.keymap
diff --git a/misc/mc.emacs.keymap b/misc/mc.emacs.keymap index 7cc305db7..f8af8b03d 100644
a b Sort = alt-t 349 349 # Mail = 350 350 ParagraphFormat = alt-p 351 351 # MatchBracket = 352 InstructionStation = alt-s 352 353 ExternalCommand = alt-u 353 354 UserMenu = f11 354 355 Menu = f9 -
src/editor/Makefile.am
diff --git a/src/editor/Makefile.am b/src/editor/Makefile.am index 235ed76af..0ac4b4af7 100644
a b libedit_la_SOURCES = \ 18 18 editmenu.c \ 19 19 editoptions.c \ 20 20 editwidget.c editwidget.h \ 21 instr_station.c instr_station.h \ 21 22 etags.c etags.h \ 22 23 format.c \ 23 24 syntax.c -
src/editor/edit-impl.h
diff --git a/src/editor/edit-impl.h b/src/editor/edit-impl.h index 3ad04dbea..9d09f181e 100644
a b typedef enum 87 87 LB_MAC 88 88 } LineBreaks; 89 89 90 typedef enum 91 { 92 EDIT_DO_INIT_BASE_CLASS = 1 << 0, 93 94 } edit_init_flags_t; 95 96 typedef enum instr_stn_flags 97 { 98 INSTR_STN_NO_FLAGS = 0, 99 INSTR_STN_NO_STDIN = 1 << 17, 100 INSTR_STN_NO_PROMPT = 1 << 18 101 } instr_stn_flags_t; 102 90 103 typedef enum 91 104 { 92 105 EDIT_QUICK_SAVE = 0, … … extern char *edit_window_state_char; 135 148 extern char *edit_window_close_char; 136 149 137 150 /*** declarations of public functions ************************************************************/ 151 cb_ret_t edit_dialog_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data); 152 cb_ret_t edit_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data); 153 cb_ret_t edit_dialog_command_execute (WDialog * h, long command, void *data); 154 155 void edit_mouse_callback (Widget * w, mouse_msg_t msg, mouse_event_t * event); 138 156 139 157 gboolean edit_add_window (WDialog * h, int y, int x, int lines, int cols, 140 158 const vfs_path_t * f, long fline); … … void edit_move_to_prev_col (WEdit * edit, off_t p); 158 176 long edit_get_col (const WEdit * edit); 159 177 void edit_update_curs_row (WEdit * edit); 160 178 void edit_update_curs_col (WEdit * edit); 179 gboolean edit_newline_end_check_only (const WEdit * edit); 161 180 void edit_find_bracket (WEdit * edit); 162 181 gboolean edit_reload_line (WEdit * edit, const vfs_path_t * filename_vpath, long line); 163 182 void edit_set_codeset (WEdit * edit); … … void edit_delete_line (WEdit * edit); 169 188 170 189 int edit_delete (WEdit * edit, gboolean byte_delete); 171 190 int edit_backspace (WEdit * edit, gboolean byte_delete); 191 void edit_move_to_top (WEdit * edit); 192 void edit_move_to_bottom (WEdit * edit); 193 void edit_cursor_to_bol (WEdit * edit); 194 void edit_cursor_to_eol (WEdit * edit); 195 void edit_insert_string (WEdit * edit, char *str_text, gsize len); 172 196 void edit_insert (WEdit * edit, int c); 173 197 void edit_insert_over (WEdit * edit); 174 198 void edit_cursor_move (WEdit * edit, off_t increment); … … char *edit_get_write_filter (const vfs_path_t * write_name_vpath, 182 206 gboolean edit_save_confirm_cmd (WEdit * edit); 183 207 gboolean edit_save_as_cmd (WEdit * edit); 184 208 WEdit *edit_init (WEdit * edit, int y, int x, int lines, int cols, 185 const vfs_path_t * filename_vpath, long line );209 const vfs_path_t * filename_vpath, long line, edit_init_flags_t flags); 186 210 gboolean edit_clean (WEdit * edit); 187 211 gboolean edit_ok_to_exit (WEdit * edit); 188 212 gboolean edit_load_cmd (WDialog * h); … … void edit_move_to_line (WEdit * e, long line); 236 260 void edit_move_display (WEdit * e, long line); 237 261 void edit_word_wrap (WEdit * edit); 238 262 int edit_sort_cmd (WEdit * edit); 263 gboolean edit_add_instr_stn_window (WDialog * h, instr_stn_flags_t flags, void *data); 264 239 265 int edit_ext_cmd (WEdit * edit); 240 266 241 267 int edit_store_macro_cmd (WEdit * edit); -
src/editor/edit.c
diff --git a/src/editor/edit.c b/src/editor/edit.c index edda1f832..87c4f766b 100644
a b edit_modification (WEdit * edit) 621 621 { 622 622 edit->caches_valid = FALSE; 623 623 624 /* raise lock when file modified */625 if ( !edit->modified && !edit->delete_file)624 /* raise lock when file is about to be modified */ 625 if (edit->filename_vpath && !edit->modified && !edit->delete_file) 626 626 edit->locked = lock_file (edit->filename_vpath); 627 627 edit->modified = 1; 628 628 } … … edit_end_page (WEdit * edit) 821 821 } 822 822 823 823 824 /* --------------------------------------------------------------------------------------------- */825 /** goto beginning of text */826 827 static void828 edit_move_to_top (WEdit * edit)829 {830 if (edit->buffer.curs_line != 0)831 {832 edit_cursor_move (edit, -edit->buffer.curs1);833 edit_move_to_prev_col (edit, 0);834 edit->force |= REDRAW_PAGE;835 edit->search_start = 0;836 edit_update_curs_row (edit);837 }838 }839 840 /* --------------------------------------------------------------------------------------------- */841 /** goto end of text */842 843 static void844 edit_move_to_bottom (WEdit * edit)845 {846 if (edit->buffer.curs_line < edit->buffer.lines)847 {848 edit_move_down (edit, edit->buffer.lines - edit->curs_row, FALSE);849 edit->start_display = edit->buffer.size;850 edit->start_line = edit->buffer.lines;851 edit_scroll_upward (edit, WIDGET (edit)->lines - 1);852 edit->force |= REDRAW_PAGE;853 }854 }855 856 /* --------------------------------------------------------------------------------------------- */857 /** goto beginning of line */858 859 static void860 edit_cursor_to_bol (WEdit * edit)861 {862 edit_cursor_move (edit, edit_buffer_get_current_bol (&edit->buffer) - edit->buffer.curs1);863 edit->search_start = edit->buffer.curs1;864 edit->prev_col = edit_get_col (edit);865 edit->over_col = 0;866 }867 868 /* --------------------------------------------------------------------------------------------- */869 /** goto end of line */870 871 static void872 edit_cursor_to_eol (WEdit * edit)873 {874 edit_cursor_move (edit, edit_buffer_get_current_eol (&edit->buffer) - edit->buffer.curs1);875 edit->search_start = edit->buffer.curs1;876 edit->prev_col = edit_get_col (edit);877 edit->over_col = 0;878 }879 880 824 /* --------------------------------------------------------------------------------------------- */ 881 825 882 826 static unsigned long … … edit_insert_file (WEdit * edit, const vfs_path_t * filename_vpath) 2075 2019 2076 2020 WEdit * 2077 2021 edit_init (WEdit * edit, int y, int x, int lines, int cols, const vfs_path_t * filename_vpath, 2078 long line )2022 long line, edit_init_flags_t flags) 2079 2023 { 2080 2024 gboolean to_free = FALSE; 2081 2025 … … edit_init (WEdit * edit, int y, int x, int lines, int cols, const vfs_path_t * f 2097 2041 edit->fullscreen = fullscreen; 2098 2042 edit->loc_prev = loc_prev; 2099 2043 } 2100 else2044 if (has_flag (flags, EDIT_DO_INIT_BASE_CLASS) || edit == NULL) 2101 2045 { 2102 2046 Widget *w; 2103 edit = g_malloc0 (sizeof (WEdit)); 2104 to_free = TRUE; 2047 if (edit == NULL) 2048 { 2049 edit = g_malloc0 (sizeof (WEdit)); 2050 to_free = TRUE; 2051 } 2105 2052 2106 2053 w = WIDGET (edit); 2107 2054 widget_init (w, y, x, lines, cols, NULL, NULL); … … edit_reload_line (WEdit * edit, const vfs_path_t * filename_vpath, long line) 2238 2185 e->fullscreen = edit->fullscreen; 2239 2186 e->loc_prev = edit->loc_prev; 2240 2187 2241 if (edit_init (e, w->y, w->x, w->lines, w->cols, filename_vpath, line ) == NULL)2188 if (edit_init (e, w->y, w->x, w->lines, w->cols, filename_vpath, line, 0) == NULL) 2242 2189 { 2243 2190 g_free (e); 2244 2191 return FALSE; … … edit_push_redo_action (WEdit * edit, long c) 2495 2442 edit->redo_stack_bottom = edit->redo_stack_pointer = 0; 2496 2443 } 2497 2444 2445 /* --------------------------------------------------------------------------------------------- */ 2446 /** goto beginning of text */ 2447 2448 void 2449 edit_move_to_top (WEdit * edit) 2450 { 2451 if (edit->buffer.curs_line != 0) 2452 { 2453 edit_cursor_move (edit, -edit->buffer.curs1); 2454 edit_move_to_prev_col (edit, 0); 2455 edit->force |= REDRAW_PAGE; 2456 edit->search_start = 0; 2457 edit_update_curs_row (edit); 2458 } 2459 } 2460 2461 /* --------------------------------------------------------------------------------------------- */ 2462 /** goto end of text */ 2463 2464 void 2465 edit_move_to_bottom (WEdit * edit) 2466 { 2467 if (edit->buffer.curs_line < edit->buffer.lines) 2468 { 2469 edit_move_down (edit, edit->buffer.lines - edit->curs_row, FALSE); 2470 edit->start_display = edit->buffer.size; 2471 edit->start_line = edit->buffer.lines; 2472 edit_scroll_upward (edit, WIDGET (edit)->lines - 1); 2473 edit->force |= REDRAW_PAGE; 2474 } 2475 } 2476 2477 /* --------------------------------------------------------------------------------------------- */ 2478 /** goto beginning of line */ 2479 2480 void 2481 edit_cursor_to_bol (WEdit * edit) 2482 { 2483 edit_cursor_move (edit, edit_buffer_get_current_bol (&edit->buffer) - edit->buffer.curs1); 2484 edit->search_start = edit->buffer.curs1; 2485 edit->prev_col = edit_get_col (edit); 2486 edit->over_col = 0; 2487 } 2488 2489 /* --------------------------------------------------------------------------------------------- */ 2490 /** goto end of line */ 2491 2492 void 2493 edit_cursor_to_eol (WEdit * edit) 2494 { 2495 edit_cursor_move (edit, edit_buffer_get_current_eol (&edit->buffer) - edit->buffer.curs1); 2496 edit->search_start = edit->buffer.curs1; 2497 edit->prev_col = edit_get_col (edit); 2498 edit->over_col = 0; 2499 } 2500 2501 /* --------------------------------------------------------------------------------------------- */ 2502 2503 void 2504 edit_insert_string (WEdit * edit, char *str_text, gsize len) 2505 { 2506 size_t i; 2507 for (i = 0; i < len; i++) 2508 edit_insert (edit, str_text[i]); 2509 } 2510 2498 2511 /* --------------------------------------------------------------------------------------------- */ 2499 2512 /** 2500 2513 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 0d2caa923..de967b3fe 100644
a b 78 78 #include "spell_dialogs.h" 79 79 #endif 80 80 #include "etags.h" 81 #include "instr_station.h" 81 82 82 83 /*** global variables ****************************************************************************/ 83 84 … … edit_complete_word_insert_recoded_completion (WEdit * edit, char *completion, gs 1529 1530 /*** public functions ****************************************************************************/ 1530 1531 /* --------------------------------------------------------------------------------------------- */ 1531 1532 1533 gboolean 1534 edit_newline_end_check_only (const WEdit * e) 1535 { 1536 const edit_buffer_t *buf = &e->buffer; 1537 return (buf->size > 0 && edit_buffer_get_byte (buf, buf->size - 1) == '\n'); 1538 } 1539 1540 /* --------------------------------------------------------------------------------------------- */ 1541 1532 1542 void 1533 1543 edit_refresh_cmd (void) 1534 1544 { … … edit_close_cmd (WEdit * edit) 2251 2261 group_remove_widget (w); 2252 2262 widget_destroy (w); 2253 2263 2254 if (edit_widget_is_editor (CONST_WIDGET (g->current->data))) 2264 if (edit_widget_is_editor (CONST_WIDGET (g->current->data)) 2265 || edit_widget_is_cli (CONST_WIDGET (g->current->data))) 2255 2266 edit = (WEdit *) (g->current->data); 2256 2267 else 2257 2268 { 2269 /* Look for a file window or a CLI window */ 2258 2270 edit = find_editor (DIALOG (g)); 2271 if (!edit) 2272 edit = find_cli (DIALOG (g)); 2259 2273 if (edit != NULL) 2260 2274 widget_select (w); 2261 2275 } … … edit_ext_cmd (WEdit * edit) 3225 3239 { 3226 3240 char *exp, *tmp, *tmp_edit_temp_file; 3227 3241 int e; 3242 gboolean run_in_cli = FALSE, discard_output = FALSE; 3243 3244 quick_widget_t chbox[5] = { QUICK_SEPARATOR (TRUE), 3245 QUICK_CHECKBOX ("&Open in CLI window", &run_in_cli, NULL), 3246 QUICK_CHECKBOX ("&Discard output", &discard_output, NULL), 3247 QUICK_END 3248 }; 3228 3249 3229 3250 exp = 3230 input_dialog (_("Paste output of external command"), 3231 _("Enter shell command(s):"), MC_HISTORY_EDIT_PASTE_EXTCMD, INPUT_LAST_TEXT, 3232 INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_VARIABLES | INPUT_COMPLETE_USERNAMES 3233 | INPUT_COMPLETE_HOSTNAMES | INPUT_COMPLETE_CD | INPUT_COMPLETE_COMMANDS | 3234 INPUT_COMPLETE_SHELL_ESC); 3251 input_dialog_ext (_("Paste output of external command"), 3252 _("Enter shell command(s):"), MC_HISTORY_EDIT_PASTE_EXTCMD, 3253 INPUT_LAST_TEXT, 3254 INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_VARIABLES | 3255 INPUT_COMPLETE_USERNAMES | INPUT_COMPLETE_HOSTNAMES | INPUT_COMPLETE_CD | 3256 INPUT_COMPLETE_COMMANDS | INPUT_COMPLETE_SHELL_ESC, chbox); 3235 3257 3236 3258 if (!exp) 3237 3259 return 1; 3238 3260 3239 tmp_edit_temp_file = mc_config_get_full_path (EDIT_HOME_TEMP_FILE); 3240 tmp = g_strconcat (exp, " > ", tmp_edit_temp_file, (char *) NULL); 3241 g_free (tmp_edit_temp_file); 3261 /* Should start a CLI window with the command ? */ 3262 if (run_in_cli && !discard_output) 3263 { 3264 edit_add_instr_stn_window (DIALOG (WIDGET (edit)->owner), 3265 INSTR_STN_NO_STDIN | INSTR_STN_NO_PROMPT, exp); 3266 g_free (exp); 3267 return 0; 3268 } 3269 3270 /* Should discard output ? */ 3271 if (discard_output) 3272 tmp = g_strdup_printf ("%s >/dev/null 2>&1", exp); 3273 else 3274 { 3275 tmp_edit_temp_file = mc_config_get_full_path (EDIT_HOME_TEMP_FILE); 3276 tmp = g_strconcat (exp, " > ", tmp_edit_temp_file, (char *) NULL); 3277 g_free (tmp_edit_temp_file); 3278 } 3279 3280 /* Run instruction */ 3242 3281 e = system (tmp); 3243 3282 g_free (tmp); 3244 3283 g_free (exp); … … edit_ext_cmd (WEdit * edit) 3251 3290 3252 3291 edit->force |= REDRAW_COMPLETELY; 3253 3292 3293 if (!discard_output) 3254 3294 { 3255 3295 vfs_path_t *tmp_vpath; 3256 3296 -
src/editor/editdraw.c
diff --git a/src/editor/editdraw.c b/src/editor/editdraw.c index 248128b68..d2fddc13d 100644
a b edit_draw_this_line (WEdit * edit, off_t b, long row, long start_col, long end_c 820 820 } 821 821 822 822 p->ch = 0; 823 824 823 print_to_widget (edit, row, start_col, start_col_real, end_col, line, line_stat, book_mark); 825 824 } 826 825 -
src/editor/editmenu.c
diff --git a/src/editor/editmenu.c b/src/editor/editmenu.c index 489893849..02bf43aa3 100644
a b create_window_menu (void) 236 236 entries = g_list_prepend (entries, menu_entry_create (_("&Next"), CK_WindowNext)); 237 237 entries = g_list_prepend (entries, menu_entry_create (_("&Previous"), CK_WindowPrev)); 238 238 entries = g_list_prepend (entries, menu_entry_create (_("&List..."), CK_WindowList)); 239 entries = 240 g_list_prepend (entries, 241 menu_entry_create (_("Instr&uction Station"), CK_InstructionStation)); 239 242 240 243 return g_list_reverse (entries); 241 244 } -
src/editor/editwidget.c
diff --git a/src/editor/editwidget.c b/src/editor/editwidget.c index 18ac00e66..2e49fe3ed 100644
a b 67 67 #ifdef HAVE_ASPELL 68 68 #include "spell.h" 69 69 #endif 70 #include "src/editor/instr_station.h" 70 71 71 72 /*** global variables ****************************************************************************/ 72 73 … … static unsigned int edit_dlg_init_refcounter = 0; 85 86 86 87 /*** file scope functions ************************************************************************/ 87 88 88 static cb_ret_t edit_dialog_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm,89 void *data);90 91 89 /* --------------------------------------------------------------------------------------------- */ 92 90 /** 93 91 * Init the 'edit' subsystem … … edit_get_title (const WDialog * h, size_t len) 376 374 return g_strconcat (_("Edit: "), modified, file_label, (char *) NULL); 377 375 } 378 376 379 /* --------------------------------------------------------------------------------------------- */380 381 static cb_ret_t382 edit_dialog_command_execute (WDialog * h, long command)383 {384 WGroup *g = GROUP (h);385 Widget *wh = WIDGET (h);386 cb_ret_t ret = MSG_HANDLED;387 388 switch (command)389 {390 case CK_EditNew:391 edit_add_window (h, wh->y + 1, wh->x, wh->lines - 2, wh->cols, NULL, 0);392 break;393 case CK_EditFile:394 edit_load_cmd (h);395 break;396 case CK_History:397 edit_load_file_from_history (h);398 break;399 case CK_EditSyntaxFile:400 edit_load_syntax_file (h);401 break;402 case CK_EditUserMenu:403 edit_load_menu_file (h);404 break;405 case CK_Close:406 /* if there are no opened files anymore, close MC editor */407 if (edit_widget_is_editor (CONST_WIDGET (g->current->data)) &&408 edit_close_cmd ((WEdit *) g->current->data) && find_editor (h) == NULL)409 dlg_stop (h);410 break;411 case CK_Help:412 edit_help ();413 /* edit->force |= REDRAW_COMPLETELY; */414 break;415 case CK_Menu:416 edit_menu_cmd (h);417 break;418 case CK_Quit:419 case CK_Cancel:420 /* don't close editor due to SIGINT, but stop move/resize window */421 {422 Widget *w = WIDGET (g->current->data);423 424 if (edit_widget_is_editor (w) && ((WEdit *) w)->drag_state != MCEDIT_DRAG_NONE)425 edit_restore_size ((WEdit *) w);426 else if (command == CK_Quit)427 dlg_stop (h);428 }429 break;430 case CK_About:431 edit_about ();432 break;433 case CK_SyntaxOnOff:434 edit_syntax_onoff_cmd (h);435 break;436 case CK_ShowTabTws:437 edit_show_tabs_tws_cmd (h);438 break;439 case CK_ShowMargin:440 edit_show_margin_cmd (h);441 break;442 case CK_ShowNumbers:443 edit_show_numbers_cmd (h);444 break;445 case CK_Refresh:446 edit_refresh_cmd ();447 break;448 case CK_Shell:449 toggle_subshell ();450 break;451 case CK_LearnKeys:452 learn_keys ();453 break;454 case CK_WindowMove:455 case CK_WindowResize:456 if (edit_widget_is_editor (CONST_WIDGET (g->current->data)))457 edit_handle_move_resize ((WEdit *) g->current->data, command);458 break;459 case CK_WindowList:460 edit_window_list (h);461 break;462 case CK_WindowNext:463 group_select_next_widget (g);464 break;465 case CK_WindowPrev:466 group_select_prev_widget (g);467 break;468 case CK_Options:469 edit_options_dialog (h);470 break;471 case CK_OptionsSaveMode:472 edit_save_mode_cmd ();473 break;474 case CK_SaveSetup:475 save_setup_cmd ();476 break;477 default:478 ret = MSG_NOT_HANDLED;479 break;480 }481 482 return ret;483 }484 485 377 /* --------------------------------------------------------------------------------------------- */ 486 378 /* 487 379 * Translate the keycode into either 'command' or 'char_for_insertion'. … … edit_update_cursor (WEdit * edit, const mouse_event_t * event) 739 631 return done; 740 632 } 741 633 742 /* --------------------------------------------------------------------------------------------- */743 /** Callback for the edit dialog */744 745 static cb_ret_t746 edit_dialog_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)747 {748 WGroup *g = GROUP (w);749 WDialog *h = DIALOG (w);750 751 switch (msg)752 {753 case MSG_INIT:754 edit_dlg_init ();755 return MSG_HANDLED;756 757 case MSG_RESIZE:758 dlg_default_callback (w, NULL, MSG_RESIZE, 0, NULL);759 menubar_arrange (find_menubar (h));760 return MSG_HANDLED;761 762 case MSG_ACTION:763 {764 /* Handle shortcuts, menu, and buttonbar. */765 766 cb_ret_t result;767 768 result = edit_dialog_command_execute (h, parm);769 770 /* We forward any commands coming from the menu, and which haven't been771 handled by the dialog, to the focused WEdit window. */772 if (result == MSG_NOT_HANDLED && sender == WIDGET (find_menubar (h)))773 result = send_message (g->current->data, NULL, MSG_ACTION, parm, NULL);774 775 return result;776 }777 778 case MSG_KEY:779 {780 Widget *we = WIDGET (g->current->data);781 cb_ret_t ret = MSG_NOT_HANDLED;782 783 if (edit_widget_is_editor (we))784 {785 gboolean ext_mode;786 long command;787 788 /* keep and then extmod flag */789 ext_mode = we->ext_mode;790 command = widget_lookup_key (we, parm);791 we->ext_mode = ext_mode;792 793 if (command == CK_IgnoreKey)794 we->ext_mode = FALSE;795 else796 {797 ret = edit_dialog_command_execute (h, command);798 /* if command was not handled, keep the extended mode799 for the further key processing */800 if (ret == MSG_HANDLED)801 we->ext_mode = FALSE;802 }803 }804 805 /*806 * Due to the "end of bracket" escape the editor sees input with is_idle() == false807 * (expects more characters) and hence doesn't yet refresh the screen, but then808 * no further characters arrive (there's only an "end of bracket" which is swallowed809 * by tty_get_event()), so you end up with a screen that's not refreshed after pasting.810 * So let's trigger an IDLE signal.811 */812 if (!is_idle ())813 widget_idle (w, TRUE);814 return ret;815 }816 817 /* hardcoded menu hotkeys (see edit_drop_hotkey_menu) */818 case MSG_UNHANDLED_KEY:819 return edit_drop_hotkey_menu (h, parm) ? MSG_HANDLED : MSG_NOT_HANDLED;820 821 case MSG_VALIDATE:822 edit_quit (h);823 return MSG_HANDLED;824 825 case MSG_END:826 edit_dlg_deinit ();827 return MSG_HANDLED;828 829 case MSG_IDLE:830 widget_idle (w, FALSE);831 return send_message (g->current->data, NULL, MSG_IDLE, 0, NULL);832 833 default:834 return dlg_default_callback (w, sender, msg, parm, data);835 }836 }837 838 634 /* --------------------------------------------------------------------------------------------- */ 839 635 840 636 /** … … edit_dialog_bg_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm 929 725 930 726 /* --------------------------------------------------------------------------------------------- */ 931 727 932 static cb_ret_t 728 /** 729 * Handle move/resize mouse events. 730 */ 731 static void 732 edit_mouse_handle_move_resize (Widget * w, mouse_msg_t msg, mouse_event_t * event) 733 { 734 WEdit *edit = (WEdit *) (w); 735 Widget *h = WIDGET (w->owner); 736 int global_x, global_y; 737 738 if (msg == MSG_MOUSE_UP) 739 { 740 /* Exit move/resize mode. */ 741 edit_execute_cmd (edit, CK_Enter, -1); 742 edit_update_screen (edit); /* Paint the buttonbar over our possibly overlapping frame. */ 743 return; 744 } 745 746 if (msg != MSG_MOUSE_DRAG) 747 /** 748 * We ignore any other events. Specifically, MSG_MOUSE_DOWN. 749 * 750 * When the move/resize is initiated by the menu, we let the user 751 * stop it by clicking with the mouse. Which is why we don't want 752 * a mouse down to affect the window. 753 */ 754 return; 755 756 /* Convert point to global coordinates for easier calculations. */ 757 global_x = event->x + w->x; 758 global_y = event->y + w->y; 759 760 /* Clamp the point to the dialog's client area. */ 761 global_y = CLAMP (global_y, h->y + 1, h->y + h->lines - 2); /* Status line, buttonbar */ 762 global_x = CLAMP (global_x, h->x, h->x + h->cols - 1); /* Currently a no-op, as the dialog has no left/right margins. */ 763 764 if (edit->drag_state == MCEDIT_DRAG_MOVE) 765 { 766 w->y = global_y; 767 w->x = global_x - edit->drag_state_start; 768 } 769 else if (edit->drag_state == MCEDIT_DRAG_RESIZE) 770 { 771 w->lines = MAX (WINDOW_MIN_LINES, global_y - w->y + 1); 772 w->cols = MAX (WINDOW_MIN_COLS, global_x - w->x + 1); 773 } 774 775 edit->force |= REDRAW_COMPLETELY; /* Not really needed as WEdit's MSG_DRAW already does this. */ 776 777 /* We draw the whole dialog because dragging/resizing exposes area beneath. */ 778 widget_draw (WIDGET (w->owner)); 779 } 780 781 /* --------------------------------------------------------------------------------------------- */ 782 /*** public functions ****************************************************************************/ 783 /* --------------------------------------------------------------------------------------------- */ 784 785 cb_ret_t 786 edit_dialog_command_execute (WDialog * h, long command, void *data) 787 { 788 WGroup *g = GROUP (h); 789 Widget *wh = WIDGET (h); 790 cb_ret_t ret = MSG_HANDLED; 791 792 switch (command) 793 { 794 case CK_EditNew: 795 edit_add_window (h, wh->y + 1, wh->x, wh->lines - 2, wh->cols, NULL, 0); 796 break; 797 case CK_EditFile: 798 edit_load_cmd (h); 799 break; 800 case CK_History: 801 edit_load_file_from_history (h); 802 break; 803 case CK_EditSyntaxFile: 804 edit_load_syntax_file (h); 805 break; 806 case CK_EditUserMenu: 807 edit_load_menu_file (h); 808 break; 809 case CK_Close: 810 /* if there are no opened files anymore, close MC editor */ 811 if ((edit_widget_is_editor (CONST_WIDGET (g->current->data)) || 812 edit_widget_is_cli (CONST_WIDGET (g->current->data))) && 813 edit_close_cmd ((WEdit *) g->current->data) && find_editor (h) == NULL) 814 dlg_stop (h); 815 break; 816 case CK_Help: 817 edit_help (); 818 /* edit->force |= REDRAW_COMPLETELY; */ 819 break; 820 case CK_Menu: 821 edit_menu_cmd (h); 822 break; 823 case CK_Quit: 824 case CK_Cancel: 825 /* don't close editor due to SIGINT, but stop move/resize window */ 826 { 827 Widget *w = WIDGET (g->current->data); 828 829 if (edit_widget_is_editor (w) && ((WEdit *) w)->drag_state != MCEDIT_DRAG_NONE) 830 edit_restore_size ((WEdit *) w); 831 else if (command == CK_Quit) 832 dlg_stop (h); 833 } 834 break; 835 case CK_About: 836 edit_about (); 837 break; 838 case CK_SyntaxOnOff: 839 edit_syntax_onoff_cmd (h); 840 break; 841 case CK_ShowTabTws: 842 edit_show_tabs_tws_cmd (h); 843 break; 844 case CK_ShowMargin: 845 edit_show_margin_cmd (h); 846 break; 847 case CK_ShowNumbers: 848 edit_show_numbers_cmd (h); 849 break; 850 case CK_Refresh: 851 edit_refresh_cmd (); 852 break; 853 case CK_InstructionStation: 854 edit_add_instr_stn_window (h, INSTR_STN_NO_FLAGS, data); 855 break; 856 case CK_Shell: 857 toggle_subshell (); 858 break; 859 case CK_LearnKeys: 860 learn_keys (); 861 break; 862 case CK_WindowMove: 863 case CK_WindowResize: 864 if (edit_widget_is_editor (CONST_WIDGET (g->current->data))) 865 edit_handle_move_resize ((WEdit *) g->current->data, command); 866 break; 867 case CK_WindowList: 868 edit_window_list (h); 869 break; 870 case CK_WindowNext: 871 group_select_next_widget (g); 872 break; 873 case CK_WindowPrev: 874 group_select_prev_widget (g); 875 break; 876 case CK_Options: 877 edit_options_dialog (h); 878 break; 879 case CK_OptionsSaveMode: 880 edit_save_mode_cmd (); 881 break; 882 case CK_SaveSetup: 883 save_setup_cmd (); 884 break; 885 default: 886 ret = MSG_NOT_HANDLED; 887 break; 888 } 889 890 return ret; 891 } 892 893 /* --------------------------------------------------------------------------------------------- */ 894 /** Callback for the edit dialog */ 895 896 cb_ret_t 897 edit_dialog_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data) 898 { 899 WGroup *g = GROUP (w); 900 WDialog *h = DIALOG (w); 901 902 switch (msg) 903 { 904 case MSG_INIT: 905 edit_dlg_init (); 906 return MSG_HANDLED; 907 908 case MSG_RESIZE: 909 dlg_default_callback (w, NULL, MSG_RESIZE, 0, NULL); 910 menubar_arrange (find_menubar (h)); 911 return MSG_HANDLED; 912 913 case MSG_ACTION: 914 { 915 /* Handle shortcuts, menu, and buttonbar. */ 916 917 cb_ret_t result; 918 919 result = edit_dialog_command_execute (h, parm, data); 920 921 /* We forward any commands coming from the menu, and which haven't been 922 handled by the dialog, to the focused WEdit window. */ 923 if (result == MSG_NOT_HANDLED && sender == WIDGET (find_menubar (h))) 924 result = send_message (g->current->data, NULL, MSG_ACTION, parm, NULL); 925 926 return result; 927 } 928 929 case MSG_KEY: 930 { 931 Widget *we = WIDGET (g->current->data); 932 cb_ret_t ret = MSG_NOT_HANDLED; 933 934 if (edit_widget_is_editor (we)) 935 { 936 gboolean ext_mode; 937 long command; 938 939 /* keep and then extmod flag */ 940 ext_mode = we->ext_mode; 941 command = widget_lookup_key (we, parm); 942 we->ext_mode = ext_mode; 943 944 if (command == CK_IgnoreKey) 945 we->ext_mode = FALSE; 946 else 947 { 948 ret = edit_dialog_command_execute (h, command, data); 949 /* if command was not handled, keep the extended mode 950 for the further key processing */ 951 if (ret == MSG_HANDLED) 952 we->ext_mode = FALSE; 953 } 954 } 955 956 /* 957 * Due to the "end of bracket" escape the editor sees input with is_idle() == false 958 * (expects more characters) and hence doesn't yet refresh the screen, but then 959 * no further characters arrive (there's only an "end of bracket" which is swallowed 960 * by tty_get_event()), so you end up with a screen that's not refreshed after pasting. 961 * So let's trigger an IDLE signal. 962 */ 963 if (!is_idle ()) 964 widget_idle (w, TRUE); 965 return ret; 966 } 967 968 /* hardcoded menu hotkeys (see edit_drop_hotkey_menu) */ 969 case MSG_UNHANDLED_KEY: 970 return edit_drop_hotkey_menu (h, parm) ? MSG_HANDLED : MSG_NOT_HANDLED; 971 972 case MSG_VALIDATE: 973 edit_quit (h); 974 return MSG_HANDLED; 975 976 case MSG_END: 977 edit_dlg_deinit (); 978 return MSG_HANDLED; 979 980 case MSG_IDLE: 981 widget_idle (w, FALSE); 982 return send_message (g->current->data, NULL, MSG_IDLE, 0, NULL); 983 984 default: 985 return dlg_default_callback (w, sender, msg, parm, data); 986 } 987 } 988 989 /* --------------------------------------------------------------------------------------------- */ 990 991 cb_ret_t 933 992 edit_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data) 934 993 { 935 994 WEdit *e = (WEdit *) w; … … edit_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *da 999 1058 1000 1059 /* --------------------------------------------------------------------------------------------- */ 1001 1060 1002 /**1003 * Handle move/resize mouse events.1004 */1005 static void1006 edit_mouse_handle_move_resize (Widget * w, mouse_msg_t msg, mouse_event_t * event)1007 {1008 WEdit *edit = (WEdit *) (w);1009 Widget *h = WIDGET (w->owner);1010 int global_x, global_y;1011 1012 if (msg == MSG_MOUSE_UP)1013 {1014 /* Exit move/resize mode. */1015 edit_execute_cmd (edit, CK_Enter, -1);1016 edit_update_screen (edit); /* Paint the buttonbar over our possibly overlapping frame. */1017 return;1018 }1019 1020 if (msg != MSG_MOUSE_DRAG)1021 /**1022 * We ignore any other events. Specifically, MSG_MOUSE_DOWN.1023 *1024 * When the move/resize is initiated by the menu, we let the user1025 * stop it by clicking with the mouse. Which is why we don't want1026 * a mouse down to affect the window.1027 */1028 return;1029 1030 /* Convert point to global coordinates for easier calculations. */1031 global_x = event->x + w->x;1032 global_y = event->y + w->y;1033 1034 /* Clamp the point to the dialog's client area. */1035 global_y = CLAMP (global_y, h->y + 1, h->y + h->lines - 2); /* Status line, buttonbar */1036 global_x = CLAMP (global_x, h->x, h->x + h->cols - 1); /* Currently a no-op, as the dialog has no left/right margins. */1037 1038 if (edit->drag_state == MCEDIT_DRAG_MOVE)1039 {1040 w->y = global_y;1041 w->x = global_x - edit->drag_state_start;1042 }1043 else if (edit->drag_state == MCEDIT_DRAG_RESIZE)1044 {1045 w->lines = MAX (WINDOW_MIN_LINES, global_y - w->y + 1);1046 w->cols = MAX (WINDOW_MIN_COLS, global_x - w->x + 1);1047 }1048 1049 edit->force |= REDRAW_COMPLETELY; /* Not really needed as WEdit's MSG_DRAW already does this. */1050 1051 /* We draw the whole dialog because dragging/resizing exposes area beneath. */1052 widget_draw (WIDGET (w->owner));1053 }1054 1055 /* --------------------------------------------------------------------------------------------- */1056 1057 1061 /** 1058 1062 * Handle mouse events of editor window 1059 1063 * … … edit_mouse_handle_move_resize (Widget * w, mouse_msg_t msg, mouse_event_t * even 1061 1065 * @param msg mouse event message 1062 1066 * @param event mouse event data 1063 1067 */ 1064 staticvoid1068 void 1065 1069 edit_mouse_callback (Widget * w, mouse_msg_t msg, mouse_event_t * event) 1066 1070 { 1067 1071 WEdit *edit = (WEdit *) w; … … edit_mouse_callback (Widget * w, mouse_msg_t msg, mouse_event_t * event) 1176 1180 } 1177 1181 1178 1182 /* --------------------------------------------------------------------------------------------- */ 1179 /*** public functions ****************************************************************************/ 1183 /* Creates a CLI special window, windowed (not fullscreen) */ 1184 1185 gboolean 1186 edit_add_instr_stn_window (WDialog * h, instr_stn_flags_t flags, void *data) 1187 { 1188 WInstructionStation *ip; 1189 1190 /* Note passing of data - ability to alter program that's run in this station */ 1191 ip = instr_stn_create (3, 10, 17, 80, flags, (const char *) data); 1192 1193 if (ip == NULL) 1194 return FALSE; 1195 1196 /* Add CLI-extended editor window (extending WEdit) to front dialog */ 1197 group_add_widget_autopos (GROUP (h), ip, WPOS_KEEP_ALL, NULL); 1198 widget_draw (WIDGET (h)); 1199 return TRUE; 1200 } 1201 1180 1202 /* --------------------------------------------------------------------------------------------- */ 1181 1203 /** 1182 1204 * Edit one file. … … edit_add_window (WDialog * h, int y, int x, int lines, int cols, const vfs_path_ 1369 1391 WEdit *edit; 1370 1392 Widget *w; 1371 1393 1372 edit = edit_init (NULL, y, x, lines, cols, f, fline );1394 edit = edit_init (NULL, y, x, lines, cols, f, fline, 0); 1373 1395 if (edit == NULL) 1374 1396 return FALSE; 1375 1397 -
src/editor/editwidget.h
diff --git a/src/editor/editwidget.h b/src/editor/editwidget.h index 446ef07ac..e8d1586e2 100644
a b 13 13 14 14 /*** typedefs(not structures) and defined constants **********************************************/ 15 15 16 #define EDIT(x) ((WEdit *)(x)) 17 #define CONST_EDIT(x) ((const WEdit *)(x)) 18 16 19 #define N_LINE_CACHES 32 17 20 18 21 /*** enums ***************************************************************************************/ -
new file src/editor/instr_station.c
diff --git a/src/editor/instr_station.c b/src/editor/instr_station.c new file mode 100644 index 000000000..76c7bab03
- + 1 /* 2 Implementation of a CLI window for MCEdit. 3 4 Copyright (C) 2021 5 Free Software Foundation, Inc. 6 7 Written by: 8 Sebastian Gniazdowski <sgniazdowski@gmail.com>, 2021. 9 10 This file is part of the Midnight Commander. 11 12 The Midnight Commander is free software: you can redistribute it 13 and/or modify it under the terms of the GNU General Public License as 14 published by the Free Software Foundation, either version 3 of the License, 15 or (at your option) any later version. 16 17 The Midnight Commander is distributed in the hope that it will be useful, 18 but WITHOUT ANY WARRANTY; without even the implied warranty of 19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 GNU General Public License for more details. 21 22 You should have received a copy of the GNU General Public License 23 along with this program. If not, see <http://www.gnu.org/licenses/>. 24 */ 25 26 /** \file instr_station.c 27 * \brief Implementation of a CLI special window for MCEdit. 28 * \author Sebastian Gniazdowski 29 * \date 2021 30 * 31 * Such window runs a set up program (/bin/bash by default) allowing to 32 * provide an input to it and read its output. 33 */ 34 35 #include <config.h> 36 37 #include "lib/global.h" 38 39 #include <sys/types.h> 40 #include <sys/wait.h> 41 42 #include "src/editor/editwidget.h" 43 44 #include "lib/util.h" 45 #include "lib/tty/key.h" 46 #include "lib/widget.h" 47 #include "src/editor/instr_station.h" 48 49 /*** global variables ****************************************************************************/ 50 51 /*** file scope macro definitions ****************************************************************/ 52 53 /*** file scope type declarations ****************************************************************/ 54 55 typedef enum instr_stn_io_type 56 { 57 STDIN = 0, 58 STDOUT, 59 STDERR 60 } instr_stn_io_type_t; 61 62 /*** file scope variables ************************************************************************/ 63 64 static gboolean alt_prog_first_run = TRUE; 65 66 /*** file scope functions ************************************************************************/ 67 /* --------------------------------------------------------------------------------------------- */ 68 static void instr_stn_clear_prompt (WInstructionStation * ip); 69 static void instr_stn_show_prompt (WInstructionStation * ip, gboolean is_ok); 70 71 static char * 72 instr_stn_get_instruction_text (WInstructionStation * ip) 73 { 74 char *ret_str; 75 GString *instr; 76 off_t bol, eol, begin_instr; 77 long size; 78 int idx, byte; 79 80 /* Calculate offset of text after prompt */ 81 bol = edit_buffer_get_current_bol (&EDIT (ip)->buffer); 82 eol = edit_buffer_get_current_eol (&EDIT (ip)->buffer); 83 begin_instr = bol + ip->prompt_span; 84 85 /* Is there anything entered? */ 86 size = ((long) eol) - ((long) begin_instr); 87 if (size <= 0) 88 return NULL; 89 90 /* Allocate expected size string and fill it */ 91 instr = g_string_sized_new (size + 2); 92 for (idx = 0; idx < size; idx++) 93 { 94 byte = edit_buffer_get_byte (&EDIT (ip)->buffer, begin_instr + idx); 95 g_string_append_c (instr, byte); 96 } 97 98 /* Append new line if needed */ 99 if (instr->str[instr->len - 1] != '\n') 100 g_string_append_c (instr, '\n'); 101 102 /* Return char buffer */ 103 ret_str = instr->str; 104 g_string_free (instr, FALSE); 105 return ret_str; 106 } 107 108 /* --------------------------------------------------------------------------------------------- */ 109 110 static gboolean 111 append_text_to_buffer (WInstructionStation * ip, char *buf, gsize count) 112 { 113 gsize idx = 0, nl_count = 0; 114 gboolean is_first = ip->first_response; 115 116 /* Count number of read lines */ 117 char *p = buf; 118 while (idx++ < count) 119 if (*p++ == '\n') 120 nl_count++; 121 122 /* Move to next line after prompt */ 123 if (ip->first_response) 124 { 125 edit_cursor_to_eol (EDIT (ip)); 126 edit_insert (EDIT (ip), '\n'); 127 nl_count++; 128 ip->first_response = FALSE; 129 ip->prompt_shown = FALSE; 130 } 131 else if (ip->prompt_shown) 132 instr_stn_clear_prompt (ip); 133 134 /* Print whole buffer into file's window */ 135 edit_insert_string (EDIT (ip), buf, count); 136 137 /* Increase the row pointer */ 138 ip->cur_line += nl_count; 139 140 return is_first; 141 } 142 143 /* --------------------------------------------------------------------------------------------- */ 144 145 static gboolean 146 helper_try_append_flow_control_data (WInstructionStation * ip, gboolean force, gboolean restore) 147 { 148 off_t curs_save = -1; 149 long top_line_save = -1; 150 151 /* Is there any "control flow" bufered data ? Should be at prompt or force should be TRUE */ 152 if (ip->flow_ctrl_buf == NULL || ip->flow_ctrl_buf->len == 0 153 || ((EDIT (ip)->buffer.curs_line != ip->cur_line) && !force)) 154 return FALSE; 155 156 if (force) 157 { 158 if (restore) 159 { 160 top_line_save = EDIT (ip)->start_line; 161 curs_save = EDIT (ip)->buffer.curs1; 162 } 163 edit_move_to_bottom (EDIT (ip)); 164 edit_cursor_to_eol (EDIT (ip)); 165 } 166 167 /* First, append text postponed earlier */ 168 append_text_to_buffer (ip, (char *) ip->flow_ctrl_buf->data, ip->flow_ctrl_buf->len); 169 g_byte_array_free (ip->flow_ctrl_buf, TRUE); 170 ip->flow_ctrl_buf = NULL; 171 172 /* Ensure that prompt is drawn now, when at end */ 173 instr_stn_show_prompt (ip, TRUE); 174 175 if (restore && curs_save != -1) 176 { 177 edit_cursor_move (EDIT (ip), -1 * (EDIT (ip)->buffer.curs1 - curs_save)); 178 edit_move_display (EDIT (ip), top_line_save); 179 } 180 return TRUE; 181 } 182 183 /* --------------------------------------------------------------------------------------------- */ 184 185 static gboolean 186 instr_stn_release (WInstructionStation * ip, gboolean free_all) 187 { 188 /* Already relased? */ 189 if (ip == NULL) 190 { 191 if (free_all) 192 return TRUE; 193 else 194 return FALSE; 195 } 196 197 /* Already cleaned up (for a restart of program) ? */ 198 if (ip->finalized && !free_all) 199 return FALSE; 200 ip->finalized = TRUE; 201 202 /* Close process watcher first, suppressing *program_ended_cb() callback */ 203 if (ip->proc_src_id != 0) 204 { 205 GSource *src; 206 src = g_main_context_find_source_by_id (NULL, ip->proc_src_id); 207 if (src != NULL) 208 g_source_destroy (src); 209 ip->proc_src_id = 0; 210 } 211 212 /* Append any buffered text first moving to end if needed */ 213 helper_try_append_flow_control_data (ip, TRUE, FALSE); 214 215 /* Release the pipes, channels, etc. */ 216 for (int i = 0; i <= STDERR; i++) 217 { 218 /* Source */ 219 if (ip->io[i].src_id != 0) 220 { 221 GSource *src; 222 src = g_main_context_find_source_by_id (NULL, ip->io[i].src_id); 223 if (src != NULL) 224 g_source_destroy (src); 225 ip->io[i].src_id = 0; 226 } 227 /* Channel */ 228 if (ip->io[i].ch != NULL) 229 { 230 g_io_channel_unref (ip->io[i].ch); 231 ip->io[i].ch = NULL; 232 } 233 /* Pipe */ 234 if (ip->io[i].fd >= 0) 235 { 236 close (ip->io[i].fd); 237 ip->io[i].fd = -1; 238 } 239 } 240 241 /* Remove prompt guard recurring timeout callback function */ 242 if (ip->prompt_timeout_id != 0) 243 { 244 g_source_remove (ip->prompt_timeout_id); 245 ip->prompt_timeout_id = 0; 246 } 247 248 MC_PTR_FREE (ip->instruction); 249 /* Release prompt */ 250 MC_PTR_FREE (ip->prompt); 251 /* Release error prompt */ 252 MC_PTR_FREE (ip->eprompt); 253 254 /* Clear prompt information */ 255 ip->prompt_span = 0; 256 ip->eprompt_span = 0; 257 258 /* Set various indicator flags to some inactive state */ 259 ip->io_complete = TRUE; 260 ip->first_response = TRUE; 261 ip->before_first_prompt = TRUE; 262 ip->prompt_shown = FALSE; 263 ip->flags = INSTR_STN_NO_FLAGS; 264 265 if (ip->flow_ctrl_buf != NULL) 266 { 267 g_byte_array_free (ip->flow_ctrl_buf, TRUE); 268 ip->flow_ctrl_buf = NULL; 269 } 270 271 /* Clear any GError */ 272 if (ip->error != NULL) 273 { 274 g_error_free (ip->error); 275 ip->error = NULL; 276 } 277 278 /* Release space occupied by WInstructionStation object? */ 279 if (free_all) 280 { 281 MC_PTR_FREE (ip->program); 282 /* Free main object */ 283 g_free (ip); 284 } 285 else 286 /* Value is still accessible in prev_program field */ 287 ip->program = NULL; 288 289 return TRUE; 290 } 291 292 /* --------------------------------------------------------------------------------------------- */ 293 294 static cb_ret_t 295 instr_stn_run_novel_state_id (WInstructionStation * ip, long novel_state_id, void *data) 296 { 297 WGroup *owner = WIDGET (ip)->owner; 298 cb_ret_t ret = MSG_NOT_HANDLED; 299 300 switch (novel_state_id) 301 { 302 case CK_BackSpace: 303 if (ip->cur_col <= ip->prompt_span) 304 ret = MSG_HANDLED; 305 else 306 { 307 EDIT (ip)->force |= REDRAW_PAGE; 308 edit_update_screen (EDIT (ip)); 309 } 310 break; 311 case CK_Enter: 312 case CK_Return: 313 if (EDIT (ip)->buffer.curs_line == ip->cur_line) 314 { 315 ip->instruction = instr_stn_get_instruction_text (ip); 316 ip->io_complete = FALSE; 317 /* Should dispatch to write callback which will send instruction */ 318 g_main_context_iteration (NULL, 0); 319 repaint_screen (); 320 } 321 ret = MSG_HANDLED; 322 break; 323 /* InstructionStation activity invoked when focus is at a station window behaves differently */ 324 case CK_InstructionStation: 325 { 326 /* Save needed information */ 327 char *program_save; 328 329 /* End previous program */ 330 if (kill (ip->process_id, 15) == -1) 331 mc_log ("Problem sending TERM signal to %d: %s.", ip->process_id, 332 g_strerror (errno)); 333 else 334 mc_log ("Ending process %d.", ip->process_id); 335 if (!ip->proc_support) 336 waitpid (ip->process_id, NULL, 0); 337 338 /* Allow process watcher callbacks to be called */ 339 g_usleep (150000); 340 g_main_context_iteration (NULL, FALSE); 341 /* Allow the process end message to flash noticeably long */ 342 g_usleep (200000); 343 344 /* Clean WInstructionStation object */ 345 instr_stn_release (ip, FALSE); 346 /* ...and reinitialize it */ 347 program_save = data != NULL ? g_strdup (data) : ip->prev_program; 348 if (instr_stn_init (ip, -1, -1, -1, -1, ip->prev_flags, program_save) == NULL) 349 { 350 /* Init failed -> close faulty station window */ 351 ret = 352 edit_dialog_callback (WIDGET (owner), WIDGET (ip), MSG_ACTION, CK_Close, NULL); 353 if (ret == MSG_HANDLED) 354 /* Update pointer in case of some future code like widget_draw(ip), etc. */ 355 ip = INSTR_STATION (owner->current->data); 356 } 357 else 358 { 359 repaint_screen (); 360 ret = MSG_HANDLED; 361 } 362 g_free (program_save); 363 } 364 break; 365 default: 366 break; 367 } 368 return ret; 369 } 370 371 /* --------------------------------------------------------------------------------------------- */ 372 373 static void 374 instr_stn_show_prompt (WInstructionStation * ip, gboolean is_ok) 375 { 376 char *sel_prompt = is_ok ? ip->prompt : ip->eprompt; 377 int sel_prompt_span = is_ok ? ip->prompt_span : ip->eprompt_span; 378 379 if (has_flag (ip->flags, INSTR_STN_NO_PROMPT)) 380 return; 381 382 /* Print prompt */ 383 if (!edit_newline_end_check_only (EDIT (ip)) && !ip->before_first_prompt) 384 { 385 edit_insert (EDIT (ip), '\n'); 386 ip->cur_line += 1; 387 } 388 else 389 ip->before_first_prompt = FALSE; 390 edit_insert_string (EDIT (ip), sel_prompt, sel_prompt_span); 391 392 /* Raise an indicator flag */ 393 ip->prompt_shown = TRUE; 394 395 /* Set cursor position to reflect state */ 396 ip->cur_col = sel_prompt_span; 397 } 398 399 /* --------------------------------------------------------------------------------------------- */ 400 /* Remove prompt from buffer (basically clear current line). */ 401 402 static void 403 instr_stn_clear_prompt (WInstructionStation * ip) 404 { 405 off_t bol, eol, idx; 406 407 if (has_flag (ip->flags, INSTR_STN_NO_PROMPT)) 408 return; 409 410 edit_cursor_to_eol (EDIT (ip)); 411 eol = edit_buffer_get_current_eol (&EDIT (ip)->buffer); 412 bol = edit_buffer_get_current_bol (&EDIT (ip)->buffer); 413 for (idx = 0; idx < eol - bol; idx++) 414 edit_backspace (EDIT (ip), TRUE); 415 ip->prompt_shown = FALSE; 416 } 417 418 /* --------------------------------------------------------------------------------------------- */ 419 420 static gboolean 421 maintenance_timeout_cb (gpointer data) 422 { 423 WInstructionStation *ip = INSTR_STATION (data); 424 guint64 cur_time; 425 gboolean draw = FALSE; 426 427 cur_time = g_get_real_time (); 428 429 /* Returned at prompt line? Append pending text */ 430 if (EDIT (ip)->buffer.curs_line == ip->cur_line && ip->flow_ctrl_buf != NULL) 431 draw |= helper_try_append_flow_control_data (ip, FALSE, FALSE); 432 433 /* Some time scrolled up? Force temporary jump to the prompt and append pending text */ 434 if (cur_time - ip->read_time > 30000000 && ip->flow_ctrl_buf != NULL) 435 draw |= helper_try_append_flow_control_data (ip, TRUE, TRUE); 436 437 /* Draw prompt after 0.5 seconds of no output from program */ 438 if (cur_time - ip->read_time > 500000 && !ip->prompt_shown) 439 { 440 instr_stn_show_prompt (ip, TRUE); 441 draw = TRUE; 442 } 443 444 if (draw) 445 repaint_screen (); 446 return TRUE; 447 } 448 449 /* --------------------------------------------------------------------------------------------- */ 450 451 #ifdef GLIB_VERSION_2_40 452 453 static void 454 program_ended_callback (GPid pid, gint exit_code, gpointer user_data) 455 { 456 WInstructionStation *ip = INSTR_STATION (user_data); 457 char *msg; 458 gboolean st_ret; 459 460 /* Release object leaving it uninitialized (no freeing of main object pointer) */ 461 instr_stn_release (ip, FALSE); 462 463 g_spawn_close_pid (pid); 464 465 /* Examine exit code of closed program */ 466 #ifdef GLIB_VERSION_2_34 467 st_ret = g_spawn_check_exit_status (exit_code, NULL); 468 #else 469 st_ret = (exit_code == 0); 470 #endif 471 472 /* Choose message basing on exit code */ 473 if (st_ret) 474 msg = g_strdup (_("\nProgram closed.\n")); 475 else 476 msg = g_strdup_printf (_("\nProgram closed abnormally (exit code: %d).\n"), exit_code); 477 478 /* Insert/print message to buffer and to mc.log */ 479 edit_insert_string (EDIT (ip), msg, strlen (msg)); 480 mc_log ("%s", msg); 481 482 /* Release message upon using/printing it */ 483 g_free (msg); 484 repaint_screen (); 485 } 486 487 #endif 488 489 /* --------------------------------------------------------------------------------------------- */ 490 491 static gboolean 492 helper_configure_src_and_ch (GIOChannel ** ch, gint fd, guint * src_id, gboolean write, 493 GIOFunc cb_fun, gpointer data, const char *name) 494 { 495 /* Initialize the output variables */ 496 *ch = NULL; 497 *src_id = 0; 498 499 *ch = g_io_channel_unix_new (fd); 500 501 /* Channel created? */ 502 if (*ch == NULL) 503 goto cleanup_and_err; 504 505 /* Automatic shutdown of channel */ 506 g_io_channel_set_close_on_unref (*ch, TRUE); 507 508 /* Trim down buffering on this channel */ 509 g_io_channel_set_buffer_size (*ch, 5); 510 511 /* Attempt to set non-blocking flag on channel */ 512 if (g_io_channel_set_flags (*ch, G_IO_FLAG_NONBLOCK, NULL) != G_IO_STATUS_NORMAL) 513 mc_log ("Problem setting a channel to non-blocking state (channel flags: 0x%x)", 514 g_io_channel_get_flags (*ch)); 515 516 /* Create GSource */ 517 *src_id = 518 g_io_add_watch (*ch, (write ? G_IO_OUT : G_IO_IN) | G_IO_HUP | G_IO_ERR, cb_fun, data); 519 520 /* Source created OK? */ 521 if (*src_id == 0) 522 goto cleanup_and_err; 523 524 /* Configure the sources */ 525 g_source_set_name_by_id (*src_id, name); 526 527 /* Return true */ 528 return TRUE; 529 530 cleanup_and_err: 531 if (*src_id != 0) 532 { 533 GSource *src; 534 src = g_main_context_find_source_by_id (NULL, *src_id); 535 if (src != NULL) 536 /* Triggers also unref of wrapped channel */ 537 g_source_destroy (src); 538 } 539 if (*ch != NULL) 540 g_io_channel_unref (*ch); 541 *ch = NULL; 542 *src_id = 0; 543 544 return FALSE; 545 } 546 547 /* --------------------------------------------------------------------------------------------- */ 548 549 static gboolean 550 helper_read_all (GIOChannel * channel, char *buf, gsize * out_bytes_read_in_len) 551 { 552 gsize len = *out_bytes_read_in_len; 553 gsize count, retries = 10; 554 GIOStatus stat; 555 556 *out_bytes_read_in_len = 0; 557 558 while (*out_bytes_read_in_len < len) 559 { 560 count = 0; 561 stat = 562 g_io_channel_read_chars (channel, buf + *out_bytes_read_in_len, 563 len - *out_bytes_read_in_len, &count, NULL); 564 *out_bytes_read_in_len += count; 565 566 /* Should end the reading ? */ 567 if (stat == G_IO_STATUS_ERROR || (stat == G_IO_STATUS_NORMAL && count == 0) || 568 stat == G_IO_STATUS_EOF) 569 return (stat != G_IO_STATUS_ERROR); 570 else if (stat == G_IO_STATUS_AGAIN && retries-- == 0) 571 /* Exhausted retries - there must be no data to read - return OK */ 572 return TRUE; 573 } 574 575 return TRUE; 576 } 577 578 /* --------------------------------------------------------------------------------------------- */ 579 580 static gboolean 581 helper_write_all (GIOChannel * channel, char *buf, gsize * out_bytes_written_in_len) 582 { 583 gsize len = *out_bytes_written_in_len; 584 gsize count; 585 GIOError err; 586 587 *out_bytes_written_in_len = 0; 588 589 while (*out_bytes_written_in_len < len) 590 { 591 count = 0; 592 err = 593 g_io_channel_write (channel, buf + *out_bytes_written_in_len, 594 len - *out_bytes_written_in_len, &count); 595 *out_bytes_written_in_len += count; 596 597 if (err && err != G_IO_ERROR_AGAIN) 598 return FALSE; 599 } 600 601 return TRUE; 602 } 603 604 /* --------------------------------------------------------------------------------------------- */ 605 606 static gboolean 607 stdout_read_callback (GIOChannel * source, GIOCondition condition, gpointer data) 608 { 609 WInstructionStation *ip = INSTR_STATION (data); 610 char buf[2048]; 611 gsize count = 2047; 612 gboolean r_ret; 613 614 /* Active only when a recognized event of interest occurs */ 615 if ((condition & (G_IO_IN | G_IO_HUP | G_IO_ERR)) == 0) 616 return TRUE; 617 else 618 ip->io_complete = FALSE; 619 620 /* Repeat read of 2KiB-1 maximum bytes until 0 bytes are being read */ 621 do 622 { 623 r_ret = helper_read_all (source, buf, &count); 624 buf[count] = '\0'; 625 if (count == 0 && !r_ret) 626 { 627 mc_log ("Error while reading program's output: %s", g_strerror (errno)); 628 } 629 else 630 { 631 ip->read_time = g_get_real_time (); 632 buf[count] = '\0'; 633 if (count > 0 && (EDIT (ip)->buffer.curs_line < ip->cur_line)) 634 { 635 if (ip->flow_ctrl_buf == NULL) 636 ip->flow_ctrl_buf = g_byte_array_sized_new (2048); 637 /* Save text until user scrolls to prompt */ 638 g_byte_array_append (ip->flow_ctrl_buf, (guint8 *) buf, count); 639 } 640 else if (EDIT (ip)->buffer.curs_line == ip->cur_line) 641 { 642 /* Always check for any buffered data before writing present one */ 643 helper_try_append_flow_control_data (ip, FALSE, FALSE); 644 645 /* Append data read now */ 646 if (count > 0) 647 append_text_to_buffer (ip, buf, count); 648 else 649 /* No data read? Show prompt */ 650 if (!ip->prompt_shown) 651 instr_stn_show_prompt (ip, r_ret); 652 } 653 654 repaint_screen (); 655 } 656 } 657 while (count != 0); 658 659 ip->io_complete = r_ret; 660 return condition != G_IO_HUP; 661 } 662 663 /* --------------------------------------------------------------------------------------------- */ 664 665 static gboolean 666 stdin_write_callback (GIOChannel * source, GIOCondition condition, gpointer data) 667 { 668 WInstructionStation *ip = INSTR_STATION (data); 669 gsize count; 670 gboolean ret = TRUE; 671 672 /* Any instruction to send? */ 673 if (ip->instruction == NULL) 674 return ret; 675 else 676 ip->io_complete = FALSE; 677 678 if ((condition & G_IO_OUT) == 0) 679 return TRUE; 680 681 errno = 0; 682 count = strlen (ip->instruction); 683 ret = helper_write_all (source, ip->instruction, &count); 684 if (!ret) 685 { 686 mc_log ("Error during sending instruction to program: %s", g_strerror (errno)); 687 } 688 else 689 { 690 ip->first_response = TRUE; 691 MC_PTR_FREE (ip->instruction); 692 if (ip->prompt_timeout_id == 0) 693 ip->prompt_timeout_id = g_timeout_add (200, maintenance_timeout_cb, ip); 694 } 695 696 g_io_channel_flush (ip->io[STDIN].ch, NULL); 697 ip->io_complete = TRUE; 698 699 return ret; 700 } 701 702 /* --------------------------------------------------------------------------------------------- */ 703 704 /* --------------------------------------------------------------------------------------------- */ 705 /*** public functions ****************************************************************************/ 706 /* --------------------------------------------------------------------------------------------- */ 707 708 WInstructionStation * 709 instr_stn_create (int y, int x, int lines, int cols, instr_stn_flags_t flags, const char *program) 710 { 711 WInstructionStation *ip, *iret; 712 713 ip = g_new0 (WInstructionStation, 1); 714 if (ip == NULL) 715 return NULL; 716 717 iret = instr_stn_init (ip, y, x, lines, cols, flags, program); 718 if (iret == NULL) 719 MC_PTR_FREE (ip); 720 721 return ip; 722 } 723 724 /* --------------------------------------------------------------------------------------------- */ 725 /* Initializes a preallocated object */ 726 727 WInstructionStation * 728 instr_stn_init (WInstructionStation * ip, int y, int x, int lines, int cols, 729 instr_stn_flags_t flags, const char *program) 730 { 731 WEdit *iret; 732 WGroup *owner_save = NULL; 733 gboolean tmp_ret = FALSE; 734 char **program_cline; 735 int rounds = 0; 736 737 if (ip == NULL) 738 goto cleanup_and_err; 739 740 /* Handle special -1 values indicating reuse of old values */ 741 if (ip->finalized) 742 { 743 y = (y == -1) ? WIDGET (ip)->y : y; 744 x = (x == -1) ? WIDGET (ip)->x : x; 745 lines = (lines == -1) ? WIDGET (ip)->lines : lines; 746 cols = (cols == -1) ? WIDGET (ip)->cols : cols; 747 /* Save owner */ 748 owner_save = WIDGET (ip)->owner; 749 ip->finalized = FALSE; 750 } 751 752 /* Initialize base object (WEdit) */ 753 iret = edit_init (EDIT (ip), y, x, lines, cols, NULL, 0, EDIT_DO_INIT_BASE_CLASS); 754 if (iret == NULL) 755 goto cleanup_and_err; 756 757 /* Restore owner field value */ 758 WIDGET (ip)->owner = owner_save; 759 WIDGET (ip)->callback = instr_stn_callback; 760 WIDGET (ip)->mouse_callback = edit_mouse_callback; 761 EDIT (ip)->fullscreen = 0; 762 763 ip->io[0].name = g_strdup ("Std-In (WInstructionStation)"); 764 ip->io[1].name = g_strdup ("Std-Out (WInstructionStation)"); 765 ip->io[2].name = g_strdup ("Std-Error (WInstructionStation)"); 766 ip->io[0].fd = ip->io[1].fd = ip->io[2].fd = -1; 767 768 ip->before_first_prompt = TRUE; 769 ip->prompt = g_strdup ("[guest@localhost]# "); 770 ip->prompt_span = strlen (ip->prompt); 771 ip->eprompt = g_strdup ("[guest@localhost]! "); 772 ip->eprompt_span = strlen (ip->eprompt); 773 ip->cur_col = 0; 774 ip->cur_line = 0; 775 ip->flags = flags; 776 ip->prev_flags = flags; 777 778 /* Set initial program (most probably a shell, like Bash) */ 779 if (program != NULL) 780 { 781 if (program[0] == 's' && program[1] == 'h') 782 ip->program = g_strdup (program); 783 else 784 ip->program = g_strdup_printf ("sh -c '%s'", program); 785 } 786 else 787 /* Fallback instruction if no program given */ 788 ip->program = g_strdup ("bash"); 789 790 while (++rounds <= 2 && !tmp_ret) 791 { 792 int argc = 0; 793 if (ip->error != NULL) 794 { 795 g_error_free (ip->error); 796 ip->error = NULL; 797 } 798 tmp_ret = g_shell_parse_argv (ip->program, &argc, &program_cline, &ip->error); 799 if (tmp_ret) 800 { 801 tmp_ret = g_spawn_async_with_pipes (NULL, program_cline, 802 NULL, G_SPAWN_SEARCH_PATH | 803 G_SPAWN_DO_NOT_REAP_CHILD | 804 G_SPAWN_LEAVE_DESCRIPTORS_OPEN, 805 NULL, NULL, &ip->process_id, 806 has_flag (flags, 807 INSTR_STN_NO_STDIN) ? NULL : 808 &ip->io[STDIN].fd, &ip->io[STDOUT].fd, 809 &ip->io[STDERR].fd, &ip->error); 810 g_strfreev (program_cline); 811 } 812 if (!tmp_ret) 813 { 814 char *new_program, *msg_text; 815 mc_log ("Creating CLI process [instruction:%s] failed: %s", 816 program, ip->error ? ip->error->message : "<no error message>"); 817 818 /* Create a transatable, parametrized message */ 819 msg_text = g_strdup_printf ("%s%s:", _("The specified program has failed to run.\n" 820 "Enter a new full path or a program name"), 821 (rounds >= 2 ? _(" (last try)") : "")); 822 823 /* Display message asking for a new program */ 824 new_program = input_expand_dialog (_("Provide an alternate program to run"), 825 msg_text, "instruction-station", 826 alt_prog_first_run ? ip->program : INPUT_LAST_TEXT, 827 INPUT_COMPLETE_COMMANDS | INPUT_COMPLETE_FILENAMES | 828 INPUT_COMPLETE_VARIABLES); 829 g_free (msg_text); 830 alt_prog_first_run = FALSE; 831 832 /* Obtained a new program? */ 833 if (new_program != NULL) 834 { 835 g_free (ip->program); 836 ip->program = new_program; 837 } 838 } 839 } 840 841 /* If no program to run has been found, then exit. */ 842 if (!tmp_ret) 843 { 844 message (D_ERROR, _("InstructionStation startup failed"), 845 _("Failed to initialize the CLI window\n(reason: %s)."), 846 ip->error ? ip->error->message : "<no error message>"); 847 goto cleanup_and_err; 848 } 849 850 ip->prev_program = ip->program; 851 852 for (int i = 0; i <= STDERR; i++) 853 { 854 if (has_flag (flags, INSTR_STN_NO_STDIN) && i == STDIN) 855 { 856 /* There is no standard input pipe for program */ 857 ip->io[i].fd = -1; 858 continue; 859 } 860 861 if (helper_configure_src_and_ch (&ip->io[i].ch, ip->io[i].fd, &ip->io[i].src_id, 862 (i == STDIN ? TRUE : FALSE), 863 (i == STDIN ? stdin_write_callback : stdout_read_callback), 864 ip, ip->io[i].name) == FALSE) 865 goto cleanup_and_err; 866 } 867 868 #ifdef GLIB_VERSION_2_40 869 /* Set a process watcher and restarter for the program */ 870 ip->proc_src_id = g_child_watch_add (ip->process_id, program_ended_callback, ip); 871 ip->proc_support = TRUE; 872 #endif 873 874 /* Display initial prompt */ 875 instr_stn_show_prompt (ip, TRUE); 876 ip->io_complete = TRUE; 877 878 return ip; 879 880 cleanup_and_err: 881 882 instr_stn_release (ip, TRUE); 883 return NULL; 884 } 885 886 /* --------------------------------------------------------------------------------------------- */ 887 888 WEdit * 889 find_cli (const WDialog * h) 890 { 891 const WGroup *g = CONST_GROUP (h); 892 893 if (edit_widget_is_cli (CONST_WIDGET (g->current->data))) 894 return (WEdit *) g->current->data; 895 return (WEdit *) widget_find_by_type (CONST_WIDGET (h), instr_stn_callback); 896 } 897 898 /* --------------------------------------------------------------------------------------------- */ 899 /* Check if widget is a WInstructionStation class type. */ 900 901 gboolean 902 edit_widget_is_cli (const Widget * w) 903 { 904 return (w != NULL && w->callback == instr_stn_callback); 905 } 906 907 /* --------------------------------------------------------------------------------------------- */ 908 909 cb_ret_t 910 instr_stn_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data) 911 { 912 cb_ret_t ret = MSG_NOT_HANDLED; 913 WInstructionStation *ip = INSTR_STATION (w); 914 WGroup *owner = w->owner; 915 916 switch (msg) 917 { 918 919 case MSG_KEY: 920 { 921 gboolean ext_mode; 922 long novel_state_id; 923 924 /* keep and then extmod flag */ 925 ext_mode = w->ext_mode; 926 novel_state_id = widget_lookup_key (w, parm); 927 w->ext_mode = ext_mode; 928 929 if (novel_state_id == CK_IgnoreKey) 930 w->ext_mode = FALSE; 931 else 932 { 933 /* Treat Station action specially when run from within a station window */ 934 if (novel_state_id != CK_InstructionStation) 935 ret = edit_dialog_command_execute (DIALOG (owner), novel_state_id, data); 936 if (ret == MSG_NOT_HANDLED) 937 ret = instr_stn_run_novel_state_id (ip, novel_state_id, data); 938 /* 939 * If activity was not handled, keep the extended mode 940 * for the further key processing, checking if the 941 * window is still open */ 942 if (novel_state_id != CK_Close) 943 { 944 if (ret == MSG_HANDLED) 945 w->ext_mode = FALSE; 946 } 947 else if (ret == MSG_HANDLED) 948 /* Window closed -> update `w` variable */ 949 w = WIDGET (owner->current->data); 950 } 951 952 /* 953 * Due to the "end of bracket" escape the editor sees input with is_idle() == false 954 * (expects more characters) and hence doesn't yet refresh the screen, but then 955 * no further characters arrive (there's only an "end of bracket" which is swallowed 956 * by tty_get_event()), so you end up with a screen that's not refreshed after pasting. 957 * So let's trigger an IDLE signal. 958 */ 959 if (!is_idle ()) 960 widget_idle (w, TRUE); 961 } 962 break; 963 case MSG_ACTION: 964 ret = instr_stn_run_novel_state_id (ip, parm, data); 965 break; 966 case MSG_DESTROY: 967 kill (ip->process_id, 15); 968 /* Have to wait directly (no GLib support for watchers) ? */ 969 if (!ip->proc_support) 970 waitpid (ip->process_id, NULL, 0); 971 972 /* 973 * Message will be passed on to WEdit callback which will g_free(), so passing FALSE (i.e.: 974 * requesting cleanup of WInstructionStation object without full release of its main 975 * pointer) */ 976 instr_stn_release (ip, FALSE); 977 /* Not restarting the station, so release program string backup field */ 978 g_free (ip->prev_program); 979 break; 980 default: 981 break; 982 } 983 984 /* Redirect message to base object (i.e.: editor window «» WEdit) if needed */ 985 if (ret == MSG_NOT_HANDLED) 986 { 987 ret = edit_callback (w, sender, msg, parm, data); 988 if (msg != MSG_DESTROY) 989 ip->cur_col = EDIT (w)->curs_col; 990 } 991 return ret; 992 } 993 994 /* --------------------------------------------------------------------------------------------- */ -
new file src/editor/instr_station.h
diff --git a/src/editor/instr_station.h b/src/editor/instr_station.h new file mode 100644 index 000000000..e74409156
- + 1 #ifndef MC__INSTR_STATION_H 2 #define MC__INSTR_STATION_H 3 4 /*** typedefs(not structures) and defined constants **********************************************/ 5 6 #define INSTR_STATION(x) ((WInstructionStation *)x) 7 #define CONST_INSTR_STATION(x) ((const WInstructionStation *)x) 8 9 /*** enums ***************************************************************************************/ 10 11 /*** structures declarations (and typedefs of structures)*****************************************/ 12 13 typedef struct 14 { 15 WEdit base; 16 17 /* Configuration flags */ 18 instr_stn_flags_t flags; 19 /* Save of flags for use upon finalization of this object */ 20 instr_stn_flags_t prev_flags; 21 22 int cur_line; /* Current line where prompt is */ 23 int cur_col; /* Current column where prompt ends */ 24 int prompt_span; /* Length of prompt in bytes */ 25 int eprompt_span; /* Length of error prompt */ 26 27 /* Complete command that's being run in this station */ 28 char *program; 29 /* Saved program string from before calling instr_stn_release(...,FALSE) (i.e.: finalization) */ 30 char *prev_program; 31 /* OK prompt */ 32 char *prompt; 33 /* Failure prompt */ 34 char *eprompt; 35 /* The current instruction to send (filled for the writer GSource's pseudo thread) */ 36 char *instruction; 37 38 /* An indicator of whether the prompt to show is the first one */ 39 gboolean before_first_prompt; 40 /* An indicator of waiting for an initial response from program */ 41 gboolean first_response; 42 /* An indicator of the prompt being printed or not */ 43 gboolean prompt_shown; 44 /* ID of prompt drawing GLib's timeout recurring function */ 45 guint prompt_timeout_id; 46 47 /* Process ID of the program */ 48 GPid process_id; 49 50 /* Pipes to and from the program – series of fd+channel+GSource collections */ 51 struct 52 { 53 gint fd; 54 GIOChannel *ch; 55 guint src_id; 56 char *name; 57 } io[3]; 58 59 /* Time of last non empty read of data from program */ 60 guint64 read_time; 61 62 /* GSource id of program process watcher */ 63 guint proc_src_id; 64 65 /* A flag indicating whether there is (can be) a process watcher running */ 66 gboolean proc_support; 67 68 /* A buffer allocated and filled when user scrolls up blocking output */ 69 GByteArray *flow_ctrl_buf; 70 71 /* A GError placeholder */ 72 GError *error; 73 74 /* Indicator of an idle state – set when there's no data to send and read */ 75 gboolean io_complete; 76 77 /* Indicator of instr_release being already called on WInstructionStation object */ 78 gboolean finalized; 79 } WInstructionStation; 80 81 /*** global variables defined in .c file *********************************************************/ 82 83 /*** declarations of public functions ************************************************************/ 84 85 WInstructionStation *instr_stn_create (int y, int x, int lines, int cols, instr_stn_flags_t flags, 86 const char *program); 87 WInstructionStation *instr_stn_init (WInstructionStation * ip, int y, int x, int lines, int cols, 88 instr_stn_flags_t flags, const char *program); 89 WEdit *find_cli (const WDialog * h); 90 gboolean edit_widget_is_cli (const Widget * w); 91 cb_ret_t instr_stn_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data); 92 93 /*** inline functions ****************************************************************************/ 94 95 #endif /* MC__INSTR_STATION_H */ -
src/keybind-defaults.c
diff --git a/src/keybind-defaults.c b/src/keybind-defaults.c index 7b87c2f5a..0b9cf5538 100644
a b static const global_keymap_ini_t default_editor_keymap[] = { 470 470 {"FileNext", "alt-plus"}, 471 471 {"Sort", "alt-t"}, 472 472 {"Mail", "alt-m"}, 473 {"InstructionStation", "alt-s"}, 473 474 {"ExternalCommand", "alt-u"}, 474 475 #ifdef HAVE_ASPELL 475 476 {"SpellCheckCurrentWord", "ctrl-p"},