Drop old mouse API and use the new one.
[midnight-commander.git] / src / editor / editwidget.c
blobd60f03eca7c3f2948512b63806cc10e7f2d3f3c0
1 /*
2 Editor initialisation and callback handler.
4 Copyright (C) 1996-2016
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/strutil.h" /* str_term_trim() */
49 #include "lib/util.h" /* mc_build_filename() */
50 #include "lib/widget.h"
51 #include "lib/mcconfig.h"
52 #include "lib/event.h" /* mc_event_raise() */
53 #ifdef HAVE_CHARSET
54 #include "lib/charsets.h"
55 #endif
57 #include "src/keybind-defaults.h" /* keybind_lookup_keymap_command() */
58 #include "src/setup.h" /* home_dir */
59 #include "src/filemanager/cmd.h" /* view_other_cmd(), save_setup_cmd() */
60 #include "src/learn.h" /* learn_keys() */
61 #include "src/args.h" /* mcedit_arg_t */
63 #include "edit-impl.h"
64 #include "editwidget.h"
65 #ifdef HAVE_ASPELL
66 #include "spell.h"
67 #endif
69 /*** global variables ****************************************************************************/
71 char *edit_window_state_char = NULL;
72 char *edit_window_close_char = NULL;
74 /*** file scope macro definitions ****************************************************************/
76 #define WINDOW_MIN_LINES (2 + 2)
77 #define WINDOW_MIN_COLS (2 + LINE_STATE_WIDTH + 2)
79 /*** file scope type declarations ****************************************************************/
81 /*** file scope variables ************************************************************************/
82 static unsigned int edit_dlg_init_refcounter = 0;
84 /*** file scope functions ************************************************************************/
86 static cb_ret_t edit_dialog_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm,
87 void *data);
89 /* --------------------------------------------------------------------------------------------- */
90 /**
91 * Init the 'edit' subsystem
94 static void
95 edit_dlg_init (void)
97 if (edit_dlg_init_refcounter == 0)
99 edit_window_state_char = mc_skin_get ("editor", "window-state-char", "*");
100 edit_window_close_char = mc_skin_get ("editor", "window-close-char", "X");
102 #ifdef HAVE_ASPELL
103 aspell_init ();
104 #endif
107 edit_dlg_init_refcounter++;
110 /* --------------------------------------------------------------------------------------------- */
112 * Deinit the 'edit' subsystem
115 static void
116 edit_dlg_deinit (void)
118 if (edit_dlg_init_refcounter != 0)
119 edit_dlg_init_refcounter--;
121 if (edit_dlg_init_refcounter == 0)
123 g_free (edit_window_state_char);
124 g_free (edit_window_close_char);
126 #ifdef HAVE_ASPELL
127 aspell_clean ();
128 #endif
132 /* --------------------------------------------------------------------------------------------- */
134 * Show info about editor
137 static void
138 edit_about (void)
140 quick_widget_t quick_widgets[] = {
141 /* *INDENT-OFF* */
142 QUICK_LABEL ("MCEdit " VERSION, NULL),
143 QUICK_SEPARATOR (TRUE),
144 QUICK_LABEL (N_("A user friendly text editor\n"
145 "written for the Midnight Commander."), NULL),
146 QUICK_SEPARATOR (FALSE),
147 QUICK_LABEL (N_("Copyright (C) 1996-2016 the Free Software Foundation"), NULL),
148 QUICK_START_BUTTONS (TRUE, TRUE),
149 QUICK_BUTTON (N_("&OK"), B_ENTER, NULL, NULL),
150 QUICK_END
151 /* *INDENT-ON* */
154 quick_dialog_t qdlg = {
155 -1, -1, 40,
156 N_("About"), "[Internal File Editor]",
157 quick_widgets, NULL, NULL
160 quick_widgets[0].pos_flags = WPOS_KEEP_TOP | WPOS_CENTER_HORZ;
161 quick_widgets[2].pos_flags = WPOS_KEEP_TOP | WPOS_CENTER_HORZ;
162 quick_widgets[4].pos_flags = WPOS_KEEP_TOP | WPOS_CENTER_HORZ;
164 (void) quick_dialog (&qdlg);
167 /* --------------------------------------------------------------------------------------------- */
169 * Show a help window
172 static void
173 edit_help (void)
175 ev_help_t event_data = { NULL, "[Internal File Editor]" };
176 mc_event_raise (MCEVENT_GROUP_CORE, "help", &event_data);
179 /* --------------------------------------------------------------------------------------------- */
181 * Callback for the iteration of objects in the 'editors' array.
182 * Resize the editor window.
184 * @param data probably WEdit object
185 * @param user_data unused
188 static void
189 edit_dialog_resize_cb (void *data, void *user_data)
191 Widget *w = WIDGET (data);
193 (void) user_data;
195 if (edit_widget_is_editor (w) && ((WEdit *) w)->fullscreen)
197 Widget *wh = WIDGET (w->owner);
199 w->lines = wh->lines - 2;
200 w->cols = wh->cols;
204 /* --------------------------------------------------------------------------------------------- */
206 * Restore saved window size.
208 * @param edit editor object
211 static void
212 edit_restore_size (WEdit * edit)
214 Widget *w = WIDGET (edit);
216 edit->drag_state = MCEDIT_DRAG_NONE;
217 w->mouse.forced_capture = FALSE;
218 widget_set_size (w, edit->y_prev, edit->x_prev, edit->lines_prev, edit->cols_prev);
219 dlg_redraw (w->owner);
222 /* --------------------------------------------------------------------------------------------- */
224 * Move window by one row or column in any direction.
226 * @param edit editor object
227 * @param command direction (CK_Up, CK_Down, CK_Left, CK_Right)
230 static void
231 edit_window_move (WEdit * edit, long command)
233 Widget *w = WIDGET (edit);
234 Widget *wh = WIDGET (w->owner);
236 switch (command)
238 case CK_Up:
239 if (w->y > wh->y + 1) /* menubar */
240 w->y--;
241 break;
242 case CK_Down:
243 if (w->y < wh->y + wh->lines - 2) /* buttonbar */
244 w->y++;
245 break;
246 case CK_Left:
247 if (w->x + w->cols > wh->x)
248 w->x--;
249 break;
250 case CK_Right:
251 if (w->x < wh->x + wh->cols)
252 w->x++;
253 break;
254 default:
255 return;
258 edit->force |= REDRAW_PAGE;
259 dlg_redraw (w->owner);
262 /* --------------------------------------------------------------------------------------------- */
264 * Resize window by one row or column in any direction.
266 * @param edit editor object
267 * @param command direction (CK_Up, CK_Down, CK_Left, CK_Right)
270 static void
271 edit_window_resize (WEdit * edit, long command)
273 Widget *w = WIDGET (edit);
274 Widget *wh = WIDGET (w->owner);
276 switch (command)
278 case CK_Up:
279 if (w->lines > WINDOW_MIN_LINES)
280 w->lines--;
281 break;
282 case CK_Down:
283 if (w->y + w->lines < wh->y + wh->lines - 1) /* buttonbar */
284 w->lines++;
285 break;
286 case CK_Left:
287 if (w->cols > WINDOW_MIN_COLS)
288 w->cols--;
289 break;
290 case CK_Right:
291 if (w->x + w->cols < wh->x + wh->cols)
292 w->cols++;
293 break;
294 default:
295 return;
298 edit->force |= REDRAW_COMPLETELY;
299 dlg_redraw (w->owner);
302 /* --------------------------------------------------------------------------------------------- */
304 * Get hotkey by number.
306 * @param n number
307 * @return hotkey
310 static unsigned char
311 get_hotkey (int n)
313 return (n <= 9) ? '0' + n : 'a' + n - 10;
316 /* --------------------------------------------------------------------------------------------- */
318 static void
319 edit_window_list (const WDialog * h)
321 const size_t offset = 2; /* skip menu and buttonbar */
322 const size_t dlg_num = g_list_length (h->widgets) - offset;
323 int lines, cols;
324 Listbox *listbox;
325 GList *w;
326 int i = 0;
327 int rv;
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 (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), NULL, FALSE);
349 g_free (fname);
352 rv = g_list_position (h->widgets, h->current) - offset;
353 listbox_select_entry (listbox->list, rv);
354 rv = run_listbox (listbox);
355 if (rv >= 0)
357 w = g_list_nth (h->widgets, rv + offset);
358 dlg_set_top_widget (w->data);
362 /* --------------------------------------------------------------------------------------------- */
364 static char *
365 edit_get_shortcut (long command)
367 const char *ext_map;
368 const char *shortcut = NULL;
370 shortcut = keybind_lookup_keymap_shortcut (editor_map, command);
371 if (shortcut != NULL)
372 return g_strdup (shortcut);
374 ext_map = keybind_lookup_keymap_shortcut (editor_map, CK_ExtendedKeyMap);
375 if (ext_map != NULL)
376 shortcut = keybind_lookup_keymap_shortcut (editor_x_map, command);
377 if (shortcut != NULL)
378 return g_strdup_printf ("%s %s", ext_map, shortcut);
380 return NULL;
383 /* --------------------------------------------------------------------------------------------- */
385 static char *
386 edit_get_title (const WDialog * h, size_t len)
388 const WEdit *edit = find_editor (h);
389 const char *modified = edit->modified ? "(*) " : " ";
390 const char *file_label;
391 char *filename;
393 len -= 4;
395 if (edit->filename_vpath == NULL)
396 filename = g_strdup (_("[NoName]"));
397 else
398 filename = g_strdup (vfs_path_as_str (edit->filename_vpath));
400 file_label = str_term_trim (filename, len - str_term_width1 (_("Edit: ")));
401 g_free (filename);
403 return g_strconcat (_("Edit: "), modified, file_label, (char *) NULL);
406 /* --------------------------------------------------------------------------------------------- */
408 static cb_ret_t
409 edit_dialog_command_execute (WDialog * h, long command)
411 Widget *wh = WIDGET (h);
412 cb_ret_t ret = MSG_HANDLED;
414 switch (command)
416 case CK_EditNew:
417 edit_add_window (h, wh->y + 1, wh->x, wh->lines - 2, wh->cols, NULL, 0);
418 break;
419 case CK_EditFile:
420 edit_load_cmd (h);
421 break;
422 case CK_EditSyntaxFile:
423 edit_load_syntax_file (h);
424 break;
425 case CK_EditUserMenu:
426 edit_load_menu_file (h);
427 break;
428 case CK_Close:
429 /* if there are no opened files anymore, close MC editor */
430 if (edit_widget_is_editor (WIDGET (h->current->data)) &&
431 edit_close_cmd ((WEdit *) h->current->data) && find_editor (h) == NULL)
432 dlg_stop (h);
433 break;
434 case CK_Help:
435 edit_help ();
436 /* edit->force |= REDRAW_COMPLETELY; */
437 break;
438 case CK_Menu:
439 edit_menu_cmd (h);
440 break;
441 case CK_Quit:
442 case CK_Cancel:
443 /* don't close editor due to SIGINT, but stop move/resize window */
445 Widget *w = WIDGET (h->current->data);
447 if (edit_widget_is_editor (w) && ((WEdit *) w)->drag_state != MCEDIT_DRAG_NONE)
448 edit_restore_size ((WEdit *) w);
449 else if (command == CK_Quit)
450 dlg_stop (h);
452 break;
453 case CK_About:
454 edit_about ();
455 break;
456 case CK_SyntaxOnOff:
457 edit_syntax_onoff_cmd (h);
458 break;
459 case CK_ShowTabTws:
460 edit_show_tabs_tws_cmd (h);
461 break;
462 case CK_ShowMargin:
463 edit_show_margin_cmd (h);
464 break;
465 case CK_ShowNumbers:
466 edit_show_numbers_cmd (h);
467 break;
468 case CK_Refresh:
469 edit_refresh_cmd ();
470 break;
471 case CK_Shell:
472 view_other_cmd ();
473 break;
474 case CK_LearnKeys:
475 learn_keys ();
476 break;
477 case CK_WindowMove:
478 case CK_WindowResize:
479 if (edit_widget_is_editor (WIDGET (h->current->data)))
480 edit_handle_move_resize ((WEdit *) h->current->data, command);
481 break;
482 case CK_WindowList:
483 edit_window_list (h);
484 break;
485 case CK_WindowNext:
486 dlg_one_down (h);
487 dlg_set_top_widget (h->current->data);
488 break;
489 case CK_WindowPrev:
490 dlg_one_up (h);
491 dlg_set_top_widget (h->current->data);
492 break;
493 case CK_Options:
494 edit_options_dialog (h);
495 break;
496 case CK_OptionsSaveMode:
497 edit_save_mode_cmd ();
498 break;
499 case CK_SaveSetup:
500 save_setup_cmd ();
501 break;
502 default:
503 ret = MSG_NOT_HANDLED;
504 break;
507 return ret;
510 /* --------------------------------------------------------------------------------------------- */
512 * Translate the keycode into either 'command' or 'char_for_insertion'.
513 * 'command' is one of the editor commands from cmddef.h.
516 static gboolean
517 edit_translate_key (WEdit * edit, long x_key, int *cmd, int *ch)
519 long command = CK_InsertChar;
520 int char_for_insertion = -1;
522 /* an ordinary insertable character */
523 if (!edit->extmod && x_key < 256)
525 #ifndef HAVE_CHARSET
526 if (is_printable (x_key))
528 char_for_insertion = x_key;
529 goto fin;
531 #else
532 int c;
534 if (edit->charpoint >= 4)
536 edit->charpoint = 0;
537 edit->charbuf[edit->charpoint] = '\0';
539 if (edit->charpoint < 4)
541 edit->charbuf[edit->charpoint++] = x_key;
542 edit->charbuf[edit->charpoint] = '\0';
545 /* input from 8-bit locale */
546 if (!mc_global.utf8_display)
548 /* source in 8-bit codeset */
549 c = convert_from_input_c (x_key);
551 if (is_printable (c))
553 if (!edit->utf8)
554 char_for_insertion = c;
555 else
556 char_for_insertion = convert_from_8bit_to_utf_c2 ((char) x_key);
557 goto fin;
560 else
562 /* UTF-8 locale */
563 int res;
565 res = str_is_valid_char (edit->charbuf, edit->charpoint);
566 if (res < 0 && res != -2)
568 edit->charpoint = 0; /* broken multibyte char, skip */
569 goto fin;
572 if (edit->utf8)
574 /* source in UTF-8 codeset */
575 if (res < 0)
577 char_for_insertion = x_key;
578 goto fin;
581 edit->charbuf[edit->charpoint] = '\0';
582 edit->charpoint = 0;
583 if (g_unichar_isprint (g_utf8_get_char (edit->charbuf)))
585 char_for_insertion = x_key;
586 goto fin;
589 else
591 /* 8-bit source */
592 if (res < 0)
594 /* not finised multibyte input (in meddle multibyte utf-8 char) */
595 goto fin;
598 if (g_unichar_isprint (g_utf8_get_char (edit->charbuf)))
600 c = convert_from_utf_to_current (edit->charbuf);
601 edit->charbuf[0] = '\0';
602 edit->charpoint = 0;
603 char_for_insertion = c;
604 goto fin;
607 /* unprinteble utf input, skip it */
608 edit->charbuf[0] = '\0';
609 edit->charpoint = 0;
612 #endif /* HAVE_CHARSET */
615 /* Commands specific to the key emulation */
616 if (edit->extmod)
618 edit->extmod = FALSE;
619 command = keybind_lookup_keymap_command (editor_x_map, x_key);
621 else
622 command = keybind_lookup_keymap_command (editor_map, x_key);
624 if (command == CK_IgnoreKey)
625 command = CK_InsertChar;
627 fin:
628 *cmd = (int) command; /* FIXME */
629 *ch = char_for_insertion;
631 return !(command == CK_InsertChar && char_for_insertion == -1);
635 /* --------------------------------------------------------------------------------------------- */
637 static inline void
638 edit_quit (WDialog * h)
640 GList *l;
641 WEdit *e = NULL;
643 h->state = DLG_ACTIVE; /* don't stop the dialog before final decision */
645 for (l = h->widgets; l != NULL; l = g_list_next (l))
646 if (edit_widget_is_editor (WIDGET (l->data)))
648 e = (WEdit *) l->data;
650 if (e->drag_state != MCEDIT_DRAG_NONE)
652 edit_restore_size (e);
653 return;
656 if (e->modified)
658 dlg_select_widget (e);
660 if (!edit_ok_to_exit (e))
661 return;
665 /* no editors in dialog at all or no any file required to be saved */
666 if (e == NULL || l == NULL)
667 h->state = DLG_CLOSED;
670 /* --------------------------------------------------------------------------------------------- */
672 static inline void
673 edit_set_buttonbar (WEdit * edit, WButtonBar * bb)
675 buttonbar_set_label (bb, 1, Q_ ("ButtonBar|Help"), editor_map, NULL);
676 buttonbar_set_label (bb, 2, Q_ ("ButtonBar|Save"), editor_map, WIDGET (edit));
677 buttonbar_set_label (bb, 3, Q_ ("ButtonBar|Mark"), editor_map, WIDGET (edit));
678 buttonbar_set_label (bb, 4, Q_ ("ButtonBar|Replac"), editor_map, WIDGET (edit));
679 buttonbar_set_label (bb, 5, Q_ ("ButtonBar|Copy"), editor_map, WIDGET (edit));
680 buttonbar_set_label (bb, 6, Q_ ("ButtonBar|Move"), editor_map, WIDGET (edit));
681 buttonbar_set_label (bb, 7, Q_ ("ButtonBar|Search"), editor_map, WIDGET (edit));
682 buttonbar_set_label (bb, 8, Q_ ("ButtonBar|Delete"), editor_map, WIDGET (edit));
683 buttonbar_set_label (bb, 9, Q_ ("ButtonBar|PullDn"), editor_map, NULL);
684 buttonbar_set_label (bb, 10, Q_ ("ButtonBar|Quit"), editor_map, NULL);
687 /* --------------------------------------------------------------------------------------------- */
689 static void
690 edit_total_update (WEdit * edit)
692 edit_find_bracket (edit);
693 edit->force |= REDRAW_COMPLETELY;
694 edit_update_curs_row (edit);
695 edit_update_screen (edit);
698 /* --------------------------------------------------------------------------------------------- */
700 static gboolean
701 edit_update_cursor (WEdit * edit, const mouse_event_t * event)
703 int x, y;
704 gboolean done;
706 x = event->x - (edit->fullscreen ? 0 : 1);
707 y = event->y - (edit->fullscreen ? 0 : 1);
709 if (edit->mark2 != -1 && event->msg == MSG_MOUSE_UP)
710 return TRUE; /* don't do anything */
712 if (event->msg == MSG_MOUSE_DOWN || event->msg == MSG_MOUSE_UP)
713 edit_push_key_press (edit);
715 if (!option_cursor_beyond_eol)
716 edit->prev_col = x - edit->start_col - option_line_state_width;
717 else
719 long line_len;
721 line_len =
722 edit_move_forward3 (edit, edit_buffer_get_current_bol (&edit->buffer), 0,
723 edit_buffer_get_current_eol (&edit->buffer));
725 if (x > line_len - 1)
727 edit->over_col = x - line_len - edit->start_col - option_line_state_width;
728 edit->prev_col = line_len;
730 else
732 edit->over_col = 0;
733 edit->prev_col = x - option_line_state_width - edit->start_col;
737 if (y > edit->curs_row)
738 edit_move_down (edit, y - edit->curs_row, 0);
739 else if (y < edit->curs_row)
740 edit_move_up (edit, edit->curs_row - y, 0);
741 else
742 edit_move_to_prev_col (edit, edit_buffer_get_current_bol (&edit->buffer));
744 if (event->msg == MSG_MOUSE_CLICK)
746 edit_mark_cmd (edit, TRUE); /* reset */
747 edit->highlight = 0;
750 done = (event->msg != MSG_MOUSE_DRAG);
751 if (done)
752 edit_mark_cmd (edit, FALSE);
754 return done;
757 /* --------------------------------------------------------------------------------------------- */
758 /** Callback for the edit dialog */
760 static cb_ret_t
761 edit_dialog_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
763 WMenuBar *menubar;
764 WButtonBar *buttonbar;
765 WDialog *h = DIALOG (w);
767 switch (msg)
769 case MSG_INIT:
770 edit_dlg_init ();
771 return MSG_HANDLED;
773 case MSG_DRAW:
774 /* don't use dlg_default_repaint() -- we don't need a frame */
775 tty_setcolor (EDITOR_BACKGROUND);
776 dlg_erase (h);
777 return MSG_HANDLED;
779 case MSG_RESIZE:
780 menubar = find_menubar (h);
781 buttonbar = find_buttonbar (h);
782 /* dlg_set_size() is surplus for this case */
783 w->lines = LINES;
784 w->cols = COLS;
785 widget_set_size (WIDGET (buttonbar), w->lines - 1, w->x, 1, w->cols);
786 widget_set_size (WIDGET (menubar), w->y, w->x, 1, w->cols);
787 menubar_arrange (menubar);
788 g_list_foreach (h->widgets, (GFunc) edit_dialog_resize_cb, NULL);
789 return MSG_HANDLED;
791 case MSG_ACTION:
793 /* Handle shortcuts, menu, and buttonbar. */
795 cb_ret_t result;
797 result = edit_dialog_command_execute (h, parm);
799 /* We forward any commands coming from the menu, and which haven't been
800 handled by the dialog, to the focused WEdit window. */
801 if (result == MSG_NOT_HANDLED && sender == WIDGET (find_menubar (h)))
802 result = send_message (h->current->data, NULL, MSG_ACTION, parm, NULL);
804 return result;
807 case MSG_KEY:
809 Widget *we = WIDGET (h->current->data);
810 cb_ret_t ret = MSG_NOT_HANDLED;
812 if (edit_widget_is_editor (we))
814 WEdit *e = (WEdit *) we;
815 long command;
817 if (!e->extmod)
818 command = keybind_lookup_keymap_command (editor_map, parm);
819 else
821 e->extmod = FALSE;
822 command = keybind_lookup_keymap_command (editor_x_map, parm);
825 if (command != CK_IgnoreKey)
826 ret = edit_dialog_command_execute (h, command);
830 * Due to the "end of bracket" escape the editor sees input with is_idle() == false
831 * (expects more characters) and hence doesn't yet refresh the screen, but then
832 * no further characters arrive (there's only an "end of bracket" which is swallowed
833 * by tty_get_event()), so you end up with a screen that's not refreshed after pasting.
834 * So let's trigger an IDLE signal.
836 if (!is_idle ())
837 widget_want_idle (w, TRUE);
838 return ret;
841 /* hardcoded menu hotkeys (see edit_drop_hotkey_menu) */
842 case MSG_UNHANDLED_KEY:
843 return edit_drop_hotkey_menu (h, parm) ? MSG_HANDLED : MSG_NOT_HANDLED;
845 case MSG_VALIDATE:
846 edit_quit (h);
847 return MSG_HANDLED;
849 case MSG_END:
850 edit_dlg_deinit ();
851 return MSG_HANDLED;
853 case MSG_IDLE:
854 widget_want_idle (w, FALSE);
855 return send_message (h->current->data, NULL, MSG_IDLE, 0, NULL);
857 default:
858 return dlg_default_callback (w, sender, msg, parm, data);
862 /* --------------------------------------------------------------------------------------------- */
865 * Handle mouse events of editor screen.
867 * @param w Widget object (the editor)
868 * @param msg mouse event message
869 * @param event mouse event data
871 static void
872 edit_dialog_mouse_callback (Widget * w, mouse_msg_t msg, mouse_event_t * event)
874 gboolean unhandled = TRUE;
876 if (msg == MSG_MOUSE_DOWN && event->y == 0)
878 WDialog *h = DIALOG (w);
879 WMenuBar *b;
881 b = find_menubar (h);
883 if (!b->is_active)
885 /* menubar */
887 GList *l;
888 GList *top = NULL;
889 int x;
891 /* Try find top fullscreen window */
892 for (l = h->widgets; l != NULL; l = g_list_next (l))
893 if (edit_widget_is_editor (WIDGET (l->data)) && ((WEdit *) l->data)->fullscreen)
894 top = l;
896 /* Handle fullscreen/close buttons in the top line */
897 x = w->cols - 5;
899 if (top != NULL && event->x >= x)
901 WEdit *e = (WEdit *) top->data;
903 if (top != h->current)
905 /* Window is not active. Activate it */
906 dlg_set_top_widget (e);
909 /* Handle buttons */
910 if (event->x - x <= 2)
911 edit_toggle_fullscreen (e);
912 else
913 send_message (h, NULL, MSG_ACTION, CK_Close, NULL);
915 unhandled = FALSE;
918 if (unhandled)
919 menubar_activate (b, drop_menus != 0, -1);
923 /* Continue handling of unhandled event in window or menu */
924 event->result.abort = unhandled;
927 /* --------------------------------------------------------------------------------------------- */
929 static cb_ret_t
930 edit_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
932 WEdit *e = (WEdit *) w;
934 switch (msg)
936 case MSG_FOCUS:
937 edit_set_buttonbar (e, find_buttonbar (w->owner));
938 /* fall through */
940 case MSG_DRAW:
941 e->force |= REDRAW_COMPLETELY;
942 edit_update_screen (e);
943 return MSG_HANDLED;
945 case MSG_UNFOCUS:
946 /* redraw frame and status */
947 edit_status (e, FALSE);
948 return MSG_HANDLED;
950 case MSG_KEY:
952 int cmd, ch;
953 cb_ret_t ret = MSG_NOT_HANDLED;
955 /* The user may override the access-keys for the menu bar. */
956 if (macro_index == -1 && edit_execute_macro (e, parm))
958 edit_update_screen (e);
959 ret = MSG_HANDLED;
961 else if (edit_translate_key (e, parm, &cmd, &ch))
963 edit_execute_key_command (e, cmd, ch);
964 edit_update_screen (e);
965 ret = MSG_HANDLED;
968 return ret;
971 case MSG_ACTION:
972 /* command from menubar or buttonbar */
973 edit_execute_key_command (e, parm, -1);
974 edit_update_screen (e);
975 return MSG_HANDLED;
977 case MSG_CURSOR:
979 int y, x;
981 y = (e->fullscreen ? 0 : 1) + EDIT_TEXT_VERTICAL_OFFSET + e->curs_row;
982 x = (e->fullscreen ? 0 : 1) + EDIT_TEXT_HORIZONTAL_OFFSET + option_line_state_width +
983 e->curs_col + e->start_col + e->over_col;
985 widget_move (w, y, x);
986 return MSG_HANDLED;
989 case MSG_IDLE:
990 edit_update_screen (e);
991 return MSG_HANDLED;
993 case MSG_DESTROY:
994 edit_clean (e);
995 return MSG_HANDLED;
997 default:
998 return widget_default_callback (w, sender, msg, parm, data);
1002 /* --------------------------------------------------------------------------------------------- */
1005 * Handle move/resize mouse events.
1007 static void
1008 edit_mouse_handle_move_resize (Widget * w, mouse_msg_t msg, mouse_event_t * event)
1010 WEdit *edit = (WEdit *) (w);
1011 Widget *h = WIDGET (w->owner);
1012 int global_x, global_y;
1014 if (msg == MSG_MOUSE_UP)
1016 /* Exit move/resize mode. */
1017 edit_execute_cmd (edit, CK_Enter, -1);
1018 return;
1021 if (msg != MSG_MOUSE_DRAG)
1023 * We ignore any other events. Specifically, MSG_MOUSE_DOWN.
1025 * When the move/resize is initiated by the menu, we let the user
1026 * stop it by clicking with the mouse. Which is why we don't want
1027 * a mouse down to affect the window.
1029 return;
1031 /* Convert point to global coordinates for easier calculations. */
1032 global_x = event->x + w->x;
1033 global_y = event->y + w->y;
1035 /* Clamp the point to the dialog's client area. */
1036 global_y = CLAMP (global_y, h->y + 1, h->y + h->lines - 2); /* Status line, buttonbar */
1037 global_x = CLAMP (global_x, h->x, h->x + h->cols - 1); /* Currently a no-op, as the dialog has no left/right margins. */
1039 if (edit->drag_state == MCEDIT_DRAG_MOVE)
1041 w->y = global_y;
1042 w->x = global_x - edit->drag_state_start;
1044 else if (edit->drag_state == MCEDIT_DRAG_RESIZE)
1046 w->lines = max (WINDOW_MIN_LINES, global_y - w->y + 1);
1047 w->cols = max (WINDOW_MIN_COLS, global_x - w->x + 1);
1050 edit->force |= REDRAW_COMPLETELY; /* Not really needed as WEdit's MSG_DRAW already does this. */
1052 /* We draw the whole dialog because dragging/resizing exposes area beneath. */
1053 dlg_redraw (w->owner);
1056 /* --------------------------------------------------------------------------------------------- */
1059 * Handle mouse events of editor window
1061 * @param w Widget object (the editor window)
1062 * @param msg mouse event message
1063 * @param event mouse event data
1065 static void
1066 edit_mouse_callback (Widget * w, mouse_msg_t msg, mouse_event_t * event)
1068 WEdit *edit = (WEdit *) w;
1069 /* offset for top line */
1070 int dx = edit->fullscreen ? 0 : 2;
1071 /* location of 'Close' and 'Toggle fullscreen' pictograms */
1072 int close_x, toggle_fullscreen_x;
1074 close_x = (w->cols - 1) - dx - 1;
1075 toggle_fullscreen_x = close_x - 3;
1077 if (edit->drag_state != MCEDIT_DRAG_NONE)
1079 /* window is being resized/moved */
1080 edit_mouse_handle_move_resize (w, msg, event);
1081 return;
1084 switch (msg)
1086 case MSG_MOUSE_DOWN:
1087 dlg_set_top_widget (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, 1);
1155 edit_total_update (edit);
1156 break;
1158 case MSG_MOUSE_SCROLL_DOWN:
1159 edit_move_down (edit, 2, 1);
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, 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, 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, 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, LINES, COLS, NULL, edit_dialog_callback,
1224 edit_dialog_mouse_callback, "[Internal File Editor]", NULL, DLG_WANT_TAB);
1226 edit_dlg->get_shortcut = edit_get_shortcut;
1227 edit_dlg->get_title = edit_get_title;
1229 menubar = menubar_new (0, 0, COLS, NULL, TRUE);
1230 add_widget (edit_dlg, menubar);
1231 edit_init_menu (menubar);
1233 add_widget (edit_dlg, buttonbar_new (TRUE));
1235 for (file = files; file != NULL; file = g_list_next (file))
1237 Widget *w = WIDGET (edit_dlg);
1238 mcedit_arg_t *f = (mcedit_arg_t *) file->data;
1239 gboolean f_ok;
1241 f_ok = edit_add_window (edit_dlg, w->y + 1, w->x, w->lines - 2, w->cols, f->file_vpath,
1242 f->line_number);
1243 /* at least one file has been opened succefully */
1244 ok = ok || f_ok;
1247 if (ok)
1248 dlg_run (edit_dlg);
1250 if (!ok || edit_dlg->state == DLG_CLOSED)
1251 dlg_destroy (edit_dlg);
1253 return ok;
1256 /* --------------------------------------------------------------------------------------------- */
1258 const char *
1259 edit_get_file_name (const WEdit * edit)
1261 return vfs_path_as_str (edit->filename_vpath);
1264 /* --------------------------------------------------------------------------------------------- */
1266 WEdit *
1267 find_editor (const WDialog * h)
1269 if (edit_widget_is_editor (WIDGET (h->current->data)))
1270 return (WEdit *) h->current->data;
1271 return (WEdit *) find_widget_type (h, edit_callback);
1274 /* --------------------------------------------------------------------------------------------- */
1276 * Check if widget is an WEdit class.
1278 * @param w probably editor object
1279 * @return TRUE if widget is an WEdit class, FALSE otherwise
1282 gboolean
1283 edit_widget_is_editor (const Widget * w)
1285 return (w != NULL && w->callback == edit_callback);
1288 /* --------------------------------------------------------------------------------------------- */
1290 void
1291 edit_update_screen (WEdit * e)
1293 WDialog *h = WIDGET (e)->owner;
1295 edit_scroll_screen_over_cursor (e);
1296 edit_update_curs_col (e);
1297 edit_status (e, (void *) e == h->current->data);
1299 /* pop all events for this window for internal handling */
1300 if (!is_idle ())
1301 e->force |= REDRAW_PAGE;
1302 else
1304 if ((e->force & REDRAW_COMPLETELY) != 0)
1305 e->force |= REDRAW_PAGE;
1306 edit_render_keypress (e);
1309 widget_redraw (WIDGET (find_buttonbar (h)));
1312 /* --------------------------------------------------------------------------------------------- */
1314 * Save current window size.
1316 * @param edit editor object
1319 void
1320 edit_save_size (WEdit * edit)
1322 Widget *w = WIDGET (edit);
1324 edit->y_prev = w->y;
1325 edit->x_prev = w->x;
1326 edit->lines_prev = w->lines;
1327 edit->cols_prev = w->cols;
1330 /* --------------------------------------------------------------------------------------------- */
1332 * Create new editor window and insert it into editor screen.
1334 * @param h editor dialog (screen)
1335 * @param y y coordinate
1336 * @param x x coordinate
1337 * @param lines window height
1338 * @param cols window width
1339 * @param f file object
1340 * @param fline line number in file
1341 * @return TRUE if new window was successfully created and inserted into editor screen,
1342 * FALSE otherwise
1345 gboolean
1346 edit_add_window (WDialog * h, int y, int x, int lines, int cols, const vfs_path_t * f, long fline)
1348 WEdit *edit;
1349 Widget *w;
1351 edit = edit_init (NULL, y, x, lines, cols, f, fline);
1352 if (edit == NULL)
1353 return FALSE;
1355 w = WIDGET (edit);
1356 w->callback = edit_callback;
1357 w->mouse_callback = edit_mouse_callback;
1359 add_widget (h, w);
1360 dlg_redraw (h);
1362 return TRUE;
1365 /* --------------------------------------------------------------------------------------------- */
1367 * Handle move/resize events.
1369 * @param edit editor object
1370 * @param command action id
1371 * @return TRUE if the action was handled, FALSE otherwise
1374 gboolean
1375 edit_handle_move_resize (WEdit * edit, long command)
1377 Widget *w = WIDGET (edit);
1378 gboolean ret = FALSE;
1380 if (edit->fullscreen)
1382 edit->drag_state = MCEDIT_DRAG_NONE;
1383 w->mouse.forced_capture = FALSE;
1384 return ret;
1387 switch (edit->drag_state)
1389 case MCEDIT_DRAG_NONE:
1390 /* possible start move/resize */
1391 switch (command)
1393 case CK_WindowMove:
1394 edit->drag_state = MCEDIT_DRAG_MOVE;
1395 edit_save_size (edit);
1396 edit_status (edit, TRUE); /* redraw frame and status */
1398 * If a user initiates a move by the menu, not by the mouse, we
1399 * make a subsequent mouse drag pull the frame from its middle.
1400 * (We can instead choose '0' to pull it from the corner.)
1402 edit->drag_state_start = w->cols / 2;
1403 ret = TRUE;
1404 break;
1405 case CK_WindowResize:
1406 edit->drag_state = MCEDIT_DRAG_RESIZE;
1407 edit_save_size (edit);
1408 edit_status (edit, TRUE); /* redraw frame and status */
1409 ret = TRUE;
1410 break;
1411 default:
1412 break;
1414 break;
1416 case MCEDIT_DRAG_MOVE:
1417 switch (command)
1419 case CK_WindowResize:
1420 edit->drag_state = MCEDIT_DRAG_RESIZE;
1421 ret = TRUE;
1422 break;
1423 case CK_Up:
1424 case CK_Down:
1425 case CK_Left:
1426 case CK_Right:
1427 edit_window_move (edit, command);
1428 ret = TRUE;
1429 break;
1430 case CK_Enter:
1431 case CK_WindowMove:
1432 edit->drag_state = MCEDIT_DRAG_NONE;
1433 edit_status (edit, TRUE); /* redraw frame and status */
1434 default:
1435 ret = TRUE;
1436 break;
1438 break;
1440 case MCEDIT_DRAG_RESIZE:
1441 switch (command)
1443 case CK_WindowMove:
1444 edit->drag_state = MCEDIT_DRAG_MOVE;
1445 ret = TRUE;
1446 break;
1447 case CK_Up:
1448 case CK_Down:
1449 case CK_Left:
1450 case CK_Right:
1451 edit_window_resize (edit, command);
1452 ret = TRUE;
1453 break;
1454 case CK_Enter:
1455 case CK_WindowResize:
1456 edit->drag_state = MCEDIT_DRAG_NONE;
1457 edit_status (edit, TRUE); /* redraw frame and status */
1458 default:
1459 ret = TRUE;
1460 break;
1462 break;
1464 default:
1465 break;
1469 * - We let the user stop a resize/move operation by clicking with the
1470 * mouse anywhere. ("clicking" = pressing and releasing a button.)
1471 * - We let the user perform a resize/move operation by a mouse drag
1472 * initiated anywhere.
1474 * "Anywhere" means: inside or outside the window. We make this happen
1475 * with the 'forced_capture' flag.
1477 w->mouse.forced_capture = (edit->drag_state != MCEDIT_DRAG_NONE);
1479 return ret;
1482 /* --------------------------------------------------------------------------------------------- */
1484 * Toggle window fuulscreen mode.
1486 * @param edit editor object
1489 void
1490 edit_toggle_fullscreen (WEdit * edit)
1492 edit->fullscreen = !edit->fullscreen;
1493 edit->force = REDRAW_COMPLETELY;
1495 if (!edit->fullscreen)
1496 edit_restore_size (edit);
1497 else
1499 Widget *w = WIDGET (edit);
1500 Widget *h = WIDGET (w->owner);
1502 edit_save_size (edit);
1503 widget_set_size (w, h->y + 1, h->x, h->lines - 2, h->cols);
1504 edit->force |= REDRAW_PAGE;
1505 edit_update_screen (edit);
1509 /* --------------------------------------------------------------------------------------------- */