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

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