Ticket #3557: mcedit should not be closed on Ctrl-G.
[midnight-commander.git] / src / editor / editwidget.c
blob6f72fd4a802e35fb0292cf1917fb0cfc1b04ad64
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 edit->drag_state = MCEDIT_DRAG_NORMAL;
215 widget_set_size (WIDGET (edit), edit->y_prev, edit->x_prev, edit->lines_prev, edit->cols_prev);
216 dlg_redraw (WIDGET (edit)->owner);
219 /* --------------------------------------------------------------------------------------------- */
221 * Move window by one row or column in any direction.
223 * @param edit editor object
224 * @param command direction (CK_Up, CK_Down, CK_Left, CK_Right)
227 static void
228 edit_window_move (WEdit * edit, long command)
230 Widget *w = WIDGET (edit);
231 Widget *wh = WIDGET (w->owner);
233 switch (command)
235 case CK_Up:
236 if (w->y > wh->y + 1) /* menubar */
237 w->y--;
238 break;
239 case CK_Down:
240 if (w->y < wh->y + wh->lines - 2) /* buttonbar */
241 w->y++;
242 break;
243 case CK_Left:
244 if (w->x + w->cols > wh->x)
245 w->x--;
246 break;
247 case CK_Right:
248 if (w->x < wh->x + wh->cols)
249 w->x++;
250 break;
251 default:
252 return;
255 edit->force |= REDRAW_PAGE;
256 dlg_redraw (w->owner);
259 /* --------------------------------------------------------------------------------------------- */
261 * Resize window by one row or column in any direction.
263 * @param edit editor object
264 * @param command direction (CK_Up, CK_Down, CK_Left, CK_Right)
267 static void
268 edit_window_resize (WEdit * edit, long command)
270 Widget *w = WIDGET (edit);
271 Widget *wh = WIDGET (w->owner);
273 switch (command)
275 case CK_Up:
276 if (w->lines > WINDOW_MIN_LINES)
277 w->lines--;
278 break;
279 case CK_Down:
280 if (w->y + w->lines < wh->y + wh->lines - 1) /* buttonbar */
281 w->lines++;
282 break;
283 case CK_Left:
284 if (w->cols > WINDOW_MIN_COLS)
285 w->cols--;
286 break;
287 case CK_Right:
288 if (w->x + w->cols < wh->x + wh->cols)
289 w->cols++;
290 break;
291 default:
292 return;
295 edit->force |= REDRAW_COMPLETELY;
296 dlg_redraw (w->owner);
299 /* --------------------------------------------------------------------------------------------- */
301 * Get hotkey by number.
303 * @param n number
304 * @return hotkey
307 static unsigned char
308 get_hotkey (int n)
310 return (n <= 9) ? '0' + n : 'a' + n - 10;
313 /* --------------------------------------------------------------------------------------------- */
315 static void
316 edit_window_list (const WDialog * h)
318 const size_t offset = 2; /* skip menu and buttonbar */
319 const size_t dlg_num = g_list_length (h->widgets) - offset;
320 int lines, cols;
321 Listbox *listbox;
322 GList *w;
323 int i = 0;
324 int rv;
326 lines = min ((size_t) (LINES * 2 / 3), dlg_num);
327 cols = COLS * 2 / 3;
329 listbox = create_listbox_window (lines, cols, _("Open files"), "[Open files]");
331 for (w = h->widgets; w != NULL; w = g_list_next (w))
332 if (edit_widget_is_editor (WIDGET (w->data)))
334 WEdit *e = (WEdit *) w->data;
335 char *fname;
337 if (e->filename_vpath == NULL)
338 fname = g_strdup_printf ("%c [%s]", e->modified ? '*' : ' ', _("NoName"));
339 else
340 fname =
341 g_strdup_printf ("%c%s", e->modified ? '*' : ' ',
342 vfs_path_as_str (e->filename_vpath));
344 listbox_add_item (listbox->list, LISTBOX_APPEND_AT_END, get_hotkey (i++),
345 str_term_trim (fname, WIDGET (listbox->list)->cols - 2), NULL, FALSE);
346 g_free (fname);
349 rv = g_list_position (h->widgets, h->current) - offset;
350 listbox_select_entry (listbox->list, rv);
351 rv = run_listbox (listbox);
352 if (rv >= 0)
354 w = g_list_nth (h->widgets, rv + offset);
355 dlg_set_top_widget (w->data);
359 /* --------------------------------------------------------------------------------------------- */
361 static char *
362 edit_get_shortcut (long command)
364 const char *ext_map;
365 const char *shortcut = NULL;
367 shortcut = keybind_lookup_keymap_shortcut (editor_map, command);
368 if (shortcut != NULL)
369 return g_strdup (shortcut);
371 ext_map = keybind_lookup_keymap_shortcut (editor_map, CK_ExtendedKeyMap);
372 if (ext_map != NULL)
373 shortcut = keybind_lookup_keymap_shortcut (editor_x_map, command);
374 if (shortcut != NULL)
375 return g_strdup_printf ("%s %s", ext_map, shortcut);
377 return NULL;
380 /* --------------------------------------------------------------------------------------------- */
382 static char *
383 edit_get_title (const WDialog * h, size_t len)
385 const WEdit *edit = find_editor (h);
386 const char *modified = edit->modified ? "(*) " : " ";
387 const char *file_label;
388 char *filename;
390 len -= 4;
392 if (edit->filename_vpath == NULL)
393 filename = g_strdup (_("[NoName]"));
394 else
395 filename = g_strdup (vfs_path_as_str (edit->filename_vpath));
397 file_label = str_term_trim (filename, len - str_term_width1 (_("Edit: ")));
398 g_free (filename);
400 return g_strconcat (_("Edit: "), modified, file_label, (char *) NULL);
403 /* --------------------------------------------------------------------------------------------- */
405 * Handle mouse events of editor window
407 * @param event mouse event
408 * @param data editor window
409 * @return MOU_NORMAL if event was handled, MOU_UNHANDLED otherwise
412 static int
413 edit_event (Gpm_Event * event, void *data)
415 WEdit *edit = (WEdit *) data;
416 Widget *w = WIDGET (data);
417 Gpm_Event local;
419 if (!mouse_global_in_widget (event, w))
420 return MOU_UNHANDLED;
422 local = mouse_get_local (event, w);
424 /* Unknown event type */
425 if ((event->type & (GPM_DOWN | GPM_DRAG | GPM_UP)) == 0)
426 return MOU_NORMAL;
428 dlg_set_top_widget (w);
430 edit_update_curs_row (edit);
431 edit_update_curs_col (edit);
433 if (edit->fullscreen || (local.buttons & GPM_B_LEFT) == 0 || (local.type & GPM_UP) != 0)
434 edit->drag_state = MCEDIT_DRAG_NORMAL;
435 else if (local.y == 1 && edit->drag_state != MCEDIT_DRAG_RESIZE)
437 /* click on the top line (move) */
438 int dx = edit->fullscreen ? 0 : 2;
440 if (local.x == w->cols - dx - 1)
442 send_message (w->owner, NULL, MSG_ACTION, CK_Close, NULL);
443 return MOU_NORMAL;
446 if (local.x == w->cols - dx - 4)
448 edit_toggle_fullscreen (edit);
449 return MOU_NORMAL;
452 if ((local.type & (GPM_DOWN | GPM_DRAG)) != 0)
454 /* move if not fullscreen */
455 edit->drag_state_start = local.x;
456 edit->drag_state = MCEDIT_DRAG_MOVE;
457 edit->force |= REDRAW_COMPLETELY;
458 edit_update_screen (edit);
461 else if (!edit->fullscreen && local.y == w->lines && local.x == w->cols)
463 /* click on bottom-right corner (resize) */
464 if ((local.type & (GPM_DOWN | GPM_DRAG)) != 0)
466 edit->drag_state = MCEDIT_DRAG_RESIZE;
467 edit->force |= REDRAW_COMPLETELY;
468 edit_update_screen (edit);
472 if (edit->drag_state == MCEDIT_DRAG_NORMAL)
474 gboolean done = TRUE;
476 /* Double click */
477 if ((local.type & (GPM_DOUBLE | GPM_UP)) == (GPM_UP | GPM_DOUBLE))
479 edit_mark_current_word_cmd (edit);
480 goto update;
482 #if 0
483 /* Triple click */
484 if ((local.type & (GPM_TRIPLE | GPM_UP)) == (GPM_UP | GPM_TRIPLE))
486 edit_mark_current_line_cmd (edit);
487 goto update;
489 #endif
490 /* Wheel events */
491 if ((local.buttons & GPM_B_UP) != 0 && (local.type & GPM_DOWN) != 0)
493 edit_move_up (edit, 2, 1);
494 goto update;
496 if ((local.buttons & GPM_B_DOWN) != 0 && (local.type & GPM_DOWN) != 0)
498 edit_move_down (edit, 2, 1);
499 goto update;
502 /* continue handle current event */
503 goto cont;
505 /* handle DRAG mouse event, don't use standard way to continue
506 * event handling outside of widget */
509 int c;
511 c = tty_get_event (event, FALSE, TRUE);
512 if (c == EV_NONE || c != EV_MOUSE)
513 break;
515 local = mouse_get_local (event, w);
517 cont:
518 /* A lone up mustn't do anything */
519 if (edit->mark2 != -1 && (local.type & (GPM_UP | GPM_DRAG)) != 0)
520 return MOU_NORMAL;
522 if ((local.type & (GPM_DOWN | GPM_UP)) != 0)
523 edit_push_key_press (edit);
525 if (!edit->fullscreen)
526 local.x--;
527 if (!option_cursor_beyond_eol)
528 edit->prev_col = local.x - edit->start_col - option_line_state_width - 1;
529 else
531 long line_len;
533 line_len = edit_move_forward3 (edit, edit_buffer_get_current_bol (&edit->buffer), 0,
534 edit_buffer_get_current_eol (&edit->buffer));
536 if (local.x > line_len)
538 edit->over_col =
539 local.x - line_len - edit->start_col - option_line_state_width - 1;
540 edit->prev_col = line_len;
542 else
544 edit->over_col = 0;
545 edit->prev_col = local.x - option_line_state_width - edit->start_col - 1;
549 if (!edit->fullscreen)
550 local.y--;
551 if (local.y > (edit->curs_row + 1))
552 edit_move_down (edit, local.y - (edit->curs_row + 1), 0);
553 else if (local.y < (edit->curs_row + 1))
554 edit_move_up (edit, (edit->curs_row + 1) - local.y, 0);
555 else
556 edit_move_to_prev_col (edit, edit_buffer_get_current_bol (&edit->buffer));
558 if ((local.type & GPM_DOWN) != 0)
560 edit_mark_cmd (edit, TRUE); /* reset */
561 edit->highlight = 0;
564 done = (local.type & GPM_DRAG) == 0;
565 if (done)
566 edit_mark_cmd (edit, FALSE);
568 update:
569 edit_find_bracket (edit);
570 edit->force |= REDRAW_COMPLETELY;
571 edit_update_curs_row (edit);
572 edit_update_curs_col (edit);
573 edit_update_screen (edit);
575 while (!edit->fullscreen && !done);
577 else
578 while (edit->drag_state != MCEDIT_DRAG_NORMAL)
580 int c;
581 int y;
583 c = tty_get_event (event, FALSE, TRUE);
584 y = event->y - 1;
586 if (c == EV_NONE || c != EV_MOUSE)
588 /* redraw frame */
589 edit->drag_state = MCEDIT_DRAG_NORMAL;
590 edit->force |= REDRAW_COMPLETELY;
591 edit_update_screen (edit);
593 else if (y == w->y && (event->type & (GPM_DOUBLE | GPM_UP)) == (GPM_DOUBLE | GPM_UP))
595 /* double click on top line (toggle fullscreen) */
596 edit_toggle_fullscreen (edit);
597 edit->drag_state = MCEDIT_DRAG_NORMAL;
598 edit->force |= REDRAW_COMPLETELY;
599 edit_update_screen (edit);
601 else if ((event->type & (GPM_DRAG | GPM_DOWN)) == 0)
603 /* redraw frame */
604 edit->drag_state = MCEDIT_DRAG_NORMAL;
605 edit->force |= REDRAW_COMPLETELY;
606 edit_update_screen (edit);
608 else if (!edit->fullscreen)
610 Widget *h = WIDGET (w->owner);
612 if (edit->drag_state == MCEDIT_DRAG_MOVE)
614 int x = event->x - 1;
616 y = max (y, h->y + 1); /* status line */
617 y = min (y, h->y + h->lines - 2); /* buttonbar */
618 x = max (x, h->x);
619 x = min (x, h->x + h->cols - 1);
620 /* don't use widget_set_size() here to avoid double draw */
621 w->y = y;
622 w->x = x - edit->drag_state_start;
623 edit->force |= REDRAW_COMPLETELY;
625 else if (edit->drag_state == MCEDIT_DRAG_RESIZE)
627 event->y = min (event->y, h->y + h->lines - 1); /* buttonbar */
628 event->x = min (event->x, h->x + h->cols);
629 local = mouse_get_local (event, w);
631 /* don't use widget_set_size() here to avoid double draw */
632 w->lines = max (WINDOW_MIN_LINES, local.y);
633 w->cols = max (WINDOW_MIN_COLS, local.x);
634 edit->force |= REDRAW_COMPLETELY;
637 dlg_redraw (w->owner);
641 return MOU_NORMAL;
644 /* --------------------------------------------------------------------------------------------- */
646 * Handle mouse events of editor screen.
648 * @param event mouse event
649 * @param data editor screen
650 * @return MOU_NORMAL if event was handled, MOU_UNHANDLED otherwise
653 static int
654 edit_dialog_event (Gpm_Event * event, void *data)
656 WDialog *h = DIALOG (data);
657 Widget *w;
658 Widget *wh = WIDGET (h);
659 int ret = MOU_UNHANDLED;
661 w = WIDGET (find_menubar (h));
663 if (event->y == wh->y + 1 && (event->type & GPM_DOWN) != 0 && !MENUBAR (w)->is_active)
665 /* menubar */
667 GList *l;
668 GList *top = NULL;
669 int x;
671 /* Try find top fullscreen window */
672 for (l = h->widgets; l != NULL; l = g_list_next (l))
673 if (edit_widget_is_editor (WIDGET (l->data)) && ((WEdit *) l->data)->fullscreen)
674 top = l;
676 /* Handle fullscreen/close buttons in the top line */
677 x = wh->x + wh->cols + 1 - 6;
679 if (top != NULL && event->x >= x)
681 WEdit *e;
683 e = (WEdit *) top->data;
684 x = event->x - x;
686 if (top != h->current)
688 /* Window is not active. Activate it */
689 dlg_set_top_widget (e);
692 /* Handle buttons */
693 if (x <= 2)
694 edit_toggle_fullscreen (e);
695 else
696 send_message (h, NULL, MSG_ACTION, CK_Close, NULL);
698 ret = MOU_NORMAL;
701 if (ret == MOU_UNHANDLED)
702 dlg_select_widget (w);
705 return ret;
708 /* --------------------------------------------------------------------------------------------- */
710 static cb_ret_t
711 edit_dialog_command_execute (WDialog * h, long command)
713 Widget *wh = WIDGET (h);
714 cb_ret_t ret = MSG_HANDLED;
716 switch (command)
718 case CK_EditNew:
719 edit_add_window (h, wh->y + 1, wh->x, wh->lines - 2, wh->cols, NULL, 0);
720 break;
721 case CK_EditFile:
722 edit_load_cmd (h);
723 break;
724 case CK_EditSyntaxFile:
725 edit_load_syntax_file (h);
726 break;
727 case CK_EditUserMenu:
728 edit_load_menu_file (h);
729 break;
730 case CK_Close:
731 /* if there are no opened files anymore, close MC editor */
732 if (edit_widget_is_editor (WIDGET (h->current->data)) &&
733 edit_close_cmd ((WEdit *) h->current->data) && find_editor (h) == NULL)
734 dlg_stop (h);
735 break;
736 case CK_Help:
737 edit_help ();
738 /* edit->force |= REDRAW_COMPLETELY; */
739 break;
740 case CK_Menu:
741 edit_menu_cmd (h);
742 break;
743 case CK_Quit:
744 case CK_Cancel:
745 /* don't close editor due to SIGINT, but stop move/resize window */
747 Widget *w = WIDGET (h->current->data);
749 if (edit_widget_is_editor (w) && ((WEdit *) w)->drag_state != MCEDIT_DRAG_NORMAL)
750 edit_restore_size ((WEdit *) w);
751 else if (command == CK_Quit)
752 dlg_stop (h);
754 break;
755 case CK_About:
756 edit_about ();
757 break;
758 case CK_SyntaxOnOff:
759 edit_syntax_onoff_cmd (h);
760 break;
761 case CK_ShowTabTws:
762 edit_show_tabs_tws_cmd (h);
763 break;
764 case CK_ShowMargin:
765 edit_show_margin_cmd (h);
766 break;
767 case CK_ShowNumbers:
768 edit_show_numbers_cmd (h);
769 break;
770 case CK_Refresh:
771 edit_refresh_cmd ();
772 break;
773 case CK_Shell:
774 view_other_cmd ();
775 break;
776 case CK_LearnKeys:
777 learn_keys ();
778 break;
779 case CK_WindowMove:
780 case CK_WindowResize:
781 if (edit_widget_is_editor (WIDGET (h->current->data)))
782 edit_handle_move_resize ((WEdit *) h->current->data, command);
783 break;
784 case CK_WindowList:
785 edit_window_list (h);
786 break;
787 case CK_WindowNext:
788 dlg_one_down (h);
789 dlg_set_top_widget (h->current->data);
790 break;
791 case CK_WindowPrev:
792 dlg_one_up (h);
793 dlg_set_top_widget (h->current->data);
794 break;
795 case CK_Options:
796 edit_options_dialog (h);
797 break;
798 case CK_OptionsSaveMode:
799 edit_save_mode_cmd ();
800 break;
801 case CK_SaveSetup:
802 save_setup_cmd ();
803 break;
804 default:
805 ret = MSG_NOT_HANDLED;
806 break;
809 return ret;
812 /* --------------------------------------------------------------------------------------------- */
814 * Translate the keycode into either 'command' or 'char_for_insertion'.
815 * 'command' is one of the editor commands from cmddef.h.
818 static gboolean
819 edit_translate_key (WEdit * edit, long x_key, int *cmd, int *ch)
821 long command = CK_InsertChar;
822 int char_for_insertion = -1;
824 /* an ordinary insertable character */
825 if (!edit->extmod && x_key < 256)
827 #ifndef HAVE_CHARSET
828 if (is_printable (x_key))
830 char_for_insertion = x_key;
831 goto fin;
833 #else
834 int c;
836 if (edit->charpoint >= 4)
838 edit->charpoint = 0;
839 edit->charbuf[edit->charpoint] = '\0';
841 if (edit->charpoint < 4)
843 edit->charbuf[edit->charpoint++] = x_key;
844 edit->charbuf[edit->charpoint] = '\0';
847 /* input from 8-bit locale */
848 if (!mc_global.utf8_display)
850 /* source in 8-bit codeset */
851 c = convert_from_input_c (x_key);
853 if (is_printable (c))
855 if (!edit->utf8)
856 char_for_insertion = c;
857 else
858 char_for_insertion = convert_from_8bit_to_utf_c2 ((char) x_key);
859 goto fin;
862 else
864 /* UTF-8 locale */
865 int res;
867 res = str_is_valid_char (edit->charbuf, edit->charpoint);
868 if (res < 0 && res != -2)
870 edit->charpoint = 0; /* broken multibyte char, skip */
871 goto fin;
874 if (edit->utf8)
876 /* source in UTF-8 codeset */
877 if (res < 0)
879 char_for_insertion = x_key;
880 goto fin;
883 edit->charbuf[edit->charpoint] = '\0';
884 edit->charpoint = 0;
885 if (g_unichar_isprint (g_utf8_get_char (edit->charbuf)))
887 char_for_insertion = x_key;
888 goto fin;
891 else
893 /* 8-bit source */
894 if (res < 0)
896 /* not finised multibyte input (in meddle multibyte utf-8 char) */
897 goto fin;
900 if (g_unichar_isprint (g_utf8_get_char (edit->charbuf)))
902 c = convert_from_utf_to_current (edit->charbuf);
903 edit->charbuf[0] = '\0';
904 edit->charpoint = 0;
905 char_for_insertion = c;
906 goto fin;
909 /* unprinteble utf input, skip it */
910 edit->charbuf[0] = '\0';
911 edit->charpoint = 0;
914 #endif /* HAVE_CHARSET */
917 /* Commands specific to the key emulation */
918 if (edit->extmod)
920 edit->extmod = FALSE;
921 command = keybind_lookup_keymap_command (editor_x_map, x_key);
923 else
924 command = keybind_lookup_keymap_command (editor_map, x_key);
926 if (command == CK_IgnoreKey)
927 command = CK_InsertChar;
929 fin:
930 *cmd = (int) command; /* FIXME */
931 *ch = char_for_insertion;
933 return !(command == CK_InsertChar && char_for_insertion == -1);
937 /* --------------------------------------------------------------------------------------------- */
939 static inline void
940 edit_quit (WDialog * h)
942 GList *l;
943 WEdit *e = NULL;
945 h->state = DLG_ACTIVE; /* don't stop the dialog before final decision */
947 for (l = h->widgets; l != NULL; l = g_list_next (l))
948 if (edit_widget_is_editor (WIDGET (l->data)))
950 e = (WEdit *) l->data;
952 if (e->drag_state != MCEDIT_DRAG_NORMAL)
954 edit_restore_size (e);
955 return;
958 if (e->modified)
960 dlg_select_widget (e);
962 if (!edit_ok_to_exit (e))
963 return;
967 /* no editors in dialog at all or no any file required to be saved */
968 if (e == NULL || l == NULL)
969 h->state = DLG_CLOSED;
972 /* --------------------------------------------------------------------------------------------- */
974 static inline void
975 edit_set_buttonbar (WEdit * edit, WButtonBar * bb)
977 buttonbar_set_label (bb, 1, Q_ ("ButtonBar|Help"), editor_map, NULL);
978 buttonbar_set_label (bb, 2, Q_ ("ButtonBar|Save"), editor_map, WIDGET (edit));
979 buttonbar_set_label (bb, 3, Q_ ("ButtonBar|Mark"), editor_map, WIDGET (edit));
980 buttonbar_set_label (bb, 4, Q_ ("ButtonBar|Replac"), editor_map, WIDGET (edit));
981 buttonbar_set_label (bb, 5, Q_ ("ButtonBar|Copy"), editor_map, WIDGET (edit));
982 buttonbar_set_label (bb, 6, Q_ ("ButtonBar|Move"), editor_map, WIDGET (edit));
983 buttonbar_set_label (bb, 7, Q_ ("ButtonBar|Search"), editor_map, WIDGET (edit));
984 buttonbar_set_label (bb, 8, Q_ ("ButtonBar|Delete"), editor_map, WIDGET (edit));
985 buttonbar_set_label (bb, 9, Q_ ("ButtonBar|PullDn"), editor_map, NULL);
986 buttonbar_set_label (bb, 10, Q_ ("ButtonBar|Quit"), editor_map, NULL);
989 /* --------------------------------------------------------------------------------------------- */
990 /** Callback for the edit dialog */
992 static cb_ret_t
993 edit_dialog_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
995 WMenuBar *menubar;
996 WButtonBar *buttonbar;
997 WDialog *h = DIALOG (w);
999 switch (msg)
1001 case MSG_INIT:
1002 edit_dlg_init ();
1003 return MSG_HANDLED;
1005 case MSG_DRAW:
1006 /* don't use dlg_default_repaint() -- we don't need a frame */
1007 tty_setcolor (EDITOR_BACKGROUND);
1008 dlg_erase (h);
1009 return MSG_HANDLED;
1011 case MSG_RESIZE:
1012 menubar = find_menubar (h);
1013 buttonbar = find_buttonbar (h);
1014 /* dlg_set_size() is surplus for this case */
1015 w->lines = LINES;
1016 w->cols = COLS;
1017 widget_set_size (WIDGET (buttonbar), w->lines - 1, w->x, 1, w->cols);
1018 widget_set_size (WIDGET (menubar), w->y, w->x, 1, w->cols);
1019 menubar_arrange (menubar);
1020 g_list_foreach (h->widgets, (GFunc) edit_dialog_resize_cb, NULL);
1021 return MSG_HANDLED;
1023 case MSG_ACTION:
1024 /* shortcut */
1025 if (sender == NULL)
1026 return edit_dialog_command_execute (h, parm);
1027 /* message from menu */
1028 menubar = find_menubar (h);
1029 if (sender == WIDGET (menubar))
1031 if (edit_dialog_command_execute (h, parm) == MSG_HANDLED)
1032 return MSG_HANDLED;
1033 /* try send command to the current window */
1034 return send_message (h->current->data, NULL, MSG_ACTION, parm, NULL);
1036 /* message from buttonbar */
1037 buttonbar = find_buttonbar (h);
1038 if (sender == WIDGET (buttonbar))
1040 if (data != NULL)
1041 return send_message (data, NULL, MSG_ACTION, parm, NULL);
1042 return edit_dialog_command_execute (h, parm);
1044 return MSG_NOT_HANDLED;
1046 case MSG_KEY:
1048 Widget *we = WIDGET (h->current->data);
1049 cb_ret_t ret = MSG_NOT_HANDLED;
1051 if (edit_widget_is_editor (we))
1053 WEdit *e = (WEdit *) we;
1054 long command;
1056 if (!e->extmod)
1057 command = keybind_lookup_keymap_command (editor_map, parm);
1058 else
1060 e->extmod = FALSE;
1061 command = keybind_lookup_keymap_command (editor_x_map, parm);
1064 if (command != CK_IgnoreKey)
1065 ret = edit_dialog_command_execute (h, command);
1069 * Due to the "end of bracket" escape the editor sees input with is_idle() == false
1070 * (expects more characters) and hence doesn't yet refresh the screen, but then
1071 * no further characters arrive (there's only an "end of bracket" which is swallowed
1072 * by tty_get_event()), so you end up with a screen that's not refreshed after pasting.
1073 * So let's trigger an IDLE signal.
1075 if (!is_idle ())
1076 widget_want_idle (w, TRUE);
1077 return ret;
1080 /* hardcoded menu hotkeys (see edit_drop_hotkey_menu) */
1081 case MSG_UNHANDLED_KEY:
1082 return edit_drop_hotkey_menu (h, parm) ? MSG_HANDLED : MSG_NOT_HANDLED;
1084 case MSG_VALIDATE:
1085 edit_quit (h);
1086 return MSG_HANDLED;
1088 case MSG_END:
1089 edit_dlg_deinit ();
1090 return MSG_HANDLED;
1092 case MSG_IDLE:
1093 widget_want_idle (w, FALSE);
1094 return send_message (h->current->data, NULL, MSG_IDLE, 0, NULL);
1096 default:
1097 return dlg_default_callback (w, sender, msg, parm, data);
1101 /* --------------------------------------------------------------------------------------------- */
1103 static cb_ret_t
1104 edit_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
1106 WEdit *e = (WEdit *) w;
1108 switch (msg)
1110 case MSG_FOCUS:
1111 edit_set_buttonbar (e, find_buttonbar (w->owner));
1112 /* fall through */
1114 case MSG_DRAW:
1115 e->force |= REDRAW_COMPLETELY;
1116 edit_update_screen (e);
1117 return MSG_HANDLED;
1119 case MSG_UNFOCUS:
1120 /* redraw frame and status */
1121 edit_status (e, FALSE);
1122 return MSG_HANDLED;
1124 case MSG_KEY:
1126 int cmd, ch;
1127 cb_ret_t ret = MSG_NOT_HANDLED;
1129 /* The user may override the access-keys for the menu bar. */
1130 if (macro_index == -1 && edit_execute_macro (e, parm))
1132 edit_update_screen (e);
1133 ret = MSG_HANDLED;
1135 else if (edit_translate_key (e, parm, &cmd, &ch))
1137 edit_execute_key_command (e, cmd, ch);
1138 edit_update_screen (e);
1139 ret = MSG_HANDLED;
1142 return ret;
1145 case MSG_ACTION:
1146 /* command from menubar or buttonbar */
1147 edit_execute_key_command (e, parm, -1);
1148 edit_update_screen (e);
1149 return MSG_HANDLED;
1151 case MSG_CURSOR:
1153 int y, x;
1155 y = (e->fullscreen ? 0 : 1) + EDIT_TEXT_VERTICAL_OFFSET + e->curs_row;
1156 x = (e->fullscreen ? 0 : 1) + EDIT_TEXT_HORIZONTAL_OFFSET + option_line_state_width +
1157 e->curs_col + e->start_col + e->over_col;
1159 widget_move (w, y, x);
1160 return MSG_HANDLED;
1163 case MSG_IDLE:
1164 edit_update_screen (e);
1165 return MSG_HANDLED;
1167 case MSG_DESTROY:
1168 edit_clean (e);
1169 return MSG_HANDLED;
1171 default:
1172 return widget_default_callback (w, sender, msg, parm, data);
1176 /* --------------------------------------------------------------------------------------------- */
1177 /*** public functions ****************************************************************************/
1178 /* --------------------------------------------------------------------------------------------- */
1180 * Edit one file.
1182 * @param file_vpath file object
1183 * @param line line number
1184 * @return TRUE if no errors was occurred, FALSE otherwise
1187 gboolean
1188 edit_file (const vfs_path_t * file_vpath, long line)
1190 mcedit_arg_t arg = { (vfs_path_t *) file_vpath, line };
1191 GList *files;
1192 gboolean ok;
1194 files = g_list_prepend (NULL, &arg);
1195 ok = edit_files (files);
1196 g_list_free (files);
1198 return ok;
1201 /* --------------------------------------------------------------------------------------------- */
1203 gboolean
1204 edit_files (const GList * files)
1206 static gboolean made_directory = FALSE;
1207 WDialog *edit_dlg;
1208 WMenuBar *menubar;
1209 const GList *file;
1210 gboolean ok = FALSE;
1212 if (!made_directory)
1214 char *dir;
1216 dir = mc_build_filename (mc_config_get_cache_path (), EDIT_DIR, NULL);
1217 made_directory = (mkdir (dir, 0700) != -1 || errno == EEXIST);
1218 g_free (dir);
1220 dir = mc_build_filename (mc_config_get_path (), EDIT_DIR, NULL);
1221 made_directory = (mkdir (dir, 0700) != -1 || errno == EEXIST);
1222 g_free (dir);
1224 dir = mc_build_filename (mc_config_get_data_path (), EDIT_DIR, NULL);
1225 made_directory = (mkdir (dir, 0700) != -1 || errno == EEXIST);
1226 g_free (dir);
1229 /* Create a new dialog and add it widgets to it */
1230 edit_dlg =
1231 dlg_create (FALSE, 0, 0, LINES, COLS, NULL, edit_dialog_callback, edit_dialog_event,
1232 "[Internal File Editor]", NULL, DLG_WANT_TAB);
1234 edit_dlg->get_shortcut = edit_get_shortcut;
1235 edit_dlg->get_title = edit_get_title;
1237 menubar = menubar_new (0, 0, COLS, NULL, TRUE);
1238 add_widget (edit_dlg, menubar);
1239 edit_init_menu (menubar);
1241 add_widget (edit_dlg, buttonbar_new (TRUE));
1243 for (file = files; file != NULL; file = g_list_next (file))
1245 Widget *w = WIDGET (edit_dlg);
1246 mcedit_arg_t *f = (mcedit_arg_t *) file->data;
1247 gboolean f_ok;
1249 f_ok = edit_add_window (edit_dlg, w->y + 1, w->x, w->lines - 2, w->cols, f->file_vpath,
1250 f->line_number);
1251 /* at least one file has been opened succefully */
1252 ok = ok || f_ok;
1255 if (ok)
1256 dlg_run (edit_dlg);
1258 if (!ok || edit_dlg->state == DLG_CLOSED)
1259 dlg_destroy (edit_dlg);
1261 return ok;
1264 /* --------------------------------------------------------------------------------------------- */
1266 const char *
1267 edit_get_file_name (const WEdit * edit)
1269 return vfs_path_as_str (edit->filename_vpath);
1272 /* --------------------------------------------------------------------------------------------- */
1274 WEdit *
1275 find_editor (const WDialog * h)
1277 if (edit_widget_is_editor (WIDGET (h->current->data)))
1278 return (WEdit *) h->current->data;
1279 return (WEdit *) find_widget_type (h, edit_callback);
1282 /* --------------------------------------------------------------------------------------------- */
1284 * Check if widget is an WEdit class.
1286 * @param w probably editor object
1287 * @return TRUE if widget is an WEdit class, FALSE otherwise
1290 gboolean
1291 edit_widget_is_editor (const Widget * w)
1293 return (w != NULL && w->callback == edit_callback);
1296 /* --------------------------------------------------------------------------------------------- */
1298 void
1299 edit_update_screen (WEdit * e)
1301 WDialog *h = WIDGET (e)->owner;
1303 edit_scroll_screen_over_cursor (e);
1304 edit_update_curs_col (e);
1305 edit_status (e, (void *) e == h->current->data);
1307 /* pop all events for this window for internal handling */
1308 if (!is_idle ())
1309 e->force |= REDRAW_PAGE;
1310 else
1312 if ((e->force & REDRAW_COMPLETELY) != 0)
1313 e->force |= REDRAW_PAGE;
1314 edit_render_keypress (e);
1317 widget_redraw (WIDGET (find_buttonbar (h)));
1320 /* --------------------------------------------------------------------------------------------- */
1322 * Save current window size.
1324 * @param edit editor object
1327 void
1328 edit_save_size (WEdit * edit)
1330 Widget *w = WIDGET (edit);
1332 edit->y_prev = w->y;
1333 edit->x_prev = w->x;
1334 edit->lines_prev = w->lines;
1335 edit->cols_prev = w->cols;
1338 /* --------------------------------------------------------------------------------------------- */
1340 * Create new editor window and insert it into editor screen.
1342 * @param h editor dialog (screen)
1343 * @param y y coordinate
1344 * @param x x coordinate
1345 * @param lines window height
1346 * @param cols window width
1347 * @param f file object
1348 * @param fline line number in file
1349 * @return TRUE if new window was successfully created and inserted into editor screen,
1350 * FALSE otherwise
1353 gboolean
1354 edit_add_window (WDialog * h, int y, int x, int lines, int cols, const vfs_path_t * f, long fline)
1356 WEdit *edit;
1357 Widget *w;
1359 edit = edit_init (NULL, y, x, lines, cols, f, fline);
1360 if (edit == NULL)
1361 return FALSE;
1363 w = WIDGET (edit);
1364 w->callback = edit_callback;
1365 w->mouse = edit_event;
1367 add_widget (h, w);
1368 dlg_redraw (h);
1370 return TRUE;
1373 /* --------------------------------------------------------------------------------------------- */
1375 * Handle move/resize events.
1377 * @param edit editor object
1378 * @param command action id
1379 * @return TRUE if mouse actions was handled, FALSE otherwise
1382 gboolean
1383 edit_handle_move_resize (WEdit * edit, long command)
1385 gboolean ret = FALSE;
1387 if (edit->fullscreen)
1389 edit->drag_state = MCEDIT_DRAG_NORMAL;
1390 return ret;
1393 switch (edit->drag_state)
1395 case MCEDIT_DRAG_NORMAL:
1396 /* possible start move/resize */
1397 switch (command)
1399 case CK_WindowMove:
1400 edit->drag_state = MCEDIT_DRAG_MOVE;
1401 edit_save_size (edit);
1402 ret = TRUE;
1403 break;
1404 case CK_WindowResize:
1405 edit->drag_state = MCEDIT_DRAG_RESIZE;
1406 edit_save_size (edit);
1407 ret = TRUE;
1408 break;
1409 default:
1410 break;
1412 break;
1414 case MCEDIT_DRAG_MOVE:
1415 switch (command)
1417 case CK_WindowResize:
1418 edit->drag_state = MCEDIT_DRAG_RESIZE;
1419 ret = TRUE;
1420 break;
1421 case CK_Up:
1422 case CK_Down:
1423 case CK_Left:
1424 case CK_Right:
1425 edit_window_move (edit, command);
1426 ret = TRUE;
1427 break;
1428 case CK_Enter:
1429 case CK_WindowMove:
1430 edit->drag_state = MCEDIT_DRAG_NORMAL;
1431 /* redraw frame and status */
1432 edit_status (edit, TRUE);
1433 default:
1434 ret = TRUE;
1435 break;
1437 break;
1439 case MCEDIT_DRAG_RESIZE:
1440 switch (command)
1442 case CK_WindowMove:
1443 edit->drag_state = MCEDIT_DRAG_MOVE;
1444 ret = TRUE;
1445 break;
1446 case CK_Up:
1447 case CK_Down:
1448 case CK_Left:
1449 case CK_Right:
1450 edit_window_resize (edit, command);
1451 ret = TRUE;
1452 break;
1453 case CK_Enter:
1454 case CK_WindowResize:
1455 edit->drag_state = MCEDIT_DRAG_NORMAL;
1456 /* redraw frame and status */
1457 edit_status (edit, TRUE);
1458 default:
1459 ret = TRUE;
1460 break;
1462 break;
1463 default:
1464 break;
1467 return ret;
1470 /* --------------------------------------------------------------------------------------------- */
1472 * Toggle window fuulscreen mode.
1474 * @param edit editor object
1477 void
1478 edit_toggle_fullscreen (WEdit * edit)
1480 edit->fullscreen = !edit->fullscreen;
1481 edit->force = REDRAW_COMPLETELY;
1483 if (!edit->fullscreen)
1484 edit_restore_size (edit);
1485 else
1487 Widget *w = WIDGET (edit);
1488 Widget *h = WIDGET (w->owner);
1490 edit_save_size (edit);
1491 widget_set_size (w, h->y + 1, h->x, h->lines - 2, h->cols);
1492 edit->force |= REDRAW_PAGE;
1493 edit_update_screen (edit);
1497 /* --------------------------------------------------------------------------------------------- */