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