2 Editor initialisation and callback handler.
4 Copyright (C) 1996-2024
5 Free Software Foundation, Inc.
9 Andrew Borodin <aborodin@vmail.ru> 2012-2024
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(), bracketed_pasting_in_progress */
46 #include "lib/tty/color.h" /* tty_setcolor() */
48 #include "lib/fileloc.h" /* EDIT_HOME_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/keymap.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() */
64 #include "edit-impl.h"
65 #include "editwidget.h"
66 #include "editmacros.h" /* edit_execute_macro() */
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 /*** forward declarations (file scope functions) *************************************************/
85 /*** file scope variables ************************************************************************/
87 static unsigned int edit_dlg_init_refcounter
= 0;
89 /* --------------------------------------------------------------------------------------------- */
90 /*** file scope functions ************************************************************************/
91 /* --------------------------------------------------------------------------------------------- */
93 * Init the 'edit' subsystem
99 edit_dlg_init_refcounter
++;
101 if (edit_dlg_init_refcounter
== 1)
103 edit_window_state_char
= mc_skin_get ("widget-editor", "window-state-char", "*");
104 edit_window_close_char
= mc_skin_get ("widget-editor", "window-close-char", "X");
112 /* --------------------------------------------------------------------------------------------- */
114 * Deinit the 'edit' subsystem
118 edit_dlg_deinit (void)
120 if (edit_dlg_init_refcounter
== 1)
122 g_free (edit_window_state_char
);
123 g_free (edit_window_close_char
);
130 if (edit_dlg_init_refcounter
!= 0)
131 edit_dlg_init_refcounter
--;
134 /* --------------------------------------------------------------------------------------------- */
136 * Show info about editor
144 ver
= g_strdup_printf ("MCEdit %s", mc_global
.mc_version
);
147 quick_widget_t quick_widgets
[] = {
149 QUICK_LABEL (ver
, NULL
),
150 QUICK_SEPARATOR (TRUE
),
151 QUICK_LABEL (N_("A user friendly text editor\n"
152 "written for the Midnight Commander."), NULL
),
153 QUICK_SEPARATOR (FALSE
),
154 QUICK_LABEL (N_("Copyright (C) 1996-2024 the Free Software Foundation"), NULL
),
155 QUICK_START_BUTTONS (TRUE
, TRUE
),
156 QUICK_BUTTON (N_("&OK"), B_ENTER
, NULL
, NULL
),
161 WRect r
= { -1, -1, 0, 40 };
163 quick_dialog_t qdlg
= {
164 r
, N_("About"), "[Internal File Editor]",
165 quick_widgets
, NULL
, NULL
168 quick_widgets
[0].pos_flags
= WPOS_KEEP_TOP
| WPOS_CENTER_HORZ
;
169 quick_widgets
[2].pos_flags
= WPOS_KEEP_TOP
| WPOS_CENTER_HORZ
;
170 quick_widgets
[4].pos_flags
= WPOS_KEEP_TOP
| WPOS_CENTER_HORZ
;
172 (void) quick_dialog (&qdlg
);
178 /* --------------------------------------------------------------------------------------------- */
184 edit_help (const WDialog
*h
)
186 ev_help_t event_data
= { NULL
, h
->help_ctx
};
188 mc_event_raise (MCEVENT_GROUP_CORE
, "help", &event_data
);
191 /* --------------------------------------------------------------------------------------------- */
193 * Restore saved window size.
195 * @param edit editor object
199 edit_restore_size (WEdit
*edit
)
201 Widget
*w
= WIDGET (edit
);
203 edit
->drag_state
= MCEDIT_DRAG_NONE
;
204 w
->mouse
.forced_capture
= FALSE
;
205 widget_set_size_rect (w
, &edit
->loc_prev
);
206 widget_draw (WIDGET (w
->owner
));
209 /* --------------------------------------------------------------------------------------------- */
211 * Move window by one row or column in any direction.
213 * @param edit editor object
214 * @param command direction (CK_Up, CK_Down, CK_Left, CK_Right)
218 edit_window_move (WEdit
*edit
, long command
)
220 Widget
*we
= WIDGET (edit
);
221 Widget
*wo
= WIDGET (we
->owner
);
222 WRect
*w
= &we
->rect
;
223 const WRect
*wh
= &wo
->rect
;
228 if (w
->y
> wh
->y
+ 1) /* menubar */
232 if (w
->y
< wh
->y
+ wh
->lines
- 2) /* buttonbar */
236 if (w
->x
+ wh
->cols
> wh
->x
)
240 if (w
->x
< wh
->x
+ wh
->cols
)
247 edit
->force
|= REDRAW_PAGE
;
251 /* --------------------------------------------------------------------------------------------- */
253 * Resize window by one row or column in any direction.
255 * @param edit editor object
256 * @param command direction (CK_Up, CK_Down, CK_Left, CK_Right)
260 edit_window_resize (WEdit
*edit
, long command
)
262 Widget
*we
= WIDGET (edit
);
263 Widget
*wo
= WIDGET (we
->owner
);
264 WRect
*w
= &we
->rect
;
265 const WRect
*wh
= &wo
->rect
;
270 if (w
->lines
> WINDOW_MIN_LINES
)
274 if (w
->y
+ w
->lines
< wh
->y
+ wh
->lines
- 1) /* buttonbar */
278 if (w
->cols
> WINDOW_MIN_COLS
)
282 if (w
->x
+ w
->cols
< wh
->x
+ wh
->cols
)
289 edit
->force
|= REDRAW_COMPLETELY
;
293 /* --------------------------------------------------------------------------------------------- */
295 * Get hotkey by number.
304 return (n
<= 9) ? '0' + n
: 'a' + n
- 10;
307 /* --------------------------------------------------------------------------------------------- */
310 edit_window_list (const WDialog
*h
)
312 const WGroup
*g
= CONST_GROUP (h
);
313 const size_t offset
= 2; /* skip menu and buttonbar */
314 const size_t dlg_num
= g_list_length (g
->widgets
) - offset
;
321 lines
= MIN ((size_t) (LINES
* 2 / 3), dlg_num
);
324 listbox
= listbox_window_new (lines
, cols
, _("Open files"), "[Open files]");
326 for (w
= g
->widgets
; w
!= NULL
; w
= g_list_next (w
))
327 if (edit_widget_is_editor (CONST_WIDGET (w
->data
)))
329 WEdit
*e
= EDIT (w
->data
);
332 if (e
->filename_vpath
== NULL
)
333 fname
= g_strdup_printf ("%c [%s]", e
->modified
? '*' : ' ', _("NoName"));
336 g_strdup_printf ("%c%s", e
->modified
? '*' : ' ',
337 vfs_path_as_str (e
->filename_vpath
));
339 listbox_add_item (listbox
->list
, LISTBOX_APPEND_AT_END
, get_hotkey (i
++),
340 str_term_trim (fname
, WIDGET (listbox
->list
)->rect
.cols
- 2), e
,
345 selected
= listbox_run_with_data (listbox
, g
->current
->data
);
346 if (selected
!= NULL
)
347 widget_select (WIDGET (selected
));
350 /* --------------------------------------------------------------------------------------------- */
353 edit_get_shortcut (long command
)
356 const char *shortcut
= NULL
;
358 shortcut
= keybind_lookup_keymap_shortcut (editor_map
, command
);
359 if (shortcut
!= NULL
)
360 return g_strdup (shortcut
);
362 ext_map
= keybind_lookup_keymap_shortcut (editor_map
, CK_ExtendedKeyMap
);
364 shortcut
= keybind_lookup_keymap_shortcut (editor_x_map
, command
);
365 if (shortcut
!= NULL
)
366 return g_strdup_printf ("%s %s", ext_map
, shortcut
);
371 /* --------------------------------------------------------------------------------------------- */
374 edit_get_title (const WDialog
*h
, size_t len
)
377 const char *modified
;
378 const char *file_label
;
381 edit
= edit_find_editor (h
);
382 modified
= edit
->modified
? "(*) " : " ";
386 if (edit
->filename_vpath
== NULL
)
387 filename
= g_strdup (_("[NoName]"));
389 filename
= g_strdup (vfs_path_as_str (edit
->filename_vpath
));
391 file_label
= str_term_trim (filename
, len
- str_term_width1 (_("Edit: ")));
394 return g_strconcat (_("Edit: "), modified
, file_label
, (char *) NULL
);
397 /* --------------------------------------------------------------------------------------------- */
400 edit_dialog_command_execute (WDialog
*h
, long command
)
402 WGroup
*g
= GROUP (h
);
403 cb_ret_t ret
= MSG_HANDLED
;
408 edit_load_file_from_filename (h
, NULL
);
414 edit_load_file_from_history (h
);
416 case CK_EditSyntaxFile
:
417 edit_load_syntax_file (h
);
419 case CK_EditUserMenu
:
420 edit_load_menu_file (h
);
423 /* if there are no opened files anymore, close MC editor */
424 if (edit_widget_is_editor (CONST_WIDGET (g
->current
->data
)) &&
425 edit_close_cmd (EDIT (g
->current
->data
)) && edit_find_editor (h
) == NULL
)
436 /* don't close editor due to SIGINT, but stop move/resize window */
438 Widget
*w
= WIDGET (g
->current
->data
);
440 if (edit_widget_is_editor (w
) && EDIT (w
)->drag_state
!= MCEDIT_DRAG_NONE
)
441 edit_restore_size (EDIT (w
));
442 else if (command
== CK_Quit
)
450 edit_syntax_onoff_cmd (h
);
453 edit_show_tabs_tws_cmd (h
);
456 edit_show_margin_cmd (h
);
459 edit_show_numbers_cmd (h
);
471 case CK_WindowResize
:
472 if (edit_widget_is_editor (CONST_WIDGET (g
->current
->data
)))
473 edit_handle_move_resize (EDIT (g
->current
->data
), command
);
476 edit_window_list (h
);
479 group_select_next_widget (g
);
482 group_select_prev_widget (g
);
485 edit_options_dialog (h
);
487 case CK_OptionsSaveMode
:
488 edit_save_mode_cmd ();
494 ret
= MSG_NOT_HANDLED
;
501 /* --------------------------------------------------------------------------------------------- */
503 * Translate the keycode into either 'command' or 'char_for_insertion'.
504 * 'command' is one of the editor commands from lib/keybind.h.
508 edit_translate_key (WEdit
*edit
, long x_key
, int *cmd
, int *ch
)
510 Widget
*w
= WIDGET (edit
);
511 long command
= CK_InsertChar
;
512 int char_for_insertion
= -1;
514 /* an ordinary insertable character */
515 if (!w
->ext_mode
&& x_key
< 256)
518 if (is_printable (x_key
))
520 char_for_insertion
= x_key
;
526 if (edit
->charpoint
>= MB_LEN_MAX
)
529 edit
->charbuf
[edit
->charpoint
] = '\0';
531 if (edit
->charpoint
< MB_LEN_MAX
)
533 edit
->charbuf
[edit
->charpoint
++] = x_key
;
534 edit
->charbuf
[edit
->charpoint
] = '\0';
537 /* input from 8-bit locale */
538 if (!mc_global
.utf8_display
)
540 /* source is in 8-bit codeset */
541 c
= convert_from_input_c (x_key
);
543 if (is_printable (c
))
546 char_for_insertion
= c
;
548 char_for_insertion
= convert_from_8bit_to_utf_c2 ((char) x_key
);
557 res
= str_is_valid_char (edit
->charbuf
, edit
->charpoint
);
558 if (res
< 0 && res
!= -2)
560 edit
->charpoint
= 0; /* broken multibyte char, skip */
566 /* source is in UTF-8 codeset */
569 char_for_insertion
= x_key
;
573 edit
->charbuf
[edit
->charpoint
] = '\0';
575 if (g_unichar_isprint (g_utf8_get_char (edit
->charbuf
)))
577 char_for_insertion
= x_key
;
586 /* not finished multibyte input (we're in the middle of multibyte utf-8 char) */
590 if (g_unichar_isprint (g_utf8_get_char (edit
->charbuf
)))
592 c
= convert_from_utf_to_current (edit
->charbuf
);
593 edit
->charbuf
[0] = '\0';
595 char_for_insertion
= c
;
599 /* non-printable utf-8 input, skip it */
600 edit
->charbuf
[0] = '\0';
604 #endif /* HAVE_CHARSET */
607 /* Commands specific to the key emulation */
608 command
= widget_lookup_key (w
, x_key
);
609 if (command
== CK_IgnoreKey
)
610 command
= CK_InsertChar
;
613 *cmd
= (int) command
; /* FIXME */
614 *ch
= char_for_insertion
;
616 return !(command
== CK_InsertChar
&& char_for_insertion
== -1);
620 /* --------------------------------------------------------------------------------------------- */
623 edit_quit (WDialog
*h
)
630 /* don't stop the dialog before final decision */
631 widget_set_state (WIDGET (h
), WST_ACTIVE
, TRUE
);
633 /* check window state and get modified files */
634 for (l
= GROUP (h
)->widgets
; l
!= NULL
; l
= g_list_next (l
))
635 if (edit_widget_is_editor (CONST_WIDGET (l
->data
)))
639 if (e
->drag_state
!= MCEDIT_DRAG_NONE
)
641 edit_restore_size (e
);
646 /* create separate list because widget_select()
647 changes the window position in Z order */
649 m
= g_slist_prepend (m
, l
->data
);
652 for (me
= m
; me
!= NULL
; me
= g_slist_next (me
))
656 widget_select (WIDGET (e
));
658 if (!edit_ok_to_exit (e
))
662 /* if all files were checked, quit editor */
669 /* --------------------------------------------------------------------------------------------- */
672 edit_set_buttonbar (WEdit
*edit
, WButtonBar
*bb
)
674 Widget
*w
= WIDGET (edit
);
676 buttonbar_set_label (bb
, 1, Q_ ("ButtonBar|Help"), w
->keymap
, NULL
);
677 buttonbar_set_label (bb
, 2, Q_ ("ButtonBar|Save"), w
->keymap
, w
);
678 buttonbar_set_label (bb
, 3, Q_ ("ButtonBar|Mark"), w
->keymap
, w
);
679 buttonbar_set_label (bb
, 4, Q_ ("ButtonBar|Replac"), w
->keymap
, w
);
680 buttonbar_set_label (bb
, 5, Q_ ("ButtonBar|Copy"), w
->keymap
, w
);
681 buttonbar_set_label (bb
, 6, Q_ ("ButtonBar|Move"), w
->keymap
, w
);
682 buttonbar_set_label (bb
, 7, Q_ ("ButtonBar|Search"), w
->keymap
, w
);
683 buttonbar_set_label (bb
, 8, Q_ ("ButtonBar|Delete"), w
->keymap
, w
);
684 buttonbar_set_label (bb
, 9, Q_ ("ButtonBar|PullDn"), w
->keymap
, NULL
);
685 buttonbar_set_label (bb
, 10, Q_ ("ButtonBar|Quit"), w
->keymap
, NULL
);
688 /* --------------------------------------------------------------------------------------------- */
691 edit_total_update (WEdit
*edit
)
693 edit_find_bracket (edit
);
694 edit
->force
|= REDRAW_COMPLETELY
;
695 edit_update_curs_row (edit
);
696 edit_update_screen (edit
);
699 /* --------------------------------------------------------------------------------------------- */
702 edit_update_cursor (WEdit
*edit
, const mouse_event_t
*event
)
707 x
= event
->x
- (edit
->fullscreen
? 0 : 1);
708 y
= event
->y
- (edit
->fullscreen
? 0 : 1);
710 if (edit
->mark2
!= -1 && event
->msg
== MSG_MOUSE_UP
)
711 return TRUE
; /* don't do anything */
713 if (event
->msg
== MSG_MOUSE_DOWN
|| event
->msg
== MSG_MOUSE_UP
)
714 edit_push_key_press (edit
);
716 if (!edit_options
.cursor_beyond_eol
)
717 edit
->prev_col
= x
- edit
->start_col
- edit_options
.line_state_width
;
723 edit_move_forward3 (edit
, edit_buffer_get_current_bol (&edit
->buffer
), 0,
724 edit_buffer_get_current_eol (&edit
->buffer
));
726 if (x
> line_len
- 1)
728 edit
->over_col
= x
- line_len
- edit
->start_col
- edit_options
.line_state_width
;
729 edit
->prev_col
= line_len
;
734 edit
->prev_col
= x
- edit_options
.line_state_width
- edit
->start_col
;
738 if (y
> edit
->curs_row
)
739 edit_move_down (edit
, y
- edit
->curs_row
, FALSE
);
740 else if (y
< edit
->curs_row
)
741 edit_move_up (edit
, edit
->curs_row
- y
, FALSE
);
743 edit_move_to_prev_col (edit
, edit_buffer_get_current_bol (&edit
->buffer
));
745 if (event
->msg
== MSG_MOUSE_CLICK
)
747 edit_mark_cmd (edit
, TRUE
); /* reset */
751 done
= (event
->msg
!= MSG_MOUSE_DRAG
);
753 edit_mark_cmd (edit
, FALSE
);
758 /* --------------------------------------------------------------------------------------------- */
759 /** Callback for the edit dialog */
762 edit_dialog_callback (Widget
*w
, Widget
*sender
, widget_msg_t msg
, int parm
, void *data
)
764 WGroup
*g
= GROUP (w
);
765 WDialog
*h
= DIALOG (w
);
774 dlg_default_callback (w
, NULL
, MSG_RESIZE
, 0, NULL
);
775 menubar_arrange (menubar_find (h
));
780 /* Handle shortcuts, menu, and buttonbar. */
784 result
= edit_dialog_command_execute (h
, parm
);
786 /* We forward any commands coming from the menu, and which haven't been
787 handled by the dialog, to the focused WEdit window. */
788 if (result
== MSG_NOT_HANDLED
&& sender
== WIDGET (menubar_find (h
)))
789 result
= send_message (g
->current
->data
, NULL
, MSG_ACTION
, parm
, NULL
);
796 Widget
*we
= WIDGET (g
->current
->data
);
797 cb_ret_t ret
= MSG_NOT_HANDLED
;
799 if (edit_widget_is_editor (we
))
804 /* keep and then extmod flag */
805 ext_mode
= we
->ext_mode
;
806 command
= widget_lookup_key (we
, parm
);
807 we
->ext_mode
= ext_mode
;
809 if (command
== CK_IgnoreKey
)
810 we
->ext_mode
= FALSE
;
813 ret
= edit_dialog_command_execute (h
, command
);
814 /* if command was not handled, keep the extended mode
815 for the further key processing */
816 if (ret
== MSG_HANDLED
)
817 we
->ext_mode
= FALSE
;
822 * Due to the "end of bracket" escape the editor sees input with is_idle() == false
823 * (expects more characters) and hence doesn't yet refresh the screen, but then
824 * no further characters arrive (there's only an "end of bracket" which is swallowed
825 * by tty_get_event()), so you end up with a screen that's not refreshed after pasting.
826 * So let's trigger an IDLE signal.
829 widget_idle (w
, TRUE
);
833 /* hardcoded menu hotkeys (see edit_drop_hotkey_menu) */
834 case MSG_UNHANDLED_KEY
:
835 return edit_drop_hotkey_menu (h
, parm
) ? MSG_HANDLED
: MSG_NOT_HANDLED
;
846 widget_idle (w
, FALSE
);
847 return send_message (g
->current
->data
, NULL
, MSG_IDLE
, 0, NULL
);
850 return dlg_default_callback (w
, sender
, msg
, parm
, data
);
854 /* --------------------------------------------------------------------------------------------- */
857 * Handle mouse events of editor screen.
859 * @param w Widget object (the editor)
860 * @param msg mouse event message
861 * @param event mouse event data
864 edit_dialog_mouse_callback (Widget
*w
, mouse_msg_t msg
, mouse_event_t
*event
)
866 gboolean unhandled
= TRUE
;
868 if (msg
== MSG_MOUSE_DOWN
&& event
->y
== 0)
870 WGroup
*g
= GROUP (w
);
871 WDialog
*h
= DIALOG (w
);
874 b
= menubar_find (h
);
876 if (!widget_get_state (WIDGET (b
), WST_FOCUSED
))
884 /* Try find top fullscreen window */
885 for (l
= g
->widgets
; l
!= NULL
; l
= g_list_next (l
))
886 if (edit_widget_is_editor (CONST_WIDGET (l
->data
)) && EDIT (l
->data
)->fullscreen
)
889 /* Handle fullscreen/close buttons in the top line */
890 x
= w
->rect
.cols
- 6;
892 if (top
!= NULL
&& event
->x
>= x
)
894 WEdit
*e
= EDIT (top
->data
);
896 if (top
!= g
->current
)
898 /* Window is not active. Activate it */
899 widget_select (WIDGET (e
));
903 if (event
->x
- x
<= 2)
904 edit_toggle_fullscreen (e
);
906 send_message (h
, NULL
, MSG_ACTION
, CK_Close
, NULL
);
912 menubar_activate (b
, drop_menus
, -1);
916 /* Continue handling of unhandled event in window or menu */
917 event
->result
.abort
= unhandled
;
920 /* --------------------------------------------------------------------------------------------- */
923 edit_dialog_bg_callback (Widget
*w
, Widget
*sender
, widget_msg_t msg
, int parm
, void *data
)
928 w
->rect
= WIDGET (w
->owner
)->rect
;
929 rect_grow (&w
->rect
, -1, 0);
930 w
->pos_flags
|= WPOS_KEEP_ALL
;
934 return background_callback (w
, sender
, msg
, parm
, data
);
938 /* --------------------------------------------------------------------------------------------- */
941 edit_callback (Widget
*w
, Widget
*sender
, widget_msg_t msg
, int parm
, void *data
)
948 edit_set_buttonbar (e
, buttonbar_find (DIALOG (w
->owner
)));
952 e
->force
|= REDRAW_COMPLETELY
;
953 edit_update_screen (e
);
959 cb_ret_t ret
= MSG_NOT_HANDLED
;
961 /* The user may override the access-keys for the menu bar. */
962 if (macro_index
== -1 && !bracketed_pasting_in_progress
&& edit_execute_macro (e
, parm
))
964 edit_update_screen (e
);
967 else if (edit_translate_key (e
, parm
, &cmd
, &ch
))
969 edit_execute_key_command (e
, cmd
, ch
);
970 edit_update_screen (e
);
978 /* command from menubar or buttonbar */
979 edit_execute_key_command (e
, parm
, -1);
980 edit_update_screen (e
);
987 y
= (e
->fullscreen
? 0 : 1) + EDIT_TEXT_VERTICAL_OFFSET
+ e
->curs_row
;
988 x
= (e
->fullscreen
? 0 : 1) + EDIT_TEXT_HORIZONTAL_OFFSET
+
989 edit_options
.line_state_width
+ e
->curs_col
+ e
->start_col
+ e
->over_col
;
991 widget_gotoyx (w
, y
, x
);
996 edit_update_screen (e
);
1004 return widget_default_callback (w
, sender
, msg
, parm
, data
);
1008 /* --------------------------------------------------------------------------------------------- */
1011 * Handle move/resize mouse events.
1014 edit_mouse_handle_move_resize (Widget
*w
, mouse_msg_t msg
, mouse_event_t
*event
)
1016 WEdit
*edit
= EDIT (w
);
1017 WRect
*r
= &w
->rect
;
1018 const WRect
*h
= &CONST_WIDGET (w
->owner
)->rect
;
1019 int global_x
, global_y
;
1021 if (msg
== MSG_MOUSE_UP
)
1023 /* Exit move/resize mode. */
1024 edit_execute_cmd (edit
, CK_Enter
, -1);
1025 edit_update_screen (edit
); /* Paint the buttonbar over our possibly overlapping frame. */
1029 if (msg
!= MSG_MOUSE_DRAG
)
1031 * We ignore any other events. Specifically, MSG_MOUSE_DOWN.
1033 * When the move/resize is initiated by the menu, we let the user
1034 * stop it by clicking with the mouse. Which is why we don't want
1035 * a mouse down to affect the window.
1039 /* Convert point to global coordinates for easier calculations. */
1040 global_x
= event
->x
+ r
->x
;
1041 global_y
= event
->y
+ r
->y
;
1043 /* Clamp the point to the dialog's client area. */
1044 global_y
= CLAMP (global_y
, h
->y
+ 1, h
->y
+ h
->lines
- 2); /* Status line, buttonbar */
1045 global_x
= CLAMP (global_x
, h
->x
, h
->x
+ h
->cols
- 1); /* Currently a no-op, as the dialog has no left/right margins. */
1047 if (edit
->drag_state
== MCEDIT_DRAG_MOVE
)
1050 r
->x
= global_x
- edit
->drag_state_start
;
1052 else if (edit
->drag_state
== MCEDIT_DRAG_RESIZE
)
1054 r
->lines
= MAX (WINDOW_MIN_LINES
, global_y
- r
->y
+ 1);
1055 r
->cols
= MAX (WINDOW_MIN_COLS
, global_x
- r
->x
+ 1);
1058 edit
->force
|= REDRAW_COMPLETELY
; /* Not really needed as WEdit's MSG_DRAW already does this. */
1060 /* We draw the whole dialog because dragging/resizing exposes area beneath. */
1061 widget_draw (WIDGET (w
->owner
));
1064 /* --------------------------------------------------------------------------------------------- */
1067 * Handle mouse events of editor window
1069 * @param w Widget object (the editor window)
1070 * @param msg mouse event message
1071 * @param event mouse event data
1074 edit_mouse_callback (Widget
*w
, mouse_msg_t msg
, mouse_event_t
*event
)
1076 WEdit
*edit
= EDIT (w
);
1077 /* buttons' distance from right edge */
1078 int dx
= edit
->fullscreen
? 0 : 2;
1079 /* location of 'Close' and 'Toggle fullscreen' pictograms */
1080 int close_x
, toggle_fullscreen_x
;
1082 close_x
= (w
->rect
.cols
- 1) - dx
- 1;
1083 toggle_fullscreen_x
= close_x
- 3;
1085 if (edit
->drag_state
!= MCEDIT_DRAG_NONE
)
1087 /* window is being resized/moved */
1088 edit_mouse_handle_move_resize (w
, msg
, event
);
1092 /* If it's the last line on the screen, we abort the event to make the
1093 * system channel it to the overlapping buttonbar instead. We have to do
1094 * this because a WEdit has the WOP_TOP_SELECT flag, which makes it above
1095 * the buttonbar in Z-order. */
1096 if (msg
== MSG_MOUSE_DOWN
&& (event
->y
+ w
->rect
.y
== LINES
- 1))
1098 event
->result
.abort
= TRUE
;
1104 case MSG_MOUSE_DOWN
:
1106 edit_update_curs_row (edit
);
1107 edit_update_curs_col (edit
);
1109 if (!edit
->fullscreen
)
1113 if (event
->x
>= close_x
- 1 && event
->x
<= close_x
+ 1)
1114 ; /* do nothing (see MSG_MOUSE_CLICK) */
1115 else if (event
->x
>= toggle_fullscreen_x
- 1 && event
->x
<= toggle_fullscreen_x
+ 1)
1116 ; /* do nothing (see MSG_MOUSE_CLICK) */
1119 /* start window move */
1120 edit_execute_cmd (edit
, CK_WindowMove
, -1);
1121 edit_update_screen (edit
); /* Paint the buttonbar over our possibly overlapping frame. */
1122 edit
->drag_state_start
= event
->x
;
1127 if (event
->y
== w
->rect
.lines
- 1 && event
->x
== w
->rect
.cols
- 1)
1129 /* bottom-right corner -- start window resize */
1130 edit_execute_cmd (edit
, CK_WindowResize
, -1);
1135 MC_FALLTHROUGH
; /* to start/stop text selection */
1138 edit_update_cursor (edit
, event
);
1139 edit_total_update (edit
);
1142 case MSG_MOUSE_CLICK
:
1145 if (event
->x
>= close_x
- 1 && event
->x
<= close_x
+ 1)
1146 send_message (w
->owner
, NULL
, MSG_ACTION
, CK_Close
, NULL
);
1147 else if (event
->x
>= toggle_fullscreen_x
- 1 && event
->x
<= toggle_fullscreen_x
+ 1)
1148 edit_toggle_fullscreen (edit
);
1149 else if (!edit
->fullscreen
&& event
->count
== GPM_DOUBLE
)
1150 /* double click on top line (toggle fullscreen) */
1151 edit_toggle_fullscreen (edit
);
1153 else if (event
->count
== GPM_DOUBLE
)
1156 edit_mark_current_word_cmd (edit
);
1157 edit_total_update (edit
);
1159 else if (event
->count
== GPM_TRIPLE
)
1161 /* triple click: works in GPM only, not in xterm */
1162 edit_mark_current_line_cmd (edit
);
1163 edit_total_update (edit
);
1167 case MSG_MOUSE_DRAG
:
1168 edit_update_cursor (edit
, event
);
1169 edit_total_update (edit
);
1172 case MSG_MOUSE_SCROLL_UP
:
1173 edit_move_up (edit
, 2, TRUE
);
1174 edit_total_update (edit
);
1177 case MSG_MOUSE_SCROLL_DOWN
:
1178 edit_move_down (edit
, 2, TRUE
);
1179 edit_total_update (edit
);
1187 /* --------------------------------------------------------------------------------------------- */
1188 /*** public functions ****************************************************************************/
1189 /* --------------------------------------------------------------------------------------------- */
1193 * @param file_vpath file object
1194 * @param line line number
1195 * @return TRUE if no errors was occurred, FALSE otherwise
1199 edit_file (const edit_arg_t
*arg
)
1204 files
= g_list_prepend (NULL
, (edit_arg_t
*) arg
);
1205 ok
= edit_files (files
);
1206 g_list_free (files
);
1211 /* --------------------------------------------------------------------------------------------- */
1214 edit_files (const GList
*files
)
1216 static gboolean made_directory
= FALSE
;
1222 gboolean ok
= FALSE
;
1224 if (!made_directory
)
1228 dir
= mc_build_filename (mc_config_get_cache_path (), EDIT_HOME_DIR
, (char *) NULL
);
1229 made_directory
= (mkdir (dir
, 0700) != -1 || errno
== EEXIST
);
1232 dir
= mc_build_filename (mc_config_get_path (), EDIT_HOME_DIR
, (char *) NULL
);
1233 made_directory
= (mkdir (dir
, 0700) != -1 || errno
== EEXIST
);
1236 dir
= mc_build_filename (mc_config_get_data_path (), EDIT_HOME_DIR
, (char *) NULL
);
1237 made_directory
= (mkdir (dir
, 0700) != -1 || errno
== EEXIST
);
1241 /* Create a new dialog and add it widgets to it */
1243 dlg_create (FALSE
, 0, 0, 1, 1, WPOS_FULLSCREEN
, FALSE
, NULL
, edit_dialog_callback
,
1244 edit_dialog_mouse_callback
, "[Internal File Editor]", NULL
);
1245 wd
= WIDGET (edit_dlg
);
1246 widget_want_tab (wd
, TRUE
);
1247 wd
->keymap
= editor_map
;
1248 wd
->ext_keymap
= editor_x_map
;
1250 edit_dlg
->get_shortcut
= edit_get_shortcut
;
1251 edit_dlg
->get_title
= edit_get_title
;
1253 g
= GROUP (edit_dlg
);
1256 WIDGET (background_new
1257 (1, 0, wd
->rect
.lines
- 2, wd
->rect
.cols
, EDITOR_BACKGROUND
, ' ',
1258 edit_dialog_bg_callback
));
1259 group_add_widget (g
, edit_dlg
->bg
);
1261 menubar
= menubar_new (NULL
);
1262 w
= WIDGET (menubar
);
1263 group_add_widget_autopos (g
, w
, w
->pos_flags
, NULL
);
1264 edit_init_menu (menubar
);
1266 w
= WIDGET (buttonbar_new ());
1267 group_add_widget_autopos (g
, w
, w
->pos_flags
, NULL
);
1269 for (file
= files
; file
!= NULL
; file
= g_list_next (file
))
1273 f_ok
= edit_load_file_from_filename (edit_dlg
, (const edit_arg_t
*) file
->data
);
1274 /* at least one file has been opened succefully */
1281 if (!ok
|| widget_get_state (wd
, WST_CLOSED
))
1282 widget_destroy (wd
);
1287 /* --------------------------------------------------------------------------------------------- */
1290 edit_find_editor (const WDialog
*h
)
1292 const WGroup
*g
= CONST_GROUP (h
);
1294 if (edit_widget_is_editor (CONST_WIDGET (g
->current
->data
)))
1295 return EDIT (g
->current
->data
);
1296 return EDIT (widget_find_by_type (CONST_WIDGET (h
), edit_callback
));
1299 /* --------------------------------------------------------------------------------------------- */
1301 * Check if widget is an WEdit class.
1303 * @param w probably editor object
1304 * @return TRUE if widget is an WEdit class, FALSE otherwise
1308 edit_widget_is_editor (const Widget
*w
)
1310 return (w
!= NULL
&& w
->callback
== edit_callback
);
1313 /* --------------------------------------------------------------------------------------------- */
1316 edit_update_screen (WEdit
*e
)
1318 edit_scroll_screen_over_cursor (e
);
1319 edit_update_curs_col (e
);
1320 edit_status (e
, widget_get_state (WIDGET (e
), WST_FOCUSED
));
1322 /* pop all events for this window for internal handling */
1324 e
->force
|= REDRAW_PAGE
;
1327 if ((e
->force
& REDRAW_COMPLETELY
) != 0)
1328 e
->force
|= REDRAW_PAGE
;
1329 edit_render_keypress (e
);
1332 widget_draw (WIDGET (buttonbar_find (DIALOG (WIDGET (e
)->owner
))));
1335 /* --------------------------------------------------------------------------------------------- */
1337 * Save current window size.
1339 * @param edit editor object
1343 edit_save_size (WEdit
*edit
)
1345 edit
->loc_prev
= WIDGET (edit
)->rect
;
1348 /* --------------------------------------------------------------------------------------------- */
1350 * Create new editor window and insert it into editor screen.
1352 * @param h editor dialog (screen)
1353 * @param y y coordinate
1354 * @param x x coordinate
1355 * @param lines window height
1356 * @param cols window width
1357 * @param f file object
1358 * @param fline line number in file
1359 * @return TRUE if new window was successfully created and inserted into editor screen,
1364 edit_add_window (WDialog
*h
, const WRect
*r
, const edit_arg_t
*arg
)
1369 edit
= edit_init (NULL
, r
, arg
);
1374 w
->callback
= edit_callback
;
1375 w
->mouse_callback
= edit_mouse_callback
;
1377 group_add_widget_autopos (GROUP (h
), w
, WPOS_KEEP_ALL
, NULL
);
1378 edit_set_buttonbar (edit
, buttonbar_find (h
));
1379 widget_draw (WIDGET (h
));
1384 /* --------------------------------------------------------------------------------------------- */
1386 * Handle move/resize events.
1388 * @param edit editor object
1389 * @param command action id
1390 * @return TRUE if the action was handled, FALSE otherwise
1394 edit_handle_move_resize (WEdit
*edit
, long command
)
1396 Widget
*w
= WIDGET (edit
);
1397 gboolean ret
= FALSE
;
1399 if (edit
->fullscreen
)
1401 edit
->drag_state
= MCEDIT_DRAG_NONE
;
1402 w
->mouse
.forced_capture
= FALSE
;
1406 switch (edit
->drag_state
)
1408 case MCEDIT_DRAG_NONE
:
1409 /* possible start move/resize */
1413 edit
->drag_state
= MCEDIT_DRAG_MOVE
;
1414 edit_save_size (edit
);
1415 edit_status (edit
, TRUE
); /* redraw frame and status */
1417 * If a user initiates a move by the menu, not by the mouse, we
1418 * make a subsequent mouse drag pull the frame from its middle.
1419 * (We can instead choose '0' to pull it from the corner.)
1421 edit
->drag_state_start
= w
->rect
.cols
/ 2;
1424 case CK_WindowResize
:
1425 edit
->drag_state
= MCEDIT_DRAG_RESIZE
;
1426 edit_save_size (edit
);
1427 edit_status (edit
, TRUE
); /* redraw frame and status */
1435 case MCEDIT_DRAG_MOVE
:
1438 case CK_WindowResize
:
1439 edit
->drag_state
= MCEDIT_DRAG_RESIZE
;
1446 edit_window_move (edit
, command
);
1451 edit
->drag_state
= MCEDIT_DRAG_NONE
;
1452 edit_status (edit
, TRUE
); /* redraw frame and status */
1460 case MCEDIT_DRAG_RESIZE
:
1464 edit
->drag_state
= MCEDIT_DRAG_MOVE
;
1471 edit_window_resize (edit
, command
);
1475 case CK_WindowResize
:
1476 edit
->drag_state
= MCEDIT_DRAG_NONE
;
1477 edit_status (edit
, TRUE
); /* redraw frame and status */
1490 * - We let the user stop a resize/move operation by clicking with the
1491 * mouse anywhere. ("clicking" = pressing and releasing a button.)
1492 * - We let the user perform a resize/move operation by a mouse drag
1493 * initiated anywhere.
1495 * "Anywhere" means: inside or outside the window. We make this happen
1496 * with the 'forced_capture' flag.
1498 w
->mouse
.forced_capture
= (edit
->drag_state
!= MCEDIT_DRAG_NONE
);
1503 /* --------------------------------------------------------------------------------------------- */
1505 * Toggle window fuulscreen mode.
1507 * @param edit editor object
1511 edit_toggle_fullscreen (WEdit
*edit
)
1513 Widget
*w
= WIDGET (edit
);
1515 edit
->fullscreen
= !edit
->fullscreen
;
1516 edit
->force
= REDRAW_COMPLETELY
;
1518 if (!edit
->fullscreen
)
1520 edit_restore_size (edit
);
1521 /* do not follow screen size on resize */
1522 w
->pos_flags
= WPOS_KEEP_DEFAULT
;
1528 edit_save_size (edit
);
1529 r
= WIDGET (w
->owner
)->rect
;
1530 rect_grow (&r
, -1, 0);
1531 widget_set_size_rect (w
, &r
);
1532 /* follow screen size on resize */
1533 w
->pos_flags
= WPOS_KEEP_ALL
;
1534 edit
->force
|= REDRAW_PAGE
;
1535 edit_update_screen (edit
);
1539 /* --------------------------------------------------------------------------------------------- */