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

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