1 /*
2    Editor initialisation and callback handler.
3 
4    Copyright (C) 1996-2021
5    Free Software Foundation, Inc.
6 
7    Written by:
8    Paul Sheer, 1996, 1997
9    Andrew Borodin <aborodin@vmail.ru> 2012, 2013
10 
11    This file is part of the Midnight Commander.
12 
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.
17 
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.
22 
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/>.
25  */
26 
27 /** \file
28  *  \brief Source: editor initialisation and callback handler
29  *  \author Paul Sheer
30  *  \date 1996, 1997
31  */
32 
33 #include <config.h>
34 
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>
41 
42 #include "lib/global.h"
43 
44 #include "lib/tty/tty.h"        /* LINES, COLS */
45 #include "lib/tty/key.h"        /* is_idle() */
46 #include "lib/tty/color.h"      /* tty_setcolor() */
47 #include "lib/skin.h"
48 #include "lib/fileloc.h"        /* EDIT_HOME_DIR */
49 #include "lib/strutil.h"        /* str_term_trim() */
50 #include "lib/util.h"           /* mc_build_filename() */
51 #include "lib/widget.h"
52 #include "lib/mcconfig.h"
53 #include "lib/event.h"          /* mc_event_raise() */
54 #ifdef HAVE_CHARSET
55 #include "lib/charsets.h"
56 #endif
57 
58 #include "src/keymap.h"         /* keybind_lookup_keymap_command() */
59 #include "src/setup.h"          /* home_dir */
60 #include "src/execute.h"        /* toggle_subshell()  */
61 #include "src/filemanager/cmd.h"        /* save_setup_cmd()  */
62 #include "src/learn.h"          /* learn_keys() */
63 #include "src/args.h"           /* mcedit_arg_t */
64 
65 #include "edit-impl.h"
66 #include "editwidget.h"
67 #include "editmacros.h"         /* edit_execute_macro() */
68 #ifdef HAVE_ASPELL
69 #include "spell.h"
70 #endif
71 
72 /*** global variables ****************************************************************************/
73 
74 char *edit_window_state_char = NULL;
75 char *edit_window_close_char = NULL;
76 
77 /*** file scope macro definitions ****************************************************************/
78 
79 #define WINDOW_MIN_LINES (2 + 2)
80 #define WINDOW_MIN_COLS (2 + LINE_STATE_WIDTH + 2)
81 
82 /*** file scope type declarations ****************************************************************/
83 
84 /*** file scope variables ************************************************************************/
85 static unsigned int edit_dlg_init_refcounter = 0;
86 
87 /*** file scope functions ************************************************************************/
88 
89 static cb_ret_t edit_dialog_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm,
90                                       void *data);
91 
92 /* --------------------------------------------------------------------------------------------- */
93 /**
94  * Init the 'edit' subsystem
95  */
96 
97 static void
edit_dlg_init(void)98 edit_dlg_init (void)
99 {
100     edit_dlg_init_refcounter++;
101 
102     if (edit_dlg_init_refcounter == 1)
103     {
104         edit_window_state_char = mc_skin_get ("widget-editor", "window-state-char", "*");
105         edit_window_close_char = mc_skin_get ("widget-editor", "window-close-char", "X");
106 
107 #ifdef HAVE_ASPELL
108         aspell_init ();
109 #endif
110     }
111 }
112 
113 /* --------------------------------------------------------------------------------------------- */
114 /**
115  * Deinit the 'edit' subsystem
116  */
117 
118 static void
edit_dlg_deinit(void)119 edit_dlg_deinit (void)
120 {
121     if (edit_dlg_init_refcounter == 1)
122     {
123         g_free (edit_window_state_char);
124         g_free (edit_window_close_char);
125 
126 #ifdef HAVE_ASPELL
127         aspell_clean ();
128 #endif
129     }
130 
131     if (edit_dlg_init_refcounter != 0)
132         edit_dlg_init_refcounter--;
133 }
134 
135 /* --------------------------------------------------------------------------------------------- */
136 /**
137  * Show info about editor
138  */
139 
140 static void
edit_about(void)141 edit_about (void)
142 {
143     char *ver;
144 
145     ver = g_strdup_printf ("MCEdit %s", mc_global.mc_version);
146 
147     {
148         quick_widget_t quick_widgets[] = {
149             /* *INDENT-OFF* */
150             QUICK_LABEL (ver, NULL),
151             QUICK_SEPARATOR (TRUE),
152             QUICK_LABEL (N_("A user friendly text editor\n"
153                             "written for the Midnight Commander."), NULL),
154             QUICK_SEPARATOR (FALSE),
155             QUICK_LABEL (N_("Copyright (C) 1996-2021 the Free Software Foundation"), NULL),
156             QUICK_START_BUTTONS (TRUE, TRUE),
157             QUICK_BUTTON (N_("&OK"), B_ENTER, NULL, NULL),
158             QUICK_END
159             /* *INDENT-ON* */
160         };
161 
162         quick_dialog_t qdlg = {
163             -1, -1, 40,
164             N_("About"), "[Internal File Editor]",
165             quick_widgets, NULL, NULL
166         };
167 
168         quick_widgets[0].pos_flags = WPOS_KEEP_TOP | WPOS_CENTER_HORZ;
169         quick_widgets[2].pos_flags = WPOS_KEEP_TOP | WPOS_CENTER_HORZ;
170         quick_widgets[4].pos_flags = WPOS_KEEP_TOP | WPOS_CENTER_HORZ;
171 
172         (void) quick_dialog (&qdlg);
173     }
174 
175     g_free (ver);
176 }
177 
178 /* --------------------------------------------------------------------------------------------- */
179 /**
180  * Show a help window
181  */
182 
183 static void
edit_help(void)184 edit_help (void)
185 {
186     ev_help_t event_data = { NULL, "[Internal File Editor]" };
187     mc_event_raise (MCEVENT_GROUP_CORE, "help", &event_data);
188 }
189 
190 /* --------------------------------------------------------------------------------------------- */
191 /**
192  * Restore saved window size.
193  *
194  * @param edit editor object
195  */
196 
197 static void
edit_restore_size(WEdit * edit)198 edit_restore_size (WEdit * edit)
199 {
200     Widget *w = WIDGET (edit);
201 
202     edit->drag_state = MCEDIT_DRAG_NONE;
203     w->mouse.forced_capture = FALSE;
204     widget_set_size_rect (w, &edit->loc_prev);
205     widget_draw (WIDGET (w->owner));
206 }
207 
208 /* --------------------------------------------------------------------------------------------- */
209 /**
210  * Move window by one row or column in any direction.
211  *
212  * @param edit    editor object
213  * @param command direction (CK_Up, CK_Down, CK_Left, CK_Right)
214  */
215 
216 static void
edit_window_move(WEdit * edit,long command)217 edit_window_move (WEdit * edit, long command)
218 {
219     Widget *w = WIDGET (edit);
220     Widget *wh = WIDGET (w->owner);
221 
222     switch (command)
223     {
224     case CK_Up:
225         if (w->y > wh->y + 1)   /* menubar */
226             w->y--;
227         break;
228     case CK_Down:
229         if (w->y < wh->y + wh->lines - 2)       /* buttonbar */
230             w->y++;
231         break;
232     case CK_Left:
233         if (w->x + w->cols > wh->x)
234             w->x--;
235         break;
236     case CK_Right:
237         if (w->x < wh->x + wh->cols)
238             w->x++;
239         break;
240     default:
241         return;
242     }
243 
244     edit->force |= REDRAW_PAGE;
245     widget_draw (WIDGET (w->owner));
246 }
247 
248 /* --------------------------------------------------------------------------------------------- */
249 /**
250  * Resize window by one row or column in any direction.
251  *
252  * @param edit    editor object
253  * @param command direction (CK_Up, CK_Down, CK_Left, CK_Right)
254  */
255 
256 static void
edit_window_resize(WEdit * edit,long command)257 edit_window_resize (WEdit * edit, long command)
258 {
259     Widget *w = WIDGET (edit);
260     Widget *wh = WIDGET (w->owner);
261 
262     switch (command)
263     {
264     case CK_Up:
265         if (w->lines > WINDOW_MIN_LINES)
266             w->lines--;
267         break;
268     case CK_Down:
269         if (w->y + w->lines < wh->y + wh->lines - 1)    /* buttonbar */
270             w->lines++;
271         break;
272     case CK_Left:
273         if (w->cols > WINDOW_MIN_COLS)
274             w->cols--;
275         break;
276     case CK_Right:
277         if (w->x + w->cols < wh->x + wh->cols)
278             w->cols++;
279         break;
280     default:
281         return;
282     }
283 
284     edit->force |= REDRAW_COMPLETELY;
285     widget_draw (WIDGET (w->owner));
286 }
287 
288 /* --------------------------------------------------------------------------------------------- */
289 /**
290  * Get hotkey by number.
291  *
292  * @param n number
293  * @return hotkey
294  */
295 
296 static unsigned char
get_hotkey(int n)297 get_hotkey (int n)
298 {
299     return (n <= 9) ? '0' + n : 'a' + n - 10;
300 }
301 
302 /* --------------------------------------------------------------------------------------------- */
303 
304 static void
edit_window_list(const WDialog * h)305 edit_window_list (const WDialog * h)
306 {
307     const WGroup *g = CONST_GROUP (h);
308     const size_t offset = 2;    /* skip menu and buttonbar */
309     const size_t dlg_num = g_list_length (g->widgets) - offset;
310     int lines, cols;
311     Listbox *listbox;
312     GList *w;
313     WEdit *selected;
314     int i = 0;
315 
316     lines = MIN ((size_t) (LINES * 2 / 3), dlg_num);
317     cols = COLS * 2 / 3;
318 
319     listbox = create_listbox_window (lines, cols, _("Open files"), "[Open files]");
320 
321     for (w = g->widgets; w != NULL; w = g_list_next (w))
322         if (edit_widget_is_editor (CONST_WIDGET (w->data)))
323         {
324             WEdit *e = (WEdit *) w->data;
325             char *fname;
326 
327             if (e->filename_vpath == NULL)
328                 fname = g_strdup_printf ("%c [%s]", e->modified ? '*' : ' ', _("NoName"));
329             else
330                 fname =
331                     g_strdup_printf ("%c%s", e->modified ? '*' : ' ',
332                                      vfs_path_as_str (e->filename_vpath));
333 
334             listbox_add_item (listbox->list, LISTBOX_APPEND_AT_END, get_hotkey (i++),
335                               str_term_trim (fname, WIDGET (listbox->list)->cols - 2), e, FALSE);
336             g_free (fname);
337         }
338 
339     selected = run_listbox_with_data (listbox, g->current->data);
340     if (selected != NULL)
341         widget_select (WIDGET (selected));
342 }
343 
344 /* --------------------------------------------------------------------------------------------- */
345 
346 static char *
edit_get_shortcut(long command)347 edit_get_shortcut (long command)
348 {
349     const char *ext_map;
350     const char *shortcut = NULL;
351 
352     shortcut = keybind_lookup_keymap_shortcut (editor_map, command);
353     if (shortcut != NULL)
354         return g_strdup (shortcut);
355 
356     ext_map = keybind_lookup_keymap_shortcut (editor_map, CK_ExtendedKeyMap);
357     if (ext_map != NULL)
358         shortcut = keybind_lookup_keymap_shortcut (editor_x_map, command);
359     if (shortcut != NULL)
360         return g_strdup_printf ("%s %s", ext_map, shortcut);
361 
362     return NULL;
363 }
364 
365 /* --------------------------------------------------------------------------------------------- */
366 
367 static char *
edit_get_title(const WDialog * h,size_t len)368 edit_get_title (const WDialog * h, size_t len)
369 {
370     const WEdit *edit = find_editor (h);
371     const char *modified = edit->modified ? "(*) " : "    ";
372     const char *file_label;
373     char *filename;
374 
375     len -= 4;
376 
377     if (edit->filename_vpath == NULL)
378         filename = g_strdup (_("[NoName]"));
379     else
380         filename = g_strdup (vfs_path_as_str (edit->filename_vpath));
381 
382     file_label = str_term_trim (filename, len - str_term_width1 (_("Edit: ")));
383     g_free (filename);
384 
385     return g_strconcat (_("Edit: "), modified, file_label, (char *) NULL);
386 }
387 
388 /* --------------------------------------------------------------------------------------------- */
389 
390 static cb_ret_t
edit_dialog_command_execute(WDialog * h,long command)391 edit_dialog_command_execute (WDialog * h, long command)
392 {
393     WGroup *g = GROUP (h);
394     Widget *wh = WIDGET (h);
395     cb_ret_t ret = MSG_HANDLED;
396 
397     switch (command)
398     {
399     case CK_EditNew:
400         edit_add_window (h, wh->y + 1, wh->x, wh->lines - 2, wh->cols, NULL, 0);
401         break;
402     case CK_EditFile:
403         edit_load_cmd (h);
404         break;
405     case CK_History:
406         edit_load_file_from_history (h);
407         break;
408     case CK_EditSyntaxFile:
409         edit_load_syntax_file (h);
410         break;
411     case CK_EditUserMenu:
412         edit_load_menu_file (h);
413         break;
414     case CK_Close:
415         /* if there are no opened files anymore, close MC editor */
416         if (edit_widget_is_editor (CONST_WIDGET (g->current->data)) &&
417             edit_close_cmd ((WEdit *) g->current->data) && find_editor (h) == NULL)
418             dlg_stop (h);
419         break;
420     case CK_Help:
421         edit_help ();
422         /* edit->force |= REDRAW_COMPLETELY; */
423         break;
424     case CK_Menu:
425         edit_menu_cmd (h);
426         break;
427     case CK_Quit:
428     case CK_Cancel:
429         /* don't close editor due to SIGINT, but stop move/resize window */
430         {
431             Widget *w = WIDGET (g->current->data);
432 
433             if (edit_widget_is_editor (w) && ((WEdit *) w)->drag_state != MCEDIT_DRAG_NONE)
434                 edit_restore_size ((WEdit *) w);
435             else if (command == CK_Quit)
436                 dlg_stop (h);
437         }
438         break;
439     case CK_About:
440         edit_about ();
441         break;
442     case CK_SyntaxOnOff:
443         edit_syntax_onoff_cmd (h);
444         break;
445     case CK_ShowTabTws:
446         edit_show_tabs_tws_cmd (h);
447         break;
448     case CK_ShowMargin:
449         edit_show_margin_cmd (h);
450         break;
451     case CK_ShowNumbers:
452         edit_show_numbers_cmd (h);
453         break;
454     case CK_Refresh:
455         edit_refresh_cmd ();
456         break;
457     case CK_Shell:
458         toggle_subshell ();
459         break;
460     case CK_LearnKeys:
461         learn_keys ();
462         break;
463     case CK_WindowMove:
464     case CK_WindowResize:
465         if (edit_widget_is_editor (CONST_WIDGET (g->current->data)))
466             edit_handle_move_resize ((WEdit *) g->current->data, command);
467         break;
468     case CK_WindowList:
469         edit_window_list (h);
470         break;
471     case CK_WindowNext:
472         group_select_next_widget (g);
473         break;
474     case CK_WindowPrev:
475         group_select_prev_widget (g);
476         break;
477     case CK_Options:
478         edit_options_dialog (h);
479         break;
480     case CK_OptionsSaveMode:
481         edit_save_mode_cmd ();
482         break;
483     case CK_SaveSetup:
484         save_setup_cmd ();
485         break;
486     default:
487         ret = MSG_NOT_HANDLED;
488         break;
489     }
490 
491     return ret;
492 }
493 
494 /* --------------------------------------------------------------------------------------------- */
495 /*
496  * Translate the keycode into either 'command' or 'char_for_insertion'.
497  * 'command' is one of the editor commands from cmddef.h.
498  */
499 
500 static gboolean
edit_translate_key(WEdit * edit,long x_key,int * cmd,int * ch)501 edit_translate_key (WEdit * edit, long x_key, int *cmd, int *ch)
502 {
503     Widget *w = WIDGET (edit);
504     long command = CK_InsertChar;
505     int char_for_insertion = -1;
506 
507     /* an ordinary insertable character */
508     if (!w->ext_mode && x_key < 256)
509     {
510 #ifndef HAVE_CHARSET
511         if (is_printable (x_key))
512         {
513             char_for_insertion = x_key;
514             goto fin;
515         }
516 #else
517         int c;
518 
519         if (edit->charpoint >= 4)
520         {
521             edit->charpoint = 0;
522             edit->charbuf[edit->charpoint] = '\0';
523         }
524         if (edit->charpoint < 4)
525         {
526             edit->charbuf[edit->charpoint++] = x_key;
527             edit->charbuf[edit->charpoint] = '\0';
528         }
529 
530         /* input from 8-bit locale */
531         if (!mc_global.utf8_display)
532         {
533             /* source in 8-bit codeset */
534             c = convert_from_input_c (x_key);
535 
536             if (is_printable (c))
537             {
538                 if (!edit->utf8)
539                     char_for_insertion = c;
540                 else
541                     char_for_insertion = convert_from_8bit_to_utf_c2 ((char) x_key);
542                 goto fin;
543             }
544         }
545         else
546         {
547             /* UTF-8 locale */
548             int res;
549 
550             res = str_is_valid_char (edit->charbuf, edit->charpoint);
551             if (res < 0 && res != -2)
552             {
553                 edit->charpoint = 0;    /* broken multibyte char, skip */
554                 goto fin;
555             }
556 
557             if (edit->utf8)
558             {
559                 /* source in UTF-8 codeset */
560                 if (res < 0)
561                 {
562                     char_for_insertion = x_key;
563                     goto fin;
564                 }
565 
566                 edit->charbuf[edit->charpoint] = '\0';
567                 edit->charpoint = 0;
568                 if (g_unichar_isprint (g_utf8_get_char (edit->charbuf)))
569                 {
570                     char_for_insertion = x_key;
571                     goto fin;
572                 }
573             }
574             else
575             {
576                 /* 8-bit source */
577                 if (res < 0)
578                 {
579                     /* not finised multibyte input (in meddle multibyte utf-8 char) */
580                     goto fin;
581                 }
582 
583                 if (g_unichar_isprint (g_utf8_get_char (edit->charbuf)))
584                 {
585                     c = convert_from_utf_to_current (edit->charbuf);
586                     edit->charbuf[0] = '\0';
587                     edit->charpoint = 0;
588                     char_for_insertion = c;
589                     goto fin;
590                 }
591 
592                 /* unprinteble utf input, skip it */
593                 edit->charbuf[0] = '\0';
594                 edit->charpoint = 0;
595             }
596         }
597 #endif /* HAVE_CHARSET */
598     }
599 
600     /* Commands specific to the key emulation */
601     command = widget_lookup_key (w, x_key);
602     if (command == CK_IgnoreKey)
603         command = CK_InsertChar;
604 
605   fin:
606     *cmd = (int) command;       /* FIXME */
607     *ch = char_for_insertion;
608 
609     return !(command == CK_InsertChar && char_for_insertion == -1);
610 }
611 
612 
613 /* --------------------------------------------------------------------------------------------- */
614 
615 static inline void
edit_quit(WDialog * h)616 edit_quit (WDialog * h)
617 {
618     GList *l;
619     WEdit *e = NULL;
620     GSList *m = NULL;
621     GSList *me;
622 
623     /* don't stop the dialog before final decision */
624     widget_set_state (WIDGET (h), WST_ACTIVE, TRUE);
625 
626     /* check window state and get modified files */
627     for (l = GROUP (h)->widgets; l != NULL; l = g_list_next (l))
628         if (edit_widget_is_editor (CONST_WIDGET (l->data)))
629         {
630             e = (WEdit *) l->data;
631 
632             if (e->drag_state != MCEDIT_DRAG_NONE)
633             {
634                 edit_restore_size (e);
635                 g_slist_free (m);
636                 return;
637             }
638 
639             /* create separate list because widget_select()
640                changes the window position in Z order */
641             if (e->modified)
642                 m = g_slist_prepend (m, l->data);
643         }
644 
645     for (me = m; me != NULL; me = g_slist_next (me))
646     {
647         e = (WEdit *) me->data;
648 
649         widget_select (WIDGET (e));
650 
651         if (!edit_ok_to_exit (e))
652             break;
653     }
654 
655     /* if all files were checked, quit editor */
656     if (me == NULL)
657         dlg_stop (h);
658 
659     g_slist_free (m);
660 }
661 
662 /* --------------------------------------------------------------------------------------------- */
663 
664 static inline void
edit_set_buttonbar(WEdit * edit,WButtonBar * bb)665 edit_set_buttonbar (WEdit * edit, WButtonBar * bb)
666 {
667     Widget *w = WIDGET (edit);
668 
669     buttonbar_set_label (bb, 1, Q_ ("ButtonBar|Help"), w->keymap, NULL);
670     buttonbar_set_label (bb, 2, Q_ ("ButtonBar|Save"), w->keymap, w);
671     buttonbar_set_label (bb, 3, Q_ ("ButtonBar|Mark"), w->keymap, w);
672     buttonbar_set_label (bb, 4, Q_ ("ButtonBar|Replac"), w->keymap, w);
673     buttonbar_set_label (bb, 5, Q_ ("ButtonBar|Copy"), w->keymap, w);
674     buttonbar_set_label (bb, 6, Q_ ("ButtonBar|Move"), w->keymap, w);
675     buttonbar_set_label (bb, 7, Q_ ("ButtonBar|Search"), w->keymap, w);
676     buttonbar_set_label (bb, 8, Q_ ("ButtonBar|Delete"), w->keymap, w);
677     buttonbar_set_label (bb, 9, Q_ ("ButtonBar|PullDn"), w->keymap, NULL);
678     buttonbar_set_label (bb, 10, Q_ ("ButtonBar|Quit"), w->keymap, NULL);
679 }
680 
681 /* --------------------------------------------------------------------------------------------- */
682 
683 static void
edit_total_update(WEdit * edit)684 edit_total_update (WEdit * edit)
685 {
686     edit_find_bracket (edit);
687     edit->force |= REDRAW_COMPLETELY;
688     edit_update_curs_row (edit);
689     edit_update_screen (edit);
690 }
691 
692 /* --------------------------------------------------------------------------------------------- */
693 
694 static gboolean
edit_update_cursor(WEdit * edit,const mouse_event_t * event)695 edit_update_cursor (WEdit * edit, const mouse_event_t * event)
696 {
697     int x, y;
698     gboolean done;
699 
700     x = event->x - (edit->fullscreen ? 0 : 1);
701     y = event->y - (edit->fullscreen ? 0 : 1);
702 
703     if (edit->mark2 != -1 && event->msg == MSG_MOUSE_UP)
704         return TRUE;            /* don't do anything */
705 
706     if (event->msg == MSG_MOUSE_DOWN || event->msg == MSG_MOUSE_UP)
707         edit_push_key_press (edit);
708 
709     if (!option_cursor_beyond_eol)
710         edit->prev_col = x - edit->start_col - option_line_state_width;
711     else
712     {
713         long line_len;
714 
715         line_len =
716             edit_move_forward3 (edit, edit_buffer_get_current_bol (&edit->buffer), 0,
717                                 edit_buffer_get_current_eol (&edit->buffer));
718 
719         if (x > line_len - 1)
720         {
721             edit->over_col = x - line_len - edit->start_col - option_line_state_width;
722             edit->prev_col = line_len;
723         }
724         else
725         {
726             edit->over_col = 0;
727             edit->prev_col = x - option_line_state_width - edit->start_col;
728         }
729     }
730 
731     if (y > edit->curs_row)
732         edit_move_down (edit, y - edit->curs_row, FALSE);
733     else if (y < edit->curs_row)
734         edit_move_up (edit, edit->curs_row - y, FALSE);
735     else
736         edit_move_to_prev_col (edit, edit_buffer_get_current_bol (&edit->buffer));
737 
738     if (event->msg == MSG_MOUSE_CLICK)
739     {
740         edit_mark_cmd (edit, TRUE);     /* reset */
741         edit->highlight = 0;
742     }
743 
744     done = (event->msg != MSG_MOUSE_DRAG);
745     if (done)
746         edit_mark_cmd (edit, FALSE);
747 
748     return done;
749 }
750 
751 /* --------------------------------------------------------------------------------------------- */
752 /** Callback for the edit dialog */
753 
754 static cb_ret_t
edit_dialog_callback(Widget * w,Widget * sender,widget_msg_t msg,int parm,void * data)755 edit_dialog_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
756 {
757     WGroup *g = GROUP (w);
758     WDialog *h = DIALOG (w);
759 
760     switch (msg)
761     {
762     case MSG_INIT:
763         edit_dlg_init ();
764         return MSG_HANDLED;
765 
766     case MSG_RESIZE:
767         dlg_default_callback (w, NULL, MSG_RESIZE, 0, NULL);
768         menubar_arrange (find_menubar (h));
769         return MSG_HANDLED;
770 
771     case MSG_ACTION:
772         {
773             /* Handle shortcuts, menu, and buttonbar. */
774 
775             cb_ret_t result;
776 
777             result = edit_dialog_command_execute (h, parm);
778 
779             /* We forward any commands coming from the menu, and which haven't been
780                handled by the dialog, to the focused WEdit window. */
781             if (result == MSG_NOT_HANDLED && sender == WIDGET (find_menubar (h)))
782                 result = send_message (g->current->data, NULL, MSG_ACTION, parm, NULL);
783 
784             return result;
785         }
786 
787     case MSG_KEY:
788         {
789             Widget *we = WIDGET (g->current->data);
790             cb_ret_t ret = MSG_NOT_HANDLED;
791 
792             if (edit_widget_is_editor (we))
793             {
794                 gboolean ext_mode;
795                 long command;
796 
797                 /* keep and then extmod flag */
798                 ext_mode = we->ext_mode;
799                 command = widget_lookup_key (we, parm);
800                 we->ext_mode = ext_mode;
801 
802                 if (command == CK_IgnoreKey)
803                     we->ext_mode = FALSE;
804                 else
805                 {
806                     ret = edit_dialog_command_execute (h, command);
807                     /* if command was not handled, keep the extended mode
808                        for the further key processing */
809                     if (ret == MSG_HANDLED)
810                         we->ext_mode = FALSE;
811                 }
812             }
813 
814             /*
815              * Due to the "end of bracket" escape the editor sees input with is_idle() == false
816              * (expects more characters) and hence doesn't yet refresh the screen, but then
817              * no further characters arrive (there's only an "end of bracket" which is swallowed
818              * by tty_get_event()), so you end up with a screen that's not refreshed after pasting.
819              * So let's trigger an IDLE signal.
820              */
821             if (!is_idle ())
822                 widget_idle (w, TRUE);
823             return ret;
824         }
825 
826         /* hardcoded menu hotkeys (see edit_drop_hotkey_menu) */
827     case MSG_UNHANDLED_KEY:
828         return edit_drop_hotkey_menu (h, parm) ? MSG_HANDLED : MSG_NOT_HANDLED;
829 
830     case MSG_VALIDATE:
831         edit_quit (h);
832         return MSG_HANDLED;
833 
834     case MSG_DESTROY:
835         edit_dlg_deinit ();
836         return MSG_HANDLED;
837 
838     case MSG_IDLE:
839         widget_idle (w, FALSE);
840         return send_message (g->current->data, NULL, MSG_IDLE, 0, NULL);
841 
842     default:
843         return dlg_default_callback (w, sender, msg, parm, data);
844     }
845 }
846 
847 /* --------------------------------------------------------------------------------------------- */
848 
849 /**
850  * Handle mouse events of editor screen.
851  *
852  * @param w Widget object (the editor)
853  * @param msg mouse event message
854  * @param event mouse event data
855  */
856 static void
edit_dialog_mouse_callback(Widget * w,mouse_msg_t msg,mouse_event_t * event)857 edit_dialog_mouse_callback (Widget * w, mouse_msg_t msg, mouse_event_t * event)
858 {
859     gboolean unhandled = TRUE;
860 
861     if (msg == MSG_MOUSE_DOWN && event->y == 0)
862     {
863         WGroup *g = GROUP (w);
864         WDialog *h = DIALOG (w);
865         WMenuBar *b;
866 
867         b = find_menubar (h);
868 
869         if (!widget_get_state (WIDGET (b), WST_FOCUSED))
870         {
871             /* menubar */
872 
873             GList *l;
874             GList *top = NULL;
875             int x;
876 
877             /* Try find top fullscreen window */
878             for (l = g->widgets; l != NULL; l = g_list_next (l))
879                 if (edit_widget_is_editor (CONST_WIDGET (l->data))
880                     && ((WEdit *) l->data)->fullscreen)
881                     top = l;
882 
883             /* Handle fullscreen/close buttons in the top line */
884             x = w->cols - 6;
885 
886             if (top != NULL && event->x >= x)
887             {
888                 WEdit *e = (WEdit *) top->data;
889 
890                 if (top != g->current)
891                 {
892                     /* Window is not active. Activate it */
893                     widget_select (WIDGET (e));
894                 }
895 
896                 /* Handle buttons */
897                 if (event->x - x <= 2)
898                     edit_toggle_fullscreen (e);
899                 else
900                     send_message (h, NULL, MSG_ACTION, CK_Close, NULL);
901 
902                 unhandled = FALSE;
903             }
904 
905             if (unhandled)
906                 menubar_activate (b, drop_menus, -1);
907         }
908     }
909 
910     /* Continue handling of unhandled event in window or menu */
911     event->result.abort = unhandled;
912 }
913 
914 /* --------------------------------------------------------------------------------------------- */
915 
916 static cb_ret_t
edit_dialog_bg_callback(Widget * w,Widget * sender,widget_msg_t msg,int parm,void * data)917 edit_dialog_bg_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
918 {
919     switch (msg)
920     {
921     case MSG_INIT:
922         {
923             Widget *wo = WIDGET (w->owner);
924 
925             w->y = wo->y + 1;
926             w->x = wo->x;
927             w->lines = wo->lines - 2;
928             w->cols = wo->cols;
929             w->pos_flags |= WPOS_KEEP_ALL;
930 
931             return MSG_HANDLED;
932         }
933 
934     default:
935         return background_callback (w, sender, msg, parm, data);
936     }
937 }
938 
939 /* --------------------------------------------------------------------------------------------- */
940 
941 static cb_ret_t
edit_callback(Widget * w,Widget * sender,widget_msg_t msg,int parm,void * data)942 edit_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
943 {
944     WEdit *e = (WEdit *) w;
945 
946     switch (msg)
947     {
948     case MSG_FOCUS:
949         edit_set_buttonbar (e, find_buttonbar (DIALOG (w->owner)));
950         return MSG_HANDLED;
951 
952     case MSG_DRAW:
953         e->force |= REDRAW_COMPLETELY;
954         edit_update_screen (e);
955         return MSG_HANDLED;
956 
957     case MSG_KEY:
958         {
959             int cmd, ch;
960             cb_ret_t ret = MSG_NOT_HANDLED;
961 
962             /* The user may override the access-keys for the menu bar. */
963             if (macro_index == -1 && edit_execute_macro (e, parm))
964             {
965                 edit_update_screen (e);
966                 ret = MSG_HANDLED;
967             }
968             else if (edit_translate_key (e, parm, &cmd, &ch))
969             {
970                 edit_execute_key_command (e, cmd, ch);
971                 edit_update_screen (e);
972                 ret = MSG_HANDLED;
973             }
974 
975             return ret;
976         }
977 
978     case MSG_ACTION:
979         /* command from menubar or buttonbar */
980         edit_execute_key_command (e, parm, -1);
981         edit_update_screen (e);
982         return MSG_HANDLED;
983 
984     case MSG_CURSOR:
985         {
986             int y, x;
987 
988             y = (e->fullscreen ? 0 : 1) + EDIT_TEXT_VERTICAL_OFFSET + e->curs_row;
989             x = (e->fullscreen ? 0 : 1) + EDIT_TEXT_HORIZONTAL_OFFSET + option_line_state_width +
990                 e->curs_col + e->start_col + e->over_col;
991 
992             widget_gotoyx (w, y, x);
993             return MSG_HANDLED;
994         }
995 
996     case MSG_IDLE:
997         edit_update_screen (e);
998         return MSG_HANDLED;
999 
1000     case MSG_DESTROY:
1001         edit_clean (e);
1002         return MSG_HANDLED;
1003 
1004     default:
1005         return widget_default_callback (w, sender, msg, parm, data);
1006     }
1007 }
1008 
1009 /* --------------------------------------------------------------------------------------------- */
1010 
1011 /**
1012  * Handle move/resize mouse events.
1013  */
1014 static void
edit_mouse_handle_move_resize(Widget * w,mouse_msg_t msg,mouse_event_t * event)1015 edit_mouse_handle_move_resize (Widget * w, mouse_msg_t msg, mouse_event_t * event)
1016 {
1017     WEdit *edit = (WEdit *) (w);
1018     Widget *h = WIDGET (w->owner);
1019     int global_x, global_y;
1020 
1021     if (msg == MSG_MOUSE_UP)
1022     {
1023         /* Exit move/resize mode. */
1024         edit_execute_cmd (edit, CK_Enter, -1);
1025         edit_update_screen (edit);      /* Paint the buttonbar over our possibly overlapping frame. */
1026         return;
1027     }
1028 
1029     if (msg != MSG_MOUSE_DRAG)
1030         /**
1031          * We ignore any other events. Specifically, MSG_MOUSE_DOWN.
1032          *
1033          * When the move/resize is initiated by the menu, we let the user
1034          * stop it by clicking with the mouse. Which is why we don't want
1035          * a mouse down to affect the window.
1036          */
1037         return;
1038 
1039     /* Convert point to global coordinates for easier calculations. */
1040     global_x = event->x + w->x;
1041     global_y = event->y + w->y;
1042 
1043     /* Clamp the point to the dialog's client area. */
1044     global_y = CLAMP (global_y, h->y + 1, h->y + h->lines - 2); /* Status line, buttonbar */
1045     global_x = CLAMP (global_x, h->x, h->x + h->cols - 1);      /* Currently a no-op, as the dialog has no left/right margins. */
1046 
1047     if (edit->drag_state == MCEDIT_DRAG_MOVE)
1048     {
1049         w->y = global_y;
1050         w->x = global_x - edit->drag_state_start;
1051     }
1052     else if (edit->drag_state == MCEDIT_DRAG_RESIZE)
1053     {
1054         w->lines = MAX (WINDOW_MIN_LINES, global_y - w->y + 1);
1055         w->cols = MAX (WINDOW_MIN_COLS, global_x - w->x + 1);
1056     }
1057 
1058     edit->force |= REDRAW_COMPLETELY;   /* Not really needed as WEdit's MSG_DRAW already does this. */
1059 
1060     /* We draw the whole dialog because dragging/resizing exposes area beneath. */
1061     widget_draw (WIDGET (w->owner));
1062 }
1063 
1064 /* --------------------------------------------------------------------------------------------- */
1065 
1066 /**
1067  * Handle mouse events of editor window
1068  *
1069  * @param w Widget object (the editor window)
1070  * @param msg mouse event message
1071  * @param event mouse event data
1072  */
1073 static void
edit_mouse_callback(Widget * w,mouse_msg_t msg,mouse_event_t * event)1074 edit_mouse_callback (Widget * w, mouse_msg_t msg, mouse_event_t * event)
1075 {
1076     WEdit *edit = (WEdit *) w;
1077     /* buttons' distance from right edge */
1078     int dx = edit->fullscreen ? 0 : 2;
1079     /* location of 'Close' and 'Toggle fullscreen' pictograms */
1080     int close_x, toggle_fullscreen_x;
1081 
1082     close_x = (w->cols - 1) - dx - 1;
1083     toggle_fullscreen_x = close_x - 3;
1084 
1085     if (edit->drag_state != MCEDIT_DRAG_NONE)
1086     {
1087         /* window is being resized/moved */
1088         edit_mouse_handle_move_resize (w, msg, event);
1089         return;
1090     }
1091 
1092     /* If it's the last line on the screen, we abort the event to make the
1093      * system channel it to the overlapping buttonbar instead. We have to do
1094      * this because a WEdit has the WOP_TOP_SELECT flag, which makes it above
1095      * the buttonbar in Z-order. */
1096     if (msg == MSG_MOUSE_DOWN && (event->y + w->y == LINES - 1))
1097     {
1098         event->result.abort = TRUE;
1099         return;
1100     }
1101 
1102     switch (msg)
1103     {
1104     case MSG_MOUSE_DOWN:
1105         widget_select (w);
1106         edit_update_curs_row (edit);
1107         edit_update_curs_col (edit);
1108 
1109         if (!edit->fullscreen)
1110         {
1111             if (event->y == 0)
1112             {
1113                 if (event->x >= close_x - 1 && event->x <= close_x + 1)
1114                     ;           /* do nothing (see MSG_MOUSE_CLICK) */
1115                 else if (event->x >= toggle_fullscreen_x - 1 && event->x <= toggle_fullscreen_x + 1)
1116                     ;           /* do nothing (see MSG_MOUSE_CLICK) */
1117                 else
1118                 {
1119                     /* start window move */
1120                     edit_execute_cmd (edit, CK_WindowMove, -1);
1121                     edit_update_screen (edit);  /* Paint the buttonbar over our possibly overlapping frame. */
1122                     edit->drag_state_start = event->x;
1123                 }
1124                 break;
1125             }
1126 
1127             if (event->y == w->lines - 1 && event->x == w->cols - 1)
1128             {
1129                 /* bottom-right corner -- start window resize */
1130                 edit_execute_cmd (edit, CK_WindowResize, -1);
1131                 break;
1132             }
1133         }
1134 
1135         MC_FALLTHROUGH;         /* to start/stop text selection */
1136 
1137     case MSG_MOUSE_UP:
1138         edit_update_cursor (edit, event);
1139         edit_total_update (edit);
1140         break;
1141 
1142     case MSG_MOUSE_CLICK:
1143         if (event->y == 0)
1144         {
1145             if (event->x >= close_x - 1 && event->x <= close_x + 1)
1146                 send_message (w->owner, NULL, MSG_ACTION, CK_Close, NULL);
1147             else if (event->x >= toggle_fullscreen_x - 1 && event->x <= toggle_fullscreen_x + 1)
1148                 edit_toggle_fullscreen (edit);
1149             else if (!edit->fullscreen && event->count == GPM_DOUBLE)
1150                 /* double click on top line (toggle fullscreen) */
1151                 edit_toggle_fullscreen (edit);
1152         }
1153         else if (event->count == GPM_DOUBLE)
1154         {
1155             /* double click */
1156             edit_mark_current_word_cmd (edit);
1157             edit_total_update (edit);
1158         }
1159         else if (event->count == GPM_TRIPLE)
1160         {
1161             /* triple click: works in GPM only, not in xterm */
1162             edit_mark_current_line_cmd (edit);
1163             edit_total_update (edit);
1164         }
1165         break;
1166 
1167     case MSG_MOUSE_DRAG:
1168         edit_update_cursor (edit, event);
1169         edit_total_update (edit);
1170         break;
1171 
1172     case MSG_MOUSE_SCROLL_UP:
1173         edit_move_up (edit, 2, TRUE);
1174         edit_total_update (edit);
1175         break;
1176 
1177     case MSG_MOUSE_SCROLL_DOWN:
1178         edit_move_down (edit, 2, TRUE);
1179         edit_total_update (edit);
1180         break;
1181 
1182     default:
1183         break;
1184     }
1185 }
1186 
1187 /* --------------------------------------------------------------------------------------------- */
1188 /*** public functions ****************************************************************************/
1189 /* --------------------------------------------------------------------------------------------- */
1190 /**
1191  * Edit one file.
1192  *
1193  * @param file_vpath file object
1194  * @param line       line number
1195  * @return TRUE if no errors was occurred, FALSE otherwise
1196  */
1197 
1198 gboolean
edit_file(const vfs_path_t * file_vpath,long line)1199 edit_file (const vfs_path_t * file_vpath, long line)
1200 {
1201     mcedit_arg_t arg = { (vfs_path_t *) file_vpath, line };
1202     GList *files;
1203     gboolean ok;
1204 
1205     files = g_list_prepend (NULL, &arg);
1206     ok = edit_files (files);
1207     g_list_free (files);
1208 
1209     return ok;
1210 }
1211 
1212 /* --------------------------------------------------------------------------------------------- */
1213 
1214 gboolean
edit_files(const GList * files)1215 edit_files (const GList * files)
1216 {
1217     static gboolean made_directory = FALSE;
1218     WDialog *edit_dlg;
1219     WGroup *g;
1220     WMenuBar *menubar;
1221     Widget *w, *wd;
1222     const GList *file;
1223     gboolean ok = FALSE;
1224 
1225     if (!made_directory)
1226     {
1227         char *dir;
1228 
1229         dir = mc_build_filename (mc_config_get_cache_path (), EDIT_HOME_DIR, (char *) NULL);
1230         made_directory = (mkdir (dir, 0700) != -1 || errno == EEXIST);
1231         g_free (dir);
1232 
1233         dir = mc_build_filename (mc_config_get_path (), EDIT_HOME_DIR, (char *) NULL);
1234         made_directory = (mkdir (dir, 0700) != -1 || errno == EEXIST);
1235         g_free (dir);
1236 
1237         dir = mc_build_filename (mc_config_get_data_path (), EDIT_HOME_DIR, (char *) NULL);
1238         made_directory = (mkdir (dir, 0700) != -1 || errno == EEXIST);
1239         g_free (dir);
1240     }
1241 
1242     /* Create a new dialog and add it widgets to it */
1243     edit_dlg =
1244         dlg_create (FALSE, 0, 0, 1, 1, WPOS_FULLSCREEN, FALSE, NULL, edit_dialog_callback,
1245                     edit_dialog_mouse_callback, "[Internal File Editor]", NULL);
1246     wd = WIDGET (edit_dlg);
1247     widget_want_tab (wd, TRUE);
1248     wd->keymap = editor_map;
1249     wd->ext_keymap = editor_x_map;
1250 
1251     edit_dlg->get_shortcut = edit_get_shortcut;
1252     edit_dlg->get_title = edit_get_title;
1253 
1254     g = GROUP (edit_dlg);
1255 
1256     edit_dlg->bg =
1257         WIDGET (background_new
1258                 (1, 0, wd->lines - 2, wd->cols, EDITOR_BACKGROUND, ' ', edit_dialog_bg_callback));
1259     group_add_widget (g, edit_dlg->bg);
1260 
1261     menubar = menubar_new (NULL);
1262     w = WIDGET (menubar);
1263     group_add_widget_autopos (g, w, w->pos_flags, NULL);
1264     edit_init_menu (menubar);
1265 
1266     w = WIDGET (buttonbar_new ());
1267     group_add_widget_autopos (g, w, w->pos_flags, NULL);
1268 
1269     for (file = files; file != NULL; file = g_list_next (file))
1270     {
1271         mcedit_arg_t *f = (mcedit_arg_t *) file->data;
1272         gboolean f_ok;
1273 
1274         f_ok = edit_add_window (edit_dlg, wd->y + 1, wd->x, wd->lines - 2, wd->cols, f->file_vpath,
1275                                 f->line_number);
1276         /* at least one file has been opened succefully */
1277         ok = ok || f_ok;
1278     }
1279 
1280     if (ok)
1281         dlg_run (edit_dlg);
1282 
1283     if (!ok || widget_get_state (wd, WST_CLOSED))
1284         widget_destroy (wd);
1285 
1286     return ok;
1287 }
1288 
1289 /* --------------------------------------------------------------------------------------------- */
1290 
1291 const char *
edit_get_file_name(const WEdit * edit)1292 edit_get_file_name (const WEdit * edit)
1293 {
1294     return vfs_path_as_str (edit->filename_vpath);
1295 }
1296 
1297 /* --------------------------------------------------------------------------------------------- */
1298 
1299 WEdit *
find_editor(const WDialog * h)1300 find_editor (const WDialog * h)
1301 {
1302     const WGroup *g = CONST_GROUP (h);
1303 
1304     if (edit_widget_is_editor (CONST_WIDGET (g->current->data)))
1305         return (WEdit *) g->current->data;
1306     return (WEdit *) widget_find_by_type (CONST_WIDGET (h), edit_callback);
1307 }
1308 
1309 /* --------------------------------------------------------------------------------------------- */
1310 /**
1311  * Check if widget is an WEdit class.
1312  *
1313  * @param w probably editor object
1314  * @return TRUE if widget is an WEdit class, FALSE otherwise
1315  */
1316 
1317 gboolean
edit_widget_is_editor(const Widget * w)1318 edit_widget_is_editor (const Widget * w)
1319 {
1320     return (w != NULL && w->callback == edit_callback);
1321 }
1322 
1323 /* --------------------------------------------------------------------------------------------- */
1324 
1325 void
edit_update_screen(WEdit * e)1326 edit_update_screen (WEdit * e)
1327 {
1328     edit_scroll_screen_over_cursor (e);
1329     edit_update_curs_col (e);
1330     edit_status (e, widget_get_state (WIDGET (e), WST_FOCUSED));
1331 
1332     /* pop all events for this window for internal handling */
1333     if (!is_idle ())
1334         e->force |= REDRAW_PAGE;
1335     else
1336     {
1337         if ((e->force & REDRAW_COMPLETELY) != 0)
1338             e->force |= REDRAW_PAGE;
1339         edit_render_keypress (e);
1340     }
1341 
1342     widget_draw (WIDGET (find_buttonbar (DIALOG (WIDGET (e)->owner))));
1343 }
1344 
1345 /* --------------------------------------------------------------------------------------------- */
1346 /**
1347  * Save current window size.
1348  *
1349  * @param edit editor object
1350  */
1351 
1352 void
edit_save_size(WEdit * edit)1353 edit_save_size (WEdit * edit)
1354 {
1355     Widget *w = WIDGET (edit);
1356 
1357     rect_init (&edit->loc_prev, w->y, w->x, w->lines, w->cols);
1358 }
1359 
1360 /* --------------------------------------------------------------------------------------------- */
1361 /**
1362  * Create new editor window and insert it into editor screen.
1363  *
1364  * @param h     editor dialog (screen)
1365  * @param y     y coordinate
1366  * @param x     x coordinate
1367  * @param lines window height
1368  * @param cols  window width
1369  * @param f     file object
1370  * @param fline line number in file
1371  * @return TRUE if new window was successfully created and inserted into editor screen,
1372  *         FALSE otherwise
1373  */
1374 
1375 gboolean
edit_add_window(WDialog * h,int y,int x,int lines,int cols,const vfs_path_t * f,long fline)1376 edit_add_window (WDialog * h, int y, int x, int lines, int cols, const vfs_path_t * f, long fline)
1377 {
1378     WEdit *edit;
1379     Widget *w;
1380 
1381     edit = edit_init (NULL, y, x, lines, cols, f, fline);
1382     if (edit == NULL)
1383         return FALSE;
1384 
1385     w = WIDGET (edit);
1386     w->callback = edit_callback;
1387     w->mouse_callback = edit_mouse_callback;
1388 
1389     group_add_widget_autopos (GROUP (h), w, WPOS_KEEP_ALL, NULL);
1390     edit_set_buttonbar (edit, find_buttonbar (h));
1391     widget_draw (WIDGET (h));
1392 
1393     return TRUE;
1394 }
1395 
1396 /* --------------------------------------------------------------------------------------------- */
1397 /**
1398  * Handle move/resize events.
1399  *
1400  * @param edit    editor object
1401  * @param command action id
1402  * @return TRUE if the action was handled, FALSE otherwise
1403  */
1404 
1405 gboolean
edit_handle_move_resize(WEdit * edit,long command)1406 edit_handle_move_resize (WEdit * edit, long command)
1407 {
1408     Widget *w = WIDGET (edit);
1409     gboolean ret = FALSE;
1410 
1411     if (edit->fullscreen)
1412     {
1413         edit->drag_state = MCEDIT_DRAG_NONE;
1414         w->mouse.forced_capture = FALSE;
1415         return ret;
1416     }
1417 
1418     switch (edit->drag_state)
1419     {
1420     case MCEDIT_DRAG_NONE:
1421         /* possible start move/resize */
1422         switch (command)
1423         {
1424         case CK_WindowMove:
1425             edit->drag_state = MCEDIT_DRAG_MOVE;
1426             edit_save_size (edit);
1427             edit_status (edit, TRUE);   /* redraw frame and status */
1428             /**
1429              * If a user initiates a move by the menu, not by the mouse, we
1430              * make a subsequent mouse drag pull the frame from its middle.
1431              * (We can instead choose '0' to pull it from the corner.)
1432              */
1433             edit->drag_state_start = w->cols / 2;
1434             ret = TRUE;
1435             break;
1436         case CK_WindowResize:
1437             edit->drag_state = MCEDIT_DRAG_RESIZE;
1438             edit_save_size (edit);
1439             edit_status (edit, TRUE);   /* redraw frame and status */
1440             ret = TRUE;
1441             break;
1442         default:
1443             break;
1444         }
1445         break;
1446 
1447     case MCEDIT_DRAG_MOVE:
1448         switch (command)
1449         {
1450         case CK_WindowResize:
1451             edit->drag_state = MCEDIT_DRAG_RESIZE;
1452             ret = TRUE;
1453             break;
1454         case CK_Up:
1455         case CK_Down:
1456         case CK_Left:
1457         case CK_Right:
1458             edit_window_move (edit, command);
1459             ret = TRUE;
1460             break;
1461         case CK_Enter:
1462         case CK_WindowMove:
1463             edit->drag_state = MCEDIT_DRAG_NONE;
1464             edit_status (edit, TRUE);   /* redraw frame and status */
1465             MC_FALLTHROUGH;
1466         default:
1467             ret = TRUE;
1468             break;
1469         }
1470         break;
1471 
1472     case MCEDIT_DRAG_RESIZE:
1473         switch (command)
1474         {
1475         case CK_WindowMove:
1476             edit->drag_state = MCEDIT_DRAG_MOVE;
1477             ret = TRUE;
1478             break;
1479         case CK_Up:
1480         case CK_Down:
1481         case CK_Left:
1482         case CK_Right:
1483             edit_window_resize (edit, command);
1484             ret = TRUE;
1485             break;
1486         case CK_Enter:
1487         case CK_WindowResize:
1488             edit->drag_state = MCEDIT_DRAG_NONE;
1489             edit_status (edit, TRUE);   /* redraw frame and status */
1490             MC_FALLTHROUGH;
1491         default:
1492             ret = TRUE;
1493             break;
1494         }
1495         break;
1496 
1497     default:
1498         break;
1499     }
1500 
1501     /**
1502      * - We let the user stop a resize/move operation by clicking with the
1503      *   mouse anywhere. ("clicking" = pressing and releasing a button.)
1504      * - We let the user perform a resize/move operation by a mouse drag
1505      *   initiated anywhere.
1506      *
1507      * "Anywhere" means: inside or outside the window. We make this happen
1508      * with the 'forced_capture' flag.
1509      */
1510     w->mouse.forced_capture = (edit->drag_state != MCEDIT_DRAG_NONE);
1511 
1512     return ret;
1513 }
1514 
1515 /* --------------------------------------------------------------------------------------------- */
1516 /**
1517  * Toggle window fuulscreen mode.
1518  *
1519  * @param edit editor object
1520  */
1521 
1522 void
edit_toggle_fullscreen(WEdit * edit)1523 edit_toggle_fullscreen (WEdit * edit)
1524 {
1525     Widget *w = WIDGET (edit);
1526 
1527     edit->fullscreen = !edit->fullscreen;
1528     edit->force = REDRAW_COMPLETELY;
1529 
1530     if (!edit->fullscreen)
1531     {
1532         edit_restore_size (edit);
1533         /* do not follow screen size on resize */
1534         w->pos_flags = WPOS_KEEP_DEFAULT;
1535     }
1536     else
1537     {
1538         Widget *h = WIDGET (w->owner);
1539 
1540         edit_save_size (edit);
1541         widget_set_size (w, h->y + 1, h->x, h->lines - 2, h->cols);
1542         /* follow screen size on resize */
1543         w->pos_flags = WPOS_KEEP_ALL;
1544         edit->force |= REDRAW_PAGE;
1545         edit_update_screen (edit);
1546     }
1547 }
1548 
1549 /* --------------------------------------------------------------------------------------------- */
1550