WEdit shouldn't handle mouse events of overlapping buttonbar.
[midnight-commander.git] / src / editor / editwidget.c
blob0d1b6b31628b2d54853fd8800a843cca3c727bc3
1 /*
2 Editor initialisation and callback handler.
4 Copyright (C) 1996-2017
5 Free Software Foundation, Inc.
7 Written by:
8 Paul Sheer, 1996, 1997
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/>.
27 /** \file
28 * \brief Source: editor initialisation and callback handler
29 * \author Paul Sheer
30 * \date 1996, 1997
33 #include <config.h>
35 #include <ctype.h>
36 #include <errno.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <sys/types.h>
40 #include <unistd.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() */
47 #include "lib/skin.h"
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() */
54 #ifdef HAVE_CHARSET
55 #include "lib/charsets.h"
56 #endif
58 #include "src/keybind-defaults.h" /* keybind_lookup_keymap_command() */
59 #include "src/setup.h" /* home_dir */
60 #include "src/filemanager/cmd.h" /* view_other_cmd(), save_setup_cmd() */
61 #include "src/learn.h" /* learn_keys() */
62 #include "src/args.h" /* mcedit_arg_t */
64 #include "edit-impl.h"
65 #include "editwidget.h"
66 #ifdef HAVE_ASPELL
67 #include "spell.h"
68 #endif
70 /*** global variables ****************************************************************************/
72 char *edit_window_state_char = NULL;
73 char *edit_window_close_char = NULL;
75 /*** file scope macro definitions ****************************************************************/
77 #define WINDOW_MIN_LINES (2 + 2)
78 #define WINDOW_MIN_COLS (2 + LINE_STATE_WIDTH + 2)
80 /*** file scope type declarations ****************************************************************/
82 /*** file scope variables ************************************************************************/
83 static unsigned int edit_dlg_init_refcounter = 0;
85 /*** file scope functions ************************************************************************/
87 static cb_ret_t edit_dialog_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm,
88 void *data);
90 /* --------------------------------------------------------------------------------------------- */
91 /**
92 * Init the 'edit' subsystem
95 static void
96 edit_dlg_init (void)
98 if (edit_dlg_init_refcounter == 0)
100 edit_window_state_char = mc_skin_get ("widget-editor", "window-state-char", "*");
101 edit_window_close_char = mc_skin_get ("widget-editor", "window-close-char", "X");
103 #ifdef HAVE_ASPELL
104 aspell_init ();
105 #endif
108 edit_dlg_init_refcounter++;
111 /* --------------------------------------------------------------------------------------------- */
113 * Deinit the 'edit' subsystem
116 static void
117 edit_dlg_deinit (void)
119 if (edit_dlg_init_refcounter != 0)
120 edit_dlg_init_refcounter--;
122 if (edit_dlg_init_refcounter == 0)
124 g_free (edit_window_state_char);
125 g_free (edit_window_close_char);
127 #ifdef HAVE_ASPELL
128 aspell_clean ();
129 #endif
133 /* --------------------------------------------------------------------------------------------- */
135 * Show info about editor
138 static void
139 edit_about (void)
141 quick_widget_t quick_widgets[] = {
142 /* *INDENT-OFF* */
143 QUICK_LABEL ("MCEdit " VERSION, NULL),
144 QUICK_SEPARATOR (TRUE),
145 QUICK_LABEL (N_("A user friendly text editor\n"
146 "written for the Midnight Commander."), NULL),
147 QUICK_SEPARATOR (FALSE),
148 QUICK_LABEL (N_("Copyright (C) 1996-2016 the Free Software Foundation"), NULL),
149 QUICK_START_BUTTONS (TRUE, TRUE),
150 QUICK_BUTTON (N_("&OK"), B_ENTER, NULL, NULL),
151 QUICK_END
152 /* *INDENT-ON* */
155 quick_dialog_t qdlg = {
156 -1, -1, 40,
157 N_("About"), "[Internal File Editor]",
158 quick_widgets, NULL, NULL
161 quick_widgets[0].pos_flags = WPOS_KEEP_TOP | WPOS_CENTER_HORZ;
162 quick_widgets[2].pos_flags = WPOS_KEEP_TOP | WPOS_CENTER_HORZ;
163 quick_widgets[4].pos_flags = WPOS_KEEP_TOP | WPOS_CENTER_HORZ;
165 (void) quick_dialog (&qdlg);
168 /* --------------------------------------------------------------------------------------------- */
170 * Show a help window
173 static void
174 edit_help (void)
176 ev_help_t event_data = { NULL, "[Internal File Editor]" };
177 mc_event_raise (MCEVENT_GROUP_CORE, "help", &event_data);
180 /* --------------------------------------------------------------------------------------------- */
182 * Callback for the iteration of objects in the 'editors' array.
183 * Resize the editor window.
185 * @param data probably WEdit object
186 * @param user_data unused
189 static void
190 edit_dialog_resize_cb (void *data, void *user_data)
192 Widget *w = WIDGET (data);
194 (void) user_data;
196 if (edit_widget_is_editor (w) && ((WEdit *) w)->fullscreen)
198 Widget *wh = WIDGET (w->owner);
200 w->lines = wh->lines - 2;
201 w->cols = wh->cols;
205 /* --------------------------------------------------------------------------------------------- */
207 * Restore saved window size.
209 * @param edit editor object
212 static void
213 edit_restore_size (WEdit * edit)
215 Widget *w = WIDGET (edit);
217 edit->drag_state = MCEDIT_DRAG_NONE;
218 w->mouse.forced_capture = FALSE;
219 widget_set_size (w, edit->y_prev, edit->x_prev, edit->lines_prev, edit->cols_prev);
220 dlg_redraw (w->owner);
223 /* --------------------------------------------------------------------------------------------- */
225 * Move window by one row or column in any direction.
227 * @param edit editor object
228 * @param command direction (CK_Up, CK_Down, CK_Left, CK_Right)
231 static void
232 edit_window_move (WEdit * edit, long command)
234 Widget *w = WIDGET (edit);
235 Widget *wh = WIDGET (w->owner);
237 switch (command)
239 case CK_Up:
240 if (w->y > wh->y + 1) /* menubar */
241 w->y--;
242 break;
243 case CK_Down:
244 if (w->y < wh->y + wh->lines - 2) /* buttonbar */
245 w->y++;
246 break;
247 case CK_Left:
248 if (w->x + w->cols > wh->x)
249 w->x--;
250 break;
251 case CK_Right:
252 if (w->x < wh->x + wh->cols)
253 w->x++;
254 break;
255 default:
256 return;
259 edit->force |= REDRAW_PAGE;
260 dlg_redraw (w->owner);
263 /* --------------------------------------------------------------------------------------------- */
265 * Resize window by one row or column in any direction.
267 * @param edit editor object
268 * @param command direction (CK_Up, CK_Down, CK_Left, CK_Right)
271 static void
272 edit_window_resize (WEdit * edit, long command)
274 Widget *w = WIDGET (edit);
275 Widget *wh = WIDGET (w->owner);
277 switch (command)
279 case CK_Up:
280 if (w->lines > WINDOW_MIN_LINES)
281 w->lines--;
282 break;
283 case CK_Down:
284 if (w->y + w->lines < wh->y + wh->lines - 1) /* buttonbar */
285 w->lines++;
286 break;
287 case CK_Left:
288 if (w->cols > WINDOW_MIN_COLS)
289 w->cols--;
290 break;
291 case CK_Right:
292 if (w->x + w->cols < wh->x + wh->cols)
293 w->cols++;
294 break;
295 default:
296 return;
299 edit->force |= REDRAW_COMPLETELY;
300 dlg_redraw (w->owner);
303 /* --------------------------------------------------------------------------------------------- */
305 * Get hotkey by number.
307 * @param n number
308 * @return hotkey
311 static unsigned char
312 get_hotkey (int n)
314 return (n <= 9) ? '0' + n : 'a' + n - 10;
317 /* --------------------------------------------------------------------------------------------- */
319 static void
320 edit_window_list (const WDialog * h)
322 const size_t dlg_num = g_list_length (h->widgets) - 2; /* 2 = skip menu and buttonbar */
323 int lines, cols;
324 Listbox *listbox;
325 GList *w;
326 WEdit *selected;
327 int i = 0;
329 lines = MIN ((size_t) (LINES * 2 / 3), dlg_num);
330 cols = COLS * 2 / 3;
332 listbox = create_listbox_window (lines, cols, _("Open files"), "[Open files]");
334 for (w = h->widgets; w != NULL; w = g_list_next (w))
335 if (edit_widget_is_editor (CONST_WIDGET (w->data)))
337 WEdit *e = (WEdit *) w->data;
338 char *fname;
340 if (e->filename_vpath == NULL)
341 fname = g_strdup_printf ("%c [%s]", e->modified ? '*' : ' ', _("NoName"));
342 else
343 fname =
344 g_strdup_printf ("%c%s", e->modified ? '*' : ' ',
345 vfs_path_as_str (e->filename_vpath));
347 listbox_add_item (listbox->list, LISTBOX_APPEND_AT_END, get_hotkey (i++),
348 str_term_trim (fname, WIDGET (listbox->list)->cols - 2), e, FALSE);
349 g_free (fname);
352 selected = run_listbox_with_data (listbox, h->current->data);
353 if (selected != NULL)
354 widget_select (WIDGET (selected));
357 /* --------------------------------------------------------------------------------------------- */
359 static char *
360 edit_get_shortcut (long command)
362 const char *ext_map;
363 const char *shortcut = NULL;
365 shortcut = keybind_lookup_keymap_shortcut (editor_map, command);
366 if (shortcut != NULL)
367 return g_strdup (shortcut);
369 ext_map = keybind_lookup_keymap_shortcut (editor_map, CK_ExtendedKeyMap);
370 if (ext_map != NULL)
371 shortcut = keybind_lookup_keymap_shortcut (editor_x_map, command);
372 if (shortcut != NULL)
373 return g_strdup_printf ("%s %s", ext_map, shortcut);
375 return NULL;
378 /* --------------------------------------------------------------------------------------------- */
380 static char *
381 edit_get_title (const WDialog * h, size_t len)
383 const WEdit *edit = find_editor (h);
384 const char *modified = edit->modified ? "(*) " : " ";
385 const char *file_label;
386 char *filename;
388 len -= 4;
390 if (edit->filename_vpath == NULL)
391 filename = g_strdup (_("[NoName]"));
392 else
393 filename = g_strdup (vfs_path_as_str (edit->filename_vpath));
395 file_label = str_term_trim (filename, len - str_term_width1 (_("Edit: ")));
396 g_free (filename);
398 return g_strconcat (_("Edit: "), modified, file_label, (char *) NULL);
401 /* --------------------------------------------------------------------------------------------- */
403 static cb_ret_t
404 edit_dialog_command_execute (WDialog * h, long command)
406 Widget *wh = WIDGET (h);
407 cb_ret_t ret = MSG_HANDLED;
409 switch (command)
411 case CK_EditNew:
412 edit_add_window (h, wh->y + 1, wh->x, wh->lines - 2, wh->cols, NULL, 0);
413 break;
414 case CK_EditFile:
415 edit_load_cmd (h);
416 break;
417 case CK_EditSyntaxFile:
418 edit_load_syntax_file (h);
419 break;
420 case CK_EditUserMenu:
421 edit_load_menu_file (h);
422 break;
423 case CK_Close:
424 /* if there are no opened files anymore, close MC editor */
425 if (edit_widget_is_editor (CONST_WIDGET (h->current->data)) &&
426 edit_close_cmd ((WEdit *) h->current->data) && find_editor (h) == NULL)
427 dlg_stop (h);
428 break;
429 case CK_Help:
430 edit_help ();
431 /* edit->force |= REDRAW_COMPLETELY; */
432 break;
433 case CK_Menu:
434 edit_menu_cmd (h);
435 break;
436 case CK_Quit:
437 case CK_Cancel:
438 /* don't close editor due to SIGINT, but stop move/resize window */
440 Widget *w = WIDGET (h->current->data);
442 if (edit_widget_is_editor (w) && ((WEdit *) w)->drag_state != MCEDIT_DRAG_NONE)
443 edit_restore_size ((WEdit *) w);
444 else if (command == CK_Quit)
445 dlg_stop (h);
447 break;
448 case CK_About:
449 edit_about ();
450 break;
451 case CK_SyntaxOnOff:
452 edit_syntax_onoff_cmd (h);
453 break;
454 case CK_ShowTabTws:
455 edit_show_tabs_tws_cmd (h);
456 break;
457 case CK_ShowMargin:
458 edit_show_margin_cmd (h);
459 break;
460 case CK_ShowNumbers:
461 edit_show_numbers_cmd (h);
462 break;
463 case CK_Refresh:
464 edit_refresh_cmd ();
465 break;
466 case CK_Shell:
467 view_other_cmd ();
468 break;
469 case CK_LearnKeys:
470 learn_keys ();
471 break;
472 case CK_WindowMove:
473 case CK_WindowResize:
474 if (edit_widget_is_editor (CONST_WIDGET (h->current->data)))
475 edit_handle_move_resize ((WEdit *) h->current->data, command);
476 break;
477 case CK_WindowList:
478 edit_window_list (h);
479 break;
480 case CK_WindowNext:
481 dlg_select_next_widget (h);
482 break;
483 case CK_WindowPrev:
484 dlg_select_prev_widget (h);
485 break;
486 case CK_Options:
487 edit_options_dialog (h);
488 break;
489 case CK_OptionsSaveMode:
490 edit_save_mode_cmd ();
491 break;
492 case CK_SaveSetup:
493 save_setup_cmd ();
494 break;
495 default:
496 ret = MSG_NOT_HANDLED;
497 break;
500 return ret;
503 /* --------------------------------------------------------------------------------------------- */
505 * Translate the keycode into either 'command' or 'char_for_insertion'.
506 * 'command' is one of the editor commands from cmddef.h.
509 static gboolean
510 edit_translate_key (WEdit * edit, long x_key, int *cmd, int *ch)
512 long command = CK_InsertChar;
513 int char_for_insertion = -1;
515 /* an ordinary insertable character */
516 if (!edit->extmod && x_key < 256)
518 #ifndef HAVE_CHARSET
519 if (is_printable (x_key))
521 char_for_insertion = x_key;
522 goto fin;
524 #else
525 int c;
527 if (edit->charpoint >= 4)
529 edit->charpoint = 0;
530 edit->charbuf[edit->charpoint] = '\0';
532 if (edit->charpoint < 4)
534 edit->charbuf[edit->charpoint++] = x_key;
535 edit->charbuf[edit->charpoint] = '\0';
538 /* input from 8-bit locale */
539 if (!mc_global.utf8_display)
541 /* source in 8-bit codeset */
542 c = convert_from_input_c (x_key);
544 if (is_printable (c))
546 if (!edit->utf8)
547 char_for_insertion = c;
548 else
549 char_for_insertion = convert_from_8bit_to_utf_c2 ((char) x_key);
550 goto fin;
553 else
555 /* UTF-8 locale */
556 int res;
558 res = str_is_valid_char (edit->charbuf, edit->charpoint);
559 if (res < 0 && res != -2)
561 edit->charpoint = 0; /* broken multibyte char, skip */
562 goto fin;
565 if (edit->utf8)
567 /* source in UTF-8 codeset */
568 if (res < 0)
570 char_for_insertion = x_key;
571 goto fin;
574 edit->charbuf[edit->charpoint] = '\0';
575 edit->charpoint = 0;
576 if (g_unichar_isprint (g_utf8_get_char (edit->charbuf)))
578 char_for_insertion = x_key;
579 goto fin;
582 else
584 /* 8-bit source */
585 if (res < 0)
587 /* not finised multibyte input (in meddle multibyte utf-8 char) */
588 goto fin;
591 if (g_unichar_isprint (g_utf8_get_char (edit->charbuf)))
593 c = convert_from_utf_to_current (edit->charbuf);
594 edit->charbuf[0] = '\0';
595 edit->charpoint = 0;
596 char_for_insertion = c;
597 goto fin;
600 /* unprinteble utf input, skip it */
601 edit->charbuf[0] = '\0';
602 edit->charpoint = 0;
605 #endif /* HAVE_CHARSET */
608 /* Commands specific to the key emulation */
609 if (edit->extmod)
611 edit->extmod = FALSE;
612 command = keybind_lookup_keymap_command (editor_x_map, x_key);
614 else
615 command = keybind_lookup_keymap_command (editor_map, x_key);
617 if (command == CK_IgnoreKey)
618 command = CK_InsertChar;
620 fin:
621 *cmd = (int) command; /* FIXME */
622 *ch = char_for_insertion;
624 return !(command == CK_InsertChar && char_for_insertion == -1);
628 /* --------------------------------------------------------------------------------------------- */
630 static inline void
631 edit_quit (WDialog * h)
633 GList *l;
634 WEdit *e = NULL;
636 /* don't stop the dialog before final decision */
637 widget_set_state (WIDGET (h), WST_ACTIVE, TRUE);
639 for (l = h->widgets; l != NULL; l = g_list_next (l))
640 if (edit_widget_is_editor (CONST_WIDGET (l->data)))
642 e = (WEdit *) l->data;
644 if (e->drag_state != MCEDIT_DRAG_NONE)
646 edit_restore_size (e);
647 return;
650 if (e->modified)
652 widget_select (WIDGET (e));
654 if (!edit_ok_to_exit (e))
655 return;
659 /* no editors in dialog at all or no any file required to be saved */
660 if (e == NULL || l == NULL)
661 dlg_stop (h);
664 /* --------------------------------------------------------------------------------------------- */
666 static inline void
667 edit_set_buttonbar (WEdit * edit, WButtonBar * bb)
669 buttonbar_set_label (bb, 1, Q_ ("ButtonBar|Help"), editor_map, NULL);
670 buttonbar_set_label (bb, 2, Q_ ("ButtonBar|Save"), editor_map, WIDGET (edit));
671 buttonbar_set_label (bb, 3, Q_ ("ButtonBar|Mark"), editor_map, WIDGET (edit));
672 buttonbar_set_label (bb, 4, Q_ ("ButtonBar|Replac"), editor_map, WIDGET (edit));
673 buttonbar_set_label (bb, 5, Q_ ("ButtonBar|Copy"), editor_map, WIDGET (edit));
674 buttonbar_set_label (bb, 6, Q_ ("ButtonBar|Move"), editor_map, WIDGET (edit));
675 buttonbar_set_label (bb, 7, Q_ ("ButtonBar|Search"), editor_map, WIDGET (edit));
676 buttonbar_set_label (bb, 8, Q_ ("ButtonBar|Delete"), editor_map, WIDGET (edit));
677 buttonbar_set_label (bb, 9, Q_ ("ButtonBar|PullDn"), editor_map, NULL);
678 buttonbar_set_label (bb, 10, Q_ ("ButtonBar|Quit"), editor_map, NULL);
681 /* --------------------------------------------------------------------------------------------- */
683 static void
684 edit_total_update (WEdit * edit)
686 edit_find_bracket (edit);
687 edit->force |= REDRAW_COMPLETELY;
688 edit_update_curs_row (edit);
689 edit_update_screen (edit);
692 /* --------------------------------------------------------------------------------------------- */
694 static gboolean
695 edit_update_cursor (WEdit * edit, const mouse_event_t * event)
697 int x, y;
698 gboolean done;
700 x = event->x - (edit->fullscreen ? 0 : 1);
701 y = event->y - (edit->fullscreen ? 0 : 1);
703 if (edit->mark2 != -1 && event->msg == MSG_MOUSE_UP)
704 return TRUE; /* don't do anything */
706 if (event->msg == MSG_MOUSE_DOWN || event->msg == MSG_MOUSE_UP)
707 edit_push_key_press (edit);
709 if (!option_cursor_beyond_eol)
710 edit->prev_col = x - edit->start_col - option_line_state_width;
711 else
713 long line_len;
715 line_len =
716 edit_move_forward3 (edit, edit_buffer_get_current_bol (&edit->buffer), 0,
717 edit_buffer_get_current_eol (&edit->buffer));
719 if (x > line_len - 1)
721 edit->over_col = x - line_len - edit->start_col - option_line_state_width;
722 edit->prev_col = line_len;
724 else
726 edit->over_col = 0;
727 edit->prev_col = x - option_line_state_width - edit->start_col;
731 if (y > edit->curs_row)
732 edit_move_down (edit, y - edit->curs_row, FALSE);
733 else if (y < edit->curs_row)
734 edit_move_up (edit, edit->curs_row - y, FALSE);
735 else
736 edit_move_to_prev_col (edit, edit_buffer_get_current_bol (&edit->buffer));
738 if (event->msg == MSG_MOUSE_CLICK)
740 edit_mark_cmd (edit, TRUE); /* reset */
741 edit->highlight = 0;
744 done = (event->msg != MSG_MOUSE_DRAG);
745 if (done)
746 edit_mark_cmd (edit, FALSE);
748 return done;
751 /* --------------------------------------------------------------------------------------------- */
752 /** Callback for the edit dialog */
754 static cb_ret_t
755 edit_dialog_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
757 WMenuBar *menubar;
758 WButtonBar *buttonbar;
759 WDialog *h = DIALOG (w);
761 switch (msg)
763 case MSG_INIT:
764 edit_dlg_init ();
765 return MSG_HANDLED;
767 case MSG_DRAW:
768 /* don't use dlg_default_repaint() -- we don't need a frame */
769 tty_setcolor (EDITOR_BACKGROUND);
770 dlg_erase (h);
771 return MSG_HANDLED;
773 case MSG_RESIZE:
774 menubar = find_menubar (h);
775 buttonbar = find_buttonbar (h);
776 /* dlg_set_size() is surplus for this case */
777 w->lines = LINES;
778 w->cols = COLS;
779 widget_set_size (WIDGET (buttonbar), w->lines - 1, w->x, 1, w->cols);
780 widget_set_size (WIDGET (menubar), w->y, w->x, 1, w->cols);
781 menubar_arrange (menubar);
782 g_list_foreach (h->widgets, (GFunc) edit_dialog_resize_cb, NULL);
783 return MSG_HANDLED;
785 case MSG_ACTION:
787 /* Handle shortcuts, menu, and buttonbar. */
789 cb_ret_t result;
791 result = edit_dialog_command_execute (h, parm);
793 /* We forward any commands coming from the menu, and which haven't been
794 handled by the dialog, to the focused WEdit window. */
795 if (result == MSG_NOT_HANDLED && sender == WIDGET (find_menubar (h)))
796 result = send_message (h->current->data, NULL, MSG_ACTION, parm, NULL);
798 return result;
801 case MSG_KEY:
803 Widget *we = WIDGET (h->current->data);
804 cb_ret_t ret = MSG_NOT_HANDLED;
806 if (edit_widget_is_editor (we))
808 WEdit *e = (WEdit *) we;
809 long command;
811 if (!e->extmod)
812 command = keybind_lookup_keymap_command (editor_map, parm);
813 else
815 e->extmod = FALSE;
816 command = keybind_lookup_keymap_command (editor_x_map, parm);
819 if (command != CK_IgnoreKey)
820 ret = edit_dialog_command_execute (h, command);
824 * Due to the "end of bracket" escape the editor sees input with is_idle() == false
825 * (expects more characters) and hence doesn't yet refresh the screen, but then
826 * no further characters arrive (there's only an "end of bracket" which is swallowed
827 * by tty_get_event()), so you end up with a screen that's not refreshed after pasting.
828 * So let's trigger an IDLE signal.
830 if (!is_idle ())
831 widget_idle (w, TRUE);
832 return ret;
835 /* hardcoded menu hotkeys (see edit_drop_hotkey_menu) */
836 case MSG_UNHANDLED_KEY:
837 return edit_drop_hotkey_menu (h, parm) ? MSG_HANDLED : MSG_NOT_HANDLED;
839 case MSG_VALIDATE:
840 edit_quit (h);
841 return MSG_HANDLED;
843 case MSG_END:
844 edit_dlg_deinit ();
845 return MSG_HANDLED;
847 case MSG_IDLE:
848 widget_idle (w, FALSE);
849 return send_message (h->current->data, NULL, MSG_IDLE, 0, NULL);
851 default:
852 return dlg_default_callback (w, sender, msg, parm, data);
856 /* --------------------------------------------------------------------------------------------- */
859 * Handle mouse events of editor screen.
861 * @param w Widget object (the editor)
862 * @param msg mouse event message
863 * @param event mouse event data
865 static void
866 edit_dialog_mouse_callback (Widget * w, mouse_msg_t msg, mouse_event_t * event)
868 gboolean unhandled = TRUE;
870 if (msg == MSG_MOUSE_DOWN && event->y == 0)
872 WDialog *h = DIALOG (w);
873 WMenuBar *b;
875 b = find_menubar (h);
877 if (!widget_get_state (WIDGET (b), WST_FOCUSED))
879 /* menubar */
881 GList *l;
882 GList *top = NULL;
883 int x;
885 /* Try find top fullscreen window */
886 for (l = h->widgets; l != NULL; l = g_list_next (l))
887 if (edit_widget_is_editor (CONST_WIDGET (l->data))
888 && ((WEdit *) l->data)->fullscreen)
889 top = l;
891 /* Handle fullscreen/close buttons in the top line */
892 x = w->cols - 5;
894 if (top != NULL && event->x >= x)
896 WEdit *e = (WEdit *) top->data;
898 if (top != h->current)
900 /* Window is not active. Activate it */
901 widget_select (WIDGET (e));
904 /* Handle buttons */
905 if (event->x - x <= 2)
906 edit_toggle_fullscreen (e);
907 else
908 send_message (h, NULL, MSG_ACTION, CK_Close, NULL);
910 unhandled = FALSE;
913 if (unhandled)
914 menubar_activate (b, drop_menus, -1);
918 /* Continue handling of unhandled event in window or menu */
919 event->result.abort = unhandled;
922 /* --------------------------------------------------------------------------------------------- */
924 static cb_ret_t
925 edit_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
927 WEdit *e = (WEdit *) w;
929 switch (msg)
931 case MSG_FOCUS:
932 edit_set_buttonbar (e, find_buttonbar (w->owner));
933 return MSG_HANDLED;
935 case MSG_DRAW:
936 e->force |= REDRAW_COMPLETELY;
937 edit_update_screen (e);
938 return MSG_HANDLED;
940 case MSG_KEY:
942 int cmd, ch;
943 cb_ret_t ret = MSG_NOT_HANDLED;
945 /* The user may override the access-keys for the menu bar. */
946 if (macro_index == -1 && edit_execute_macro (e, parm))
948 edit_update_screen (e);
949 ret = MSG_HANDLED;
951 else if (edit_translate_key (e, parm, &cmd, &ch))
953 edit_execute_key_command (e, cmd, ch);
954 edit_update_screen (e);
955 ret = MSG_HANDLED;
958 return ret;
961 case MSG_ACTION:
962 /* command from menubar or buttonbar */
963 edit_execute_key_command (e, parm, -1);
964 edit_update_screen (e);
965 return MSG_HANDLED;
967 case MSG_CURSOR:
969 int y, x;
971 y = (e->fullscreen ? 0 : 1) + EDIT_TEXT_VERTICAL_OFFSET + e->curs_row;
972 x = (e->fullscreen ? 0 : 1) + EDIT_TEXT_HORIZONTAL_OFFSET + option_line_state_width +
973 e->curs_col + e->start_col + e->over_col;
975 widget_move (w, y, x);
976 return MSG_HANDLED;
979 case MSG_IDLE:
980 edit_update_screen (e);
981 return MSG_HANDLED;
983 case MSG_DESTROY:
984 edit_clean (e);
985 return MSG_HANDLED;
987 default:
988 return widget_default_callback (w, sender, msg, parm, data);
992 /* --------------------------------------------------------------------------------------------- */
995 * Handle move/resize mouse events.
997 static void
998 edit_mouse_handle_move_resize (Widget * w, mouse_msg_t msg, mouse_event_t * event)
1000 WEdit *edit = (WEdit *) (w);
1001 Widget *h = WIDGET (w->owner);
1002 int global_x, global_y;
1004 if (msg == MSG_MOUSE_UP)
1006 /* Exit move/resize mode. */
1007 edit_execute_cmd (edit, CK_Enter, -1);
1008 return;
1011 if (msg != MSG_MOUSE_DRAG)
1013 * We ignore any other events. Specifically, MSG_MOUSE_DOWN.
1015 * When the move/resize is initiated by the menu, we let the user
1016 * stop it by clicking with the mouse. Which is why we don't want
1017 * a mouse down to affect the window.
1019 return;
1021 /* Convert point to global coordinates for easier calculations. */
1022 global_x = event->x + w->x;
1023 global_y = event->y + w->y;
1025 /* Clamp the point to the dialog's client area. */
1026 global_y = CLAMP (global_y, h->y + 1, h->y + h->lines - 2); /* Status line, buttonbar */
1027 global_x = CLAMP (global_x, h->x, h->x + h->cols - 1); /* Currently a no-op, as the dialog has no left/right margins. */
1029 if (edit->drag_state == MCEDIT_DRAG_MOVE)
1031 w->y = global_y;
1032 w->x = global_x - edit->drag_state_start;
1034 else if (edit->drag_state == MCEDIT_DRAG_RESIZE)
1036 w->lines = MAX (WINDOW_MIN_LINES, global_y - w->y + 1);
1037 w->cols = MAX (WINDOW_MIN_COLS, global_x - w->x + 1);
1040 edit->force |= REDRAW_COMPLETELY; /* Not really needed as WEdit's MSG_DRAW already does this. */
1042 /* We draw the whole dialog because dragging/resizing exposes area beneath. */
1043 dlg_redraw (w->owner);
1046 /* --------------------------------------------------------------------------------------------- */
1049 * Handle mouse events of editor window
1051 * @param w Widget object (the editor window)
1052 * @param msg mouse event message
1053 * @param event mouse event data
1055 static void
1056 edit_mouse_callback (Widget * w, mouse_msg_t msg, mouse_event_t * event)
1058 WEdit *edit = (WEdit *) w;
1059 /* offset for top line */
1060 int dx = edit->fullscreen ? 0 : 2;
1061 /* location of 'Close' and 'Toggle fullscreen' pictograms */
1062 int close_x, toggle_fullscreen_x;
1064 close_x = (w->cols - 1) - dx - 1;
1065 toggle_fullscreen_x = close_x - 3;
1067 if (edit->drag_state != MCEDIT_DRAG_NONE)
1069 /* window is being resized/moved */
1070 edit_mouse_handle_move_resize (w, msg, event);
1071 return;
1074 /* If it's the last line on the screen, we abort the event to make the
1075 * system channel it to the overlapping buttonbar instead. We have to do
1076 * this because a WEdit has the WOP_TOP_SELECT flag, which makes it above
1077 * the buttonbar in Z-order. */
1078 if (msg == MSG_MOUSE_DOWN && (event->y + w->y == LINES - 1))
1080 event->result.abort = TRUE;
1081 return;
1084 switch (msg)
1086 case MSG_MOUSE_DOWN:
1087 widget_select (w);
1088 edit_update_curs_row (edit);
1089 edit_update_curs_col (edit);
1091 if (!edit->fullscreen)
1093 if (event->y == 0)
1095 if (event->x == close_x)
1096 ; /* do nothing (see MSG_MOUSE_CLICK) */
1097 else if (event->x == toggle_fullscreen_x)
1098 ; /* do nothing (see MSG_MOUSE_CLICK) */
1099 else
1101 /* start window move */
1102 edit_execute_cmd (edit, CK_WindowMove, -1);
1103 edit->drag_state_start = event->x;
1105 break;
1108 if (event->y == w->lines - 1 && event->x == w->cols - 1)
1110 /* bottom-right corner -- start window resize */
1111 edit_execute_cmd (edit, CK_WindowResize, -1);
1112 break;
1116 /* fall through to start/stop text selection */
1118 case MSG_MOUSE_UP:
1119 edit_update_cursor (edit, event);
1120 edit_total_update (edit);
1121 break;
1123 case MSG_MOUSE_CLICK:
1124 if (event->y == 0)
1126 if (event->x == close_x)
1127 send_message (w->owner, NULL, MSG_ACTION, CK_Close, NULL);
1128 else if (event->x == toggle_fullscreen_x)
1129 edit_toggle_fullscreen (edit);
1130 else if (!edit->fullscreen && event->count == GPM_DOUBLE)
1131 /* double click on top line (toggle fullscreen) */
1132 edit_toggle_fullscreen (edit);
1134 else if (event->count == GPM_DOUBLE)
1136 /* double click */
1137 edit_mark_current_word_cmd (edit);
1138 edit_total_update (edit);
1140 else if (event->count == GPM_TRIPLE)
1142 /* triple click: works in GPM only, not in xterm */
1143 edit_mark_current_line_cmd (edit);
1144 edit_total_update (edit);
1146 break;
1148 case MSG_MOUSE_DRAG:
1149 edit_update_cursor (edit, event);
1150 edit_total_update (edit);
1151 break;
1153 case MSG_MOUSE_SCROLL_UP:
1154 edit_move_up (edit, 2, TRUE);
1155 edit_total_update (edit);
1156 break;
1158 case MSG_MOUSE_SCROLL_DOWN:
1159 edit_move_down (edit, 2, TRUE);
1160 edit_total_update (edit);
1161 break;
1163 default:
1164 break;
1168 /* --------------------------------------------------------------------------------------------- */
1169 /*** public functions ****************************************************************************/
1170 /* --------------------------------------------------------------------------------------------- */
1172 * Edit one file.
1174 * @param file_vpath file object
1175 * @param line line number
1176 * @return TRUE if no errors was occurred, FALSE otherwise
1179 gboolean
1180 edit_file (const vfs_path_t * file_vpath, long line)
1182 mcedit_arg_t arg = { (vfs_path_t *) file_vpath, line };
1183 GList *files;
1184 gboolean ok;
1186 files = g_list_prepend (NULL, &arg);
1187 ok = edit_files (files);
1188 g_list_free (files);
1190 return ok;
1193 /* --------------------------------------------------------------------------------------------- */
1195 gboolean
1196 edit_files (const GList * files)
1198 static gboolean made_directory = FALSE;
1199 WDialog *edit_dlg;
1200 WMenuBar *menubar;
1201 const GList *file;
1202 gboolean ok = FALSE;
1204 if (!made_directory)
1206 char *dir;
1208 dir = mc_build_filename (mc_config_get_cache_path (), EDIT_DIR, (char *) NULL);
1209 made_directory = (mkdir (dir, 0700) != -1 || errno == EEXIST);
1210 g_free (dir);
1212 dir = mc_build_filename (mc_config_get_path (), EDIT_DIR, (char *) NULL);
1213 made_directory = (mkdir (dir, 0700) != -1 || errno == EEXIST);
1214 g_free (dir);
1216 dir = mc_build_filename (mc_config_get_data_path (), EDIT_DIR, (char *) NULL);
1217 made_directory = (mkdir (dir, 0700) != -1 || errno == EEXIST);
1218 g_free (dir);
1221 /* Create a new dialog and add it widgets to it */
1222 edit_dlg =
1223 dlg_create (FALSE, 0, 0, 1, 1, WPOS_FULLSCREEN, FALSE, NULL, edit_dialog_callback,
1224 edit_dialog_mouse_callback, "[Internal File Editor]", NULL);
1225 widget_want_tab (WIDGET (edit_dlg), TRUE);
1227 edit_dlg->get_shortcut = edit_get_shortcut;
1228 edit_dlg->get_title = edit_get_title;
1230 menubar = menubar_new (0, 0, COLS, NULL, TRUE);
1231 add_widget (edit_dlg, menubar);
1232 edit_init_menu (menubar);
1234 add_widget (edit_dlg, buttonbar_new (TRUE));
1236 for (file = files; file != NULL; file = g_list_next (file))
1238 Widget *w = WIDGET (edit_dlg);
1239 mcedit_arg_t *f = (mcedit_arg_t *) file->data;
1240 gboolean f_ok;
1242 f_ok = edit_add_window (edit_dlg, w->y + 1, w->x, w->lines - 2, w->cols, f->file_vpath,
1243 f->line_number);
1244 /* at least one file has been opened succefully */
1245 ok = ok || f_ok;
1248 if (ok)
1249 dlg_run (edit_dlg);
1251 if (!ok || widget_get_state (WIDGET (edit_dlg), WST_CLOSED))
1252 dlg_destroy (edit_dlg);
1254 return ok;
1257 /* --------------------------------------------------------------------------------------------- */
1259 const char *
1260 edit_get_file_name (const WEdit * edit)
1262 return vfs_path_as_str (edit->filename_vpath);
1265 /* --------------------------------------------------------------------------------------------- */
1267 WEdit *
1268 find_editor (const WDialog * h)
1270 if (edit_widget_is_editor (CONST_WIDGET (h->current->data)))
1271 return (WEdit *) h->current->data;
1272 return (WEdit *) find_widget_type (h, edit_callback);
1275 /* --------------------------------------------------------------------------------------------- */
1277 * Check if widget is an WEdit class.
1279 * @param w probably editor object
1280 * @return TRUE if widget is an WEdit class, FALSE otherwise
1283 gboolean
1284 edit_widget_is_editor (const Widget * w)
1286 return (w != NULL && w->callback == edit_callback);
1289 /* --------------------------------------------------------------------------------------------- */
1291 void
1292 edit_update_screen (WEdit * e)
1294 WDialog *h = WIDGET (e)->owner;
1296 edit_scroll_screen_over_cursor (e);
1297 edit_update_curs_col (e);
1298 edit_status (e, widget_get_state (WIDGET (e), WST_FOCUSED));
1300 /* pop all events for this window for internal handling */
1301 if (!is_idle ())
1302 e->force |= REDRAW_PAGE;
1303 else
1305 if ((e->force & REDRAW_COMPLETELY) != 0)
1306 e->force |= REDRAW_PAGE;
1307 edit_render_keypress (e);
1310 widget_redraw (WIDGET (find_buttonbar (h)));
1313 /* --------------------------------------------------------------------------------------------- */
1315 * Save current window size.
1317 * @param edit editor object
1320 void
1321 edit_save_size (WEdit * edit)
1323 Widget *w = WIDGET (edit);
1325 edit->y_prev = w->y;
1326 edit->x_prev = w->x;
1327 edit->lines_prev = w->lines;
1328 edit->cols_prev = w->cols;
1331 /* --------------------------------------------------------------------------------------------- */
1333 * Create new editor window and insert it into editor screen.
1335 * @param h editor dialog (screen)
1336 * @param y y coordinate
1337 * @param x x coordinate
1338 * @param lines window height
1339 * @param cols window width
1340 * @param f file object
1341 * @param fline line number in file
1342 * @return TRUE if new window was successfully created and inserted into editor screen,
1343 * FALSE otherwise
1346 gboolean
1347 edit_add_window (WDialog * h, int y, int x, int lines, int cols, const vfs_path_t * f, long fline)
1349 WEdit *edit;
1350 Widget *w;
1352 edit = edit_init (NULL, y, x, lines, cols, f, fline);
1353 if (edit == NULL)
1354 return FALSE;
1356 w = WIDGET (edit);
1357 w->callback = edit_callback;
1358 w->mouse_callback = edit_mouse_callback;
1360 add_widget (h, w);
1361 edit_set_buttonbar (edit, find_buttonbar (h));
1362 dlg_redraw (h);
1364 return TRUE;
1367 /* --------------------------------------------------------------------------------------------- */
1369 * Handle move/resize events.
1371 * @param edit editor object
1372 * @param command action id
1373 * @return TRUE if the action was handled, FALSE otherwise
1376 gboolean
1377 edit_handle_move_resize (WEdit * edit, long command)
1379 Widget *w = WIDGET (edit);
1380 gboolean ret = FALSE;
1382 if (edit->fullscreen)
1384 edit->drag_state = MCEDIT_DRAG_NONE;
1385 w->mouse.forced_capture = FALSE;
1386 return ret;
1389 switch (edit->drag_state)
1391 case MCEDIT_DRAG_NONE:
1392 /* possible start move/resize */
1393 switch (command)
1395 case CK_WindowMove:
1396 edit->drag_state = MCEDIT_DRAG_MOVE;
1397 edit_save_size (edit);
1398 edit_status (edit, TRUE); /* redraw frame and status */
1400 * If a user initiates a move by the menu, not by the mouse, we
1401 * make a subsequent mouse drag pull the frame from its middle.
1402 * (We can instead choose '0' to pull it from the corner.)
1404 edit->drag_state_start = w->cols / 2;
1405 ret = TRUE;
1406 break;
1407 case CK_WindowResize:
1408 edit->drag_state = MCEDIT_DRAG_RESIZE;
1409 edit_save_size (edit);
1410 edit_status (edit, TRUE); /* redraw frame and status */
1411 ret = TRUE;
1412 break;
1413 default:
1414 break;
1416 break;
1418 case MCEDIT_DRAG_MOVE:
1419 switch (command)
1421 case CK_WindowResize:
1422 edit->drag_state = MCEDIT_DRAG_RESIZE;
1423 ret = TRUE;
1424 break;
1425 case CK_Up:
1426 case CK_Down:
1427 case CK_Left:
1428 case CK_Right:
1429 edit_window_move (edit, command);
1430 ret = TRUE;
1431 break;
1432 case CK_Enter:
1433 case CK_WindowMove:
1434 edit->drag_state = MCEDIT_DRAG_NONE;
1435 edit_status (edit, TRUE); /* redraw frame and status */
1436 default:
1437 ret = TRUE;
1438 break;
1440 break;
1442 case MCEDIT_DRAG_RESIZE:
1443 switch (command)
1445 case CK_WindowMove:
1446 edit->drag_state = MCEDIT_DRAG_MOVE;
1447 ret = TRUE;
1448 break;
1449 case CK_Up:
1450 case CK_Down:
1451 case CK_Left:
1452 case CK_Right:
1453 edit_window_resize (edit, command);
1454 ret = TRUE;
1455 break;
1456 case CK_Enter:
1457 case CK_WindowResize:
1458 edit->drag_state = MCEDIT_DRAG_NONE;
1459 edit_status (edit, TRUE); /* redraw frame and status */
1460 default:
1461 ret = TRUE;
1462 break;
1464 break;
1466 default:
1467 break;
1471 * - We let the user stop a resize/move operation by clicking with the
1472 * mouse anywhere. ("clicking" = pressing and releasing a button.)
1473 * - We let the user perform a resize/move operation by a mouse drag
1474 * initiated anywhere.
1476 * "Anywhere" means: inside or outside the window. We make this happen
1477 * with the 'forced_capture' flag.
1479 w->mouse.forced_capture = (edit->drag_state != MCEDIT_DRAG_NONE);
1481 return ret;
1484 /* --------------------------------------------------------------------------------------------- */
1486 * Toggle window fuulscreen mode.
1488 * @param edit editor object
1491 void
1492 edit_toggle_fullscreen (WEdit * edit)
1494 edit->fullscreen = !edit->fullscreen;
1495 edit->force = REDRAW_COMPLETELY;
1497 if (!edit->fullscreen)
1498 edit_restore_size (edit);
1499 else
1501 Widget *w = WIDGET (edit);
1502 Widget *h = WIDGET (w->owner);
1504 edit_save_size (edit);
1505 widget_set_size (w, h->y + 1, h->x, h->lines - 2, h->cols);
1506 edit->force |= REDRAW_PAGE;
1507 edit_update_screen (edit);
1511 /* --------------------------------------------------------------------------------------------- */