Manual pages: mcmcdiffmceditmcview

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

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