Use long for line numbers and columns.
[midnight-commander.git] / src / editor / editwidget.c
blobac2b7d48eea3c4758beb7d68c6f119c8e0c5f9fa
1 /*
2 Editor initialisation and callback handler.
4 Copyright (C) 1996, 1997, 1998, 2001, 2002, 2003, 2004, 2005, 2006,
5 2007, 2011, 2012
6 The Free Software Foundation, Inc.
8 Written by:
9 Paul Sheer, 1996, 1997
10 Andrew Borodin <aborodin@vmail.ru> 2012
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 <stdio.h>
37 #include <stdarg.h>
38 #include <sys/types.h>
39 #include <unistd.h>
40 #include <string.h>
41 #include <ctype.h>
42 #include <errno.h>
43 #include <sys/stat.h>
44 #include <stdlib.h>
46 #include "lib/global.h"
48 #include "lib/tty/tty.h" /* LINES, COLS */
49 #include "lib/tty/key.h" /* is_idle() */
50 #include "lib/tty/color.h" /* tty_setcolor() */
51 #include "lib/skin.h"
52 #include "lib/strutil.h" /* str_term_trim() */
53 #include "lib/util.h" /* mc_build_filename() */
54 #include "lib/widget.h"
55 #include "lib/mcconfig.h"
56 #include "lib/event.h" /* mc_event_raise() */
58 #include "src/keybind-defaults.h"
59 #include "src/main.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_callback (Widget * w, widget_msg_t msg, int parm);
88 static cb_ret_t edit_dialog_callback (Dlg_head * h, Widget * sender, dlg_msg_t msg, int parm,
89 void *data);
91 /* --------------------------------------------------------------------------------------------- */
92 /**
93 * Init the 'edit' subsystem
96 static void
97 edit_dlg_init (void)
99 if (edit_dlg_init_refcounter == 0)
101 edit_window_state_char = mc_skin_get ("editor", "window-state-char", "*");
102 edit_window_close_char = mc_skin_get ("editor", "window-close-char", "X");
104 #ifdef HAVE_ASPELL
105 aspell_init ();
106 #endif
109 edit_dlg_init_refcounter++;
112 /* --------------------------------------------------------------------------------------------- */
114 * Deinit the 'edit' subsystem
117 static void
118 edit_dlg_deinit (void)
120 if (edit_dlg_init_refcounter != 0)
121 edit_dlg_init_refcounter--;
123 if (edit_dlg_init_refcounter == 0)
125 g_free (edit_window_state_char);
126 g_free (edit_window_close_char);
128 #ifdef HAVE_ASPELL
129 aspell_clean ();
130 #endif
134 /* --------------------------------------------------------------------------------------------- */
136 * Show info about editor
139 static void
140 edit_about (void)
142 const char *header = N_("About");
143 const char *button_name = N_("&OK");
144 const char *const version = "MCEdit " VERSION;
145 char text[BUF_LARGE];
147 int win_len, version_len, button_len;
148 int cols, lines;
150 Dlg_head *about_dlg;
152 #ifdef ENABLE_NLS
153 header = _(header);
154 button_name = _(button_name);
155 #endif
157 button_len = str_term_width1 (button_name) + 5;
158 version_len = str_term_width1 (version);
160 g_snprintf (text, sizeof (text),
161 _("Copyright (C) 1996-2012 the Free Software Foundation\n\n"
162 " A user friendly text editor\n"
163 " written for the Midnight Commander"));
165 win_len = str_term_width1 (header);
166 win_len = max (win_len, version_len);
167 win_len = max (win_len, button_len);
169 /* count width and height of text */
170 str_msg_term_size (text, &lines, &cols);
171 lines += 9;
172 cols = max (win_len, cols) + 6;
174 /* dialog */
175 about_dlg = create_dlg (TRUE, 0, 0, lines, cols, dialog_colors, NULL, NULL,
176 "[Internal File Editor]", header, DLG_CENTER | DLG_TRYUP);
178 add_widget (about_dlg, label_new (3, (cols - version_len) / 2, version));
179 add_widget (about_dlg, label_new (5, 3, text));
180 add_widget (about_dlg, button_new (lines - 3, (cols - button_len) / 2,
181 B_ENTER, NORMAL_BUTTON, button_name, NULL));
183 run_dlg (about_dlg);
184 destroy_dlg (about_dlg);
187 /* --------------------------------------------------------------------------------------------- */
189 * Show a help window
192 static void
193 edit_help (void)
195 ev_help_t event_data = { NULL, "[Internal File Editor]" };
196 mc_event_raise (MCEVENT_GROUP_CORE, "help", &event_data);
199 /* --------------------------------------------------------------------------------------------- */
201 * Callback for the iteration of objects in the 'editors' array.
202 * Resize the editor window.
204 * @param data probably WEdit object
205 * @param user_data unused
208 static void
209 edit_dialog_resize_cb (void *data, void *user_data)
211 Widget *w = (Widget *) data;
213 (void) user_data;
215 if (edit_widget_is_editor (w) && ((WEdit *) w)->fullscreen)
217 Dlg_head *h = w->owner;
219 w->lines = h->lines - 2;
220 w->cols = h->cols;
224 /* --------------------------------------------------------------------------------------------- */
226 * Restore saved window size.
228 * @param edit editor object
231 static void
232 edit_restore_size (WEdit * edit)
234 edit->drag_state = MCEDIT_DRAG_NORMAL;
235 widget_set_size ((Widget *) edit, edit->y_prev, edit->x_prev,
236 edit->lines_prev, edit->cols_prev);
237 dlg_redraw (((Widget *) edit)->owner);
240 /* --------------------------------------------------------------------------------------------- */
242 * Move window by one row or column in any direction.
244 * @param edit editor object
245 * @param command direction (CK_Up, CK_Down, CK_Left, CK_Right)
248 static void
249 edit_window_move (WEdit * edit, unsigned long command)
251 Widget *w = (Widget *) edit;
252 Dlg_head *h = w->owner;
254 switch (command)
256 case CK_Up:
257 if (w->y > h->y + 1) /* menubar */
258 w->y--;
259 break;
260 case CK_Down:
261 if (w->y < h->y + h->lines - 2) /* buttonbar */
262 w->y++;
263 break;
264 case CK_Left:
265 if (w->x + w->cols > h->x)
266 w->x--;
267 break;
268 case CK_Right:
269 if (w->x < h->x + h->cols)
270 w->x++;
271 break;
272 default:
273 return;
276 edit->force |= REDRAW_PAGE;
277 dlg_redraw (h);
280 /* --------------------------------------------------------------------------------------------- */
282 * Resize window by one row or column in any direction.
284 * @param edit editor object
285 * @param command direction (CK_Up, CK_Down, CK_Left, CK_Right)
288 static void
289 edit_window_resize (WEdit * edit, unsigned long command)
291 Widget *w = (Widget *) edit;
292 Dlg_head *h = w->owner;
294 switch (command)
296 case CK_Up:
297 if (w->lines > WINDOW_MIN_LINES)
298 w->lines--;
299 break;
300 case CK_Down:
301 if (w->y + w->lines < h->y + h->lines - 1) /* buttonbar */
302 w->lines++;
303 break;
304 case CK_Left:
305 if (w->cols > WINDOW_MIN_COLS)
306 w->cols--;
307 break;
308 case CK_Right:
309 if (w->x + w->cols < h->x + h->cols)
310 w->cols++;
311 break;
312 default:
313 return;
316 edit->force |= REDRAW_COMPLETELY;
317 dlg_redraw (h);
320 /* --------------------------------------------------------------------------------------------- */
322 * Get hotkey by number.
324 * @param n number
325 * @return hotkey
328 static unsigned char
329 get_hotkey (int n)
331 return (n <= 9) ? '0' + n : 'a' + n - 10;
334 /* --------------------------------------------------------------------------------------------- */
336 static void
337 edit_window_list (const Dlg_head * h)
339 const size_t offset = 2; /* skip menu and buttonbar */
340 const size_t dlg_num = g_list_length (h->widgets) - offset;
341 int lines, cols;
342 Listbox *listbox;
343 GList *w;
344 int i = 0;
345 int rv;
347 lines = min ((size_t) (LINES * 2 / 3), dlg_num);
348 cols = COLS * 2 / 3;
350 listbox = create_listbox_window (lines, cols, _("Open files"), "[Open files]");
352 for (w = h->widgets; w != NULL; w = g_list_next (w))
353 if (edit_widget_is_editor ((Widget *) w->data))
355 WEdit *e = (WEdit *) w->data;
356 char *fname;
358 if (e->filename_vpath == NULL)
359 fname = g_strdup_printf ("%c [%s]", e->modified ? '*' : ' ', _("NoName"));
360 else
362 char *fname2;
364 fname2 = vfs_path_to_str (e->filename_vpath);
365 fname = g_strdup_printf ("%c%s", e->modified ? '*' : ' ', fname2);
366 g_free (fname2);
369 listbox_add_item (listbox->list, LISTBOX_APPEND_AT_END, get_hotkey (i++),
370 str_term_trim (fname, listbox->list->widget.cols - 2), NULL);
371 g_free (fname);
374 rv = g_list_position (h->widgets, h->current) - offset;
375 listbox_select_entry (listbox->list, rv);
376 rv = run_listbox (listbox);
377 if (rv >= 0)
379 w = g_list_nth (h->widgets, rv + offset);
380 dlg_set_top_widget (w->data);
384 /* --------------------------------------------------------------------------------------------- */
386 static char *
387 edit_get_shortcut (unsigned long command)
389 const char *ext_map;
390 const char *shortcut = NULL;
392 shortcut = keybind_lookup_keymap_shortcut (editor_map, command);
393 if (shortcut != NULL)
394 return g_strdup (shortcut);
396 ext_map = keybind_lookup_keymap_shortcut (editor_map, CK_ExtendedKeyMap);
397 if (ext_map != NULL)
398 shortcut = keybind_lookup_keymap_shortcut (editor_x_map, command);
399 if (shortcut != NULL)
400 return g_strdup_printf ("%s %s", ext_map, shortcut);
402 return NULL;
405 /* --------------------------------------------------------------------------------------------- */
407 static char *
408 edit_get_title (const Dlg_head * h, size_t len)
410 const WEdit *edit = find_editor (h);
411 const char *modified = edit->modified ? "(*) " : " ";
412 const char *file_label;
413 char *filename;
415 len -= 4;
417 filename = vfs_path_to_str (edit->filename_vpath);
418 if (filename == NULL)
419 filename = g_strdup (_("[NoName]"));
420 file_label = str_term_trim (filename, len - str_term_width1 (_("Edit: ")));
421 g_free (filename);
423 return g_strconcat (_("Edit: "), modified, file_label, (char *) NULL);
426 /* --------------------------------------------------------------------------------------------- */
428 * Handle mouse events of editor window
430 * @param event mouse event
431 * @param data editor window
432 * @return MOU_NORMAL if event was handled, MOU_UNHANDLED otherwise
435 static int
436 edit_event (Gpm_Event * event, void *data)
438 WEdit *edit = (WEdit *) data;
439 Widget *w = (Widget *) data;
440 Gpm_Event local;
442 if (!mouse_global_in_widget (event, w))
443 return MOU_UNHANDLED;
445 local = mouse_get_local (event, w);
447 /* Unknown event type */
448 if ((event->type & (GPM_DOWN | GPM_DRAG | GPM_UP)) == 0)
449 return MOU_NORMAL;
451 dlg_set_top_widget (w);
453 edit_update_curs_row (edit);
454 edit_update_curs_col (edit);
456 if (edit->fullscreen || (local.buttons & GPM_B_LEFT) == 0 || (local.type & GPM_UP) != 0)
457 edit->drag_state = MCEDIT_DRAG_NORMAL;
458 else if (local.y == 1 && edit->drag_state != MCEDIT_DRAG_RESIZE)
460 /* click on the top line (move) */
461 int dx = edit->fullscreen ? 0 : 2;
463 if (local.x == edit->widget.cols - dx - 1)
465 edit_dialog_callback (w->owner, NULL, DLG_ACTION, CK_Close, NULL);
466 return MOU_NORMAL;
469 if (local.x == edit->widget.cols - dx - 4)
471 edit_toggle_fullscreen (edit);
472 return MOU_NORMAL;
475 if ((local.type & (GPM_DOWN | GPM_DRAG)) != 0)
477 /* move if not fullscreen */
478 edit->drag_state_start = local.x;
479 edit->drag_state = MCEDIT_DRAG_MOVE;
480 edit->force |= REDRAW_COMPLETELY;
481 edit_update_screen (edit);
484 else if (!edit->fullscreen && local.y == w->lines && local.x == w->cols)
486 /* click on bottom-right corner (resize) */
487 if ((local.type & (GPM_DOWN | GPM_DRAG)) != 0)
489 edit->drag_state = MCEDIT_DRAG_RESIZE;
490 edit->force |= REDRAW_COMPLETELY;
491 edit_update_screen (edit);
495 if (edit->drag_state == MCEDIT_DRAG_NORMAL)
497 gboolean done = TRUE;
499 /* Double click */
500 if ((local.type & (GPM_DOUBLE | GPM_UP)) == (GPM_UP | GPM_DOUBLE))
502 edit_mark_current_word_cmd (edit);
503 goto update;
505 #if 0
506 /* Triple click */
507 if ((local.type & (GPM_TRIPLE | GPM_UP)) == (GPM_UP | GPM_TRIPLE))
509 edit_mark_current_line_cmd (edit);
510 goto update;
512 #endif
513 /* Wheel events */
514 if ((local.buttons & GPM_B_UP) != 0 && (local.type & GPM_DOWN) != 0)
516 edit_move_up (edit, 2, 1);
517 goto update;
519 if ((local.buttons & GPM_B_DOWN) != 0 && (local.type & GPM_DOWN) != 0)
521 edit_move_down (edit, 2, 1);
522 goto update;
525 /* continue handle current event */
526 goto cont;
528 /* handle DRAG mouse event, don't use standard way to continue
529 * event handling outside of widget */
532 int c;
534 c = tty_get_event (event, FALSE, TRUE);
535 if (c == EV_NONE || c != EV_MOUSE)
536 break;
538 local = mouse_get_local (event, w);
540 cont:
541 /* A lone up mustn't do anything */
542 if (edit->mark2 != -1 && (local.type & (GPM_UP | GPM_DRAG)) != 0)
543 return MOU_NORMAL;
545 if ((local.type & (GPM_DOWN | GPM_UP)) != 0)
546 edit_push_key_press (edit);
548 if (!edit->fullscreen)
549 local.x--;
550 if (!option_cursor_beyond_eol)
551 edit->prev_col = local.x - edit->start_col - option_line_state_width - 1;
552 else
554 long line_len = edit_move_forward3 (edit, edit_bol (edit, edit->curs1), 0,
555 edit_eol (edit, edit->curs1));
557 if (local.x > line_len)
559 edit->over_col =
560 local.x - line_len - edit->start_col - option_line_state_width - 1;
561 edit->prev_col = line_len;
563 else
565 edit->over_col = 0;
566 edit->prev_col = local.x - option_line_state_width - edit->start_col - 1;
570 if (!edit->fullscreen)
571 local.y--;
572 if (local.y > (edit->curs_row + 1))
573 edit_move_down (edit, local.y - (edit->curs_row + 1), 0);
574 else if (local.y < (edit->curs_row + 1))
575 edit_move_up (edit, (edit->curs_row + 1) - local.y, 0);
576 else
577 edit_move_to_prev_col (edit, edit_bol (edit, edit->curs1));
579 if ((local.type & GPM_DOWN) != 0)
581 edit_mark_cmd (edit, TRUE); /* reset */
582 edit->highlight = 0;
585 done = (local.type & GPM_DRAG) == 0;
586 if (done)
587 edit_mark_cmd (edit, FALSE);
589 update:
590 edit_find_bracket (edit);
591 edit->force |= REDRAW_COMPLETELY;
592 edit_update_curs_row (edit);
593 edit_update_curs_col (edit);
594 edit_update_screen (edit);
596 while (!edit->fullscreen && !done);
598 else
599 while (edit->drag_state != MCEDIT_DRAG_NORMAL)
601 int c;
602 int y;
604 c = tty_get_event (event, FALSE, TRUE);
605 y = event->y - 1;
607 if (c == EV_NONE || c != EV_MOUSE)
609 /* redraw frame */
610 edit->drag_state = MCEDIT_DRAG_NORMAL;
611 edit->force |= REDRAW_COMPLETELY;
612 edit_update_screen (edit);
614 else if (y == w->y && (event->type & (GPM_DOUBLE | GPM_UP)) == (GPM_DOUBLE | GPM_UP))
616 /* double click on top line (toggle fullscreen) */
617 edit_toggle_fullscreen (edit);
618 edit->drag_state = MCEDIT_DRAG_NORMAL;
619 edit->force |= REDRAW_COMPLETELY;
620 edit_update_screen (edit);
622 else if ((event->type & (GPM_DRAG | GPM_DOWN)) == 0)
624 /* redraw frame */
625 edit->drag_state = MCEDIT_DRAG_NORMAL;
626 edit->force |= REDRAW_COMPLETELY;
627 edit_update_screen (edit);
629 else if (!edit->fullscreen)
631 Dlg_head *h = w->owner;
633 if (edit->drag_state == MCEDIT_DRAG_MOVE)
635 int x = event->x - 1;
637 y = max (y, h->y + 1); /* status line */
638 y = min (y, h->y + h->lines - 2); /* buttonbar */
639 x = max (x, h->x);
640 x = min (x, h->x + h->cols - 1);
641 /* don't use widget_set_size() here to avoid double draw */
642 w->y = y;
643 w->x = x - edit->drag_state_start;
644 edit->force |= REDRAW_COMPLETELY;
646 else if (edit->drag_state == MCEDIT_DRAG_RESIZE)
648 event->y = min (event->y, h->y + h->lines - 1); /* buttonbar */
649 event->x = min (event->x, h->x + h->cols);
650 local = mouse_get_local (event, w);
652 /* don't use widget_set_size() here to avoid double draw */
653 w->lines = max (WINDOW_MIN_LINES, local.y);
654 w->cols = max (WINDOW_MIN_COLS, local.x);
655 edit->force |= REDRAW_COMPLETELY;
658 dlg_redraw (h);
662 return MOU_NORMAL;
665 /* --------------------------------------------------------------------------------------------- */
667 * Handle mouse events of editor screen.
669 * @param event mouse event
670 * @param data editor screen
671 * @return MOU_NORMAL if event was handled, MOU_UNHANDLED otherwise
674 static int
675 edit_dialog_event (Gpm_Event * event, void *data)
677 Dlg_head *h = (Dlg_head *) data;
678 Widget *w;
679 int ret = MOU_UNHANDLED;
681 w = (Widget *) find_menubar (h);
683 if (event->y == h->y + 1 && (event->type & GPM_DOWN) != 0 && !((WMenuBar *) w)->is_active)
685 /* menubar */
687 GList *l;
688 GList *top = NULL;
689 int x;
691 /* Try find top fullscreen window */
692 for (l = h->widgets; l != NULL; l = g_list_next (l))
693 if (edit_widget_is_editor ((Widget *) l->data) && ((WEdit *) l->data)->fullscreen)
694 top = l;
696 /* Handle fullscreen/close buttons in the top line */
697 x = h->x + h->cols + 1 - 6;
699 if (top != NULL && event->x >= x)
701 WEdit *e;
703 e = (WEdit *) top->data;
704 x = event->x - x;
706 if (top != h->current)
708 /* Window is not active. Activate it */
709 dlg_set_top_widget (e);
712 /* Handle buttons */
713 if (x <= 2)
714 edit_toggle_fullscreen (e);
715 else
716 edit_dialog_callback (h, NULL, DLG_ACTION, CK_Close, NULL);
718 ret = MOU_NORMAL;
721 if (ret == MOU_UNHANDLED)
722 dlg_select_widget (w);
724 else if (event->y == h->y + h->lines)
726 /* buttonbar */
728 /* In general, this can be handled in default way (dlg_mouse_event)
729 * but let make it here to avoid walking in widget list */
730 w = (Widget *) find_buttonbar (h);
731 ret = w->mouse (event, w);
734 return ret;
737 /* --------------------------------------------------------------------------------------------- */
739 static cb_ret_t
740 edit_dialog_command_execute (Dlg_head * h, unsigned long command)
742 gboolean ret = MSG_HANDLED;
744 switch (command)
746 case CK_EditNew:
747 edit_add_window (h, h->y + 1, h->x, h->lines - 2, h->cols, NULL, 0);
748 break;
749 case CK_EditFile:
750 edit_load_cmd (h);
751 break;
752 case CK_EditSyntaxFile:
753 edit_load_syntax_file (h);
754 break;
755 case CK_EditUserMenu:
756 edit_load_menu_file (h);
757 break;
758 case CK_Close:
759 /* if there are no opened files anymore, close MC editor */
760 if (edit_widget_is_editor ((Widget *) h->current->data) &&
761 edit_close_cmd ((WEdit *) h->current->data) && find_editor (h) == NULL)
762 dlg_stop (h);
763 break;
764 case CK_Help:
765 edit_help ();
766 /* edit->force |= REDRAW_COMPLETELY; */
767 break;
768 case CK_Menu:
769 edit_menu_cmd (h);
770 break;
771 case CK_Quit:
772 case CK_Cancel:
774 Widget *w = (Widget *) h->current->data;
776 if (!edit_widget_is_editor (w) || ((WEdit *) w)->drag_state == MCEDIT_DRAG_NORMAL)
777 dlg_stop (h);
778 else
779 edit_restore_size ((WEdit *) w);
781 break;
782 case CK_About:
783 edit_about ();
784 break;
785 case CK_SyntaxOnOff:
786 edit_syntax_onoff_cmd (h);
787 break;
788 case CK_ShowTabTws:
789 edit_show_tabs_tws_cmd (h);
790 break;
791 case CK_ShowMargin:
792 edit_show_margin_cmd (h);
793 break;
794 case CK_ShowNumbers:
795 edit_show_numbers_cmd (h);
796 break;
797 case CK_Refresh:
798 edit_refresh_cmd ();
799 break;
800 case CK_Shell:
801 view_other_cmd ();
802 break;
803 case CK_LearnKeys:
804 learn_keys ();
805 break;
806 case CK_WindowMove:
807 case CK_WindowResize:
808 if (edit_widget_is_editor ((Widget *) h->current->data))
809 edit_handle_move_resize ((WEdit *) h->current->data, command);
810 break;
811 case CK_WindowList:
812 edit_window_list (h);
813 break;
814 case CK_WindowNext:
815 dlg_one_down (h);
816 dlg_set_top_widget (h->current->data);
817 break;
818 case CK_WindowPrev:
819 dlg_one_up (h);
820 dlg_set_top_widget (h->current->data);
821 break;
822 case CK_Options:
823 edit_options_dialog (h);
824 break;
825 case CK_OptionsSaveMode:
826 edit_save_mode_cmd ();
827 break;
828 case CK_SaveSetup:
829 save_setup_cmd ();
830 break;
831 default:
832 ret = MSG_NOT_HANDLED;
833 break;
836 return ret;
839 /* --------------------------------------------------------------------------------------------- */
841 static inline void
842 edit_quit (Dlg_head * h)
844 GList *l;
845 WEdit *e = NULL;
847 h->state = DLG_ACTIVE; /* don't stop the dialog before final decision */
849 for (l = h->widgets; l != NULL; l = g_list_next (l))
850 if (edit_widget_is_editor ((Widget *) l->data))
852 e = (WEdit *) l->data;
854 if (e->drag_state != MCEDIT_DRAG_NORMAL)
856 edit_restore_size (e);
857 return;
860 if (e->modified)
862 dlg_select_widget (e);
864 if (!edit_ok_to_exit (e))
865 return;
869 /* no editors in dialog at all or no any file required to be saved */
870 if (e == NULL || l == NULL)
871 h->state = DLG_CLOSED;
874 /* --------------------------------------------------------------------------------------------- */
876 static inline void
877 edit_set_buttonbar (WEdit * edit, WButtonBar * bb)
879 buttonbar_set_label (bb, 1, Q_ ("ButtonBar|Help"), editor_map, NULL);
880 buttonbar_set_label (bb, 2, Q_ ("ButtonBar|Save"), editor_map, (Widget *) edit);
881 buttonbar_set_label (bb, 3, Q_ ("ButtonBar|Mark"), editor_map, (Widget *) edit);
882 buttonbar_set_label (bb, 4, Q_ ("ButtonBar|Replac"), editor_map, (Widget *) edit);
883 buttonbar_set_label (bb, 5, Q_ ("ButtonBar|Copy"), editor_map, (Widget *) edit);
884 buttonbar_set_label (bb, 6, Q_ ("ButtonBar|Move"), editor_map, (Widget *) edit);
885 buttonbar_set_label (bb, 7, Q_ ("ButtonBar|Search"), editor_map, (Widget *) edit);
886 buttonbar_set_label (bb, 8, Q_ ("ButtonBar|Delete"), editor_map, (Widget *) edit);
887 buttonbar_set_label (bb, 9, Q_ ("ButtonBar|PullDn"), editor_map, NULL);
888 buttonbar_set_label (bb, 10, Q_ ("ButtonBar|Quit"), editor_map, NULL);
891 /* --------------------------------------------------------------------------------------------- */
892 /** Callback for the edit dialog */
894 static cb_ret_t
895 edit_dialog_callback (Dlg_head * h, Widget * sender, dlg_msg_t msg, int parm, void *data)
897 WMenuBar *menubar;
898 WButtonBar *buttonbar;
900 switch (msg)
902 case DLG_INIT:
903 edit_dlg_init ();
904 return MSG_HANDLED;
906 case DLG_DRAW:
907 /* don't use common_dialog_repaint() -- we don't need a frame */
908 tty_setcolor (EDITOR_BACKGROUND);
909 dlg_erase (h);
910 return MSG_HANDLED;
912 case DLG_RESIZE:
913 menubar = find_menubar (h);
914 buttonbar = find_buttonbar (h);
915 /* dlg_set_size() is surplus for this case */
916 h->lines = LINES;
917 h->cols = COLS;
918 widget_set_size (&buttonbar->widget, h->lines - 1, h->x, 1, h->cols);
919 widget_set_size (&menubar->widget, h->y, h->x, 1, h->cols);
920 menubar_arrange (menubar);
921 g_list_foreach (h->widgets, (GFunc) edit_dialog_resize_cb, NULL);
922 return MSG_HANDLED;
924 case DLG_ACTION:
925 /* shortcut */
926 if (sender == NULL)
927 return edit_dialog_command_execute (h, parm);
928 /* message from menu */
929 menubar = find_menubar (h);
930 if (sender == (Widget *) menubar)
932 if (edit_dialog_command_execute (h, parm) == MSG_HANDLED)
933 return MSG_HANDLED;
934 /* try send command to the current window */
935 return send_message ((Widget *) h->current->data, WIDGET_COMMAND, parm);
937 /* message from buttonbar */
938 buttonbar = find_buttonbar (h);
939 if (sender == (Widget *) buttonbar)
941 if (data != NULL)
942 return send_message ((Widget *) data, WIDGET_COMMAND, parm);
943 return edit_dialog_command_execute (h, parm);
945 return MSG_NOT_HANDLED;
947 case DLG_KEY:
949 Widget *w = h->current->data;
950 cb_ret_t ret = MSG_NOT_HANDLED;
952 if (edit_widget_is_editor (w))
954 WEdit *e = (WEdit *) w;
955 unsigned long command;
957 if (!e->extmod)
958 command = keybind_lookup_keymap_command (editor_map, parm);
959 else
961 e->extmod = FALSE;
962 command = keybind_lookup_keymap_command (editor_x_map, parm);
965 if (command != CK_IgnoreKey)
966 ret = edit_dialog_command_execute (h, command);
969 return ret;
972 /* hardcoded menu hotkeys (see edit_drop_hotkey_menu) */
973 case DLG_UNHANDLED_KEY:
974 return edit_drop_hotkey_menu (h, parm) ? MSG_HANDLED : MSG_NOT_HANDLED;
976 case DLG_VALIDATE:
977 edit_quit (h);
978 return MSG_HANDLED;
980 case DLG_END:
981 edit_dlg_deinit ();
982 return MSG_HANDLED;
984 default:
985 return default_dlg_callback (h, sender, msg, parm, data);
989 /* --------------------------------------------------------------------------------------------- */
991 static cb_ret_t
992 edit_callback (Widget * w, widget_msg_t msg, int parm)
994 WEdit *e = (WEdit *) w;
996 switch (msg)
998 case WIDGET_FOCUS:
999 edit_set_buttonbar (e, find_buttonbar (e->widget.owner));
1000 /* fall through */
1002 case WIDGET_DRAW:
1003 e->force |= REDRAW_COMPLETELY;
1004 edit_update_screen (e);
1005 return MSG_HANDLED;
1007 case WIDGET_UNFOCUS:
1008 /* redraw frame and status */
1009 edit_status (e, FALSE);
1010 return MSG_HANDLED;
1012 case WIDGET_KEY:
1014 int cmd, ch;
1015 cb_ret_t ret = MSG_NOT_HANDLED;
1017 /* The user may override the access-keys for the menu bar. */
1018 if (macro_index == -1 && edit_execute_macro (e, parm))
1020 edit_update_screen (e);
1021 ret = MSG_HANDLED;
1023 else if (edit_translate_key (e, parm, &cmd, &ch))
1025 edit_execute_key_command (e, cmd, ch);
1026 edit_update_screen (e);
1027 ret = MSG_HANDLED;
1030 return ret;
1033 case WIDGET_COMMAND:
1034 /* command from menubar or buttonbar */
1035 edit_execute_key_command (e, parm, -1);
1036 edit_update_screen (e);
1037 return MSG_HANDLED;
1039 case WIDGET_CURSOR:
1041 int y, x;
1043 y = (e->fullscreen ? 0 : 1) + EDIT_TEXT_VERTICAL_OFFSET + e->curs_row;
1044 x = (e->fullscreen ? 0 : 1) + EDIT_TEXT_HORIZONTAL_OFFSET + option_line_state_width +
1045 e->curs_col + e->start_col + e->over_col;
1047 widget_move (w, y, x);
1048 return MSG_HANDLED;
1051 case WIDGET_DESTROY:
1052 edit_clean (e);
1053 return MSG_HANDLED;
1055 default:
1056 return default_proc (msg, parm);
1060 /* --------------------------------------------------------------------------------------------- */
1061 /*** public functions ****************************************************************************/
1062 /* --------------------------------------------------------------------------------------------- */
1064 * Edit one file.
1066 * @param file_vpath file object
1067 * @param line line number
1068 * @return TRUE if no errors was occured, FALSE otherwise
1071 gboolean
1072 edit_file (const vfs_path_t * file_vpath, long line)
1074 mcedit_arg_t arg = { (vfs_path_t *) file_vpath, line };
1075 GList *files;
1076 gboolean ok;
1078 files = g_list_prepend (NULL, &arg);
1079 ok = edit_files (files);
1080 g_list_free (files);
1082 return ok;
1085 /* --------------------------------------------------------------------------------------------- */
1087 gboolean
1088 edit_files (const GList * files)
1090 static gboolean made_directory = FALSE;
1091 Dlg_head *edit_dlg;
1092 WMenuBar *menubar;
1093 const GList *file;
1094 gboolean ok = FALSE;
1096 if (!made_directory)
1098 char *dir;
1100 dir = mc_build_filename (mc_config_get_cache_path (), EDIT_DIR, NULL);
1101 made_directory = (mkdir (dir, 0700) != -1 || errno == EEXIST);
1102 g_free (dir);
1104 dir = mc_build_filename (mc_config_get_path (), EDIT_DIR, NULL);
1105 made_directory = (mkdir (dir, 0700) != -1 || errno == EEXIST);
1106 g_free (dir);
1108 dir = mc_build_filename (mc_config_get_data_path (), EDIT_DIR, NULL);
1109 made_directory = (mkdir (dir, 0700) != -1 || errno == EEXIST);
1110 g_free (dir);
1113 /* Create a new dialog and add it widgets to it */
1114 edit_dlg =
1115 create_dlg (FALSE, 0, 0, LINES, COLS, NULL, edit_dialog_callback, edit_dialog_event,
1116 "[Internal File Editor]", NULL, DLG_WANT_TAB);
1118 edit_dlg->get_shortcut = edit_get_shortcut;
1119 edit_dlg->get_title = edit_get_title;
1121 menubar = menubar_new (0, 0, COLS, NULL);
1122 add_widget (edit_dlg, menubar);
1123 edit_init_menu (menubar);
1125 add_widget (edit_dlg, buttonbar_new (TRUE));
1127 for (file = files; file != NULL; file = g_list_next (file))
1129 mcedit_arg_t *f = (mcedit_arg_t *) file->data;
1130 gboolean f_ok;
1132 f_ok = edit_add_window (edit_dlg, edit_dlg->y + 1, edit_dlg->x,
1133 edit_dlg->lines - 2, edit_dlg->cols, f->file_vpath, f->line_number);
1134 /* at least one file has been opened succefully */
1135 ok = ok || f_ok;
1138 if (ok)
1139 run_dlg (edit_dlg);
1141 if (!ok || edit_dlg->state == DLG_CLOSED)
1142 destroy_dlg (edit_dlg);
1144 return ok;
1147 /* --------------------------------------------------------------------------------------------- */
1149 char *
1150 edit_get_file_name (const WEdit * edit)
1152 return vfs_path_to_str (edit->filename_vpath);
1155 /* --------------------------------------------------------------------------------------------- */
1157 WEdit *
1158 find_editor (const Dlg_head * h)
1160 if (edit_widget_is_editor ((Widget *) h->current->data))
1161 return (WEdit *) h->current->data;
1162 return (WEdit *) find_widget_type (h, edit_callback);
1165 /* --------------------------------------------------------------------------------------------- */
1167 * Check if widget is an WEdit class.
1169 * @param w probably editor object
1170 * @return TRUE if widget is an WEdit class, FALSE otherwise
1173 gboolean
1174 edit_widget_is_editor (const Widget * w)
1176 return (w != NULL && w->callback == edit_callback);
1179 /* --------------------------------------------------------------------------------------------- */
1181 void
1182 edit_update_screen (WEdit * e)
1184 edit_scroll_screen_over_cursor (e);
1185 edit_update_curs_col (e);
1187 edit_status (e, (e->force & REDRAW_COMPLETELY) != 0 &&
1188 (void *) e == ((Widget *) e)->owner->current->data);
1190 /* pop all events for this window for internal handling */
1191 if (!is_idle ())
1192 e->force |= REDRAW_PAGE;
1193 else
1195 if ((e->force & REDRAW_COMPLETELY) != 0)
1196 e->force |= REDRAW_PAGE;
1197 edit_render_keypress (e);
1200 buttonbar_redraw (find_buttonbar (((Widget *) e)->owner));
1203 /* --------------------------------------------------------------------------------------------- */
1205 * Save current window size.
1207 * @param edit editor object
1210 void
1211 edit_save_size (WEdit * edit)
1213 edit->y_prev = edit->widget.y;
1214 edit->x_prev = edit->widget.x;
1215 edit->lines_prev = edit->widget.lines;
1216 edit->cols_prev = edit->widget.cols;
1219 /* --------------------------------------------------------------------------------------------- */
1221 * Create new editor window and insert it into editor screen.
1223 * @param h editor dialog (screen)
1224 * @param y y coordinate
1225 * @param x x coordinate
1226 * @param lines window height
1227 * @param cols window width
1228 * @param f file object
1229 * @param fline line number in file
1230 * @return TRUE if new window was successfully created and inserted into editor screen,
1231 * FALSE otherwise
1234 gboolean
1235 edit_add_window (Dlg_head * h, int y, int x, int lines, int cols, const vfs_path_t * f, long fline)
1237 WEdit *edit;
1238 Widget *w;
1240 edit = edit_init (NULL, y, x, lines, cols, f, fline);
1241 if (edit == NULL)
1242 return FALSE;
1244 w = (Widget *) edit;
1245 w->callback = edit_callback;
1246 w->mouse = edit_event;
1247 widget_want_cursor (*w, TRUE);
1249 add_widget (h, w);
1250 dlg_redraw (h);
1252 return TRUE;
1255 /* --------------------------------------------------------------------------------------------- */
1257 * Handle move/resize events.
1259 * @param edit editor object
1260 * @param command action id
1261 * @return TRUE if mouse actions was handled, FALSE otherwise
1264 gboolean
1265 edit_handle_move_resize (WEdit * edit, unsigned long command)
1267 gboolean ret = FALSE;
1269 if (edit->fullscreen)
1271 edit->drag_state = MCEDIT_DRAG_NORMAL;
1272 return ret;
1275 switch (edit->drag_state)
1277 case MCEDIT_DRAG_NORMAL:
1278 /* possible start move/resize */
1279 switch (command)
1281 case CK_WindowMove:
1282 edit->drag_state = MCEDIT_DRAG_MOVE;
1283 edit_save_size (edit);
1284 ret = TRUE;
1285 break;
1286 case CK_WindowResize:
1287 edit->drag_state = MCEDIT_DRAG_RESIZE;
1288 edit_save_size (edit);
1289 ret = TRUE;
1290 break;
1291 default:
1292 break;
1294 break;
1296 case MCEDIT_DRAG_MOVE:
1297 switch (command)
1299 case CK_WindowResize:
1300 edit->drag_state = MCEDIT_DRAG_RESIZE;
1301 ret = TRUE;
1302 break;
1303 case CK_Up:
1304 case CK_Down:
1305 case CK_Left:
1306 case CK_Right:
1307 edit_window_move (edit, command);
1308 ret = TRUE;
1309 break;
1310 case CK_Enter:
1311 case CK_WindowMove:
1312 edit->drag_state = MCEDIT_DRAG_NORMAL;
1313 /* redraw frame and status */
1314 edit_status (edit, TRUE);
1315 default:
1316 ret = TRUE;
1317 break;
1319 break;
1321 case MCEDIT_DRAG_RESIZE:
1322 switch (command)
1324 case CK_WindowMove:
1325 edit->drag_state = MCEDIT_DRAG_MOVE;
1326 ret = TRUE;
1327 break;
1328 case CK_Up:
1329 case CK_Down:
1330 case CK_Left:
1331 case CK_Right:
1332 edit_window_resize (edit, command);
1333 ret = TRUE;
1334 break;
1335 case CK_Enter:
1336 case CK_WindowResize:
1337 edit->drag_state = MCEDIT_DRAG_NORMAL;
1338 /* redraw frame and status */
1339 edit_status (edit, TRUE);
1340 default:
1341 ret = TRUE;
1342 break;
1344 break;
1347 return ret;
1350 /* --------------------------------------------------------------------------------------------- */
1352 * Toggle window fuulscreen mode.
1354 * @param edit editor object
1357 void
1358 edit_toggle_fullscreen (WEdit * edit)
1360 Dlg_head *h = ((Widget *) edit)->owner;
1362 edit->fullscreen = !edit->fullscreen;
1363 edit->force = REDRAW_COMPLETELY;
1365 if (!edit->fullscreen)
1366 edit_restore_size (edit);
1367 else
1369 edit_save_size (edit);
1370 widget_set_size ((Widget *) edit, h->y + 1, h->x, h->lines - 2, h->cols);
1371 edit->force |= REDRAW_PAGE;
1372 edit_update_screen (edit);
1376 /* --------------------------------------------------------------------------------------------- */