(exec_extension_view): refactoring: remove temporary file in exec_extension().
[midnight-commander.git] / src / editor / editwidget.c
blob808046c29b60aea4f8f9436e36e26d6df81efd7d
1 /*
2 Editor initialisation and callback handler.
4 Copyright (C) 1996, 1997, 1998, 2001, 2002, 2003, 2004, 2005, 2006,
5 2007, 2011, 2012, 2013
6 The Free Software Foundation, Inc.
8 Written by:
9 Paul Sheer, 1996, 1997
10 Andrew Borodin <aborodin@vmail.ru> 2012, 2013
12 This file is part of the Midnight Commander.
14 The Midnight Commander is free software: you can redistribute it
15 and/or modify it under the terms of the GNU General Public License as
16 published by the Free Software Foundation, either version 3 of the License,
17 or (at your option) any later version.
19 The Midnight Commander is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 GNU General Public License for more details.
24 You should have received a copy of the GNU General Public License
25 along with this program. If not, see <http://www.gnu.org/licenses/>.
28 /** \file
29 * \brief Source: editor initialisation and callback handler
30 * \author Paul Sheer
31 * \date 1996, 1997
34 #include <config.h>
36 #include <ctype.h>
37 #include <errno.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <sys/types.h>
41 #include <unistd.h>
43 #include "lib/global.h"
45 #include "lib/tty/tty.h" /* LINES, COLS */
46 #include "lib/tty/key.h" /* is_idle() */
47 #include "lib/tty/color.h" /* tty_setcolor() */
48 #include "lib/skin.h"
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-2012 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 edit->drag_state = MCEDIT_DRAG_NORMAL;
216 widget_set_size (WIDGET (edit), edit->y_prev, edit->x_prev, edit->lines_prev, edit->cols_prev);
217 dlg_redraw (WIDGET (edit)->owner);
220 /* --------------------------------------------------------------------------------------------- */
222 * Move window by one row or column in any direction.
224 * @param edit editor object
225 * @param command direction (CK_Up, CK_Down, CK_Left, CK_Right)
228 static void
229 edit_window_move (WEdit * edit, unsigned long command)
231 Widget *w = WIDGET (edit);
232 Widget *wh = WIDGET (w->owner);
234 switch (command)
236 case CK_Up:
237 if (w->y > wh->y + 1) /* menubar */
238 w->y--;
239 break;
240 case CK_Down:
241 if (w->y < wh->y + wh->lines - 2) /* buttonbar */
242 w->y++;
243 break;
244 case CK_Left:
245 if (w->x + w->cols > wh->x)
246 w->x--;
247 break;
248 case CK_Right:
249 if (w->x < wh->x + wh->cols)
250 w->x++;
251 break;
252 default:
253 return;
256 edit->force |= REDRAW_PAGE;
257 dlg_redraw (w->owner);
260 /* --------------------------------------------------------------------------------------------- */
262 * Resize window by one row or column in any direction.
264 * @param edit editor object
265 * @param command direction (CK_Up, CK_Down, CK_Left, CK_Right)
268 static void
269 edit_window_resize (WEdit * edit, unsigned long command)
271 Widget *w = WIDGET (edit);
272 Widget *wh = WIDGET (w->owner);
274 switch (command)
276 case CK_Up:
277 if (w->lines > WINDOW_MIN_LINES)
278 w->lines--;
279 break;
280 case CK_Down:
281 if (w->y + w->lines < wh->y + wh->lines - 1) /* buttonbar */
282 w->lines++;
283 break;
284 case CK_Left:
285 if (w->cols > WINDOW_MIN_COLS)
286 w->cols--;
287 break;
288 case CK_Right:
289 if (w->x + w->cols < wh->x + wh->cols)
290 w->cols++;
291 break;
292 default:
293 return;
296 edit->force |= REDRAW_COMPLETELY;
297 dlg_redraw (w->owner);
300 /* --------------------------------------------------------------------------------------------- */
302 * Get hotkey by number.
304 * @param n number
305 * @return hotkey
308 static unsigned char
309 get_hotkey (int n)
311 return (n <= 9) ? '0' + n : 'a' + n - 10;
314 /* --------------------------------------------------------------------------------------------- */
316 static void
317 edit_window_list (const WDialog * h)
319 const size_t offset = 2; /* skip menu and buttonbar */
320 const size_t dlg_num = g_list_length (h->widgets) - offset;
321 int lines, cols;
322 Listbox *listbox;
323 GList *w;
324 int i = 0;
325 int rv;
327 lines = min ((size_t) (LINES * 2 / 3), dlg_num);
328 cols = COLS * 2 / 3;
330 listbox = create_listbox_window (lines, cols, _("Open files"), "[Open files]");
332 for (w = h->widgets; w != NULL; w = g_list_next (w))
333 if (edit_widget_is_editor (WIDGET (w->data)))
335 WEdit *e = (WEdit *) w->data;
336 char *fname;
338 if (e->filename_vpath == NULL)
339 fname = g_strdup_printf ("%c [%s]", e->modified ? '*' : ' ', _("NoName"));
340 else
342 char *fname2;
344 fname2 = vfs_path_to_str (e->filename_vpath);
345 fname = g_strdup_printf ("%c%s", e->modified ? '*' : ' ', fname2);
346 g_free (fname2);
349 listbox_add_item (listbox->list, LISTBOX_APPEND_AT_END, get_hotkey (i++),
350 str_term_trim (fname, WIDGET (listbox->list)->cols - 2), NULL);
351 g_free (fname);
354 rv = g_list_position (h->widgets, h->current) - offset;
355 listbox_select_entry (listbox->list, rv);
356 rv = run_listbox (listbox);
357 if (rv >= 0)
359 w = g_list_nth (h->widgets, rv + offset);
360 dlg_set_top_widget (w->data);
364 /* --------------------------------------------------------------------------------------------- */
366 static char *
367 edit_get_shortcut (unsigned long command)
369 const char *ext_map;
370 const char *shortcut = NULL;
372 shortcut = keybind_lookup_keymap_shortcut (editor_map, command);
373 if (shortcut != NULL)
374 return g_strdup (shortcut);
376 ext_map = keybind_lookup_keymap_shortcut (editor_map, CK_ExtendedKeyMap);
377 if (ext_map != NULL)
378 shortcut = keybind_lookup_keymap_shortcut (editor_x_map, command);
379 if (shortcut != NULL)
380 return g_strdup_printf ("%s %s", ext_map, shortcut);
382 return NULL;
385 /* --------------------------------------------------------------------------------------------- */
387 static char *
388 edit_get_title (const WDialog * h, size_t len)
390 const WEdit *edit = find_editor (h);
391 const char *modified = edit->modified ? "(*) " : " ";
392 const char *file_label;
393 char *filename;
395 len -= 4;
397 filename = vfs_path_to_str (edit->filename_vpath);
398 if (filename == NULL)
399 filename = g_strdup (_("[NoName]"));
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 * Handle mouse events of editor window
410 * @param event mouse event
411 * @param data editor window
412 * @return MOU_NORMAL if event was handled, MOU_UNHANDLED otherwise
415 static int
416 edit_event (Gpm_Event * event, void *data)
418 WEdit *edit = (WEdit *) data;
419 Widget *w = WIDGET (data);
420 Gpm_Event local;
422 if (!mouse_global_in_widget (event, w))
423 return MOU_UNHANDLED;
425 local = mouse_get_local (event, w);
427 /* Unknown event type */
428 if ((event->type & (GPM_DOWN | GPM_DRAG | GPM_UP)) == 0)
429 return MOU_NORMAL;
431 dlg_set_top_widget (w);
433 edit_update_curs_row (edit);
434 edit_update_curs_col (edit);
436 if (edit->fullscreen || (local.buttons & GPM_B_LEFT) == 0 || (local.type & GPM_UP) != 0)
437 edit->drag_state = MCEDIT_DRAG_NORMAL;
438 else if (local.y == 1 && edit->drag_state != MCEDIT_DRAG_RESIZE)
440 /* click on the top line (move) */
441 int dx = edit->fullscreen ? 0 : 2;
443 if (local.x == w->cols - dx - 1)
445 send_message (w->owner, NULL, MSG_ACTION, CK_Close, NULL);
446 return MOU_NORMAL;
449 if (local.x == w->cols - dx - 4)
451 edit_toggle_fullscreen (edit);
452 return MOU_NORMAL;
455 if ((local.type & (GPM_DOWN | GPM_DRAG)) != 0)
457 /* move if not fullscreen */
458 edit->drag_state_start = local.x;
459 edit->drag_state = MCEDIT_DRAG_MOVE;
460 edit->force |= REDRAW_COMPLETELY;
461 edit_update_screen (edit);
464 else if (!edit->fullscreen && local.y == w->lines && local.x == w->cols)
466 /* click on bottom-right corner (resize) */
467 if ((local.type & (GPM_DOWN | GPM_DRAG)) != 0)
469 edit->drag_state = MCEDIT_DRAG_RESIZE;
470 edit->force |= REDRAW_COMPLETELY;
471 edit_update_screen (edit);
475 if (edit->drag_state == MCEDIT_DRAG_NORMAL)
477 gboolean done = TRUE;
479 /* Double click */
480 if ((local.type & (GPM_DOUBLE | GPM_UP)) == (GPM_UP | GPM_DOUBLE))
482 edit_mark_current_word_cmd (edit);
483 goto update;
485 #if 0
486 /* Triple click */
487 if ((local.type & (GPM_TRIPLE | GPM_UP)) == (GPM_UP | GPM_TRIPLE))
489 edit_mark_current_line_cmd (edit);
490 goto update;
492 #endif
493 /* Wheel events */
494 if ((local.buttons & GPM_B_UP) != 0 && (local.type & GPM_DOWN) != 0)
496 edit_move_up (edit, 2, 1);
497 goto update;
499 if ((local.buttons & GPM_B_DOWN) != 0 && (local.type & GPM_DOWN) != 0)
501 edit_move_down (edit, 2, 1);
502 goto update;
505 /* continue handle current event */
506 goto cont;
508 /* handle DRAG mouse event, don't use standard way to continue
509 * event handling outside of widget */
512 int c;
514 c = tty_get_event (event, FALSE, TRUE);
515 if (c == EV_NONE || c != EV_MOUSE)
516 break;
518 local = mouse_get_local (event, w);
520 cont:
521 /* A lone up mustn't do anything */
522 if (edit->mark2 != -1 && (local.type & (GPM_UP | GPM_DRAG)) != 0)
523 return MOU_NORMAL;
525 if ((local.type & (GPM_DOWN | GPM_UP)) != 0)
526 edit_push_key_press (edit);
528 if (!edit->fullscreen)
529 local.x--;
530 if (!option_cursor_beyond_eol)
531 edit->prev_col = local.x - edit->start_col - option_line_state_width - 1;
532 else
534 long line_len = edit_move_forward3 (edit, edit_bol (edit, edit->curs1), 0,
535 edit_eol (edit, edit->curs1));
537 if (local.x > line_len)
539 edit->over_col =
540 local.x - line_len - edit->start_col - option_line_state_width - 1;
541 edit->prev_col = line_len;
543 else
545 edit->over_col = 0;
546 edit->prev_col = local.x - option_line_state_width - edit->start_col - 1;
550 if (!edit->fullscreen)
551 local.y--;
552 if (local.y > (edit->curs_row + 1))
553 edit_move_down (edit, local.y - (edit->curs_row + 1), 0);
554 else if (local.y < (edit->curs_row + 1))
555 edit_move_up (edit, (edit->curs_row + 1) - local.y, 0);
556 else
557 edit_move_to_prev_col (edit, edit_bol (edit, edit->curs1));
559 if ((local.type & GPM_DOWN) != 0)
561 edit_mark_cmd (edit, TRUE); /* reset */
562 edit->highlight = 0;
565 done = (local.type & GPM_DRAG) == 0;
566 if (done)
567 edit_mark_cmd (edit, FALSE);
569 update:
570 edit_find_bracket (edit);
571 edit->force |= REDRAW_COMPLETELY;
572 edit_update_curs_row (edit);
573 edit_update_curs_col (edit);
574 edit_update_screen (edit);
576 while (!edit->fullscreen && !done);
578 else
579 while (edit->drag_state != MCEDIT_DRAG_NORMAL)
581 int c;
582 int y;
584 c = tty_get_event (event, FALSE, TRUE);
585 y = event->y - 1;
587 if (c == EV_NONE || c != EV_MOUSE)
589 /* redraw frame */
590 edit->drag_state = MCEDIT_DRAG_NORMAL;
591 edit->force |= REDRAW_COMPLETELY;
592 edit_update_screen (edit);
594 else if (y == w->y && (event->type & (GPM_DOUBLE | GPM_UP)) == (GPM_DOUBLE | GPM_UP))
596 /* double click on top line (toggle fullscreen) */
597 edit_toggle_fullscreen (edit);
598 edit->drag_state = MCEDIT_DRAG_NORMAL;
599 edit->force |= REDRAW_COMPLETELY;
600 edit_update_screen (edit);
602 else if ((event->type & (GPM_DRAG | GPM_DOWN)) == 0)
604 /* redraw frame */
605 edit->drag_state = MCEDIT_DRAG_NORMAL;
606 edit->force |= REDRAW_COMPLETELY;
607 edit_update_screen (edit);
609 else if (!edit->fullscreen)
611 Widget *h = WIDGET (w->owner);
613 if (edit->drag_state == MCEDIT_DRAG_MOVE)
615 int x = event->x - 1;
617 y = max (y, h->y + 1); /* status line */
618 y = min (y, h->y + h->lines - 2); /* buttonbar */
619 x = max (x, h->x);
620 x = min (x, h->x + h->cols - 1);
621 /* don't use widget_set_size() here to avoid double draw */
622 w->y = y;
623 w->x = x - edit->drag_state_start;
624 edit->force |= REDRAW_COMPLETELY;
626 else if (edit->drag_state == MCEDIT_DRAG_RESIZE)
628 event->y = min (event->y, h->y + h->lines - 1); /* buttonbar */
629 event->x = min (event->x, h->x + h->cols);
630 local = mouse_get_local (event, w);
632 /* don't use widget_set_size() here to avoid double draw */
633 w->lines = max (WINDOW_MIN_LINES, local.y);
634 w->cols = max (WINDOW_MIN_COLS, local.x);
635 edit->force |= REDRAW_COMPLETELY;
638 dlg_redraw (w->owner);
642 return MOU_NORMAL;
645 /* --------------------------------------------------------------------------------------------- */
647 * Handle mouse events of editor screen.
649 * @param event mouse event
650 * @param data editor screen
651 * @return MOU_NORMAL if event was handled, MOU_UNHANDLED otherwise
654 static int
655 edit_dialog_event (Gpm_Event * event, void *data)
657 WDialog *h = DIALOG (data);
658 Widget *w;
659 Widget *wh = WIDGET (h);
660 int ret = MOU_UNHANDLED;
662 w = WIDGET (find_menubar (h));
664 if (event->y == wh->y + 1 && (event->type & GPM_DOWN) != 0 && !MENUBAR (w)->is_active)
666 /* menubar */
668 GList *l;
669 GList *top = NULL;
670 int x;
672 /* Try find top fullscreen window */
673 for (l = h->widgets; l != NULL; l = g_list_next (l))
674 if (edit_widget_is_editor (WIDGET (l->data)) && ((WEdit *) l->data)->fullscreen)
675 top = l;
677 /* Handle fullscreen/close buttons in the top line */
678 x = wh->x + wh->cols + 1 - 6;
680 if (top != NULL && event->x >= x)
682 WEdit *e;
684 e = (WEdit *) top->data;
685 x = event->x - x;
687 if (top != h->current)
689 /* Window is not active. Activate it */
690 dlg_set_top_widget (e);
693 /* Handle buttons */
694 if (x <= 2)
695 edit_toggle_fullscreen (e);
696 else
697 send_message (h, NULL, MSG_ACTION, CK_Close, NULL);
699 ret = MOU_NORMAL;
702 if (ret == MOU_UNHANDLED)
703 dlg_select_widget (w);
706 return ret;
709 /* --------------------------------------------------------------------------------------------- */
711 static cb_ret_t
712 edit_dialog_command_execute (WDialog * h, unsigned long command)
714 Widget *wh = WIDGET (h);
715 gboolean ret = MSG_HANDLED;
717 switch (command)
719 case CK_EditNew:
720 edit_add_window (h, wh->y + 1, wh->x, wh->lines - 2, wh->cols, NULL, 0);
721 break;
722 case CK_EditFile:
723 edit_load_cmd (h);
724 break;
725 case CK_EditSyntaxFile:
726 edit_load_syntax_file (h);
727 break;
728 case CK_EditUserMenu:
729 edit_load_menu_file (h);
730 break;
731 case CK_Close:
732 /* if there are no opened files anymore, close MC editor */
733 if (edit_widget_is_editor (WIDGET (h->current->data)) &&
734 edit_close_cmd ((WEdit *) h->current->data) && find_editor (h) == NULL)
735 dlg_stop (h);
736 break;
737 case CK_Help:
738 edit_help ();
739 /* edit->force |= REDRAW_COMPLETELY; */
740 break;
741 case CK_Menu:
742 edit_menu_cmd (h);
743 break;
744 case CK_Quit:
745 case CK_Cancel:
747 Widget *w = WIDGET (h->current->data);
749 if (!edit_widget_is_editor (w) || ((WEdit *) w)->drag_state == MCEDIT_DRAG_NORMAL)
750 dlg_stop (h);
751 else
752 edit_restore_size ((WEdit *) w);
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 unsigned long command = (unsigned long) 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 ((unsigned 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 == (unsigned long) 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 unsigned 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);
1068 return ret;
1071 /* hardcoded menu hotkeys (see edit_drop_hotkey_menu) */
1072 case MSG_UNHANDLED_KEY:
1073 return edit_drop_hotkey_menu (h, parm) ? MSG_HANDLED : MSG_NOT_HANDLED;
1075 case MSG_VALIDATE:
1076 edit_quit (h);
1077 return MSG_HANDLED;
1079 case MSG_END:
1080 edit_dlg_deinit ();
1081 return MSG_HANDLED;
1083 default:
1084 return dlg_default_callback (w, sender, msg, parm, data);
1088 /* --------------------------------------------------------------------------------------------- */
1090 static cb_ret_t
1091 edit_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
1093 WEdit *e = (WEdit *) w;
1095 switch (msg)
1097 case MSG_FOCUS:
1098 edit_set_buttonbar (e, find_buttonbar (w->owner));
1099 /* fall through */
1101 case MSG_DRAW:
1102 e->force |= REDRAW_COMPLETELY;
1103 edit_update_screen (e);
1104 return MSG_HANDLED;
1106 case MSG_UNFOCUS:
1107 /* redraw frame and status */
1108 edit_status (e, FALSE);
1109 return MSG_HANDLED;
1111 case MSG_KEY:
1113 int cmd, ch;
1114 cb_ret_t ret = MSG_NOT_HANDLED;
1116 /* The user may override the access-keys for the menu bar. */
1117 if (macro_index == -1 && edit_execute_macro (e, parm))
1119 edit_update_screen (e);
1120 ret = MSG_HANDLED;
1122 else if (edit_translate_key (e, parm, &cmd, &ch))
1124 edit_execute_key_command (e, cmd, ch);
1125 edit_update_screen (e);
1126 ret = MSG_HANDLED;
1129 return ret;
1132 case MSG_ACTION:
1133 /* command from menubar or buttonbar */
1134 edit_execute_key_command (e, parm, -1);
1135 edit_update_screen (e);
1136 return MSG_HANDLED;
1138 case MSG_CURSOR:
1140 int y, x;
1142 y = (e->fullscreen ? 0 : 1) + EDIT_TEXT_VERTICAL_OFFSET + e->curs_row;
1143 x = (e->fullscreen ? 0 : 1) + EDIT_TEXT_HORIZONTAL_OFFSET + option_line_state_width +
1144 e->curs_col + e->start_col + e->over_col;
1146 widget_move (w, y, x);
1147 return MSG_HANDLED;
1150 case MSG_DESTROY:
1151 edit_clean (e);
1152 return MSG_HANDLED;
1154 default:
1155 return widget_default_callback (w, sender, msg, parm, data);
1159 /* --------------------------------------------------------------------------------------------- */
1160 /*** public functions ****************************************************************************/
1161 /* --------------------------------------------------------------------------------------------- */
1163 * Edit one file.
1165 * @param file_vpath file object
1166 * @param line line number
1167 * @return TRUE if no errors was occurred, FALSE otherwise
1170 gboolean
1171 edit_file (const vfs_path_t * file_vpath, long line)
1173 mcedit_arg_t arg = { (vfs_path_t *) file_vpath, line };
1174 GList *files;
1175 gboolean ok;
1177 files = g_list_prepend (NULL, &arg);
1178 ok = edit_files (files);
1179 g_list_free (files);
1181 return ok;
1184 /* --------------------------------------------------------------------------------------------- */
1186 gboolean
1187 edit_files (const GList * files)
1189 static gboolean made_directory = FALSE;
1190 WDialog *edit_dlg;
1191 WMenuBar *menubar;
1192 const GList *file;
1193 gboolean ok = FALSE;
1195 if (!made_directory)
1197 char *dir;
1199 dir = mc_build_filename (mc_config_get_cache_path (), EDIT_DIR, NULL);
1200 made_directory = (mkdir (dir, 0700) != -1 || errno == EEXIST);
1201 g_free (dir);
1203 dir = mc_build_filename (mc_config_get_path (), EDIT_DIR, NULL);
1204 made_directory = (mkdir (dir, 0700) != -1 || errno == EEXIST);
1205 g_free (dir);
1207 dir = mc_build_filename (mc_config_get_data_path (), EDIT_DIR, NULL);
1208 made_directory = (mkdir (dir, 0700) != -1 || errno == EEXIST);
1209 g_free (dir);
1212 /* Create a new dialog and add it widgets to it */
1213 edit_dlg =
1214 create_dlg (FALSE, 0, 0, LINES, COLS, NULL, edit_dialog_callback, edit_dialog_event,
1215 "[Internal File Editor]", NULL, DLG_WANT_TAB);
1217 edit_dlg->get_shortcut = edit_get_shortcut;
1218 edit_dlg->get_title = edit_get_title;
1220 menubar = menubar_new (0, 0, COLS, NULL);
1221 add_widget (edit_dlg, menubar);
1222 edit_init_menu (menubar);
1224 add_widget (edit_dlg, buttonbar_new (TRUE));
1226 for (file = files; file != NULL; file = g_list_next (file))
1228 Widget *w = WIDGET (edit_dlg);
1229 mcedit_arg_t *f = (mcedit_arg_t *) file->data;
1230 gboolean f_ok;
1232 f_ok = edit_add_window (edit_dlg, w->y + 1, w->x, w->lines - 2, w->cols, f->file_vpath,
1233 f->line_number);
1234 /* at least one file has been opened succefully */
1235 ok = ok || f_ok;
1238 if (ok)
1239 run_dlg (edit_dlg);
1241 if (!ok || edit_dlg->state == DLG_CLOSED)
1242 destroy_dlg (edit_dlg);
1244 return ok;
1247 /* --------------------------------------------------------------------------------------------- */
1249 char *
1250 edit_get_file_name (const WEdit * edit)
1252 return vfs_path_to_str (edit->filename_vpath);
1255 /* --------------------------------------------------------------------------------------------- */
1257 WEdit *
1258 find_editor (const WDialog * h)
1260 if (edit_widget_is_editor (WIDGET (h->current->data)))
1261 return (WEdit *) h->current->data;
1262 return (WEdit *) find_widget_type (h, edit_callback);
1265 /* --------------------------------------------------------------------------------------------- */
1267 * Check if widget is an WEdit class.
1269 * @param w probably editor object
1270 * @return TRUE if widget is an WEdit class, FALSE otherwise
1273 gboolean
1274 edit_widget_is_editor (const Widget * w)
1276 return (w != NULL && w->callback == edit_callback);
1279 /* --------------------------------------------------------------------------------------------- */
1281 void
1282 edit_update_screen (WEdit * e)
1284 WDialog *h = WIDGET (e)->owner;
1286 edit_scroll_screen_over_cursor (e);
1287 edit_update_curs_col (e);
1289 edit_status (e, (e->force & REDRAW_COMPLETELY) != 0 && (void *) e == h->current->data);
1291 /* pop all events for this window for internal handling */
1292 if (!is_idle ())
1293 e->force |= REDRAW_PAGE;
1294 else
1296 if ((e->force & REDRAW_COMPLETELY) != 0)
1297 e->force |= REDRAW_PAGE;
1298 edit_render_keypress (e);
1301 widget_redraw (WIDGET (find_buttonbar (h)));
1304 /* --------------------------------------------------------------------------------------------- */
1306 * Save current window size.
1308 * @param edit editor object
1311 void
1312 edit_save_size (WEdit * edit)
1314 Widget *w = WIDGET (edit);
1316 edit->y_prev = w->y;
1317 edit->x_prev = w->x;
1318 edit->lines_prev = w->lines;
1319 edit->cols_prev = w->cols;
1322 /* --------------------------------------------------------------------------------------------- */
1324 * Create new editor window and insert it into editor screen.
1326 * @param h editor dialog (screen)
1327 * @param y y coordinate
1328 * @param x x coordinate
1329 * @param lines window height
1330 * @param cols window width
1331 * @param f file object
1332 * @param fline line number in file
1333 * @return TRUE if new window was successfully created and inserted into editor screen,
1334 * FALSE otherwise
1337 gboolean
1338 edit_add_window (WDialog * h, int y, int x, int lines, int cols, const vfs_path_t * f, long fline)
1340 WEdit *edit;
1341 Widget *w;
1343 edit = edit_init (NULL, y, x, lines, cols, f, fline);
1344 if (edit == NULL)
1345 return FALSE;
1347 w = WIDGET (edit);
1348 w->callback = edit_callback;
1349 w->mouse = edit_event;
1351 add_widget (h, w);
1352 dlg_redraw (h);
1354 return TRUE;
1357 /* --------------------------------------------------------------------------------------------- */
1359 * Handle move/resize events.
1361 * @param edit editor object
1362 * @param command action id
1363 * @return TRUE if mouse actions was handled, FALSE otherwise
1366 gboolean
1367 edit_handle_move_resize (WEdit * edit, unsigned long command)
1369 gboolean ret = FALSE;
1371 if (edit->fullscreen)
1373 edit->drag_state = MCEDIT_DRAG_NORMAL;
1374 return ret;
1377 switch (edit->drag_state)
1379 case MCEDIT_DRAG_NORMAL:
1380 /* possible start move/resize */
1381 switch (command)
1383 case CK_WindowMove:
1384 edit->drag_state = MCEDIT_DRAG_MOVE;
1385 edit_save_size (edit);
1386 ret = TRUE;
1387 break;
1388 case CK_WindowResize:
1389 edit->drag_state = MCEDIT_DRAG_RESIZE;
1390 edit_save_size (edit);
1391 ret = TRUE;
1392 break;
1393 default:
1394 break;
1396 break;
1398 case MCEDIT_DRAG_MOVE:
1399 switch (command)
1401 case CK_WindowResize:
1402 edit->drag_state = MCEDIT_DRAG_RESIZE;
1403 ret = TRUE;
1404 break;
1405 case CK_Up:
1406 case CK_Down:
1407 case CK_Left:
1408 case CK_Right:
1409 edit_window_move (edit, command);
1410 ret = TRUE;
1411 break;
1412 case CK_Enter:
1413 case CK_WindowMove:
1414 edit->drag_state = MCEDIT_DRAG_NORMAL;
1415 /* redraw frame and status */
1416 edit_status (edit, TRUE);
1417 default:
1418 ret = TRUE;
1419 break;
1421 break;
1423 case MCEDIT_DRAG_RESIZE:
1424 switch (command)
1426 case CK_WindowMove:
1427 edit->drag_state = MCEDIT_DRAG_MOVE;
1428 ret = TRUE;
1429 break;
1430 case CK_Up:
1431 case CK_Down:
1432 case CK_Left:
1433 case CK_Right:
1434 edit_window_resize (edit, command);
1435 ret = TRUE;
1436 break;
1437 case CK_Enter:
1438 case CK_WindowResize:
1439 edit->drag_state = MCEDIT_DRAG_NORMAL;
1440 /* redraw frame and status */
1441 edit_status (edit, TRUE);
1442 default:
1443 ret = TRUE;
1444 break;
1446 break;
1449 return ret;
1452 /* --------------------------------------------------------------------------------------------- */
1454 * Toggle window fuulscreen mode.
1456 * @param edit editor object
1459 void
1460 edit_toggle_fullscreen (WEdit * edit)
1462 edit->fullscreen = !edit->fullscreen;
1463 edit->force = REDRAW_COMPLETELY;
1465 if (!edit->fullscreen)
1466 edit_restore_size (edit);
1467 else
1469 Widget *w = WIDGET (edit);
1470 Widget *h = WIDGET (w->owner);
1472 edit_save_size (edit);
1473 widget_set_size (w, h->y + 1, h->x, h->lines - 2, h->cols);
1474 edit->force |= REDRAW_PAGE;
1475 edit_update_screen (edit);
1479 /* --------------------------------------------------------------------------------------------- */