1 /*
2    Internal file viewer for the Midnight Commander
3    Callback function for some actions (hotkeys, menu)
4 
5    Copyright (C) 1994-2021
6    Free Software Foundation, Inc.
7 
8    Written by:
9    Miguel de Icaza, 1994, 1995, 1998
10    Janne Kukonlehto, 1994, 1995
11    Jakub Jelinek, 1995
12    Joseph M. Hinkle, 1996
13    Norbert Warmuth, 1997
14    Pavel Machek, 1998
15    Roland Illig <roland.illig@gmx.de>, 2004, 2005
16    Slava Zanko <slavazanko@google.com>, 2009, 2013
17    Andrew Borodin <aborodin@vmail.ru>, 2009, 2013
18    Ilia Maslakov <il.smind@gmail.com>, 2009
19 
20    This file is part of the Midnight Commander.
21 
22    The Midnight Commander is free software: you can redistribute it
23    and/or modify it under the terms of the GNU General Public License as
24    published by the Free Software Foundation, either version 3 of the License,
25    or (at your option) any later version.
26 
27    The Midnight Commander is distributed in the hope that it will be useful,
28    but WITHOUT ANY WARRANTY; without even the implied warranty of
29    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
30    GNU General Public License for more details.
31 
32    You should have received a copy of the GNU General Public License
33    along with this program.  If not, see <http://www.gnu.org/licenses/>.
34  */
35 
36 /*
37    The functions in this section can be bound to hotkeys. They are all
38    of the same type (taking a pointer to WView as parameter and
39    returning void). TODO: In the not-too-distant future, these commands
40    will become fully configurable, like they already are in the
41    internal editor. By convention, all the function names end in
42    "_cmd".
43  */
44 
45 #include <config.h>
46 
47 #include <errno.h>
48 #include <stdlib.h>
49 
50 #include "lib/global.h"
51 
52 #include "lib/tty/tty.h"
53 #include "lib/tty/key.h"        /* is_idle() */
54 #include "lib/lock.h"           /* lock_file() */
55 #include "lib/util.h"
56 #include "lib/widget.h"
57 #ifdef HAVE_CHARSET
58 #include "lib/charsets.h"
59 #endif
60 #include "lib/event.h"          /* mc_event_raise() */
61 #include "lib/mcconfig.h"       /* mc_config_history_get() */
62 
63 #include "src/filemanager/layout.h"
64 #include "src/filemanager/filemanager.h"        /* current_panel */
65 #include "src/filemanager/ext.h"        /* regex_command_for() */
66 
67 #include "src/history.h"
68 #include "src/file_history.h"   /* show_file_history() */
69 #include "src/execute.h"
70 #include "src/keymap.h"
71 
72 #include "internal.h"
73 
74 /*** global variables ****************************************************************************/
75 
76 /*** file scope macro definitions ****************************************************************/
77 
78 /*** file scope type declarations ****************************************************************/
79 
80 /*** file scope variables ************************************************************************/
81 
82 /* --------------------------------------------------------------------------------------------- */
83 /*** file scope functions ************************************************************************/
84 /* --------------------------------------------------------------------------------------------- */
85 
86 static void
mcview_remove_ext_script(WView * view)87 mcview_remove_ext_script (WView * view)
88 {
89     if (view->ext_script != NULL)
90     {
91         mc_unlink (view->ext_script);
92         vfs_path_free (view->ext_script, TRUE);
93         view->ext_script = NULL;
94     }
95 }
96 
97 /* --------------------------------------------------------------------------------------------- */
98 
99 /* Both views */
100 static void
mcview_search(WView * view,gboolean start_search)101 mcview_search (WView * view, gboolean start_search)
102 {
103     off_t want_search_start = view->search_start;
104 
105     if (start_search)
106     {
107         if (mcview_dialog_search (view))
108         {
109             if (view->mode_flags.hex)
110                 want_search_start = view->hex_cursor;
111 
112             mcview_do_search (view, want_search_start);
113         }
114     }
115     else
116     {
117         if (view->mode_flags.hex)
118         {
119             if (!mcview_search_options.backwards)
120                 want_search_start = view->hex_cursor + 1;
121             else if (view->hex_cursor > 0)
122                 want_search_start = view->hex_cursor - 1;
123             else
124                 want_search_start = 0;
125         }
126 
127         mcview_do_search (view, want_search_start);
128     }
129 }
130 
131 /* --------------------------------------------------------------------------------------------- */
132 
133 static void
mcview_continue_search_cmd(WView * view)134 mcview_continue_search_cmd (WView * view)
135 {
136     if (view->last_search_string != NULL)
137         mcview_search (view, FALSE);
138     else
139     {
140         /* find last search string in history */
141         GList *history;
142 
143         history = mc_config_history_get (MC_HISTORY_SHARED_SEARCH);
144         if (history != NULL)
145         {
146             /* FIXME: is it possible that history->data == NULL? */
147             view->last_search_string = (gchar *) history->data;
148             history->data = NULL;
149             history = g_list_first (history);
150             g_list_free_full (history, g_free);
151 
152             if (mcview_search_init (view))
153             {
154                 mcview_search (view, FALSE);
155                 return;
156             }
157 
158             /* found, but cannot init search */
159             MC_PTR_FREE (view->last_search_string);
160         }
161 
162         /* if not... then ask for an expression */
163         mcview_search (view, TRUE);
164     }
165 }
166 
167 /* --------------------------------------------------------------------------------------------- */
168 
169 static void
mcview_hook(void * v)170 mcview_hook (void *v)
171 {
172     WView *view = (WView *) v;
173     WPanel *panel;
174 
175     /* If the user is busy typing, wait until he finishes to update the
176        screen */
177     if (!is_idle ())
178     {
179         if (!hook_present (idle_hook, mcview_hook))
180             add_hook (&idle_hook, mcview_hook, v);
181         return;
182     }
183 
184     delete_hook (&idle_hook, mcview_hook);
185 
186     if (get_current_type () == view_listing)
187         panel = current_panel;
188     else if (get_other_type () == view_listing)
189         panel = other_panel;
190     else
191         return;
192 
193     mcview_done (view);
194     mcview_init (view);
195     mcview_load (view, 0, panel->dir.list[panel->selected].fname->str, 0, 0, 0);
196     mcview_display (view);
197 }
198 
199 /* --------------------------------------------------------------------------------------------- */
200 
201 static cb_ret_t
mcview_handle_editkey(WView * view,int key)202 mcview_handle_editkey (WView * view, int key)
203 {
204     struct hexedit_change_node *node;
205     int byte_val = -1;
206 
207     /* Has there been a change at this position? */
208     node = view->change_list;
209     while ((node != NULL) && (node->offset != view->hex_cursor))
210         node = node->next;
211 
212     if (!view->hexview_in_text)
213     {
214         /* Hex editing */
215         unsigned int hexvalue = 0;
216 
217         if (key >= '0' && key <= '9')
218             hexvalue = 0 + (key - '0');
219         else if (key >= 'A' && key <= 'F')
220             hexvalue = 10 + (key - 'A');
221         else if (key >= 'a' && key <= 'f')
222             hexvalue = 10 + (key - 'a');
223         else
224             return MSG_NOT_HANDLED;
225 
226         if (node != NULL)
227             byte_val = node->value;
228         else
229             mcview_get_byte (view, view->hex_cursor, &byte_val);
230 
231         if (view->hexedit_lownibble)
232             byte_val = (byte_val & 0xf0) | (hexvalue);
233         else
234             byte_val = (byte_val & 0x0f) | (hexvalue << 4);
235     }
236     else
237     {
238         /* Text editing */
239         if (key < 256 && key != '\t')
240             byte_val = key;
241         else
242             return MSG_NOT_HANDLED;
243     }
244 
245     if ((view->filename_vpath != NULL)
246         && (*(vfs_path_get_last_path_str (view->filename_vpath)) != '\0')
247         && (view->change_list == NULL))
248         view->locked = lock_file (view->filename_vpath);
249 
250     if (node == NULL)
251     {
252         node = g_new (struct hexedit_change_node, 1);
253         node->offset = view->hex_cursor;
254         node->value = byte_val;
255         mcview_enqueue_change (&view->change_list, node);
256     }
257     else
258         node->value = byte_val;
259 
260     view->dirty++;
261     mcview_move_right (view, 1);
262 
263     return MSG_HANDLED;
264 }
265 
266 /* --------------------------------------------------------------------------------------------- */
267 
268 static void
mcview_load_next_prev_init(WView * view)269 mcview_load_next_prev_init (WView * view)
270 {
271     if (mc_global.mc_run_mode != MC_RUN_VIEWER)
272     {
273         /* get file list from current panel. Update it each time */
274         view->dir = &current_panel->dir;
275         view->dir_idx = &current_panel->selected;
276     }
277     else if (view->dir == NULL)
278     {
279         /* Run from command line */
280         /* Run 1st time. Load/get directory */
281 
282         /* TODO: check mtime of directory to reload it */
283 
284         dir_sort_options_t sort_op = { FALSE, TRUE, FALSE };
285 
286         /* load directory where requested file is */
287         view->dir = g_new0 (dir_list, 1);
288         view->dir_idx = g_new (int, 1);
289 
290         if (dir_list_load
291             (view->dir, view->workdir_vpath, (GCompareFunc) sort_name, &sort_op, NULL))
292         {
293             const char *fname;
294             size_t fname_len;
295             int i;
296 
297             fname = x_basename (vfs_path_as_str (view->filename_vpath));
298             fname_len = strlen (fname);
299 
300             /* search current file in the list */
301             for (i = 0; i != view->dir->len; i++)
302             {
303                 const file_entry_t *fe = &view->dir->list[i];
304 
305                 if (fname_len == fe->fname->len && strncmp (fname, fe->fname->str, fname_len) == 0)
306                     break;
307             }
308 
309             *view->dir_idx = i;
310         }
311         else
312         {
313             message (D_ERROR, MSG_ERROR, _("Cannot read directory contents"));
314             MC_PTR_FREE (view->dir);
315             MC_PTR_FREE (view->dir_idx);
316         }
317     }
318 }
319 
320 /* --------------------------------------------------------------------------------------------- */
321 
322 static void
mcview_scan_for_file(WView * view,int direction)323 mcview_scan_for_file (WView * view, int direction)
324 {
325     int i;
326 
327     for (i = *view->dir_idx + direction; i != *view->dir_idx; i += direction)
328     {
329         if (i < 0)
330             i = view->dir->len - 1;
331         if (i == view->dir->len)
332             i = 0;
333         if (!S_ISDIR (view->dir->list[i].st.st_mode))
334             break;
335     }
336 
337     *view->dir_idx = i;
338 }
339 
340 /* --------------------------------------------------------------------------------------------- */
341 
342 static void
mcview_load_next_prev(WView * view,int direction)343 mcview_load_next_prev (WView * view, int direction)
344 {
345     dir_list *dir;
346     int *dir_idx;
347     vfs_path_t *vfile;
348     vfs_path_t *ext_script = NULL;
349 
350     mcview_load_next_prev_init (view);
351     mcview_scan_for_file (view, direction);
352 
353     /* reinit view */
354     dir = view->dir;
355     dir_idx = view->dir_idx;
356     view->dir = NULL;
357     view->dir_idx = NULL;
358     vfile =
359         vfs_path_append_new (view->workdir_vpath, dir->list[*dir_idx].fname->str, (char *) NULL);
360     mcview_done (view);
361     mcview_remove_ext_script (view);
362     mcview_init (view);
363     if (regex_command_for (view, vfile, "View", &ext_script) == 0)
364         mcview_load (view, NULL, vfs_path_as_str (vfile), 0, 0, 0);
365     vfs_path_free (vfile, TRUE);
366     view->dir = dir;
367     view->dir_idx = dir_idx;
368     view->ext_script = ext_script;
369 
370     view->dpy_bbar_dirty = FALSE;       /* FIXME */
371     view->dirty++;
372 }
373 
374 /* --------------------------------------------------------------------------------------------- */
375 
376 static void
mcview_load_file_from_history(WView * view)377 mcview_load_file_from_history (WView * view)
378 {
379     char *filename;
380     int action;
381 
382     filename = show_file_history (CONST_WIDGET (view), &action);
383 
384     if (filename != NULL && (action == CK_View || action == CK_Enter))
385     {
386         mcview_done (view);
387         mcview_init (view);
388 
389         mcview_load (view, NULL, filename, 0, 0, 0);
390 
391         view->dpy_bbar_dirty = FALSE;   /* FIXME */
392         view->dirty++;
393     }
394 
395     g_free (filename);
396 }
397 
398 /* --------------------------------------------------------------------------------------------- */
399 
400 static cb_ret_t
mcview_execute_cmd(WView * view,long command)401 mcview_execute_cmd (WView * view, long command)
402 {
403     int res = MSG_HANDLED;
404 
405     switch (command)
406     {
407     case CK_Help:
408         {
409             ev_help_t event_data = { NULL, "[Internal File Viewer]" };
410             mc_event_raise (MCEVENT_GROUP_CORE, "help", &event_data);
411         }
412         break;
413     case CK_HexMode:
414         /* Toggle between hex view and text view */
415         mcview_toggle_hex_mode (view);
416         break;
417     case CK_HexEditMode:
418         /* Toggle between hexview and hexedit mode */
419         mcview_toggle_hexedit_mode (view);
420         break;
421     case CK_ToggleNavigation:
422         view->hexview_in_text = !view->hexview_in_text;
423         view->dirty++;
424         break;
425     case CK_LeftQuick:
426         if (!view->mode_flags.hex)
427             mcview_move_left (view, 10);
428         break;
429     case CK_RightQuick:
430         if (!view->mode_flags.hex)
431             mcview_move_right (view, 10);
432         break;
433     case CK_Goto:
434         {
435             off_t addr;
436 
437             if (mcview_dialog_goto (view, &addr))
438             {
439                 if (addr >= 0)
440                     mcview_moveto_offset (view, addr);
441                 else
442                 {
443                     message (D_ERROR, _("Warning"), "%s", _("Invalid value"));
444                     view->dirty++;
445                 }
446             }
447             break;
448         }
449     case CK_Save:
450         mcview_hexedit_save_changes (view);
451         break;
452     case CK_Search:
453         mcview_search (view, TRUE);
454         break;
455     case CK_SearchContinue:
456         mcview_continue_search_cmd (view);
457         break;
458     case CK_SearchForward:
459         mcview_search_options.backwards = FALSE;
460         mcview_search (view, TRUE);
461         break;
462     case CK_SearchForwardContinue:
463         mcview_search_options.backwards = FALSE;
464         mcview_continue_search_cmd (view);
465         break;
466     case CK_SearchBackward:
467         mcview_search_options.backwards = TRUE;
468         mcview_search (view, TRUE);
469         break;
470     case CK_SearchBackwardContinue:
471         mcview_search_options.backwards = TRUE;
472         mcview_continue_search_cmd (view);
473         break;
474     case CK_SearchOppositeContinue:
475         {
476             gboolean direction;
477 
478             direction = mcview_search_options.backwards;
479             mcview_search_options.backwards = !direction;
480             mcview_continue_search_cmd (view);
481             mcview_search_options.backwards = direction;
482         }
483         break;
484     case CK_WrapMode:
485         /* Toggle between wrapped and unwrapped view */
486         mcview_toggle_wrap_mode (view);
487         break;
488     case CK_MagicMode:
489         mcview_toggle_magic_mode (view);
490         break;
491     case CK_NroffMode:
492         mcview_toggle_nroff_mode (view);
493         break;
494     case CK_Home:
495         mcview_moveto_bol (view);
496         break;
497     case CK_End:
498         mcview_moveto_eol (view);
499         break;
500     case CK_Left:
501         mcview_move_left (view, 1);
502         break;
503     case CK_Right:
504         mcview_move_right (view, 1);
505         break;
506     case CK_Up:
507         mcview_move_up (view, 1);
508         break;
509     case CK_Down:
510         mcview_move_down (view, 1);
511         break;
512     case CK_HalfPageUp:
513         mcview_move_up (view, (view->data_area.height + 1) / 2);
514         break;
515     case CK_HalfPageDown:
516         mcview_move_down (view, (view->data_area.height + 1) / 2);
517         break;
518     case CK_PageUp:
519         mcview_move_up (view, view->data_area.height);
520         break;
521     case CK_PageDown:
522         mcview_move_down (view, view->data_area.height);
523         break;
524     case CK_Top:
525         mcview_moveto_top (view);
526         break;
527     case CK_Bottom:
528         mcview_moveto_bottom (view);
529         break;
530     case CK_Shell:
531         toggle_subshell ();
532         break;
533     case CK_Ruler:
534         mcview_display_toggle_ruler (view);
535         break;
536     case CK_Bookmark:
537         view->dpy_start = view->marks[view->marker];
538         view->dpy_paragraph_skip_lines = 0;     /* TODO: remember this value in the marker? */
539         view->dpy_wrap_dirty = TRUE;
540         view->dirty++;
541         break;
542     case CK_BookmarkGoto:
543         view->marks[view->marker] = view->dpy_start;
544         break;
545 #ifdef HAVE_CHARSET
546     case CK_SelectCodepage:
547         mcview_select_encoding (view);
548         view->dirty++;
549         break;
550 #endif
551     case CK_FileNext:
552     case CK_FilePrev:
553         /* Does not work in panel mode */
554         if (!mcview_is_in_panel (view))
555             mcview_load_next_prev (view, command == CK_FileNext ? 1 : -1);
556         break;
557     case CK_History:
558         mcview_load_file_from_history (view);
559         break;
560     case CK_Quit:
561         if (!mcview_is_in_panel (view))
562             dlg_stop (DIALOG (WIDGET (view)->owner));
563         break;
564     case CK_Cancel:
565         /* don't close viewer due to SIGINT */
566         break;
567     default:
568         res = MSG_NOT_HANDLED;
569     }
570     return res;
571 }
572 
573 /* --------------------------------------------------------------------------------------------- */
574 
575 static long
mcview_lookup_key(WView * view,int key)576 mcview_lookup_key (WView * view, int key)
577 {
578     if (view->mode_flags.hex)
579         return keybind_lookup_keymap_command (view->hex_keymap, key);
580 
581     return widget_lookup_key (WIDGET (view), key);
582 }
583 
584 /* --------------------------------------------------------------------------------------------- */
585 /** Both views */
586 static cb_ret_t
mcview_handle_key(WView * view,int key)587 mcview_handle_key (WView * view, int key)
588 {
589     long command;
590 
591 #ifdef HAVE_CHARSET
592     key = convert_from_input_c (key);
593 #endif
594 
595     if (view->hexedit_mode && view->mode_flags.hex
596         && mcview_handle_editkey (view, key) == MSG_HANDLED)
597         return MSG_HANDLED;
598 
599     command = mcview_lookup_key (view, key);
600     if (command != CK_IgnoreKey && mcview_execute_cmd (view, command) == MSG_HANDLED)
601         return MSG_HANDLED;
602 
603 #ifdef MC_ENABLE_DEBUGGING_CODE
604     if (c == 't')
605     {                           /* mnemonic: "test" */
606         mcview_ccache_dump (view);
607         return MSG_HANDLED;
608     }
609 #endif
610     if (key >= '0' && key <= '9')
611         view->marker = key - '0';
612 
613     /* Key not used */
614     return MSG_NOT_HANDLED;
615 }
616 
617 
618 /* --------------------------------------------------------------------------------------------- */
619 
620 static inline void
mcview_resize(WView * view)621 mcview_resize (WView * view)
622 {
623     view->dpy_wrap_dirty = TRUE;
624     mcview_compute_areas (view);
625     mcview_update_bytes_per_line (view);
626 }
627 
628 /* --------------------------------------------------------------------------------------------- */
629 
630 static gboolean
mcview_ok_to_quit(WView * view)631 mcview_ok_to_quit (WView * view)
632 {
633     int r;
634 
635     if (view->change_list == NULL)
636         return TRUE;
637 
638     if (!mc_global.midnight_shutdown)
639     {
640         query_set_sel (2);
641         r = query_dialog (_("Quit"),
642                           _("File was modified. Save with exit?"), D_NORMAL, 3,
643                           _("&Yes"), _("&No"), _("&Cancel quit"));
644     }
645     else
646     {
647         r = query_dialog (_("Quit"),
648                           _("Midnight Commander is being shut down.\nSave modified file?"),
649                           D_NORMAL, 2, _("&Yes"), _("&No"));
650         /* Esc is No */
651         if (r == -1)
652             r = 1;
653     }
654 
655     switch (r)
656     {
657     case 0:                    /* Yes */
658         return mcview_hexedit_save_changes (view) || mc_global.midnight_shutdown;
659     case 1:                    /* No */
660         mcview_hexedit_free_change_list (view);
661         return TRUE;
662     default:
663         return FALSE;
664     }
665 }
666 
667 /* --------------------------------------------------------------------------------------------- */
668 /*** public functions ****************************************************************************/
669 /* --------------------------------------------------------------------------------------------- */
670 
671 cb_ret_t
mcview_callback(Widget * w,Widget * sender,widget_msg_t msg,int parm,void * data)672 mcview_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
673 {
674     WView *view = (WView *) w;
675     cb_ret_t i;
676 
677     mcview_compute_areas (view);
678     mcview_update_bytes_per_line (view);
679 
680     switch (msg)
681     {
682     case MSG_INIT:
683         if (mcview_is_in_panel (view))
684             add_hook (&select_file_hook, mcview_hook, view);
685         else
686             view->dpy_bbar_dirty = TRUE;
687         return MSG_HANDLED;
688 
689     case MSG_DRAW:
690         mcview_display (view);
691         return MSG_HANDLED;
692 
693     case MSG_CURSOR:
694         if (view->mode_flags.hex)
695             mcview_place_cursor (view);
696         return MSG_HANDLED;
697 
698     case MSG_KEY:
699         i = mcview_handle_key (view, parm);
700         mcview_update (view);
701         return i;
702 
703     case MSG_ACTION:
704         i = mcview_execute_cmd (view, parm);
705         mcview_update (view);
706         return i;
707 
708     case MSG_FOCUS:
709         view->dpy_bbar_dirty = TRUE;
710         /* TODO: get rid of draw here before MSG_DRAW */
711         mcview_update (view);
712         return MSG_HANDLED;
713 
714     case MSG_RESIZE:
715         widget_default_callback (w, NULL, MSG_RESIZE, 0, data);
716         mcview_resize (view);
717         return MSG_HANDLED;
718 
719     case MSG_DESTROY:
720         if (mcview_is_in_panel (view))
721         {
722             delete_hook (&select_file_hook, mcview_hook);
723 
724             /*
725              * In some cases when mc startup is very slow and one panel is in quick vew mode,
726              * @view is registered in two hook lists at the same time:
727              *   mcview_callback (MSG_INIT) -> add_hook (&select_file_hook)
728              *   mcview_hook () -> add_hook (&idle_hook).
729              * If initialization of file manager is not completed yet, but user switches
730              * panel mode from qick view to another one (by pressing C-x q), the following
731              * occurs:
732              *   view hook is deleted from select_file_hook list via following call chain:
733              *      create_panel (view_listing) -> widget_replace () ->
734              *      send_message (MSG_DESTROY) -> mcview_callback (MSG_DESTROY) ->
735              *      delete_hook (&select_file_hook);
736              *   @view object is free'd:
737              *      create_panel (view_listing) -> g_free (old_widget);
738              *   but @view still is in idle_hook list and tried to be executed:
739              *      frontend_dlg_run () -> execute_hooks (idle_hook).
740              * Thus here we have access to free'd @view object. To prevent this, remove view hook
741              * from idle_hook list.
742              */
743             delete_hook (&idle_hook, mcview_hook);
744 
745             if (mc_global.midnight_shutdown)
746                 mcview_ok_to_quit (view);
747         }
748         mcview_done (view);
749         mcview_remove_ext_script (view);
750         return MSG_HANDLED;
751 
752     default:
753         return widget_default_callback (w, sender, msg, parm, data);
754     }
755 }
756 
757 /* --------------------------------------------------------------------------------------------- */
758 
759 cb_ret_t
mcview_dialog_callback(Widget * w,Widget * sender,widget_msg_t msg,int parm,void * data)760 mcview_dialog_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
761 {
762     WDialog *h = DIALOG (w);
763     WView *view;
764 
765     switch (msg)
766     {
767     case MSG_ACTION:
768         /* Handle shortcuts. */
769 
770         /* Note: the buttonbar sends messages directly to the the WView, not to
771          * here, which is why we can pass NULL in the following call. */
772         return mcview_execute_cmd (NULL, parm);
773 
774     case MSG_VALIDATE:
775         view = (WView *) widget_find_by_type (w, mcview_callback);
776         /* don't stop the dialog before final decision */
777         widget_set_state (w, WST_ACTIVE, TRUE);
778         if (mcview_ok_to_quit (view))
779             dlg_stop (h);
780         else
781             mcview_update (view);
782         return MSG_HANDLED;
783 
784     default:
785         return dlg_default_callback (w, sender, msg, parm, data);
786     }
787 }
788 
789 /* --------------------------------------------------------------------------------------------- */
790