Ticket #4196: InstructionStation_v3.5.patch
File InstructionStation_v3.5.patch, 73.0 KB (added by psprint, 4 years ago) |
---|
-
lib/global.h
From 4394ab2b59eeafe30dca36c561f94a177d4e8462 Mon Sep 17 00:00:00 2001 From: Sebastian Gniazdowski <sgniazdowski@gmail.com> Date: Wed, 10 Feb 2021 13:41:35 -0600 Subject: =?UTF-8?q?Instruction=20Station=20=E2=80=93=20a=20CLI=20window=20?= =?UTF-8?q?for=20MCEdit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/global.h | 3 + lib/keybind.c | 1 + lib/keybind.h | 1 + lib/tty/key.c | 10 +- lib/widget/wtools.c | 40 +- lib/widget/wtools.h | 4 + misc/mc.default.keymap | 1 + misc/mc.emacs.keymap | 1 + src/editor/Makefile.am | 1 + src/editor/edit-impl.h | 24 +- src/editor/edit.c | 27 +- src/editor/editcmd.c | 41 +- src/editor/editdraw.c | 1 - src/editor/editmenu.c | 3 + src/editor/editwidget.c | 592 ++++++++++++------------ src/editor/editwidget.h | 3 + src/editor/instr_station.c | 897 +++++++++++++++++++++++++++++++++++++ src/editor/instr_station.h | 92 ++++ src/keybind-defaults.c | 1 + 19 files changed, 1432 insertions(+), 311 deletions(-) create mode 100644 src/editor/instr_station.c create mode 100644 src/editor/instr_station.h diff --git a/lib/global.h b/lib/global.h index f1a3e702c..1f76061c6 100644
a b 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 65ac5f15c..2f9d3cf29 100644
a b static name_keymap_t command_names_start[] = { 300 300 ADD_KEYMAP_NAME (EditMail), 301 301 ADD_KEYMAP_NAME (ParagraphFormat), 302 302 ADD_KEYMAP_NAME (MatchBracket), 303 ADD_KEYMAP_NAME (InstructionStation), 303 304 ADD_KEYMAP_NAME (ExternalCommand), 304 305 ADD_KEYMAP_NAME (MacroStartRecord), 305 306 ADD_KEYMAP_NAME (MacroStopRecord), -
lib/keybind.h
diff --git a/lib/keybind.h b/lib/keybind.h index 4e954ade2..18c7d95e4 100644
a b enum 324 324 CK_SyntaxOnOff, 325 325 CK_SyntaxChoose, 326 326 CK_InsertLiteral, 327 CK_InstructionStation, 327 328 CK_ExternalCommand, 328 329 CK_Date, 329 330 CK_EditMail, -
lib/tty/key.c
diff --git a/lib/tty/key.c b/lib/tty/key.c index 58a2f018b..b55fb8f07 100644
a b tty_get_event (struct Gpm_Event *event, gboolean redo_event, gboolean block) 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 94ace3167..aa2678b83 100644
a b bg_message (int dummy, int *flags, char *title, const char *text) 193 193 static char * 194 194 fg_input_dialog_help (const char *header, const char *text, const char *help, 195 195 const char *history_name, const char *def_text, gboolean strip_password, 196 input_complete_t completion_flags )196 input_complete_t completion_flags, quick_widget_t * add_w) 197 197 { 198 198 char *p_text; 199 199 char histname[64] = "inp|"; … … fg_input_dialog_help (const char *header, const char *text, const char *help, 218 218 } 219 219 220 220 { 221 quick_widget_t quick_widgets[] = { 221 int i = 1, norm_i = 0, add_i = 0; 222 quick_widget_t quick_widgets[15] = { 222 223 /* *INDENT-OFF* */ 223 224 QUICK_LABELED_INPUT (p_text, input_label_above, def_text, histname, &my_str, 224 NULL, is_passwd, strip_password, completion_flags), 225 QUICK_BUTTONS_OK_CANCEL, 226 QUICK_END 225 NULL, is_passwd, strip_password, completion_flags) 227 226 /* *INDENT-ON* */ 228 227 }; 229 228 quick_widget_t press_end[] = { QUICK_BUTTONS_OK_CANCEL, QUICK_END }; 230 229 quick_dialog_t qdlg = { 231 230 -1, -1, COLS / 2, header, 232 231 help, quick_widgets, NULL, NULL 233 232 }; 234 233 234 /* Handling of additional widgets optionally passed via last argument `add_w` */ 235 236 /* Copy/append any extra widgets (max 10) */ 237 while (add_w != NULL && add_w[add_i].widget_type != quick_end && i < 15) 238 quick_widgets[i++] = add_w[add_i++]; 239 240 /* Copy normal buttons */ 241 while (norm_i < sizeof (press_end) / sizeof (quick_widget_t) && i < 15) 242 quick_widgets[i++] = press_end[norm_i++]; 243 235 244 ret = quick_dialog (&qdlg); 236 245 } 237 246 … … input_dialog_help (const char *header, const char *text, const char *help, 543 552 { 544 553 void *p; 545 554 char *(*f) (const char *, const char *, const char *, const char *, const char *, 546 gboolean, input_complete_t );555 gboolean, input_complete_t, quick_widget_t * add_w); 547 556 } func; 548 557 func.f = fg_input_dialog_help; 549 return wtools_parent_call_string (func.p, 7,558 return wtools_parent_call_string (func.p, 8, 550 559 strlen (header), header, strlen (text), 551 560 text, strlen (help), help, 552 561 strlen (history_name), history_name, 553 562 strlen (def_text), def_text, 554 563 sizeof (gboolean), strip_password, 555 sizeof (input_complete_t), completion_flags); 564 sizeof (input_complete_t), completion_flags, 565 sizeof (quick_widget_t *), NULL); 556 566 } 557 567 else 558 568 #endif /* ENABLE_BACKGROUND */ 559 569 return fg_input_dialog_help (header, text, help, history_name, def_text, strip_password, 560 completion_flags );570 completion_flags, NULL); 561 571 } 562 572 563 573 /* --------------------------------------------------------------------------------------------- */ … … input_dialog (const char *header, const char *text, const char *history_name, co 573 583 574 584 /* --------------------------------------------------------------------------------------------- */ 575 585 586 char * 587 input_dialog_ext (const char *header, const char *text, const char *history_name, 588 const char *def_text, input_complete_t completion_flags, quick_widget_t * add_w) 589 { 590 return fg_input_dialog_help (header, text, "[Input Line Keys]", history_name, def_text, FALSE, 591 completion_flags, add_w); 592 } 593 594 /* --------------------------------------------------------------------------------------------- */ 595 576 596 char * 577 597 input_expand_dialog (const char *header, const char *text, 578 598 const char *history_name, const char *def_text, -
lib/widget/wtools.h
diff --git a/lib/widget/wtools.h b/lib/widget/wtools.h index b1f7a9b47..9511aed46 100644
a b void display_postponed_messages (void); 76 76 char *input_dialog (const char *header, const char *text, 77 77 const char *history_name, const char *def_text, 78 78 input_complete_t completion_flags); 79 /* The input dialogs */ 80 char *input_dialog_ext (const char *header, const char *text, 81 const char *history_name, const char *def_text, 82 input_complete_t completion_flags, quick_widget_t * add_w); 79 83 char *input_dialog_help (const char *header, const char *text, const char *help, 80 84 const char *history_name, const char *def_text, gboolean strip_password, 81 85 input_complete_t completion_flags); -
misc/mc.default.keymap
diff --git a/misc/mc.default.keymap b/misc/mc.default.keymap index 73b4c0eab..a7269d07f 100644
a b Sort = alt-t 352 352 Mail = alt-m 353 353 ParagraphFormat = alt-p 354 354 MatchBracket = alt-b 355 InstructionStation = alt-i 355 356 ExternalCommand = alt-u 356 357 UserMenu = f11 357 358 Menu = 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 351 351 # Mail = 352 352 ParagraphFormat = alt-p 353 353 # MatchBracket = 354 InstructionStation = alt-i 354 355 ExternalCommand = alt-u 355 356 UserMenu = f11 356 357 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 881ef549b..3d35e0a7e 100644
a b typedef enum 88 88 LB_MAC 89 89 } LineBreaks; 90 90 91 typedef enum 92 { 93 EDIT_DO_INIT_BASE_CLASS = 1 << 0, 94 95 } edit_init_flags_t; 96 97 typedef enum instr_stn_flags 98 { 99 INSTR_STN_NO_FLAGS = 0, 100 INSTR_STN_NO_STDIN = 1 << 17, 101 INSTR_STN_NO_PROMPT = 1 << 18 102 } instr_stn_flags_t; 103 91 104 typedef enum 92 105 { 93 106 EDIT_QUICK_SAVE = 0, … … extern char *edit_window_state_char; 146 159 extern char *edit_window_close_char; 147 160 148 161 /*** declarations of public functions ************************************************************/ 162 cb_ret_t edit_dialog_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data); 163 cb_ret_t edit_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data); 164 cb_ret_t edit_dialog_command_execute (WDialog * h, long command, void *data); 165 166 void edit_mouse_callback (Widget * w, mouse_msg_t msg, mouse_event_t * event); 149 167 150 168 gboolean edit_add_window (WDialog * h, int y, int x, int lines, int cols, 151 169 const vfs_path_t * f, long fline); … … void edit_move_to_prev_col (WEdit * edit, off_t p); 169 187 long edit_get_col (const WEdit * edit); 170 188 void edit_update_curs_row (WEdit * edit); 171 189 void edit_update_curs_col (WEdit * edit); 190 gboolean edit_newline_end_check_only (const WEdit * edit); 172 191 void edit_find_bracket (WEdit * edit); 173 192 gboolean edit_reload_line (WEdit * edit, const vfs_path_t * filename_vpath, long line); 174 193 void edit_set_codeset (WEdit * edit); … … void edit_delete_line (WEdit * edit); 180 199 181 200 int edit_delete (WEdit * edit, gboolean byte_delete); 182 201 int edit_backspace (WEdit * edit, gboolean byte_delete); 202 void edit_insert_string (WEdit * edit, char *str_text, gsize len); 183 203 void edit_insert (WEdit * edit, int c); 184 204 void edit_insert_over (WEdit * edit); 185 205 void edit_cursor_move (WEdit * edit, off_t increment); … … char *edit_get_write_filter (const vfs_path_t * write_name_vpath, 193 213 gboolean edit_save_confirm_cmd (WEdit * edit); 194 214 gboolean edit_save_as_cmd (WEdit * edit); 195 215 WEdit *edit_init (WEdit * edit, int y, int x, int lines, int cols, 196 const vfs_path_t * filename_vpath, long line );216 const vfs_path_t * filename_vpath, long line, edit_init_flags_t flags); 197 217 gboolean edit_clean (WEdit * edit); 198 218 gboolean edit_ok_to_exit (WEdit * edit); 199 219 file_suitable_rank_t edit_check_file_suitable (const vfs_path_t * fs_path); … … void edit_center_display (WEdit * e, long diff); 249 269 void edit_move_display (WEdit * e, long line); 250 270 void edit_word_wrap (WEdit * edit); 251 271 int edit_sort_cmd (WEdit * edit); 272 gboolean edit_add_instr_stn_window (WDialog * h, instr_stn_flags_t flags, void *data); 273 252 274 int edit_ext_cmd (WEdit * edit); 253 275 254 276 int edit_store_macro_cmd (WEdit * edit); -
src/editor/edit.c
diff --git a/src/editor/edit.c b/src/editor/edit.c index 8477939aa..ea193a8de 100644
a b edit_modification (WEdit * edit) 652 652 { 653 653 edit->caches_valid = FALSE; 654 654 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) 657 657 edit->locked = lock_file (edit->filename_vpath); 658 658 edit->modified = 1; 659 659 } … … edit_insert_file (WEdit * edit, const vfs_path_t * filename_vpath) 2106 2106 2107 2107 WEdit * 2108 2108 edit_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) 2110 2110 { 2111 2111 gboolean to_free = FALSE; 2112 2112 … … edit_init (WEdit * edit, int y, int x, int lines, int cols, const vfs_path_t * f 2128 2128 edit->fullscreen = fullscreen; 2129 2129 edit->loc_prev = loc_prev; 2130 2130 } 2131 else2131 if (has_flag (flags, EDIT_DO_INIT_BASE_CLASS) || edit == NULL) 2132 2132 { 2133 2133 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 } 2136 2139 2137 2140 w = WIDGET (edit); 2138 2141 widget_init (w, y, x, lines, cols, NULL, NULL); … … edit_reload_line (WEdit * edit, const vfs_path_t * filename_vpath, long line) 2271 2274 e->fullscreen = edit->fullscreen; 2272 2275 e->loc_prev = edit->loc_prev; 2273 2276 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) 2275 2278 { 2276 2279 g_free (e); 2277 2280 return FALSE; … … edit_push_redo_action (WEdit * edit, long c) 2528 2531 edit->redo_stack_bottom = edit->redo_stack_pointer = 0; 2529 2532 } 2530 2533 2534 /* --------------------------------------------------------------------------------------------- */ 2535 2536 void 2537 edit_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 2531 2544 /* --------------------------------------------------------------------------------------------- */ 2532 2545 /** 2533 2546 Basic low level single character buffer alterations and movements at the cursor. -
src/editor/editcmd.c
diff --git a/src/editor/editcmd.c b/src/editor/editcmd.c index be58f3db7..17d2d6b2f 100644
a b edit_complete_word_insert_recoded_completion (WEdit * edit, char *completion, gs 1573 1573 /*** public functions ****************************************************************************/ 1574 1574 /* --------------------------------------------------------------------------------------------- */ 1575 1575 1576 gboolean 1577 edit_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 1576 1585 void 1577 1586 edit_refresh_cmd (void) 1578 1587 { … … edit_close_cmd (WEdit * edit) 2298 2307 group_remove_widget (w); 2299 2308 widget_destroy (w); 2300 2309 2301 if (edit_widget_is_editor (CONST_WIDGET (g->current->data))) 2310 if (edit_widget_is_editor (CONST_WIDGET (g->current->data)) 2311 || edit_widget_is_cli (CONST_WIDGET (g->current->data))) 2302 2312 edit = (WEdit *) (g->current->data); 2303 2313 else 2304 2314 { 2315 /* Look for a file window or a CLI window */ 2305 2316 edit = find_editor (DIALOG (g)); 2317 if (!edit) 2318 edit = find_cli (DIALOG (g)); 2306 2319 if (edit != NULL) 2307 2320 widget_select (w); 2308 2321 } … … edit_ext_cmd (WEdit * edit) 3272 3285 { 3273 3286 char *exp, *tmp, *tmp_edit_temp_file; 3274 3287 int e; 3288 gboolean run_in_cli = FALSE; 3289 3290 quick_widget_t chbox[3] = { QUICK_SEPARATOR (TRUE), 3291 QUICK_CHECKBOX ("&Open in CLI window", &run_in_cli, NULL), 3292 QUICK_END 3293 }; 3275 3294 3276 3295 exp = 3277 input_dialog (_("Paste output of external command"), 3278 _("Enter shell command(s):"), MC_HISTORY_EDIT_PASTE_EXTCMD, INPUT_LAST_TEXT, 3279 INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_VARIABLES | INPUT_COMPLETE_USERNAMES 3280 | INPUT_COMPLETE_HOSTNAMES | INPUT_COMPLETE_CD | INPUT_COMPLETE_COMMANDS | 3281 INPUT_COMPLETE_SHELL_ESC); 3296 input_dialog_ext (_("Paste output of external command"), 3297 _("Enter shell command(s):"), MC_HISTORY_EDIT_PASTE_EXTCMD, 3298 INPUT_LAST_TEXT, 3299 INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_VARIABLES | 3300 INPUT_COMPLETE_USERNAMES | INPUT_COMPLETE_HOSTNAMES | INPUT_COMPLETE_CD | 3301 INPUT_COMPLETE_COMMANDS | INPUT_COMPLETE_SHELL_ESC, chbox); 3282 3302 3283 3303 if (!exp) 3284 3304 return 1; 3285 3305 3306 /* Should start a CLI window with the command ? */ 3307 if (run_in_cli) 3308 { 3309 edit_add_instr_stn_window (DIALOG (WIDGET (edit)->owner), 3310 INSTR_STN_NO_STDIN | INSTR_STN_NO_PROMPT, exp); 3311 g_free (exp); 3312 return 0; 3313 } 3314 3286 3315 tmp_edit_temp_file = mc_config_get_full_path (EDIT_HOME_TEMP_FILE); 3287 3316 tmp = g_strconcat (exp, " > ", tmp_edit_temp_file, (char *) NULL); 3288 3317 g_free (tmp_edit_temp_file); -
src/editor/editdraw.c
diff --git a/src/editor/editdraw.c b/src/editor/editdraw.c index 61bbdcbee..80495dcd0 100644
a b edit_draw_this_line (WEdit * edit, off_t b, long row, long start_col, long end_c 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 c20e9fb7e..0c8e60801 100644
a b create_window_menu (void) 238 238 entries = g_list_prepend (entries, menu_entry_create (_("&Next"), CK_WindowNext)); 239 239 entries = g_list_prepend (entries, menu_entry_create (_("&Previous"), CK_WindowPrev)); 240 240 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)); 241 244 242 245 return g_list_reverse (entries); 243 246 } -
src/editor/editwidget.c
diff --git a/src/editor/editwidget.c b/src/editor/editwidget.c index e32541cd9..c26a7d009 100644
a b 68 68 #ifdef HAVE_ASPELL 69 69 #include "spell.h" 70 70 #endif 71 #include "src/editor/instr_station.h" 71 72 72 73 /*** global variables ****************************************************************************/ 73 74 … … static unsigned int edit_dlg_init_refcounter = 0; 86 87 87 88 /*** file scope functions ************************************************************************/ 88 89 89 static cb_ret_t edit_dialog_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm,90 void *data);91 92 90 /* --------------------------------------------------------------------------------------------- */ 93 91 94 92 static char * … … edit_get_title (const WDialog * h, size_t len) 634 632 return g_strconcat (_("Edit: "), modified, file_label, (char *) NULL); 635 633 } 636 634 637 /* --------------------------------------------------------------------------------------------- */638 639 static cb_ret_t640 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 764 635 /* --------------------------------------------------------------------------------------------- */ 765 636 /* 766 637 * Translate the keycode into either 'command' or 'char_for_insertion'. … … edit_update_cursor (WEdit * edit, const mouse_event_t * event) 1018 889 return done; 1019 890 } 1020 891 1021 /* --------------------------------------------------------------------------------------------- */1022 /** Callback for the edit dialog */1023 1024 static cb_ret_t1025 edit_dialog_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)1026 {1027 WGroup *g = GROUP (w);1028 WDialog *h = DIALOG (w);1029 1030 switch (msg)1031 {1032 case MSG_INIT:1033 edit_dlg_init ();1034 return MSG_HANDLED;1035 1036 case MSG_RESIZE:1037 dlg_default_callback (w, NULL, MSG_RESIZE, 0, NULL);1038 menubar_arrange (find_menubar (h));1039 return MSG_HANDLED;1040 1041 case MSG_ACTION:1042 {1043 /* Handle shortcuts, menu, and buttonbar. */1044 1045 cb_ret_t result;1046 1047 result = edit_dialog_command_execute (h, parm);1048 1049 /* We forward any commands coming from the menu, and which haven't been1050 handled by the dialog, to the focused WEdit window. */1051 if (result == MSG_NOT_HANDLED && sender == WIDGET (find_menubar (h)))1052 result = send_message (g->current->data, NULL, MSG_ACTION, parm, NULL);1053 1054 return result;1055 }1056 1057 case MSG_KEY:1058 {1059 Widget *we = WIDGET (g->current->data);1060 cb_ret_t ret = MSG_NOT_HANDLED;1061 1062 if (edit_widget_is_editor (we))1063 {1064 gboolean ext_mode;1065 long command;1066 1067 /* keep and then extmod flag */1068 ext_mode = we->ext_mode;1069 command = widget_lookup_key (we, parm);1070 we->ext_mode = ext_mode;1071 1072 if (command == CK_IgnoreKey)1073 we->ext_mode = FALSE;1074 else1075 {1076 ret = edit_dialog_command_execute (h, command);1077 /* if command was not handled, keep the extended mode1078 for the further key processing */1079 if (ret == MSG_HANDLED)1080 we->ext_mode = FALSE;1081 }1082 }1083 1084 /*1085 * Due to the "end of bracket" escape the editor sees input with is_idle() == false1086 * (expects more characters) and hence doesn't yet refresh the screen, but then1087 * no further characters arrive (there's only an "end of bracket" which is swallowed1088 * by tty_get_event()), so you end up with a screen that's not refreshed after pasting.1089 * So let's trigger an IDLE signal.1090 */1091 if (!is_idle ())1092 widget_idle (w, TRUE);1093 return ret;1094 }1095 1096 /* hardcoded menu hotkeys (see edit_drop_hotkey_menu) */1097 case MSG_UNHANDLED_KEY:1098 return edit_drop_hotkey_menu (h, parm) ? MSG_HANDLED : MSG_NOT_HANDLED;1099 1100 case MSG_VALIDATE:1101 edit_quit (h);1102 return MSG_HANDLED;1103 1104 case MSG_END:1105 edit_dlg_deinit ();1106 return MSG_HANDLED;1107 1108 case MSG_IDLE:1109 widget_idle (w, FALSE);1110 return send_message (g->current->data, NULL, MSG_IDLE, 0, NULL);1111 1112 default:1113 return dlg_default_callback (w, sender, msg, parm, data);1114 }1115 }1116 1117 892 /* --------------------------------------------------------------------------------------------- */ 1118 893 1119 894 /** … … edit_dialog_bg_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm 1208 983 1209 984 /* --------------------------------------------------------------------------------------------- */ 1210 985 1211 static cb_ret_t 986 /** 987 * Handle move/resize mouse events. 988 */ 989 static void 990 edit_mouse_handle_move_resize (Widget * w, mouse_msg_t msg, mouse_event_t * event) 991 { 992 WEdit *edit = (WEdit *) (w); 993 Widget *h = WIDGET (w->owner); 994 int global_x, global_y; 995 996 if (msg == MSG_MOUSE_UP) 997 { 998 /* Exit move/resize mode. */ 999 edit_execute_cmd (edit, CK_Enter, -1); 1000 edit_update_screen (edit); /* Paint the buttonbar over our possibly overlapping frame. */ 1001 return; 1002 } 1003 1004 if (msg != MSG_MOUSE_DRAG) 1005 /** 1006 * We ignore any other events. Specifically, MSG_MOUSE_DOWN. 1007 * 1008 * When the move/resize is initiated by the menu, we let the user 1009 * stop it by clicking with the mouse. Which is why we don't want 1010 * a mouse down to affect the window. 1011 */ 1012 return; 1013 1014 /* Convert point to global coordinates for easier calculations. */ 1015 global_x = event->x + w->x; 1016 global_y = event->y + w->y; 1017 1018 /* Clamp the point to the dialog's client area. */ 1019 global_y = CLAMP (global_y, h->y + 1, h->y + h->lines - 2); /* Status line, buttonbar */ 1020 global_x = CLAMP (global_x, h->x, h->x + h->cols - 1); /* Currently a no-op, as the dialog has no left/right margins. */ 1021 1022 if (edit->drag_state == MCEDIT_DRAG_MOVE) 1023 { 1024 w->y = global_y; 1025 w->x = global_x - edit->drag_state_start; 1026 } 1027 else if (edit->drag_state == MCEDIT_DRAG_RESIZE) 1028 { 1029 w->lines = MAX (WINDOW_MIN_LINES, global_y - w->y + 1); 1030 w->cols = MAX (WINDOW_MIN_COLS, global_x - w->x + 1); 1031 } 1032 1033 edit->force |= REDRAW_COMPLETELY; /* Not really needed as WEdit's MSG_DRAW already does this. */ 1034 1035 /* We draw the whole dialog because dragging/resizing exposes area beneath. */ 1036 widget_draw (WIDGET (w->owner)); 1037 } 1038 1039 /* --------------------------------------------------------------------------------------------- */ 1040 /*** public functions ****************************************************************************/ 1041 /* --------------------------------------------------------------------------------------------- */ 1042 1043 cb_ret_t 1044 edit_dialog_command_execute (WDialog * h, long command, void *data) 1045 { 1046 WGroup *g = GROUP (h); 1047 Widget *wh = WIDGET (h); 1048 cb_ret_t ret = MSG_HANDLED; 1049 1050 switch (command) 1051 { 1052 case CK_EditNew: 1053 edit_add_window (h, wh->y + 1, wh->x, wh->lines - 2, wh->cols, NULL, 0); 1054 break; 1055 case CK_EditFile: 1056 edit_load_cmd (h, NULL); 1057 break; 1058 case CK_OtherFile: 1059 { 1060 WEdit *e = (WEdit *) g->current->data; 1061 gboolean retflag = FALSE; 1062 1063 if (e != NULL && edit_widget_is_editor (CONST_WIDGET (e))) 1064 { 1065 retflag = edit_compute_other_file_vfs_path (e); 1066 if (retflag) 1067 retflag = edit_switch_to_file (h, e->otherfile_vpath); 1068 } 1069 if (!retflag) 1070 ret = MSG_NOT_HANDLED; 1071 } 1072 break; 1073 case CK_History: 1074 edit_load_file_from_history (h); 1075 break; 1076 case CK_EditSyntaxFile: 1077 edit_load_syntax_file (h); 1078 break; 1079 case CK_EditUserMenu: 1080 edit_load_menu_file (h); 1081 break; 1082 case CK_Close: 1083 /* if there are no opened files anymore, close MC editor */ 1084 if ((edit_widget_is_editor (CONST_WIDGET (g->current->data)) || 1085 edit_widget_is_cli (CONST_WIDGET (g->current->data))) && 1086 edit_close_cmd ((WEdit *) g->current->data) && find_editor (h) == NULL) 1087 dlg_stop (h); 1088 break; 1089 case CK_Help: 1090 edit_help (); 1091 /* edit->force |= REDRAW_COMPLETELY; */ 1092 break; 1093 case CK_Menu: 1094 edit_menu_cmd (h); 1095 break; 1096 case CK_Quit: 1097 case CK_Cancel: 1098 /* don't close editor due to SIGINT, but stop move/resize window */ 1099 { 1100 Widget *w = WIDGET (g->current->data); 1101 1102 if (edit_widget_is_editor (w) && ((WEdit *) w)->drag_state != MCEDIT_DRAG_NONE) 1103 edit_restore_size ((WEdit *) w); 1104 else if (command == CK_Quit) 1105 dlg_stop (h); 1106 } 1107 break; 1108 case CK_About: 1109 edit_about (); 1110 break; 1111 case CK_SyntaxOnOff: 1112 edit_syntax_onoff_cmd (h); 1113 break; 1114 case CK_ShowTabTws: 1115 edit_show_tabs_tws_cmd (h); 1116 break; 1117 case CK_ShowMargin: 1118 edit_show_margin_cmd (h); 1119 break; 1120 case CK_ShowNumbers: 1121 edit_show_numbers_cmd (h); 1122 break; 1123 case CK_Refresh: 1124 edit_refresh_cmd (); 1125 break; 1126 case CK_InstructionStation: 1127 edit_add_instr_stn_window (h, INSTR_STN_NO_FLAGS, data); 1128 break; 1129 case CK_Shell: 1130 toggle_subshell (); 1131 break; 1132 case CK_LearnKeys: 1133 learn_keys (); 1134 break; 1135 case CK_WindowMove: 1136 case CK_WindowResize: 1137 if (edit_widget_is_editor (CONST_WIDGET (g->current->data))) 1138 edit_handle_move_resize ((WEdit *) g->current->data, command); 1139 break; 1140 case CK_WindowList: 1141 edit_window_list (h); 1142 break; 1143 case CK_WindowCascade: 1144 edit_window_cascade (h); 1145 break; 1146 case CK_WindowTile: 1147 edit_window_tile (h); 1148 break; 1149 case CK_WindowNext: 1150 group_select_next_widget (g); 1151 break; 1152 case CK_WindowPrev: 1153 group_select_prev_widget (g); 1154 break; 1155 case CK_Options: 1156 edit_options_dialog (h); 1157 break; 1158 case CK_OptionsSaveMode: 1159 edit_save_mode_cmd (); 1160 break; 1161 case CK_SaveSetup: 1162 save_setup_cmd (); 1163 break; 1164 default: 1165 ret = MSG_NOT_HANDLED; 1166 break; 1167 } 1168 1169 return ret; 1170 } 1171 1172 /* --------------------------------------------------------------------------------------------- */ 1173 /** Callback for the edit dialog */ 1174 1175 cb_ret_t 1176 edit_dialog_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data) 1177 { 1178 WGroup *g = GROUP (w); 1179 WDialog *h = DIALOG (w); 1180 1181 switch (msg) 1182 { 1183 case MSG_INIT: 1184 edit_dlg_init (); 1185 return MSG_HANDLED; 1186 1187 case MSG_RESIZE: 1188 dlg_default_callback (w, NULL, MSG_RESIZE, 0, NULL); 1189 menubar_arrange (find_menubar (h)); 1190 return MSG_HANDLED; 1191 1192 case MSG_ACTION: 1193 { 1194 /* Handle shortcuts, menu, and buttonbar. */ 1195 1196 cb_ret_t result; 1197 1198 result = edit_dialog_command_execute (h, parm, data); 1199 1200 /* We forward any commands coming from the menu, and which haven't been 1201 handled by the dialog, to the focused WEdit window. */ 1202 if (result == MSG_NOT_HANDLED && sender == WIDGET (find_menubar (h))) 1203 result = send_message (g->current->data, NULL, MSG_ACTION, parm, NULL); 1204 1205 return result; 1206 } 1207 1208 case MSG_KEY: 1209 { 1210 Widget *we = WIDGET (g->current->data); 1211 cb_ret_t ret = MSG_NOT_HANDLED; 1212 1213 if (edit_widget_is_editor (we)) 1214 { 1215 gboolean ext_mode; 1216 long command; 1217 1218 /* keep and then extmod flag */ 1219 ext_mode = we->ext_mode; 1220 command = widget_lookup_key (we, parm); 1221 we->ext_mode = ext_mode; 1222 1223 if (command == CK_IgnoreKey) 1224 we->ext_mode = FALSE; 1225 else 1226 { 1227 ret = edit_dialog_command_execute (h, command, data); 1228 /* if command was not handled, keep the extended mode 1229 for the further key processing */ 1230 if (ret == MSG_HANDLED) 1231 we->ext_mode = FALSE; 1232 } 1233 } 1234 1235 /* 1236 * Due to the "end of bracket" escape the editor sees input with is_idle() == false 1237 * (expects more characters) and hence doesn't yet refresh the screen, but then 1238 * no further characters arrive (there's only an "end of bracket" which is swallowed 1239 * by tty_get_event()), so you end up with a screen that's not refreshed after pasting. 1240 * So let's trigger an IDLE signal. 1241 */ 1242 if (!is_idle ()) 1243 widget_idle (w, TRUE); 1244 return ret; 1245 } 1246 1247 /* hardcoded menu hotkeys (see edit_drop_hotkey_menu) */ 1248 case MSG_UNHANDLED_KEY: 1249 return edit_drop_hotkey_menu (h, parm) ? MSG_HANDLED : MSG_NOT_HANDLED; 1250 1251 case MSG_VALIDATE: 1252 edit_quit (h); 1253 return MSG_HANDLED; 1254 1255 case MSG_END: 1256 edit_dlg_deinit (); 1257 return MSG_HANDLED; 1258 1259 case MSG_IDLE: 1260 widget_idle (w, FALSE); 1261 return send_message (g->current->data, NULL, MSG_IDLE, 0, NULL); 1262 1263 default: 1264 return dlg_default_callback (w, sender, msg, parm, data); 1265 } 1266 } 1267 1268 /* --------------------------------------------------------------------------------------------- */ 1269 1270 cb_ret_t 1212 1271 edit_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data) 1213 1272 { 1214 1273 WEdit *e = (WEdit *) w; … … edit_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *da 1278 1337 1279 1338 /* --------------------------------------------------------------------------------------------- */ 1280 1339 1281 /**1282 * Handle move/resize mouse events.1283 */1284 static void1285 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 user1304 * stop it by clicking with the mouse. Which is why we don't want1305 * 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 1336 1340 /** 1337 1341 * Handle mouse events of editor window 1338 1342 * … … edit_mouse_handle_move_resize (Widget * w, mouse_msg_t msg, mouse_event_t * even 1340 1344 * @param msg mouse event message 1341 1345 * @param event mouse event data 1342 1346 */ 1343 staticvoid1347 void 1344 1348 edit_mouse_callback (Widget * w, mouse_msg_t msg, mouse_event_t * event) 1345 1349 { 1346 1350 WEdit *edit = (WEdit *) w; … … edit_mouse_callback (Widget * w, mouse_msg_t msg, mouse_event_t * event) 1455 1459 } 1456 1460 1457 1461 /* --------------------------------------------------------------------------------------------- */ 1458 /*** public functions ****************************************************************************/ 1462 /* Creates a CLI special window, windowed (not fullscreen) */ 1463 1464 gboolean 1465 edit_add_instr_stn_window (WDialog * h, instr_stn_flags_t flags, void *data) 1466 { 1467 WInstructionStation *ip; 1468 1469 /* Note passing of data - ability to alter program that's run in this station */ 1470 ip = instr_stn_create (3, 10, 17, 80, flags, (const char *) data); 1471 1472 if (ip == NULL) 1473 return FALSE; 1474 1475 /* Add CLI-extended editor window (extending WEdit) to front dialog */ 1476 group_add_widget_autopos (GROUP (h), ip, WPOS_KEEP_ALL, NULL); 1477 widget_draw (WIDGET (h)); 1478 return TRUE; 1479 } 1480 1459 1481 /* --------------------------------------------------------------------------------------------- */ 1460 1482 1461 1483 gboolean … … edit_add_window (WDialog * h, int y, int x, int lines, int cols, const vfs_path_ 1686 1708 WEdit *edit; 1687 1709 Widget *w; 1688 1710 1689 edit = edit_init (NULL, y, x, lines, cols, f, fline );1711 edit = edit_init (NULL, y, x, lines, cols, f, fline, 0); 1690 1712 if (edit == NULL) 1691 1713 return FALSE; 1692 1714 -
src/editor/editwidget.h
diff --git a/src/editor/editwidget.h b/src/editor/editwidget.h index b4b10692e..a80057393 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..fe0f7d8d9
- + 1 /* 2 Implementation of a CLI window for MCEdit. 3 4 Copyright (C) 2021 5 Free Software Foundation, Inc. 6 7 Written by: 8 Sebastian Gniazdowski <sgniazdowski@gmail.com>, 2021. 9 10 This file is part of the Midnight Commander. 11 12 The Midnight Commander is free software: you can redistribute it 13 and/or modify it under the terms of the GNU General Public License as 14 published by the Free Software Foundation, either version 3 of the License, 15 or (at your option) any later version. 16 17 The Midnight Commander is distributed in the hope that it will be useful, 18 but WITHOUT ANY WARRANTY; without even the implied warranty of 19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 GNU General Public License for more details. 21 22 You should have received a copy of the GNU General Public License 23 along with this program. If not, see <http://www.gnu.org/licenses/>. 24 */ 25 26 /** \file instr_station.c 27 * \brief Implementation of a CLI special window for MCEdit. 28 * \author Sebastian Gniazdowski 29 * \date 2021 30 * 31 * Such window runs a set up program (/bin/bash by default) allowing to 32 * provide an input to it and read its output. 33 */ 34 35 #include <config.h> 36 37 #include "lib/global.h" 38 39 #include <sys/types.h> 40 #include <sys/wait.h> 41 42 #include "src/editor/editwidget.h" 43 44 #include "lib/sub-util.h" 45 #include "lib/tty/key.h" 46 #include "lib/widget.h" 47 #include "src/editor/instr_station.h" 48 49 /*** global variables ****************************************************************************/ 50 51 /*** file scope macro definitions ****************************************************************/ 52 53 /*** file scope type declarations ****************************************************************/ 54 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 69 static char * 70 instr_stn_get_instruction_text (WInstructionStation * ip) 71 { 72 char *ret_str; 73 GString *instr; 74 off_t bol, eol, begin_instr; 75 long size; 76 int idx, byte; 77 78 /* Calculate offset of text after prompt */ 79 bol = edit_buffer_get_current_bol (&EDIT (ip)->buffer); 80 eol = edit_buffer_get_current_eol (&EDIT (ip)->buffer); 81 begin_instr = bol + ip->prompt_span; 82 83 /* Is there anything entered? */ 84 size = ((long) eol) - ((long) begin_instr); 85 if (size <= 0) 86 return NULL; 87 88 /* Allocate expected size string and fill it */ 89 instr = g_string_sized_new (size + 2); 90 for (idx = 0; idx < size; idx++) 91 { 92 byte = edit_buffer_get_byte (&EDIT (ip)->buffer, begin_instr + idx); 93 g_string_append_c (instr, byte); 94 } 95 96 /* Append new line if needed */ 97 if (instr->str[instr->len - 1] != '\n') 98 g_string_append_c (instr, '\n'); 99 100 /* Return char buffer */ 101 ret_str = instr->str; 102 g_string_free (instr, FALSE); 103 return ret_str; 104 } 105 106 /* --------------------------------------------------------------------------------------------- */ 107 108 static gboolean 109 instr_stn_release (WInstructionStation * ip, gboolean free_all) 110 { 111 /* Already relased? */ 112 if (ip == NULL) 113 { 114 if (free_all) 115 return TRUE; 116 else 117 return FALSE; 118 } 119 120 /* Already cleaned up (for a restart of program) ? */ 121 if (ip->finalized && !free_all) 122 return FALSE; 123 ip->finalized = TRUE; 124 125 /* Close process watcher first, suppressing *program_ended_cb() callback */ 126 if (ip->proc_src_id != 0) 127 { 128 GSource *src; 129 src = g_main_context_find_source_by_id (NULL, ip->proc_src_id); 130 if (src != NULL) 131 g_source_destroy (src); 132 ip->proc_src_id = 0; 133 } 134 135 /* Release the pipes, channels, etc. */ 136 for (int i = 0; i <= STDERR; i++) 137 { 138 /* Source */ 139 if (ip->io[i].src_id != 0) 140 { 141 GSource *src; 142 src = g_main_context_find_source_by_id (NULL, ip->io[i].src_id); 143 if (src != NULL) 144 g_source_destroy (src); 145 ip->io[i].src_id = 0; 146 } 147 /* Channel */ 148 if (ip->io[i].ch != NULL) 149 { 150 g_io_channel_unref (ip->io[i].ch); 151 ip->io[i].ch = NULL; 152 } 153 /* Pipe */ 154 if (ip->io[i].fd >= 0) 155 { 156 close (ip->io[i].fd); 157 ip->io[i].fd = -1; 158 } 159 } 160 161 /* Remove prompt guard recurring timeout callback function */ 162 if (ip->prompt_timeout_id != 0) 163 { 164 g_source_remove (ip->prompt_timeout_id); 165 ip->prompt_timeout_id = 0; 166 } 167 168 MC_PTR_FREE (ip->instruction); 169 /* Release prompt */ 170 MC_PTR_FREE (ip->prompt); 171 /* Release error prompt */ 172 MC_PTR_FREE (ip->eprompt); 173 174 /* Clear prompt information */ 175 ip->prompt_span = 0; 176 ip->eprompt_span = 0; 177 178 /* Set various indicator flags to some inactive state */ 179 ip->io_complete = TRUE; 180 ip->first_response = TRUE; 181 ip->before_first_prompt = TRUE; 182 ip->prompt_shown = FALSE; 183 ip->flags = INSTR_STN_NO_FLAGS; 184 185 /* Clear any GError */ 186 if (ip->error != NULL) 187 { 188 g_error_free (ip->error); 189 ip->error = NULL; 190 } 191 192 /* Release space occupied by WInstructionStation object? */ 193 if (free_all) 194 { 195 MC_PTR_FREE (ip->program); 196 /* Free main object */ 197 g_free (ip); 198 } 199 else 200 /* Value is still accessible in prev_program field */ 201 ip->program = NULL; 202 203 return TRUE; 204 } 205 206 /* --------------------------------------------------------------------------------------------- */ 207 208 static cb_ret_t 209 instr_stn_run_novel_state_id (WInstructionStation * ip, long novel_state_id, void *data) 210 { 211 WGroup *owner = WIDGET (ip)->owner; 212 cb_ret_t ret = MSG_NOT_HANDLED; 213 214 switch (novel_state_id) 215 { 216 case CK_BackSpace: 217 if (ip->cur_col <= ip->prompt_span) 218 ret = MSG_HANDLED; 219 else 220 { 221 EDIT (ip)->force |= REDRAW_PAGE; 222 edit_update_screen (EDIT (ip)); 223 } 224 break; 225 case CK_Enter: 226 case CK_Return: 227 if (EDIT (ip)->buffer.curs_line == ip->cur_line) 228 { 229 ip->instruction = instr_stn_get_instruction_text (ip); 230 ip->io_complete = FALSE; 231 /* Should dispatch to write callback which will send instruction */ 232 g_main_context_iteration (NULL, 0); 233 repaint_screen (); 234 } 235 ret = MSG_HANDLED; 236 break; 237 /* InstructionStation activity invoked when focus is at a station window behaves differently */ 238 case CK_InstructionStation: 239 { 240 /* Save needed information */ 241 char *program_save; 242 243 /* End previous program */ 244 if (kill (ip->process_id, 15) == -1) 245 mc_log ("Problem sending TERM signal to %d: %s.", ip->process_id, 246 g_strerror (errno)); 247 else 248 mc_log ("Ending process %d.", ip->process_id); 249 if (!ip->proc_support) 250 waitpid (ip->process_id, NULL, 0); 251 252 /* Allow process watcher callbacks to be called */ 253 g_usleep (150000); 254 g_main_context_iteration (NULL, FALSE); 255 /* Allow the process end message to flash noticeably long */ 256 g_usleep (200000); 257 258 /* Clean WInstructionStation object */ 259 instr_stn_release (ip, FALSE); 260 /* ...and reinitialize it */ 261 program_save = data != NULL ? g_strdup (data) : ip->prev_program; 262 if (instr_stn_init (ip, -1, -1, -1, -1, ip->prev_flags, program_save) == NULL) 263 { 264 /* Init failed -> close faulty station window */ 265 ret = 266 edit_dialog_callback (WIDGET (owner), WIDGET (ip), MSG_ACTION, CK_Close, NULL); 267 if (ret == MSG_HANDLED) 268 /* Update pointer in case of some future code like widget_draw(ip), etc. */ 269 ip = INSTR_STATION (owner->current->data); 270 } 271 else 272 { 273 repaint_screen (); 274 ret = MSG_HANDLED; 275 } 276 g_free (program_save); 277 } 278 break; 279 default: 280 break; 281 } 282 return ret; 283 } 284 285 /* --------------------------------------------------------------------------------------------- */ 286 287 static void 288 instr_stn_show_prompt (WInstructionStation * ip, gboolean is_ok) 289 { 290 char *sel_prompt = is_ok ? ip->prompt : ip->eprompt; 291 int sel_prompt_span = is_ok ? ip->prompt_span : ip->eprompt_span; 292 293 if (has_flag (ip->flags, INSTR_STN_NO_PROMPT)) 294 return; 295 296 /* Print prompt */ 297 if (!edit_newline_end_check_only (EDIT (ip)) && !ip->before_first_prompt) 298 { 299 edit_insert (EDIT (ip), '\n'); 300 ip->cur_line += 1; 301 } 302 else 303 ip->before_first_prompt = FALSE; 304 edit_insert_string (EDIT (ip), sel_prompt, sel_prompt_span); 305 306 /* Raise an indicator flag */ 307 ip->prompt_shown = TRUE; 308 309 /* Set cursor position to reflect state */ 310 ip->cur_col = sel_prompt_span; 311 } 312 313 /* --------------------------------------------------------------------------------------------- */ 314 /* Remove prompt from buffer (basically clear current line). */ 315 316 static void 317 instr_stn_clear_prompt (WInstructionStation * ip) 318 { 319 off_t bol, eol, idx; 320 321 if (has_flag (ip->flags, INSTR_STN_NO_PROMPT)) 322 return; 323 324 eol = edit_buffer_get_current_eol (&EDIT (ip)->buffer); 325 bol = edit_buffer_get_current_bol (&EDIT (ip)->buffer); 326 for (idx = 0; idx < eol - bol; idx++) 327 edit_backspace (EDIT (ip), TRUE); 328 ip->prompt_shown = FALSE; 329 } 330 331 /* --------------------------------------------------------------------------------------------- */ 332 333 static gboolean 334 prompt_drawing_timeout_callback (gpointer data) 335 { 336 WInstructionStation *ip = INSTR_STATION (data); 337 guint64 cur_time; 338 cur_time = g_get_real_time (); 339 /* Draw prompt after 0.5 seconds of no output from program */ 340 if (cur_time - ip->read_time > 500000 && !ip->prompt_shown) 341 { 342 instr_stn_show_prompt (ip, TRUE); 343 repaint_screen (); 344 } 345 return TRUE; 346 } 347 348 /* --------------------------------------------------------------------------------------------- */ 349 350 #ifdef GLIB_VERSION_2_40 351 352 static void 353 program_ended_callback (GPid pid, gint exit_code, gpointer user_data) 354 { 355 WInstructionStation *ip = INSTR_STATION (user_data); 356 char *msg; 357 gboolean st_ret; 358 359 /* Release object leaving it uninitialized (no freeing of main object pointer) */ 360 instr_stn_release (ip, FALSE); 361 362 g_spawn_close_pid (pid); 363 364 /* Examine exit code of closed program */ 365 #ifdef GLIB_VERSION_2_34 366 st_ret = g_spawn_check_exit_status (exit_code, NULL); 367 #else 368 st_ret = (exit_code == 0); 369 #endif 370 371 /* Choose message basing on exit code */ 372 if (st_ret) 373 msg = g_strdup (_("\nProgram closed.\n")); 374 else 375 msg = g_strdup_printf (_("\nProgram closed abnormally (exit code: %d).\n"), exit_code); 376 377 /* Insert/print message to buffer and to mc.log */ 378 edit_insert_string (EDIT (ip), msg, strlen (msg)); 379 mc_log ("%s", msg); 380 381 /* Release message upon using/printing it */ 382 g_free (msg); 383 repaint_screen (); 384 } 385 386 #endif 387 388 /* --------------------------------------------------------------------------------------------- */ 389 390 static gboolean 391 helper_configure_src_and_ch (GIOChannel ** ch, gint fd, guint * src_id, gboolean write, 392 GIOFunc cb_fun, gpointer data, const char *name) 393 { 394 /* Initialize the output variables */ 395 *ch = NULL; 396 *src_id = 0; 397 398 *ch = g_io_channel_unix_new (fd); 399 400 /* Channel created? */ 401 if (*ch == NULL) 402 goto cleanup_and_err; 403 404 /* Automatic shutdown of channel */ 405 g_io_channel_set_close_on_unref (*ch, TRUE); 406 407 /* Trim down buffering on this channel */ 408 g_io_channel_set_buffer_size (*ch, 5); 409 410 /* Attempt to set non-blocking flag on channel */ 411 if (g_io_channel_set_flags (*ch, G_IO_FLAG_NONBLOCK, NULL) != G_IO_STATUS_NORMAL) 412 mc_log ("Problem setting a channel to non-blocking state (channel flags: 0x%x)", 413 g_io_channel_get_flags (*ch)); 414 415 /* Create GSource */ 416 *src_id = 417 g_io_add_watch (*ch, (write ? G_IO_OUT : G_IO_IN) | G_IO_HUP | G_IO_ERR, cb_fun, data); 418 419 /* Source created OK? */ 420 if (*src_id == 0) 421 goto cleanup_and_err; 422 423 /* Configure the sources */ 424 g_source_set_name_by_id (*src_id, name); 425 426 /* Return true */ 427 return TRUE; 428 429 cleanup_and_err: 430 if (*src_id != 0) 431 { 432 GSource *src; 433 src = g_main_context_find_source_by_id (NULL, *src_id); 434 if (src != NULL) 435 /* Triggers also unref of wrapped channel */ 436 g_source_destroy (src); 437 } 438 if (*ch != NULL) 439 g_io_channel_unref (*ch); 440 *ch = NULL; 441 *src_id = 0; 442 443 return FALSE; 444 } 445 446 /* --------------------------------------------------------------------------------------------- */ 447 448 static gboolean 449 helper_read_all (GIOChannel * channel, char *buf, gsize * out_bytes_read_in_len) 450 { 451 gsize len = *out_bytes_read_in_len; 452 gsize count, retries = 10; 453 GIOStatus stat; 454 455 *out_bytes_read_in_len = 0; 456 457 while (*out_bytes_read_in_len < len) 458 { 459 count = 0; 460 stat = 461 g_io_channel_read_chars (channel, buf + *out_bytes_read_in_len, 462 len - *out_bytes_read_in_len, &count, NULL); 463 *out_bytes_read_in_len += count; 464 465 /* Should end the reading ? */ 466 if (stat == G_IO_STATUS_ERROR || (stat == G_IO_STATUS_NORMAL && count == 0) || 467 stat == G_IO_STATUS_EOF) 468 return (stat != G_IO_STATUS_ERROR); 469 else if (stat == G_IO_STATUS_AGAIN && retries-- == 0) 470 /* Exhausted retries - there must be no data to read - return OK */ 471 return TRUE; 472 } 473 474 return TRUE; 475 } 476 477 /* --------------------------------------------------------------------------------------------- */ 478 479 static gboolean 480 helper_write_all (GIOChannel * channel, char *buf, gsize * out_bytes_written_in_len) 481 { 482 gsize len = *out_bytes_written_in_len; 483 gsize count; 484 GIOError err; 485 486 *out_bytes_written_in_len = 0; 487 488 while (*out_bytes_written_in_len < len) 489 { 490 count = 0; 491 err = 492 g_io_channel_write (channel, buf + *out_bytes_written_in_len, 493 len - *out_bytes_written_in_len, &count); 494 *out_bytes_written_in_len += count; 495 496 if (err && err != G_IO_ERROR_AGAIN) 497 return FALSE; 498 } 499 500 return TRUE; 501 } 502 503 /* --------------------------------------------------------------------------------------------- */ 504 505 static gboolean 506 stdout_read_callback (GIOChannel * source, GIOCondition condition, gpointer data) 507 { 508 WInstructionStation *ip = INSTR_STATION (data); 509 char buf[2048]; 510 gsize count = 2047; 511 gboolean r_ret; 512 513 /* Active only when a recognized event of interest occurs */ 514 if ((condition & (G_IO_IN | G_IO_HUP | G_IO_ERR)) == 0) 515 return TRUE; 516 else 517 ip->io_complete = FALSE; 518 519 /* Repeat read of 2KiB-1 maximum bytes until 0 bytes are being read */ 520 do 521 { 522 r_ret = helper_read_all (source, buf, &count); 523 buf[count] = '\0'; 524 if (count == 0 && !r_ret) 525 { 526 mc_log ("Error while reading program's output: %s", g_strerror (errno)); 527 } 528 else 529 { 530 ip->read_time = g_get_real_time (); 531 buf[count] = '\0'; 532 if (count > 0) 533 { 534 gsize idx = 0, nl_count = 0; 535 536 /* Count number of read lines */ 537 char *p = buf; 538 while (idx++ < count) 539 if (*p++ == '\n') 540 nl_count++; 541 542 /* Move to next line after prompt */ 543 if (ip->first_response) 544 { 545 edit_insert (EDIT (ip), '\n'); 546 nl_count++; 547 ip->first_response = FALSE; 548 ip->prompt_shown = FALSE; 549 } 550 else if (ip->prompt_shown) 551 instr_stn_clear_prompt (ip); 552 553 /* Print whole buffer into file's window */ 554 edit_insert_string (EDIT (ip), buf, count); 555 556 /* Increase the row pointer */ 557 ip->cur_line += nl_count; 558 } 559 else if (!ip->prompt_shown) 560 instr_stn_show_prompt (ip, r_ret); 561 562 repaint_screen (); 563 } 564 } 565 while (count != 0); 566 567 ip->io_complete = r_ret; 568 return condition != G_IO_HUP; 569 } 570 571 /* --------------------------------------------------------------------------------------------- */ 572 573 static gboolean 574 stdin_write_callback (GIOChannel * source, GIOCondition condition, gpointer data) 575 { 576 WInstructionStation *ip = INSTR_STATION (data); 577 gsize count; 578 gboolean ret = TRUE; 579 580 /* Any instruction to send? */ 581 if (ip->instruction == NULL) 582 return ret; 583 else 584 ip->io_complete = FALSE; 585 586 if ((condition & G_IO_OUT) == 0) 587 return TRUE; 588 589 errno = 0; 590 count = strlen (ip->instruction); 591 ret = helper_write_all (source, ip->instruction, &count); 592 if (!ret) 593 { 594 mc_log ("Error during sending instruction to program: %s", g_strerror (errno)); 595 } 596 else 597 { 598 ip->first_response = TRUE; 599 MC_PTR_FREE (ip->instruction); 600 if (ip->prompt_timeout_id == 0) 601 ip->prompt_timeout_id = g_timeout_add (200, prompt_drawing_timeout_callback, ip); 602 } 603 604 g_io_channel_flush (ip->io[STDIN].ch, NULL); 605 ip->io_complete = TRUE; 606 607 return ret; 608 } 609 610 /* --------------------------------------------------------------------------------------------- */ 611 612 /* --------------------------------------------------------------------------------------------- */ 613 /*** public functions ****************************************************************************/ 614 /* --------------------------------------------------------------------------------------------- */ 615 616 WInstructionStation * 617 instr_stn_create (int y, int x, int lines, int cols, instr_stn_flags_t flags, const char *program) 618 { 619 WInstructionStation *ip, *iret; 620 621 ip = g_new0 (WInstructionStation, 1); 622 if (ip == NULL) 623 return NULL; 624 625 iret = instr_stn_init (ip, y, x, lines, cols, flags, program); 626 if (iret == NULL) 627 MC_PTR_FREE (ip); 628 629 return ip; 630 } 631 632 /* --------------------------------------------------------------------------------------------- */ 633 /* Initializes a preallocated object */ 634 635 WInstructionStation * 636 instr_stn_init (WInstructionStation * ip, int y, int x, int lines, int cols, 637 instr_stn_flags_t flags, const char *program) 638 { 639 WEdit *iret; 640 WGroup *owner_save = NULL; 641 gboolean tmp_ret = FALSE; 642 char **program_cline; 643 int rounds = 0; 644 645 if (ip == NULL) 646 goto cleanup_and_err; 647 648 /* Handle special -1 values indicating reuse of old values */ 649 if (ip->finalized) 650 { 651 y = (y == -1) ? WIDGET (ip)->y : y; 652 x = (x == -1) ? WIDGET (ip)->x : x; 653 lines = (lines == -1) ? WIDGET (ip)->lines : lines; 654 cols = (cols == -1) ? WIDGET (ip)->cols : cols; 655 /* Save owner */ 656 owner_save = WIDGET (ip)->owner; 657 ip->finalized = FALSE; 658 } 659 660 /* Initialize base object (WEdit) */ 661 iret = edit_init (EDIT (ip), y, x, lines, cols, NULL, 0, EDIT_DO_INIT_BASE_CLASS); 662 if (iret == NULL) 663 goto cleanup_and_err; 664 665 /* Restore owner field value */ 666 WIDGET (ip)->owner = owner_save; 667 WIDGET (ip)->callback = instr_stn_callback; 668 WIDGET (ip)->mouse_callback = edit_mouse_callback; 669 EDIT (ip)->fullscreen = 0; 670 671 ip->io[0].name = g_strdup ("Std-In (WInstructionStation)"); 672 ip->io[1].name = g_strdup ("Std-Out (WInstructionStation)"); 673 ip->io[2].name = g_strdup ("Std-Error (WInstructionStation)"); 674 ip->io[0].fd = ip->io[1].fd = ip->io[2].fd = -1; 675 676 ip->before_first_prompt = TRUE; 677 ip->prompt = g_strdup ("[guest@localhost]# "); 678 ip->prompt_span = strlen (ip->prompt); 679 ip->eprompt = g_strdup ("[guest@localhost]! "); 680 ip->eprompt_span = strlen (ip->eprompt); 681 ip->cur_col = 0; 682 ip->cur_line = 0; 683 ip->flags = flags; 684 ip->prev_flags = flags; 685 686 /* Set initial program (most probably a shell, like Bash) */ 687 if (program != NULL) 688 ip->program = g_strdup (program); 689 else 690 /* Fallback instruction if no program given */ 691 ip->program = g_strdup ("bash"); 692 693 while (++rounds <= 2 && !tmp_ret) 694 { 695 int argc = 0; 696 if (ip->error != NULL) 697 { 698 g_error_free (ip->error); 699 ip->error = NULL; 700 } 701 tmp_ret = g_shell_parse_argv (ip->program, &argc, &program_cline, &ip->error); 702 if (tmp_ret) 703 { 704 tmp_ret = g_spawn_async_with_pipes (NULL, program_cline, 705 NULL, G_SPAWN_SEARCH_PATH | 706 G_SPAWN_DO_NOT_REAP_CHILD | 707 G_SPAWN_LEAVE_DESCRIPTORS_OPEN, 708 NULL, NULL, &ip->process_id, 709 has_flag (flags, 710 INSTR_STN_NO_STDIN) ? NULL : 711 &ip->io[STDIN].fd, &ip->io[STDOUT].fd, 712 &ip->io[STDERR].fd, &ip->error); 713 g_strfreev (program_cline); 714 } 715 if (!tmp_ret) 716 { 717 char *new_program, *msg_text; 718 mc_log ("Creating CLI process [instruction:%s] failed: %s", 719 program, ip->error ? ip->error->message : "<no error message>"); 720 721 /* Create a transatable, parametrized message */ 722 msg_text = g_strdup_printf ("%s%s:", _("The specified program has failed to run.\n" 723 "Enter a new full path or a program name"), 724 (rounds >= 2 ? _(" (last try)") : "")); 725 726 /* Display message asking for a new program */ 727 new_program = input_expand_dialog (_("Provide an alternate program to run"), 728 msg_text, "instruction-station", 729 alt_prog_first_run ? ip->program : INPUT_LAST_TEXT, 730 INPUT_COMPLETE_COMMANDS | INPUT_COMPLETE_FILENAMES | 731 INPUT_COMPLETE_VARIABLES); 732 g_free (msg_text); 733 alt_prog_first_run = FALSE; 734 735 /* Obtained a new program? */ 736 if (new_program != NULL) 737 { 738 g_free (ip->program); 739 ip->program = new_program; 740 } 741 } 742 } 743 744 /* If no program to run has been found, then exit. */ 745 if (!tmp_ret) 746 { 747 message (D_ERROR, _("InstructionStation startup failed"), 748 _("Failed to initialize the CLI window\n(reason: %s)."), 749 ip->error ? ip->error->message : "<no error message>"); 750 goto cleanup_and_err; 751 } 752 753 ip->prev_program = ip->program; 754 755 for (int i = 0; i <= STDERR; i++) 756 { 757 if (has_flag (flags, INSTR_STN_NO_STDIN) && i == STDIN) 758 { 759 /* There is no standard input pipe for program */ 760 ip->io[i].fd = -1; 761 continue; 762 } 763 764 if (helper_configure_src_and_ch (&ip->io[i].ch, ip->io[i].fd, &ip->io[i].src_id, 765 (i == STDIN ? TRUE : FALSE), 766 (i == STDIN ? stdin_write_callback : stdout_read_callback), 767 ip, ip->io[i].name) == FALSE) 768 goto cleanup_and_err; 769 } 770 771 #ifdef GLIB_VERSION_2_40 772 /* Set a process watcher and restarter for the program */ 773 ip->proc_src_id = g_child_watch_add (ip->process_id, program_ended_callback, ip); 774 ip->proc_support = TRUE; 775 #endif 776 777 /* Display initial prompt */ 778 instr_stn_show_prompt (ip, TRUE); 779 ip->io_complete = TRUE; 780 781 return ip; 782 783 cleanup_and_err: 784 785 instr_stn_release (ip, TRUE); 786 return NULL; 787 } 788 789 /* --------------------------------------------------------------------------------------------- */ 790 791 WEdit * 792 find_cli (const WDialog * h) 793 { 794 const WGroup *g = CONST_GROUP (h); 795 796 if (edit_widget_is_cli (CONST_WIDGET (g->current->data))) 797 return (WEdit *) g->current->data; 798 return (WEdit *) widget_find_by_type (CONST_WIDGET (h), instr_stn_callback); 799 } 800 801 /* --------------------------------------------------------------------------------------------- */ 802 /* Check if widget is a WInstructionStation class type. */ 803 804 gboolean 805 edit_widget_is_cli (const Widget * w) 806 { 807 return (w != NULL && w->callback == instr_stn_callback); 808 } 809 810 /* --------------------------------------------------------------------------------------------- */ 811 812 cb_ret_t 813 instr_stn_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data) 814 { 815 cb_ret_t ret = MSG_NOT_HANDLED; 816 WInstructionStation *ip = INSTR_STATION (w); 817 WGroup *owner = w->owner; 818 819 switch (msg) 820 { 821 822 case MSG_KEY: 823 { 824 gboolean ext_mode; 825 long novel_state_id; 826 827 /* keep and then extmod flag */ 828 ext_mode = w->ext_mode; 829 novel_state_id = widget_lookup_key (w, parm); 830 w->ext_mode = ext_mode; 831 832 if (novel_state_id == CK_IgnoreKey) 833 w->ext_mode = FALSE; 834 else 835 { 836 /* Treat Station action specially when run from within a station window */ 837 if (novel_state_id != CK_InstructionStation) 838 ret = edit_dialog_command_execute (DIALOG (owner), novel_state_id, data); 839 if (ret == MSG_NOT_HANDLED) 840 ret = instr_stn_run_novel_state_id (ip, novel_state_id, data); 841 /* 842 * If activity was not handled, keep the extended mode 843 * for the further key processing, checking if the 844 * window is still open */ 845 if (novel_state_id != CK_Close) 846 { 847 if (ret == MSG_HANDLED) 848 w->ext_mode = FALSE; 849 } 850 else if (ret == MSG_HANDLED) 851 /* Window closed -> update `w` variable */ 852 w = WIDGET (owner->current->data); 853 } 854 855 /* 856 * Due to the "end of bracket" escape the editor sees input with is_idle() == false 857 * (expects more characters) and hence doesn't yet refresh the screen, but then 858 * no further characters arrive (there's only an "end of bracket" which is swallowed 859 * by tty_get_event()), so you end up with a screen that's not refreshed after pasting. 860 * So let's trigger an IDLE signal. 861 */ 862 if (!is_idle ()) 863 widget_idle (w, TRUE); 864 } 865 break; 866 case MSG_ACTION: 867 ret = instr_stn_run_novel_state_id (ip, parm, data); 868 break; 869 case MSG_DESTROY: 870 kill (ip->process_id, 15); 871 /* Have to wait directly (no GLib support for watchers) ? */ 872 if (!ip->proc_support) 873 waitpid (ip->process_id, NULL, 0); 874 875 /* 876 * Message will be passed on to WEdit callback which will g_free(), so passing FALSE (i.e.: 877 * requesting cleanup of WInstructionStation object without full release of its main 878 * pointer) */ 879 instr_stn_release (ip, FALSE); 880 /* Not restarting the station, so release program string backup field */ 881 g_free (ip->prev_program); 882 break; 883 default: 884 break; 885 } 886 887 /* Redirect message to base object (i.e.: editor window «» WEdit) if needed */ 888 if (ret == MSG_NOT_HANDLED) 889 { 890 ret = edit_callback (w, sender, msg, parm, data); 891 if (msg != MSG_DESTROY) 892 ip->cur_col = EDIT (w)->curs_col; 893 } 894 return ret; 895 } 896 897 /* --------------------------------------------------------------------------------------------- */ -
new file src/editor/instr_station.h
diff --git a/src/editor/instr_station.h b/src/editor/instr_station.h new file mode 100644 index 000000000..0f05900c1
- + 1 #ifndef MC__INSTR_STATION_H 2 #define MC__INSTR_STATION_H 3 4 /*** typedefs(not structures) and defined constants **********************************************/ 5 6 #define INSTR_STATION(x) ((WInstructionStation *)x) 7 #define CONST_INSTR_STATION(x) ((const WInstructionStation *)x) 8 9 /*** enums ***************************************************************************************/ 10 11 /*** structures declarations (and typedefs of structures)*****************************************/ 12 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 GError placeholder */ 69 GError *error; 70 71 /* Indicator of an idle state – set when there's no data to send and read */ 72 gboolean io_complete; 73 74 /* Indicator of instr_release being already called on WInstructionStation object */ 75 gboolean finalized; 76 } WInstructionStation; 77 78 /*** global variables defined in .c file *********************************************************/ 79 80 /*** declarations of public functions ************************************************************/ 81 82 WInstructionStation *instr_stn_create (int y, int x, int lines, int cols, instr_stn_flags_t flags, 83 const char *program); 84 WInstructionStation *instr_stn_init (WInstructionStation * ip, int y, int x, int lines, int cols, 85 instr_stn_flags_t flags, const char *program); 86 WEdit *find_cli (const WDialog * h); 87 gboolean edit_widget_is_cli (const Widget * w); 88 cb_ret_t instr_stn_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data); 89 90 /*** inline functions ****************************************************************************/ 91 92 #endif /* MC__INSTR_STATION_H */ -
src/keybind-defaults.c
diff --git a/src/keybind-defaults.c b/src/keybind-defaults.c index db483382a..c0cede558 100644
a b static const global_keymap_ini_t default_editor_keymap[] = { 478 478 {"FileNext", "alt-plus"}, 479 479 {"Sort", "alt-t"}, 480 480 {"Mail", "alt-m"}, 481 {"InstructionStation", "alt-i"}, 481 482 {"ExternalCommand", "alt-u"}, 482 483 {"WindowMove", "ctrl-alt-e"}, 483 484 {"WindowResize", "ctrl-alt-s"},