2 Editor initialisation and callback handler.
4 Copyright (C) 1996-2020
5 Free Software Foundation, Inc.
9 Andrew Borodin <aborodin@vmail.ru> 2012, 2013
11 This file is part of the Midnight Commander.
13 The Midnight Commander is free software: you can redistribute it
14 and/or modify it under the terms of the GNU General Public License as
15 published by the Free Software Foundation, either version 3 of the License,
16 or (at your option) any later version.
18 The Midnight Commander is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU General Public License for more details.
23 You should have received a copy of the GNU General Public License
24 along with this program. If not, see <http://www.gnu.org/licenses/>.
28 * \brief Source: editor initialisation and callback handler
39 #include <sys/types.h>
42 #include "lib/global.h"
44 #include "lib/tty/tty.h" /* LINES, COLS */
45 #include "lib/tty/key.h" /* is_idle() */
46 #include "lib/tty/color.h" /* tty_setcolor() */
48 #include "lib/fileloc.h" /* EDIT_DIR */
49 #include "lib/strutil.h" /* str_term_trim() */
50 #include "lib/util.h" /* mc_build_filename() */
51 #include "lib/widget.h"
52 #include "lib/mcconfig.h"
53 #include "lib/event.h" /* mc_event_raise() */
55 #include "lib/charsets.h"
58 #include "src/keybind-defaults.h" /* keybind_lookup_keymap_command() */
59 #include "src/setup.h" /* home_dir */
60 #include "src/execute.h" /* toggle_subshell() */
61 #include "src/filemanager/cmd.h" /* save_setup_cmd() */
62 #include "src/learn.h" /* learn_keys() */
63 #include "src/args.h" /* mcedit_arg_t */
65 #include "edit-impl.h"
66 #include "editwidget.h"
71 /*** global variables ****************************************************************************/
73 char *edit_window_state_char
= NULL
;
74 char *edit_window_close_char
= NULL
;
76 /*** file scope macro definitions ****************************************************************/
78 #define WINDOW_MIN_LINES (2 + 2)
79 #define WINDOW_MIN_COLS (2 + LINE_STATE_WIDTH + 2)
81 /*** file scope type declarations ****************************************************************/
83 /*** file scope variables ************************************************************************/
84 static unsigned int edit_dlg_init_refcounter
= 0;
86 /*** file scope functions ************************************************************************/
88 static cb_ret_t
edit_dialog_callback (Widget
* w
, Widget
* sender
, widget_msg_t msg
, int parm
,
91 /* --------------------------------------------------------------------------------------------- */
93 * Init the 'edit' subsystem
99 if (edit_dlg_init_refcounter
== 0)
101 edit_window_state_char
= mc_skin_get ("widget-editor", "window-state-char", "*");
102 edit_window_close_char
= mc_skin_get ("widget-editor", "window-close-char", "X");
109 edit_dlg_init_refcounter
++;
112 /* --------------------------------------------------------------------------------------------- */
114 * Deinit the 'edit' subsystem
118 edit_dlg_deinit (void)
120 if (edit_dlg_init_refcounter
!= 0)
121 edit_dlg_init_refcounter
--;
123 if (edit_dlg_init_refcounter
== 0)
125 g_free (edit_window_state_char
);
126 g_free (edit_window_close_char
);
134 /* --------------------------------------------------------------------------------------------- */
136 * Show info about editor
142 quick_widget_t quick_widgets
[] = {
144 QUICK_LABEL ("MCEdit " VERSION
, NULL
),
145 QUICK_SEPARATOR (TRUE
),
146 QUICK_LABEL (N_("A user friendly text editor\n"
147 "written for the Midnight Commander."), NULL
),
148 QUICK_SEPARATOR (FALSE
),
149 QUICK_LABEL (N_("Copyright (C) 1996-2020 the Free Software Foundation"), NULL
),
150 QUICK_START_BUTTONS (TRUE
, TRUE
),
151 QUICK_BUTTON (N_("&OK"), B_ENTER
, NULL
, NULL
),
156 quick_dialog_t qdlg
= {
158 N_("About"), "[Internal File Editor]",
159 quick_widgets
, NULL
, NULL
162 quick_widgets
[0].pos_flags
= WPOS_KEEP_TOP
| WPOS_CENTER_HORZ
;
163 quick_widgets
[2].pos_flags
= WPOS_KEEP_TOP
| WPOS_CENTER_HORZ
;
164 quick_widgets
[4].pos_flags
= WPOS_KEEP_TOP
| WPOS_CENTER_HORZ
;
166 (void) quick_dialog (&qdlg
);
169 /* --------------------------------------------------------------------------------------------- */
177 ev_help_t event_data
= { NULL
, "[Internal File Editor]" };
178 mc_event_raise (MCEVENT_GROUP_CORE
, "help", &event_data
);
181 /* --------------------------------------------------------------------------------------------- */
183 * Restore saved window size.
185 * @param edit editor object
189 edit_restore_size (WEdit
* edit
)
191 Widget
*w
= WIDGET (edit
);
193 edit
->drag_state
= MCEDIT_DRAG_NONE
;
194 w
->mouse
.forced_capture
= FALSE
;
195 widget_set_size (w
, edit
->y_prev
, edit
->x_prev
, edit
->lines_prev
, edit
->cols_prev
);
196 dlg_draw (DIALOG (w
->owner
));
199 /* --------------------------------------------------------------------------------------------- */
201 * Move window by one row or column in any direction.
203 * @param edit editor object
204 * @param command direction (CK_Up, CK_Down, CK_Left, CK_Right)
208 edit_window_move (WEdit
* edit
, long command
)
210 Widget
*w
= WIDGET (edit
);
211 Widget
*wh
= WIDGET (w
->owner
);
216 if (w
->y
> wh
->y
+ 1) /* menubar */
220 if (w
->y
< wh
->y
+ wh
->lines
- 2) /* buttonbar */
224 if (w
->x
+ w
->cols
> wh
->x
)
228 if (w
->x
< wh
->x
+ wh
->cols
)
235 edit
->force
|= REDRAW_PAGE
;
236 dlg_draw (DIALOG (w
->owner
));
239 /* --------------------------------------------------------------------------------------------- */
241 * Resize window by one row or column in any direction.
243 * @param edit editor object
244 * @param command direction (CK_Up, CK_Down, CK_Left, CK_Right)
248 edit_window_resize (WEdit
* edit
, long command
)
250 Widget
*w
= WIDGET (edit
);
251 Widget
*wh
= WIDGET (w
->owner
);
256 if (w
->lines
> WINDOW_MIN_LINES
)
260 if (w
->y
+ w
->lines
< wh
->y
+ wh
->lines
- 1) /* buttonbar */
264 if (w
->cols
> WINDOW_MIN_COLS
)
268 if (w
->x
+ w
->cols
< wh
->x
+ wh
->cols
)
275 edit
->force
|= REDRAW_COMPLETELY
;
276 dlg_draw (DIALOG (w
->owner
));
279 /* --------------------------------------------------------------------------------------------- */
281 * Get hotkey by number.
290 return (n
<= 9) ? '0' + n
: 'a' + n
- 10;
293 /* --------------------------------------------------------------------------------------------- */
296 edit_window_list (const WDialog
* h
)
298 const WGroup
*g
= CONST_GROUP (h
);
299 const size_t offset
= 2; /* skip menu and buttonbar */
300 const size_t dlg_num
= g_list_length (g
->widgets
) - offset
;
307 lines
= MIN ((size_t) (LINES
* 2 / 3), dlg_num
);
310 listbox
= create_listbox_window (lines
, cols
, _("Open files"), "[Open files]");
312 for (w
= g
->widgets
; w
!= NULL
; w
= g_list_next (w
))
313 if (edit_widget_is_editor (CONST_WIDGET (w
->data
)))
315 WEdit
*e
= (WEdit
*) w
->data
;
318 if (e
->filename_vpath
== NULL
)
319 fname
= g_strdup_printf ("%c [%s]", e
->modified
? '*' : ' ', _("NoName"));
322 g_strdup_printf ("%c%s", e
->modified
? '*' : ' ',
323 vfs_path_as_str (e
->filename_vpath
));
325 listbox_add_item (listbox
->list
, LISTBOX_APPEND_AT_END
, get_hotkey (i
++),
326 str_term_trim (fname
, WIDGET (listbox
->list
)->cols
- 2), e
, FALSE
);
330 selected
= run_listbox_with_data (listbox
, g
->current
->data
);
331 if (selected
!= NULL
)
332 widget_select (WIDGET (selected
));
335 /* --------------------------------------------------------------------------------------------- */
338 edit_get_shortcut (long command
)
341 const char *shortcut
= NULL
;
343 shortcut
= keybind_lookup_keymap_shortcut (editor_map
, command
);
344 if (shortcut
!= NULL
)
345 return g_strdup (shortcut
);
347 ext_map
= keybind_lookup_keymap_shortcut (editor_map
, CK_ExtendedKeyMap
);
349 shortcut
= keybind_lookup_keymap_shortcut (editor_x_map
, command
);
350 if (shortcut
!= NULL
)
351 return g_strdup_printf ("%s %s", ext_map
, shortcut
);
356 /* --------------------------------------------------------------------------------------------- */
359 edit_get_title (const WDialog
* h
, size_t len
)
361 const WEdit
*edit
= find_editor (h
);
362 const char *modified
= edit
->modified
? "(*) " : " ";
363 const char *file_label
;
368 if (edit
->filename_vpath
== NULL
)
369 filename
= g_strdup (_("[NoName]"));
371 filename
= g_strdup (vfs_path_as_str (edit
->filename_vpath
));
373 file_label
= str_term_trim (filename
, len
- str_term_width1 (_("Edit: ")));
376 return g_strconcat (_("Edit: "), modified
, file_label
, (char *) NULL
);
379 /* --------------------------------------------------------------------------------------------- */
382 edit_dialog_command_execute (WDialog
* h
, long command
)
384 WGroup
*g
= GROUP (h
);
385 Widget
*wh
= WIDGET (h
);
386 cb_ret_t ret
= MSG_HANDLED
;
391 edit_add_window (h
, wh
->y
+ 1, wh
->x
, wh
->lines
- 2, wh
->cols
, NULL
, 0);
397 edit_load_file_from_history (h
);
399 case CK_EditSyntaxFile
:
400 edit_load_syntax_file (h
);
402 case CK_EditUserMenu
:
403 edit_load_menu_file (h
);
406 /* if there are no opened files anymore, close MC editor */
407 if (edit_widget_is_editor (CONST_WIDGET (g
->current
->data
)) &&
408 edit_close_cmd ((WEdit
*) g
->current
->data
) && find_editor (h
) == NULL
)
413 /* edit->force |= REDRAW_COMPLETELY; */
420 /* don't close editor due to SIGINT, but stop move/resize window */
422 Widget
*w
= WIDGET (g
->current
->data
);
424 if (edit_widget_is_editor (w
) && ((WEdit
*) w
)->drag_state
!= MCEDIT_DRAG_NONE
)
425 edit_restore_size ((WEdit
*) w
);
426 else if (command
== CK_Quit
)
434 edit_syntax_onoff_cmd (h
);
437 edit_show_tabs_tws_cmd (h
);
440 edit_show_margin_cmd (h
);
443 edit_show_numbers_cmd (h
);
455 case CK_WindowResize
:
456 if (edit_widget_is_editor (CONST_WIDGET (g
->current
->data
)))
457 edit_handle_move_resize ((WEdit
*) g
->current
->data
, command
);
460 edit_window_list (h
);
463 group_select_next_widget (g
);
466 group_select_prev_widget (g
);
469 edit_options_dialog (h
);
471 case CK_OptionsSaveMode
:
472 edit_save_mode_cmd ();
478 ret
= MSG_NOT_HANDLED
;
485 /* --------------------------------------------------------------------------------------------- */
487 * Translate the keycode into either 'command' or 'char_for_insertion'.
488 * 'command' is one of the editor commands from cmddef.h.
492 edit_translate_key (WEdit
* edit
, long x_key
, int *cmd
, int *ch
)
494 long command
= CK_InsertChar
;
495 int char_for_insertion
= -1;
497 /* an ordinary insertable character */
498 if (!edit
->extmod
&& x_key
< 256)
501 if (is_printable (x_key
))
503 char_for_insertion
= x_key
;
509 if (edit
->charpoint
>= 4)
512 edit
->charbuf
[edit
->charpoint
] = '\0';
514 if (edit
->charpoint
< 4)
516 edit
->charbuf
[edit
->charpoint
++] = x_key
;
517 edit
->charbuf
[edit
->charpoint
] = '\0';
520 /* input from 8-bit locale */
521 if (!mc_global
.utf8_display
)
523 /* source in 8-bit codeset */
524 c
= convert_from_input_c (x_key
);
526 if (is_printable (c
))
529 char_for_insertion
= c
;
531 char_for_insertion
= convert_from_8bit_to_utf_c2 ((char) x_key
);
540 res
= str_is_valid_char (edit
->charbuf
, edit
->charpoint
);
541 if (res
< 0 && res
!= -2)
543 edit
->charpoint
= 0; /* broken multibyte char, skip */
549 /* source in UTF-8 codeset */
552 char_for_insertion
= x_key
;
556 edit
->charbuf
[edit
->charpoint
] = '\0';
558 if (g_unichar_isprint (g_utf8_get_char (edit
->charbuf
)))
560 char_for_insertion
= x_key
;
569 /* not finised multibyte input (in meddle multibyte utf-8 char) */
573 if (g_unichar_isprint (g_utf8_get_char (edit
->charbuf
)))
575 c
= convert_from_utf_to_current (edit
->charbuf
);
576 edit
->charbuf
[0] = '\0';
578 char_for_insertion
= c
;
582 /* unprinteble utf input, skip it */
583 edit
->charbuf
[0] = '\0';
587 #endif /* HAVE_CHARSET */
590 /* Commands specific to the key emulation */
593 edit
->extmod
= FALSE
;
594 command
= keybind_lookup_keymap_command (editor_x_map
, x_key
);
597 command
= keybind_lookup_keymap_command (editor_map
, x_key
);
599 if (command
== CK_IgnoreKey
)
600 command
= CK_InsertChar
;
603 *cmd
= (int) command
; /* FIXME */
604 *ch
= char_for_insertion
;
606 return !(command
== CK_InsertChar
&& char_for_insertion
== -1);
610 /* --------------------------------------------------------------------------------------------- */
613 edit_quit (WDialog
* h
)
620 /* don't stop the dialog before final decision */
621 widget_set_state (WIDGET (h
), WST_ACTIVE
, TRUE
);
623 /* check window state and get modified files */
624 for (l
= GROUP (h
)->widgets
; l
!= NULL
; l
= g_list_next (l
))
625 if (edit_widget_is_editor (CONST_WIDGET (l
->data
)))
627 e
= (WEdit
*) l
->data
;
629 if (e
->drag_state
!= MCEDIT_DRAG_NONE
)
631 edit_restore_size (e
);
636 /* create separate list because widget_select()
637 changes the window position in Z order */
639 m
= g_slist_prepend (m
, l
->data
);
642 for (me
= m
; me
!= NULL
; me
= g_slist_next (me
))
644 e
= (WEdit
*) me
->data
;
646 widget_select (WIDGET (e
));
648 if (!edit_ok_to_exit (e
))
652 /* if all files were checked, quit editor */
659 /* --------------------------------------------------------------------------------------------- */
662 edit_set_buttonbar (WEdit
* edit
, WButtonBar
* bb
)
664 buttonbar_set_label (bb
, 1, Q_ ("ButtonBar|Help"), editor_map
, NULL
);
665 buttonbar_set_label (bb
, 2, Q_ ("ButtonBar|Save"), editor_map
, WIDGET (edit
));
666 buttonbar_set_label (bb
, 3, Q_ ("ButtonBar|Mark"), editor_map
, WIDGET (edit
));
667 buttonbar_set_label (bb
, 4, Q_ ("ButtonBar|Replac"), editor_map
, WIDGET (edit
));
668 buttonbar_set_label (bb
, 5, Q_ ("ButtonBar|Copy"), editor_map
, WIDGET (edit
));
669 buttonbar_set_label (bb
, 6, Q_ ("ButtonBar|Move"), editor_map
, WIDGET (edit
));
670 buttonbar_set_label (bb
, 7, Q_ ("ButtonBar|Search"), editor_map
, WIDGET (edit
));
671 buttonbar_set_label (bb
, 8, Q_ ("ButtonBar|Delete"), editor_map
, WIDGET (edit
));
672 buttonbar_set_label (bb
, 9, Q_ ("ButtonBar|PullDn"), editor_map
, NULL
);
673 buttonbar_set_label (bb
, 10, Q_ ("ButtonBar|Quit"), editor_map
, NULL
);
676 /* --------------------------------------------------------------------------------------------- */
679 edit_total_update (WEdit
* edit
)
681 edit_find_bracket (edit
);
682 edit
->force
|= REDRAW_COMPLETELY
;
683 edit_update_curs_row (edit
);
684 edit_update_screen (edit
);
687 /* --------------------------------------------------------------------------------------------- */
690 edit_update_cursor (WEdit
* edit
, const mouse_event_t
* event
)
695 x
= event
->x
- (edit
->fullscreen
? 0 : 1);
696 y
= event
->y
- (edit
->fullscreen
? 0 : 1);
698 if (edit
->mark2
!= -1 && event
->msg
== MSG_MOUSE_UP
)
699 return TRUE
; /* don't do anything */
701 if (event
->msg
== MSG_MOUSE_DOWN
|| event
->msg
== MSG_MOUSE_UP
)
702 edit_push_key_press (edit
);
704 if (!option_cursor_beyond_eol
)
705 edit
->prev_col
= x
- edit
->start_col
- option_line_state_width
;
711 edit_move_forward3 (edit
, edit_buffer_get_current_bol (&edit
->buffer
), 0,
712 edit_buffer_get_current_eol (&edit
->buffer
));
714 if (x
> line_len
- 1)
716 edit
->over_col
= x
- line_len
- edit
->start_col
- option_line_state_width
;
717 edit
->prev_col
= line_len
;
722 edit
->prev_col
= x
- option_line_state_width
- edit
->start_col
;
726 if (y
> edit
->curs_row
)
727 edit_move_down (edit
, y
- edit
->curs_row
, FALSE
);
728 else if (y
< edit
->curs_row
)
729 edit_move_up (edit
, edit
->curs_row
- y
, FALSE
);
731 edit_move_to_prev_col (edit
, edit_buffer_get_current_bol (&edit
->buffer
));
733 if (event
->msg
== MSG_MOUSE_CLICK
)
735 edit_mark_cmd (edit
, TRUE
); /* reset */
739 done
= (event
->msg
!= MSG_MOUSE_DRAG
);
741 edit_mark_cmd (edit
, FALSE
);
746 /* --------------------------------------------------------------------------------------------- */
747 /** Callback for the edit dialog */
750 edit_dialog_callback (Widget
* w
, Widget
* sender
, widget_msg_t msg
, int parm
, void *data
)
752 WGroup
*g
= GROUP (w
);
753 WDialog
*h
= DIALOG (w
);
762 /* don't use dlg_default_repaint() -- we don't need a frame */
763 tty_setcolor (EDITOR_BACKGROUND
);
768 dlg_default_callback (w
, NULL
, MSG_RESIZE
, 0, NULL
);
769 menubar_arrange (find_menubar (h
));
774 /* Handle shortcuts, menu, and buttonbar. */
778 result
= edit_dialog_command_execute (h
, parm
);
780 /* We forward any commands coming from the menu, and which haven't been
781 handled by the dialog, to the focused WEdit window. */
782 if (result
== MSG_NOT_HANDLED
&& sender
== WIDGET (find_menubar (h
)))
783 result
= send_message (g
->current
->data
, NULL
, MSG_ACTION
, parm
, NULL
);
790 Widget
*we
= WIDGET (g
->current
->data
);
791 cb_ret_t ret
= MSG_NOT_HANDLED
;
793 if (edit_widget_is_editor (we
))
795 WEdit
*e
= (WEdit
*) we
;
799 command
= keybind_lookup_keymap_command (editor_map
, parm
);
801 command
= keybind_lookup_keymap_command (editor_x_map
, parm
);
803 if (command
== CK_IgnoreKey
)
807 ret
= edit_dialog_command_execute (h
, command
);
808 /* if command was not handled, keep the extended mode
809 for the further key processing */
810 if (ret
== MSG_HANDLED
)
816 * Due to the "end of bracket" escape the editor sees input with is_idle() == false
817 * (expects more characters) and hence doesn't yet refresh the screen, but then
818 * no further characters arrive (there's only an "end of bracket" which is swallowed
819 * by tty_get_event()), so you end up with a screen that's not refreshed after pasting.
820 * So let's trigger an IDLE signal.
823 widget_idle (w
, TRUE
);
827 /* hardcoded menu hotkeys (see edit_drop_hotkey_menu) */
828 case MSG_UNHANDLED_KEY
:
829 return edit_drop_hotkey_menu (h
, parm
) ? MSG_HANDLED
: MSG_NOT_HANDLED
;
840 widget_idle (w
, FALSE
);
841 return send_message (g
->current
->data
, NULL
, MSG_IDLE
, 0, NULL
);
844 return dlg_default_callback (w
, sender
, msg
, parm
, data
);
848 /* --------------------------------------------------------------------------------------------- */
851 * Handle mouse events of editor screen.
853 * @param w Widget object (the editor)
854 * @param msg mouse event message
855 * @param event mouse event data
858 edit_dialog_mouse_callback (Widget
* w
, mouse_msg_t msg
, mouse_event_t
* event
)
860 gboolean unhandled
= TRUE
;
862 if (msg
== MSG_MOUSE_DOWN
&& event
->y
== 0)
864 WGroup
*g
= GROUP (w
);
865 WDialog
*h
= DIALOG (w
);
868 b
= find_menubar (h
);
870 if (!widget_get_state (WIDGET (b
), WST_FOCUSED
))
878 /* Try find top fullscreen window */
879 for (l
= g
->widgets
; l
!= NULL
; l
= g_list_next (l
))
880 if (edit_widget_is_editor (CONST_WIDGET (l
->data
))
881 && ((WEdit
*) l
->data
)->fullscreen
)
884 /* Handle fullscreen/close buttons in the top line */
887 if (top
!= NULL
&& event
->x
>= x
)
889 WEdit
*e
= (WEdit
*) top
->data
;
891 if (top
!= g
->current
)
893 /* Window is not active. Activate it */
894 widget_select (WIDGET (e
));
898 if (event
->x
- x
<= 2)
899 edit_toggle_fullscreen (e
);
901 send_message (h
, NULL
, MSG_ACTION
, CK_Close
, NULL
);
907 menubar_activate (b
, drop_menus
, -1);
911 /* Continue handling of unhandled event in window or menu */
912 event
->result
.abort
= unhandled
;
915 /* --------------------------------------------------------------------------------------------- */
918 edit_callback (Widget
* w
, Widget
* sender
, widget_msg_t msg
, int parm
, void *data
)
920 WEdit
*e
= (WEdit
*) w
;
925 edit_set_buttonbar (e
, find_buttonbar (DIALOG (w
->owner
)));
929 e
->force
|= REDRAW_COMPLETELY
;
930 edit_update_screen (e
);
936 cb_ret_t ret
= MSG_NOT_HANDLED
;
938 /* The user may override the access-keys for the menu bar. */
939 if (macro_index
== -1 && edit_execute_macro (e
, parm
))
941 edit_update_screen (e
);
944 else if (edit_translate_key (e
, parm
, &cmd
, &ch
))
946 edit_execute_key_command (e
, cmd
, ch
);
947 edit_update_screen (e
);
955 /* command from menubar or buttonbar */
956 edit_execute_key_command (e
, parm
, -1);
957 edit_update_screen (e
);
964 y
= (e
->fullscreen
? 0 : 1) + EDIT_TEXT_VERTICAL_OFFSET
+ e
->curs_row
;
965 x
= (e
->fullscreen
? 0 : 1) + EDIT_TEXT_HORIZONTAL_OFFSET
+ option_line_state_width
+
966 e
->curs_col
+ e
->start_col
+ e
->over_col
;
968 widget_gotoyx (w
, y
, x
);
973 edit_update_screen (e
);
981 return widget_default_callback (w
, sender
, msg
, parm
, data
);
985 /* --------------------------------------------------------------------------------------------- */
988 * Handle move/resize mouse events.
991 edit_mouse_handle_move_resize (Widget
* w
, mouse_msg_t msg
, mouse_event_t
* event
)
993 WEdit
*edit
= (WEdit
*) (w
);
994 Widget
*h
= WIDGET (w
->owner
);
995 int global_x
, global_y
;
997 if (msg
== MSG_MOUSE_UP
)
999 /* Exit move/resize mode. */
1000 edit_execute_cmd (edit
, CK_Enter
, -1);
1001 edit_update_screen (edit
); /* Paint the buttonbar over our possibly overlapping frame. */
1005 if (msg
!= MSG_MOUSE_DRAG
)
1007 * We ignore any other events. Specifically, MSG_MOUSE_DOWN.
1009 * When the move/resize is initiated by the menu, we let the user
1010 * stop it by clicking with the mouse. Which is why we don't want
1011 * a mouse down to affect the window.
1015 /* Convert point to global coordinates for easier calculations. */
1016 global_x
= event
->x
+ w
->x
;
1017 global_y
= event
->y
+ w
->y
;
1019 /* Clamp the point to the dialog's client area. */
1020 global_y
= CLAMP (global_y
, h
->y
+ 1, h
->y
+ h
->lines
- 2); /* Status line, buttonbar */
1021 global_x
= CLAMP (global_x
, h
->x
, h
->x
+ h
->cols
- 1); /* Currently a no-op, as the dialog has no left/right margins. */
1023 if (edit
->drag_state
== MCEDIT_DRAG_MOVE
)
1026 w
->x
= global_x
- edit
->drag_state_start
;
1028 else if (edit
->drag_state
== MCEDIT_DRAG_RESIZE
)
1030 w
->lines
= MAX (WINDOW_MIN_LINES
, global_y
- w
->y
+ 1);
1031 w
->cols
= MAX (WINDOW_MIN_COLS
, global_x
- w
->x
+ 1);
1034 edit
->force
|= REDRAW_COMPLETELY
; /* Not really needed as WEdit's MSG_DRAW already does this. */
1036 /* We draw the whole dialog because dragging/resizing exposes area beneath. */
1037 dlg_draw (DIALOG (w
->owner
));
1040 /* --------------------------------------------------------------------------------------------- */
1043 * Handle mouse events of editor window
1045 * @param w Widget object (the editor window)
1046 * @param msg mouse event message
1047 * @param event mouse event data
1050 edit_mouse_callback (Widget
* w
, mouse_msg_t msg
, mouse_event_t
* event
)
1052 WEdit
*edit
= (WEdit
*) w
;
1053 /* buttons' distance from right edge */
1054 int dx
= edit
->fullscreen
? 0 : 2;
1055 /* location of 'Close' and 'Toggle fullscreen' pictograms */
1056 int close_x
, toggle_fullscreen_x
;
1058 close_x
= (w
->cols
- 1) - dx
- 1;
1059 toggle_fullscreen_x
= close_x
- 3;
1061 if (edit
->drag_state
!= MCEDIT_DRAG_NONE
)
1063 /* window is being resized/moved */
1064 edit_mouse_handle_move_resize (w
, msg
, event
);
1068 /* If it's the last line on the screen, we abort the event to make the
1069 * system channel it to the overlapping buttonbar instead. We have to do
1070 * this because a WEdit has the WOP_TOP_SELECT flag, which makes it above
1071 * the buttonbar in Z-order. */
1072 if (msg
== MSG_MOUSE_DOWN
&& (event
->y
+ w
->y
== LINES
- 1))
1074 event
->result
.abort
= TRUE
;
1080 case MSG_MOUSE_DOWN
:
1082 edit_update_curs_row (edit
);
1083 edit_update_curs_col (edit
);
1085 if (!edit
->fullscreen
)
1089 if (event
->x
>= close_x
- 1 && event
->x
<= close_x
+ 1)
1090 ; /* do nothing (see MSG_MOUSE_CLICK) */
1091 else if (event
->x
>= toggle_fullscreen_x
- 1 && event
->x
<= toggle_fullscreen_x
+ 1)
1092 ; /* do nothing (see MSG_MOUSE_CLICK) */
1095 /* start window move */
1096 edit_execute_cmd (edit
, CK_WindowMove
, -1);
1097 edit_update_screen (edit
); /* Paint the buttonbar over our possibly overlapping frame. */
1098 edit
->drag_state_start
= event
->x
;
1103 if (event
->y
== w
->lines
- 1 && event
->x
== w
->cols
- 1)
1105 /* bottom-right corner -- start window resize */
1106 edit_execute_cmd (edit
, CK_WindowResize
, -1);
1111 MC_FALLTHROUGH
; /* to start/stop text selection */
1114 edit_update_cursor (edit
, event
);
1115 edit_total_update (edit
);
1118 case MSG_MOUSE_CLICK
:
1121 if (event
->x
>= close_x
- 1 && event
->x
<= close_x
+ 1)
1122 send_message (w
->owner
, NULL
, MSG_ACTION
, CK_Close
, NULL
);
1123 else if (event
->x
>= toggle_fullscreen_x
- 1 && event
->x
<= toggle_fullscreen_x
+ 1)
1124 edit_toggle_fullscreen (edit
);
1125 else if (!edit
->fullscreen
&& event
->count
== GPM_DOUBLE
)
1126 /* double click on top line (toggle fullscreen) */
1127 edit_toggle_fullscreen (edit
);
1129 else if (event
->count
== GPM_DOUBLE
)
1132 edit_mark_current_word_cmd (edit
);
1133 edit_total_update (edit
);
1135 else if (event
->count
== GPM_TRIPLE
)
1137 /* triple click: works in GPM only, not in xterm */
1138 edit_mark_current_line_cmd (edit
);
1139 edit_total_update (edit
);
1143 case MSG_MOUSE_DRAG
:
1144 edit_update_cursor (edit
, event
);
1145 edit_total_update (edit
);
1148 case MSG_MOUSE_SCROLL_UP
:
1149 edit_move_up (edit
, 2, TRUE
);
1150 edit_total_update (edit
);
1153 case MSG_MOUSE_SCROLL_DOWN
:
1154 edit_move_down (edit
, 2, TRUE
);
1155 edit_total_update (edit
);
1163 /* --------------------------------------------------------------------------------------------- */
1164 /*** public functions ****************************************************************************/
1165 /* --------------------------------------------------------------------------------------------- */
1169 * @param file_vpath file object
1170 * @param line line number
1171 * @return TRUE if no errors was occurred, FALSE otherwise
1175 edit_file (const vfs_path_t
* file_vpath
, long line
)
1177 mcedit_arg_t arg
= { (vfs_path_t
*) file_vpath
, line
};
1181 files
= g_list_prepend (NULL
, &arg
);
1182 ok
= edit_files (files
);
1183 g_list_free (files
);
1188 /* --------------------------------------------------------------------------------------------- */
1191 edit_files (const GList
* files
)
1193 static gboolean made_directory
= FALSE
;
1199 gboolean ok
= FALSE
;
1201 if (!made_directory
)
1205 dir
= mc_build_filename (mc_config_get_cache_path (), EDIT_DIR
, (char *) NULL
);
1206 made_directory
= (mkdir (dir
, 0700) != -1 || errno
== EEXIST
);
1209 dir
= mc_build_filename (mc_config_get_path (), EDIT_DIR
, (char *) NULL
);
1210 made_directory
= (mkdir (dir
, 0700) != -1 || errno
== EEXIST
);
1213 dir
= mc_build_filename (mc_config_get_data_path (), EDIT_DIR
, (char *) NULL
);
1214 made_directory
= (mkdir (dir
, 0700) != -1 || errno
== EEXIST
);
1218 /* Create a new dialog and add it widgets to it */
1220 dlg_create (FALSE
, 0, 0, 1, 1, WPOS_FULLSCREEN
, FALSE
, NULL
, edit_dialog_callback
,
1221 edit_dialog_mouse_callback
, "[Internal File Editor]", NULL
);
1222 wd
= WIDGET (edit_dlg
);
1223 widget_want_tab (wd
, TRUE
);
1225 edit_dlg
->get_shortcut
= edit_get_shortcut
;
1226 edit_dlg
->get_title
= edit_get_title
;
1228 g
= GROUP (edit_dlg
);
1230 menubar
= menubar_new (NULL
, TRUE
);
1231 w
= WIDGET (menubar
);
1232 group_add_widget_autopos (g
, w
, w
->pos_flags
, NULL
);
1233 edit_init_menu (menubar
);
1235 w
= WIDGET (buttonbar_new (TRUE
));
1236 group_add_widget_autopos (g
, w
, w
->pos_flags
, NULL
);
1238 for (file
= files
; file
!= NULL
; file
= g_list_next (file
))
1240 mcedit_arg_t
*f
= (mcedit_arg_t
*) file
->data
;
1243 f_ok
= edit_add_window (edit_dlg
, wd
->y
+ 1, wd
->x
, wd
->lines
- 2, wd
->cols
, f
->file_vpath
,
1245 /* at least one file has been opened succefully */
1252 if (!ok
|| widget_get_state (wd
, WST_CLOSED
))
1253 dlg_destroy (edit_dlg
);
1258 /* --------------------------------------------------------------------------------------------- */
1261 edit_get_file_name (const WEdit
* edit
)
1263 return vfs_path_as_str (edit
->filename_vpath
);
1266 /* --------------------------------------------------------------------------------------------- */
1269 find_editor (const WDialog
* h
)
1271 const WGroup
*g
= CONST_GROUP (h
);
1273 if (edit_widget_is_editor (CONST_WIDGET (g
->current
->data
)))
1274 return (WEdit
*) g
->current
->data
;
1275 return (WEdit
*) find_widget_type (h
, edit_callback
);
1278 /* --------------------------------------------------------------------------------------------- */
1280 * Check if widget is an WEdit class.
1282 * @param w probably editor object
1283 * @return TRUE if widget is an WEdit class, FALSE otherwise
1287 edit_widget_is_editor (const Widget
* w
)
1289 return (w
!= NULL
&& w
->callback
== edit_callback
);
1292 /* --------------------------------------------------------------------------------------------- */
1295 edit_update_screen (WEdit
* e
)
1297 edit_scroll_screen_over_cursor (e
);
1298 edit_update_curs_col (e
);
1299 edit_status (e
, widget_get_state (WIDGET (e
), WST_FOCUSED
));
1301 /* pop all events for this window for internal handling */
1303 e
->force
|= REDRAW_PAGE
;
1306 if ((e
->force
& REDRAW_COMPLETELY
) != 0)
1307 e
->force
|= REDRAW_PAGE
;
1308 edit_render_keypress (e
);
1311 widget_draw (WIDGET (find_buttonbar (DIALOG (WIDGET (e
)->owner
))));
1314 /* --------------------------------------------------------------------------------------------- */
1316 * Save current window size.
1318 * @param edit editor object
1322 edit_save_size (WEdit
* edit
)
1324 Widget
*w
= WIDGET (edit
);
1326 edit
->y_prev
= w
->y
;
1327 edit
->x_prev
= w
->x
;
1328 edit
->lines_prev
= w
->lines
;
1329 edit
->cols_prev
= w
->cols
;
1332 /* --------------------------------------------------------------------------------------------- */
1334 * Create new editor window and insert it into editor screen.
1336 * @param h editor dialog (screen)
1337 * @param y y coordinate
1338 * @param x x coordinate
1339 * @param lines window height
1340 * @param cols window width
1341 * @param f file object
1342 * @param fline line number in file
1343 * @return TRUE if new window was successfully created and inserted into editor screen,
1348 edit_add_window (WDialog
* h
, int y
, int x
, int lines
, int cols
, const vfs_path_t
* f
, long fline
)
1353 edit
= edit_init (NULL
, y
, x
, lines
, cols
, f
, fline
);
1358 w
->callback
= edit_callback
;
1359 w
->mouse_callback
= edit_mouse_callback
;
1361 group_add_widget_autopos (GROUP (h
), w
, WPOS_KEEP_ALL
, NULL
);
1362 edit_set_buttonbar (edit
, find_buttonbar (h
));
1368 /* --------------------------------------------------------------------------------------------- */
1370 * Handle move/resize events.
1372 * @param edit editor object
1373 * @param command action id
1374 * @return TRUE if the action was handled, FALSE otherwise
1378 edit_handle_move_resize (WEdit
* edit
, long command
)
1380 Widget
*w
= WIDGET (edit
);
1381 gboolean ret
= FALSE
;
1383 if (edit
->fullscreen
)
1385 edit
->drag_state
= MCEDIT_DRAG_NONE
;
1386 w
->mouse
.forced_capture
= FALSE
;
1390 switch (edit
->drag_state
)
1392 case MCEDIT_DRAG_NONE
:
1393 /* possible start move/resize */
1397 edit
->drag_state
= MCEDIT_DRAG_MOVE
;
1398 edit_save_size (edit
);
1399 edit_status (edit
, TRUE
); /* redraw frame and status */
1401 * If a user initiates a move by the menu, not by the mouse, we
1402 * make a subsequent mouse drag pull the frame from its middle.
1403 * (We can instead choose '0' to pull it from the corner.)
1405 edit
->drag_state_start
= w
->cols
/ 2;
1408 case CK_WindowResize
:
1409 edit
->drag_state
= MCEDIT_DRAG_RESIZE
;
1410 edit_save_size (edit
);
1411 edit_status (edit
, TRUE
); /* redraw frame and status */
1419 case MCEDIT_DRAG_MOVE
:
1422 case CK_WindowResize
:
1423 edit
->drag_state
= MCEDIT_DRAG_RESIZE
;
1430 edit_window_move (edit
, command
);
1435 edit
->drag_state
= MCEDIT_DRAG_NONE
;
1436 edit_status (edit
, TRUE
); /* redraw frame and status */
1444 case MCEDIT_DRAG_RESIZE
:
1448 edit
->drag_state
= MCEDIT_DRAG_MOVE
;
1455 edit_window_resize (edit
, command
);
1459 case CK_WindowResize
:
1460 edit
->drag_state
= MCEDIT_DRAG_NONE
;
1461 edit_status (edit
, TRUE
); /* redraw frame and status */
1474 * - We let the user stop a resize/move operation by clicking with the
1475 * mouse anywhere. ("clicking" = pressing and releasing a button.)
1476 * - We let the user perform a resize/move operation by a mouse drag
1477 * initiated anywhere.
1479 * "Anywhere" means: inside or outside the window. We make this happen
1480 * with the 'forced_capture' flag.
1482 w
->mouse
.forced_capture
= (edit
->drag_state
!= MCEDIT_DRAG_NONE
);
1487 /* --------------------------------------------------------------------------------------------- */
1489 * Toggle window fuulscreen mode.
1491 * @param edit editor object
1495 edit_toggle_fullscreen (WEdit
* edit
)
1497 Widget
*w
= WIDGET (edit
);
1499 edit
->fullscreen
= !edit
->fullscreen
;
1500 edit
->force
= REDRAW_COMPLETELY
;
1502 if (!edit
->fullscreen
)
1504 edit_restore_size (edit
);
1505 /* do not follow screen size on resize */
1506 w
->pos_flags
= WPOS_KEEP_DEFAULT
;
1510 Widget
*h
= WIDGET (w
->owner
);
1512 edit_save_size (edit
);
1513 widget_set_size (w
, h
->y
+ 1, h
->x
, h
->lines
- 2, h
->cols
);
1514 /* follow screen size on resize */
1515 w
->pos_flags
= WPOS_KEEP_ALL
;
1516 edit
->force
|= REDRAW_PAGE
;
1517 edit_update_screen (edit
);
1521 /* --------------------------------------------------------------------------------------------- */