Move WOP_WANT_IDLE option to widget_state_t flags
[midnight-commander.git] / src / editor / editwidget.c
blob9dd3683a9755bb71d5099e9f7ac7276288c743dc
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/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 ("editor", "window-state-char", "*");
101 edit_window_close_char = mc_skin_get ("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 offset = 2; /* skip menu and buttonbar */
323 const size_t dlg_num = g_list_length (h->widgets) - offset;
324 int lines, cols;
325 Listbox *listbox;
326 GList *w;
327 int i = 0;
328 int rv;
330 lines = MIN ((size_t) (LINES * 2 / 3), dlg_num);
331 cols = COLS * 2 / 3;
333 listbox = create_listbox_window (lines, cols, _("Open files"), "[Open files]");
335 for (w = h->widgets; w != NULL; w = g_list_next (w))
336 if (edit_widget_is_editor (CONST_WIDGET (w->data)))
338 WEdit *e = (WEdit *) w->data;
339 char *fname;
341 if (e->filename_vpath == NULL)
342 fname = g_strdup_printf ("%c [%s]", e->modified ? '*' : ' ', _("NoName"));
343 else
344 fname =
345 g_strdup_printf ("%c%s", e->modified ? '*' : ' ',
346 vfs_path_as_str (e->filename_vpath));
348 listbox_add_item (listbox->list, LISTBOX_APPEND_AT_END, get_hotkey (i++),
349 str_term_trim (fname, WIDGET (listbox->list)->cols - 2), NULL, FALSE);
350 g_free (fname);
353 rv = g_list_position (h->widgets, h->current) - offset;
354 listbox_select_entry (listbox->list, rv);
355 rv = run_listbox (listbox);
356 if (rv >= 0)
358 w = g_list_nth (h->widgets, rv + offset);
359 dlg_set_top_widget (w->data);
363 /* --------------------------------------------------------------------------------------------- */
365 static char *
366 edit_get_shortcut (long command)
368 const char *ext_map;
369 const char *shortcut = NULL;
371 shortcut = keybind_lookup_keymap_shortcut (editor_map, command);
372 if (shortcut != NULL)
373 return g_strdup (shortcut);
375 ext_map = keybind_lookup_keymap_shortcut (editor_map, CK_ExtendedKeyMap);
376 if (ext_map != NULL)
377 shortcut = keybind_lookup_keymap_shortcut (editor_x_map, command);
378 if (shortcut != NULL)
379 return g_strdup_printf ("%s %s", ext_map, shortcut);
381 return NULL;
384 /* --------------------------------------------------------------------------------------------- */
386 static char *
387 edit_get_title (const WDialog * h, size_t len)
389 const WEdit *edit = find_editor (h);
390 const char *modified = edit->modified ? "(*) " : " ";
391 const char *file_label;
392 char *filename;
394 len -= 4;
396 if (edit->filename_vpath == NULL)
397 filename = g_strdup (_("[NoName]"));
398 else
399 filename = g_strdup (vfs_path_as_str (edit->filename_vpath));
401 file_label = str_term_trim (filename, len - str_term_width1 (_("Edit: ")));
402 g_free (filename);
404 return g_strconcat (_("Edit: "), modified, file_label, (char *) NULL);
407 /* --------------------------------------------------------------------------------------------- */
409 static cb_ret_t
410 edit_dialog_command_execute (WDialog * h, long command)
412 Widget *wh = WIDGET (h);
413 cb_ret_t ret = MSG_HANDLED;
415 switch (command)
417 case CK_EditNew:
418 edit_add_window (h, wh->y + 1, wh->x, wh->lines - 2, wh->cols, NULL, 0);
419 break;
420 case CK_EditFile:
421 edit_load_cmd (h);
422 break;
423 case CK_EditSyntaxFile:
424 edit_load_syntax_file (h);
425 break;
426 case CK_EditUserMenu:
427 edit_load_menu_file (h);
428 break;
429 case CK_Close:
430 /* if there are no opened files anymore, close MC editor */
431 if (edit_widget_is_editor (CONST_WIDGET (h->current->data)) &&
432 edit_close_cmd ((WEdit *) h->current->data) && find_editor (h) == NULL)
433 dlg_stop (h);
434 break;
435 case CK_Help:
436 edit_help ();
437 /* edit->force |= REDRAW_COMPLETELY; */
438 break;
439 case CK_Menu:
440 edit_menu_cmd (h);
441 break;
442 case CK_Quit:
443 case CK_Cancel:
444 /* don't close editor due to SIGINT, but stop move/resize window */
446 Widget *w = WIDGET (h->current->data);
448 if (edit_widget_is_editor (w) && ((WEdit *) w)->drag_state != MCEDIT_DRAG_NONE)
449 edit_restore_size ((WEdit *) w);
450 else if (command == CK_Quit)
451 dlg_stop (h);
453 break;
454 case CK_About:
455 edit_about ();
456 break;
457 case CK_SyntaxOnOff:
458 edit_syntax_onoff_cmd (h);
459 break;
460 case CK_ShowTabTws:
461 edit_show_tabs_tws_cmd (h);
462 break;
463 case CK_ShowMargin:
464 edit_show_margin_cmd (h);
465 break;
466 case CK_ShowNumbers:
467 edit_show_numbers_cmd (h);
468 break;
469 case CK_Refresh:
470 edit_refresh_cmd ();
471 break;
472 case CK_Shell:
473 view_other_cmd ();
474 break;
475 case CK_LearnKeys:
476 learn_keys ();
477 break;
478 case CK_WindowMove:
479 case CK_WindowResize:
480 if (edit_widget_is_editor (CONST_WIDGET (h->current->data)))
481 edit_handle_move_resize ((WEdit *) h->current->data, command);
482 break;
483 case CK_WindowList:
484 edit_window_list (h);
485 break;
486 case CK_WindowNext:
487 dlg_one_down (h);
488 dlg_set_top_widget (h->current->data);
489 break;
490 case CK_WindowPrev:
491 dlg_one_up (h);
492 dlg_set_top_widget (h->current->data);
493 break;
494 case CK_Options:
495 edit_options_dialog (h);
496 break;
497 case CK_OptionsSaveMode:
498 edit_save_mode_cmd ();
499 break;
500 case CK_SaveSetup:
501 save_setup_cmd ();
502 break;
503 default:
504 ret = MSG_NOT_HANDLED;
505 break;
508 return ret;
511 /* --------------------------------------------------------------------------------------------- */
513 * Translate the keycode into either 'command' or 'char_for_insertion'.
514 * 'command' is one of the editor commands from cmddef.h.
517 static gboolean
518 edit_translate_key (WEdit * edit, long x_key, int *cmd, int *ch)
520 long command = CK_InsertChar;
521 int char_for_insertion = -1;
523 /* an ordinary insertable character */
524 if (!edit->extmod && x_key < 256)
526 #ifndef HAVE_CHARSET
527 if (is_printable (x_key))
529 char_for_insertion = x_key;
530 goto fin;
532 #else
533 int c;
535 if (edit->charpoint >= 4)
537 edit->charpoint = 0;
538 edit->charbuf[edit->charpoint] = '\0';
540 if (edit->charpoint < 4)
542 edit->charbuf[edit->charpoint++] = x_key;
543 edit->charbuf[edit->charpoint] = '\0';
546 /* input from 8-bit locale */
547 if (!mc_global.utf8_display)
549 /* source in 8-bit codeset */
550 c = convert_from_input_c (x_key);
552 if (is_printable (c))
554 if (!edit->utf8)
555 char_for_insertion = c;
556 else
557 char_for_insertion = convert_from_8bit_to_utf_c2 ((char) x_key);
558 goto fin;
561 else
563 /* UTF-8 locale */
564 int res;
566 res = str_is_valid_char (edit->charbuf, edit->charpoint);
567 if (res < 0 && res != -2)
569 edit->charpoint = 0; /* broken multibyte char, skip */
570 goto fin;
573 if (edit->utf8)
575 /* source in UTF-8 codeset */
576 if (res < 0)
578 char_for_insertion = x_key;
579 goto fin;
582 edit->charbuf[edit->charpoint] = '\0';
583 edit->charpoint = 0;
584 if (g_unichar_isprint (g_utf8_get_char (edit->charbuf)))
586 char_for_insertion = x_key;
587 goto fin;
590 else
592 /* 8-bit source */
593 if (res < 0)
595 /* not finised multibyte input (in meddle multibyte utf-8 char) */
596 goto fin;
599 if (g_unichar_isprint (g_utf8_get_char (edit->charbuf)))
601 c = convert_from_utf_to_current (edit->charbuf);
602 edit->charbuf[0] = '\0';
603 edit->charpoint = 0;
604 char_for_insertion = c;
605 goto fin;
608 /* unprinteble utf input, skip it */
609 edit->charbuf[0] = '\0';
610 edit->charpoint = 0;
613 #endif /* HAVE_CHARSET */
616 /* Commands specific to the key emulation */
617 if (edit->extmod)
619 edit->extmod = FALSE;
620 command = keybind_lookup_keymap_command (editor_x_map, x_key);
622 else
623 command = keybind_lookup_keymap_command (editor_map, x_key);
625 if (command == CK_IgnoreKey)
626 command = CK_InsertChar;
628 fin:
629 *cmd = (int) command; /* FIXME */
630 *ch = char_for_insertion;
632 return !(command == CK_InsertChar && char_for_insertion == -1);
636 /* --------------------------------------------------------------------------------------------- */
638 static inline void
639 edit_quit (WDialog * h)
641 GList *l;
642 WEdit *e = NULL;
644 h->state = DLG_ACTIVE; /* don't stop the dialog before final decision */
646 for (l = h->widgets; l != NULL; l = g_list_next (l))
647 if (edit_widget_is_editor (CONST_WIDGET (l->data)))
649 e = (WEdit *) l->data;
651 if (e->drag_state != MCEDIT_DRAG_NONE)
653 edit_restore_size (e);
654 return;
657 if (e->modified)
659 dlg_select_widget (e);
661 if (!edit_ok_to_exit (e))
662 return;
666 /* no editors in dialog at all or no any file required to be saved */
667 if (e == NULL || l == NULL)
668 h->state = DLG_CLOSED;
671 /* --------------------------------------------------------------------------------------------- */
673 static inline void
674 edit_set_buttonbar (WEdit * edit, WButtonBar * bb)
676 buttonbar_set_label (bb, 1, Q_ ("ButtonBar|Help"), editor_map, NULL);
677 buttonbar_set_label (bb, 2, Q_ ("ButtonBar|Save"), editor_map, WIDGET (edit));
678 buttonbar_set_label (bb, 3, Q_ ("ButtonBar|Mark"), editor_map, WIDGET (edit));
679 buttonbar_set_label (bb, 4, Q_ ("ButtonBar|Replac"), editor_map, WIDGET (edit));
680 buttonbar_set_label (bb, 5, Q_ ("ButtonBar|Copy"), editor_map, WIDGET (edit));
681 buttonbar_set_label (bb, 6, Q_ ("ButtonBar|Move"), editor_map, WIDGET (edit));
682 buttonbar_set_label (bb, 7, Q_ ("ButtonBar|Search"), editor_map, WIDGET (edit));
683 buttonbar_set_label (bb, 8, Q_ ("ButtonBar|Delete"), editor_map, WIDGET (edit));
684 buttonbar_set_label (bb, 9, Q_ ("ButtonBar|PullDn"), editor_map, NULL);
685 buttonbar_set_label (bb, 10, Q_ ("ButtonBar|Quit"), editor_map, NULL);
688 /* --------------------------------------------------------------------------------------------- */
690 static void
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 /* --------------------------------------------------------------------------------------------- */
701 static gboolean
702 edit_update_cursor (WEdit * edit, const mouse_event_t * event)
704 int x, y;
705 gboolean done;
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 (!option_cursor_beyond_eol)
717 edit->prev_col = x - edit->start_col - option_line_state_width;
718 else
720 long line_len;
722 line_len =
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 - option_line_state_width;
729 edit->prev_col = line_len;
731 else
733 edit->over_col = 0;
734 edit->prev_col = x - option_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);
742 else
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 */
748 edit->highlight = 0;
751 done = (event->msg != MSG_MOUSE_DRAG);
752 if (done)
753 edit_mark_cmd (edit, FALSE);
755 return done;
758 /* --------------------------------------------------------------------------------------------- */
759 /** Callback for the edit dialog */
761 static cb_ret_t
762 edit_dialog_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
764 WMenuBar *menubar;
765 WButtonBar *buttonbar;
766 WDialog *h = DIALOG (w);
768 switch (msg)
770 case MSG_INIT:
771 edit_dlg_init ();
772 return MSG_HANDLED;
774 case MSG_DRAW:
775 /* don't use dlg_default_repaint() -- we don't need a frame */
776 tty_setcolor (EDITOR_BACKGROUND);
777 dlg_erase (h);
778 return MSG_HANDLED;
780 case MSG_RESIZE:
781 menubar = find_menubar (h);
782 buttonbar = find_buttonbar (h);
783 /* dlg_set_size() is surplus for this case */
784 w->lines = LINES;
785 w->cols = COLS;
786 widget_set_size (WIDGET (buttonbar), w->lines - 1, w->x, 1, w->cols);
787 widget_set_size (WIDGET (menubar), w->y, w->x, 1, w->cols);
788 menubar_arrange (menubar);
789 g_list_foreach (h->widgets, (GFunc) edit_dialog_resize_cb, NULL);
790 return MSG_HANDLED;
792 case MSG_ACTION:
794 /* Handle shortcuts, menu, and buttonbar. */
796 cb_ret_t result;
798 result = edit_dialog_command_execute (h, parm);
800 /* We forward any commands coming from the menu, and which haven't been
801 handled by the dialog, to the focused WEdit window. */
802 if (result == MSG_NOT_HANDLED && sender == WIDGET (find_menubar (h)))
803 result = send_message (h->current->data, NULL, MSG_ACTION, parm, NULL);
805 return result;
808 case MSG_KEY:
810 Widget *we = WIDGET (h->current->data);
811 cb_ret_t ret = MSG_NOT_HANDLED;
813 if (edit_widget_is_editor (we))
815 WEdit *e = (WEdit *) we;
816 long command;
818 if (!e->extmod)
819 command = keybind_lookup_keymap_command (editor_map, parm);
820 else
822 e->extmod = FALSE;
823 command = keybind_lookup_keymap_command (editor_x_map, parm);
826 if (command != CK_IgnoreKey)
827 ret = edit_dialog_command_execute (h, command);
831 * Due to the "end of bracket" escape the editor sees input with is_idle() == false
832 * (expects more characters) and hence doesn't yet refresh the screen, but then
833 * no further characters arrive (there's only an "end of bracket" which is swallowed
834 * by tty_get_event()), so you end up with a screen that's not refreshed after pasting.
835 * So let's trigger an IDLE signal.
837 if (!is_idle ())
838 widget_idle (w, TRUE);
839 return ret;
842 /* hardcoded menu hotkeys (see edit_drop_hotkey_menu) */
843 case MSG_UNHANDLED_KEY:
844 return edit_drop_hotkey_menu (h, parm) ? MSG_HANDLED : MSG_NOT_HANDLED;
846 case MSG_VALIDATE:
847 edit_quit (h);
848 return MSG_HANDLED;
850 case MSG_END:
851 edit_dlg_deinit ();
852 return MSG_HANDLED;
854 case MSG_IDLE:
855 widget_idle (w, FALSE);
856 return send_message (h->current->data, NULL, MSG_IDLE, 0, NULL);
858 default:
859 return dlg_default_callback (w, sender, msg, parm, data);
863 /* --------------------------------------------------------------------------------------------- */
866 * Handle mouse events of editor screen.
868 * @param w Widget object (the editor)
869 * @param msg mouse event message
870 * @param event mouse event data
872 static void
873 edit_dialog_mouse_callback (Widget * w, mouse_msg_t msg, mouse_event_t * event)
875 gboolean unhandled = TRUE;
877 if (msg == MSG_MOUSE_DOWN && event->y == 0)
879 WDialog *h = DIALOG (w);
880 WMenuBar *b;
882 b = find_menubar (h);
884 if (!b->is_active)
886 /* menubar */
888 GList *l;
889 GList *top = NULL;
890 int x;
892 /* Try find top fullscreen window */
893 for (l = h->widgets; l != NULL; l = g_list_next (l))
894 if (edit_widget_is_editor (CONST_WIDGET (l->data))
895 && ((WEdit *) l->data)->fullscreen)
896 top = l;
898 /* Handle fullscreen/close buttons in the top line */
899 x = w->cols - 5;
901 if (top != NULL && event->x >= x)
903 WEdit *e = (WEdit *) top->data;
905 if (top != h->current)
907 /* Window is not active. Activate it */
908 dlg_set_top_widget (e);
911 /* Handle buttons */
912 if (event->x - x <= 2)
913 edit_toggle_fullscreen (e);
914 else
915 send_message (h, NULL, MSG_ACTION, CK_Close, NULL);
917 unhandled = FALSE;
920 if (unhandled)
921 menubar_activate (b, drop_menus != 0, -1);
925 /* Continue handling of unhandled event in window or menu */
926 event->result.abort = unhandled;
929 /* --------------------------------------------------------------------------------------------- */
931 static cb_ret_t
932 edit_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
934 WEdit *e = (WEdit *) w;
936 switch (msg)
938 case MSG_FOCUS:
939 edit_set_buttonbar (e, find_buttonbar (w->owner));
940 /* fall through */
942 case MSG_DRAW:
943 e->force |= REDRAW_COMPLETELY;
944 edit_update_screen (e);
945 return MSG_HANDLED;
947 case MSG_UNFOCUS:
948 /* redraw frame and status */
949 edit_status (e, FALSE);
950 return MSG_HANDLED;
952 case MSG_KEY:
954 int cmd, ch;
955 cb_ret_t ret = MSG_NOT_HANDLED;
957 /* The user may override the access-keys for the menu bar. */
958 if (macro_index == -1 && edit_execute_macro (e, parm))
960 edit_update_screen (e);
961 ret = MSG_HANDLED;
963 else if (edit_translate_key (e, parm, &cmd, &ch))
965 edit_execute_key_command (e, cmd, ch);
966 edit_update_screen (e);
967 ret = MSG_HANDLED;
970 return ret;
973 case MSG_ACTION:
974 /* command from menubar or buttonbar */
975 edit_execute_key_command (e, parm, -1);
976 edit_update_screen (e);
977 return MSG_HANDLED;
979 case MSG_CURSOR:
981 int y, x;
983 y = (e->fullscreen ? 0 : 1) + EDIT_TEXT_VERTICAL_OFFSET + e->curs_row;
984 x = (e->fullscreen ? 0 : 1) + EDIT_TEXT_HORIZONTAL_OFFSET + option_line_state_width +
985 e->curs_col + e->start_col + e->over_col;
987 widget_move (w, y, x);
988 return MSG_HANDLED;
991 case MSG_IDLE:
992 edit_update_screen (e);
993 return MSG_HANDLED;
995 case MSG_DESTROY:
996 edit_clean (e);
997 return MSG_HANDLED;
999 default:
1000 return widget_default_callback (w, sender, msg, parm, data);
1004 /* --------------------------------------------------------------------------------------------- */
1007 * Handle move/resize mouse events.
1009 static void
1010 edit_mouse_handle_move_resize (Widget * w, mouse_msg_t msg, mouse_event_t * event)
1012 WEdit *edit = (WEdit *) (w);
1013 Widget *h = WIDGET (w->owner);
1014 int global_x, global_y;
1016 if (msg == MSG_MOUSE_UP)
1018 /* Exit move/resize mode. */
1019 edit_execute_cmd (edit, CK_Enter, -1);
1020 return;
1023 if (msg != MSG_MOUSE_DRAG)
1025 * We ignore any other events. Specifically, MSG_MOUSE_DOWN.
1027 * When the move/resize is initiated by the menu, we let the user
1028 * stop it by clicking with the mouse. Which is why we don't want
1029 * a mouse down to affect the window.
1031 return;
1033 /* Convert point to global coordinates for easier calculations. */
1034 global_x = event->x + w->x;
1035 global_y = event->y + w->y;
1037 /* Clamp the point to the dialog's client area. */
1038 global_y = CLAMP (global_y, h->y + 1, h->y + h->lines - 2); /* Status line, buttonbar */
1039 global_x = CLAMP (global_x, h->x, h->x + h->cols - 1); /* Currently a no-op, as the dialog has no left/right margins. */
1041 if (edit->drag_state == MCEDIT_DRAG_MOVE)
1043 w->y = global_y;
1044 w->x = global_x - edit->drag_state_start;
1046 else if (edit->drag_state == MCEDIT_DRAG_RESIZE)
1048 w->lines = MAX (WINDOW_MIN_LINES, global_y - w->y + 1);
1049 w->cols = MAX (WINDOW_MIN_COLS, global_x - w->x + 1);
1052 edit->force |= REDRAW_COMPLETELY; /* Not really needed as WEdit's MSG_DRAW already does this. */
1054 /* We draw the whole dialog because dragging/resizing exposes area beneath. */
1055 dlg_redraw (w->owner);
1058 /* --------------------------------------------------------------------------------------------- */
1061 * Handle mouse events of editor window
1063 * @param w Widget object (the editor window)
1064 * @param msg mouse event message
1065 * @param event mouse event data
1067 static void
1068 edit_mouse_callback (Widget * w, mouse_msg_t msg, mouse_event_t * event)
1070 WEdit *edit = (WEdit *) w;
1071 /* offset for top line */
1072 int dx = edit->fullscreen ? 0 : 2;
1073 /* location of 'Close' and 'Toggle fullscreen' pictograms */
1074 int close_x, toggle_fullscreen_x;
1076 close_x = (w->cols - 1) - dx - 1;
1077 toggle_fullscreen_x = close_x - 3;
1079 if (edit->drag_state != MCEDIT_DRAG_NONE)
1081 /* window is being resized/moved */
1082 edit_mouse_handle_move_resize (w, msg, event);
1083 return;
1086 switch (msg)
1088 case MSG_MOUSE_DOWN:
1089 dlg_set_top_widget (w);
1090 edit_update_curs_row (edit);
1091 edit_update_curs_col (edit);
1093 if (!edit->fullscreen)
1095 if (event->y == 0)
1097 if (event->x == close_x)
1098 ; /* do nothing (see MSG_MOUSE_CLICK) */
1099 else if (event->x == toggle_fullscreen_x)
1100 ; /* do nothing (see MSG_MOUSE_CLICK) */
1101 else
1103 /* start window move */
1104 edit_execute_cmd (edit, CK_WindowMove, -1);
1105 edit->drag_state_start = event->x;
1107 break;
1110 if (event->y == w->lines - 1 && event->x == w->cols - 1)
1112 /* bottom-right corner -- start window resize */
1113 edit_execute_cmd (edit, CK_WindowResize, -1);
1114 break;
1118 /* fall through to start/stop text selection */
1120 case MSG_MOUSE_UP:
1121 edit_update_cursor (edit, event);
1122 edit_total_update (edit);
1123 break;
1125 case MSG_MOUSE_CLICK:
1126 if (event->y == 0)
1128 if (event->x == close_x)
1129 send_message (w->owner, NULL, MSG_ACTION, CK_Close, NULL);
1130 else if (event->x == toggle_fullscreen_x)
1131 edit_toggle_fullscreen (edit);
1132 else if (!edit->fullscreen && event->count == GPM_DOUBLE)
1133 /* double click on top line (toggle fullscreen) */
1134 edit_toggle_fullscreen (edit);
1136 else if (event->count == GPM_DOUBLE)
1138 /* double click */
1139 edit_mark_current_word_cmd (edit);
1140 edit_total_update (edit);
1142 else if (event->count == GPM_TRIPLE)
1144 /* triple click: works in GPM only, not in xterm */
1145 edit_mark_current_line_cmd (edit);
1146 edit_total_update (edit);
1148 break;
1150 case MSG_MOUSE_DRAG:
1151 edit_update_cursor (edit, event);
1152 edit_total_update (edit);
1153 break;
1155 case MSG_MOUSE_SCROLL_UP:
1156 edit_move_up (edit, 2, TRUE);
1157 edit_total_update (edit);
1158 break;
1160 case MSG_MOUSE_SCROLL_DOWN:
1161 edit_move_down (edit, 2, TRUE);
1162 edit_total_update (edit);
1163 break;
1165 default:
1166 break;
1170 /* --------------------------------------------------------------------------------------------- */
1171 /*** public functions ****************************************************************************/
1172 /* --------------------------------------------------------------------------------------------- */
1174 * Edit one file.
1176 * @param file_vpath file object
1177 * @param line line number
1178 * @return TRUE if no errors was occurred, FALSE otherwise
1181 gboolean
1182 edit_file (const vfs_path_t * file_vpath, long line)
1184 mcedit_arg_t arg = { (vfs_path_t *) file_vpath, line };
1185 GList *files;
1186 gboolean ok;
1188 files = g_list_prepend (NULL, &arg);
1189 ok = edit_files (files);
1190 g_list_free (files);
1192 return ok;
1195 /* --------------------------------------------------------------------------------------------- */
1197 gboolean
1198 edit_files (const GList * files)
1200 static gboolean made_directory = FALSE;
1201 WDialog *edit_dlg;
1202 WMenuBar *menubar;
1203 const GList *file;
1204 gboolean ok = FALSE;
1206 if (!made_directory)
1208 char *dir;
1210 dir = mc_build_filename (mc_config_get_cache_path (), EDIT_DIR, (char *) NULL);
1211 made_directory = (mkdir (dir, 0700) != -1 || errno == EEXIST);
1212 g_free (dir);
1214 dir = mc_build_filename (mc_config_get_path (), EDIT_DIR, (char *) NULL);
1215 made_directory = (mkdir (dir, 0700) != -1 || errno == EEXIST);
1216 g_free (dir);
1218 dir = mc_build_filename (mc_config_get_data_path (), EDIT_DIR, (char *) NULL);
1219 made_directory = (mkdir (dir, 0700) != -1 || errno == EEXIST);
1220 g_free (dir);
1223 /* Create a new dialog and add it widgets to it */
1224 edit_dlg =
1225 dlg_create (FALSE, 0, 0, LINES, COLS, NULL, edit_dialog_callback,
1226 edit_dialog_mouse_callback, "[Internal File Editor]", NULL, DLG_WANT_TAB);
1228 edit_dlg->get_shortcut = edit_get_shortcut;
1229 edit_dlg->get_title = edit_get_title;
1231 menubar = menubar_new (0, 0, COLS, NULL, TRUE);
1232 add_widget (edit_dlg, menubar);
1233 edit_init_menu (menubar);
1235 add_widget (edit_dlg, buttonbar_new (TRUE));
1237 for (file = files; file != NULL; file = g_list_next (file))
1239 Widget *w = WIDGET (edit_dlg);
1240 mcedit_arg_t *f = (mcedit_arg_t *) file->data;
1241 gboolean f_ok;
1243 f_ok = edit_add_window (edit_dlg, w->y + 1, w->x, w->lines - 2, w->cols, f->file_vpath,
1244 f->line_number);
1245 /* at least one file has been opened succefully */
1246 ok = ok || f_ok;
1249 if (ok)
1250 dlg_run (edit_dlg);
1252 if (!ok || edit_dlg->state == DLG_CLOSED)
1253 dlg_destroy (edit_dlg);
1255 return ok;
1258 /* --------------------------------------------------------------------------------------------- */
1260 const char *
1261 edit_get_file_name (const WEdit * edit)
1263 return vfs_path_as_str (edit->filename_vpath);
1266 /* --------------------------------------------------------------------------------------------- */
1268 WEdit *
1269 find_editor (const WDialog * h)
1271 if (edit_widget_is_editor (CONST_WIDGET (h->current->data)))
1272 return (WEdit *) h->current->data;
1273 return (WEdit *) find_widget_type (h, edit_callback);
1276 /* --------------------------------------------------------------------------------------------- */
1278 * Check if widget is an WEdit class.
1280 * @param w probably editor object
1281 * @return TRUE if widget is an WEdit class, FALSE otherwise
1284 gboolean
1285 edit_widget_is_editor (const Widget * w)
1287 return (w != NULL && w->callback == edit_callback);
1290 /* --------------------------------------------------------------------------------------------- */
1292 void
1293 edit_update_screen (WEdit * e)
1295 WDialog *h = WIDGET (e)->owner;
1297 edit_scroll_screen_over_cursor (e);
1298 edit_update_curs_col (e);
1299 edit_status (e, (void *) e == h->current->data);
1301 /* pop all events for this window for internal handling */
1302 if (!is_idle ())
1303 e->force |= REDRAW_PAGE;
1304 else
1306 if ((e->force & REDRAW_COMPLETELY) != 0)
1307 e->force |= REDRAW_PAGE;
1308 edit_render_keypress (e);
1311 widget_redraw (WIDGET (find_buttonbar (h)));
1314 /* --------------------------------------------------------------------------------------------- */
1316 * Save current window size.
1318 * @param edit editor object
1321 void
1322 edit_save_size (WEdit * edit)
1324 Widget *w = WIDGET (edit);
1326 edit->y_prev = w->y;
1327 edit->x_prev = w->x;
1328 edit->lines_prev = w->lines;
1329 edit->cols_prev = w->cols;
1332 /* --------------------------------------------------------------------------------------------- */
1334 * Create new editor window and insert it into editor screen.
1336 * @param h editor dialog (screen)
1337 * @param y y coordinate
1338 * @param x x coordinate
1339 * @param lines window height
1340 * @param cols window width
1341 * @param f file object
1342 * @param fline line number in file
1343 * @return TRUE if new window was successfully created and inserted into editor screen,
1344 * FALSE otherwise
1347 gboolean
1348 edit_add_window (WDialog * h, int y, int x, int lines, int cols, const vfs_path_t * f, long fline)
1350 WEdit *edit;
1351 Widget *w;
1353 edit = edit_init (NULL, y, x, lines, cols, f, fline);
1354 if (edit == NULL)
1355 return FALSE;
1357 w = WIDGET (edit);
1358 w->callback = edit_callback;
1359 w->mouse_callback = edit_mouse_callback;
1361 add_widget (h, w);
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 /* --------------------------------------------------------------------------------------------- */