root/src/viewer/actions_cmd.c

/* [previous][next][first][last][top][bottom][index][help]  */

DEFINITIONS

This source file includes following definitions.
  1. mcview_remove_ext_script
  2. mcview_continue_search_cmd
  3. mcview_hook
  4. mcview_handle_editkey
  5. mcview_load_next_prev_init
  6. mcview_scan_for_file
  7. mcview_load_next_prev
  8. mcview_load_file_from_history
  9. mcview_execute_cmd
  10. mcview_lookup_key
  11. mcview_handle_key
  12. mcview_resize
  13. mcview_ok_to_quit
  14. mcview_callback
  15. mcview_dialog_callback

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

/* [previous][next][first][last][top][bottom][index][help]  */