root/src/filemanager/filemanager.c

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

DEFINITIONS

This source file includes following definitions.
  1. stop_dialogs
  2. treebox_cmd
  3. listmode_cmd
  4. create_panel_menu
  5. create_file_menu
  6. create_command_menu
  7. create_options_menu
  8. init_menu
  9. menu_last_selected_cmd
  10. menu_cmd
  11. sort_cmd
  12. midnight_get_shortcut
  13. midnight_get_title
  14. toggle_panels_split
  15. check_panel_timestamp
  16. check_current_panel_timestamp
  17. check_other_panel_timestamp
  18. print_vfs_message
  19. create_panels
  20. midnight_put_panel_path
  21. put_link
  22. put_current_link
  23. put_other_link
  24. put_current_selected
  25. put_tagged
  26. put_current_tagged
  27. put_other_tagged
  28. setup_mc
  29. setup_dummy_mc
  30. done_mc
  31. create_file_manager
  32. prepend_cwd_on_local
  33. mc_maybe_editor_or_viewer
  34. show_editor_viewer_history
  35. quit_cmd_internal
  36. quit_cmd
  37. update_dirty_panels
  38. toggle_show_hidden
  39. midnight_execute_cmd
  40. is_cmdline_mute
  41. handle_cmdline_enter
  42. midnight_callback
  43. update_menu
  44. midnight_set_buttonbar
  45. get_random_hint
  46. load_hint
  47. change_panel
  48. save_cwds_stat
  49. quiet_quit_cmd
  50. do_nc

   1 /*
   2    Main dialog (file panels) of the Midnight Commander
   3 
   4    Copyright (C) 1994-2025
   5    Free Software Foundation, Inc.
   6 
   7    Written by:
   8    Miguel de Icaza, 1994, 1995, 1996, 1997
   9    Janne Kukonlehto, 1994, 1995
  10    Norbert Warmuth, 1997
  11    Andrew Borodin <aborodin@vmail.ru>, 2009-2022
  12    Slava Zanko <slavazanko@gmail.com>, 2013
  13 
  14    This file is part of the Midnight Commander.
  15 
  16    The Midnight Commander is free software: you can redistribute it
  17    and/or modify it under the terms of the GNU General Public License as
  18    published by the Free Software Foundation, either version 3 of the License,
  19    or (at your option) any later version.
  20 
  21    The Midnight Commander is distributed in the hope that it will be useful,
  22    but WITHOUT ANY WARRANTY; without even the implied warranty of
  23    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  24    GNU General Public License for more details.
  25 
  26    You should have received a copy of the GNU General Public License
  27    along with this program.  If not, see <https://www.gnu.org/licenses/>.
  28  */
  29 
  30 /** \file filemanager.c
  31  *  \brief Source: main dialog (file panels) of the Midnight Commander
  32  */
  33 
  34 #include <config.h>
  35 
  36 #include <ctype.h>
  37 #include <locale.h>
  38 #include <stdio.h>
  39 #include <stdlib.h>
  40 #include <string.h>
  41 #include <sys/types.h>
  42 #include <sys/stat.h>
  43 #include <sys/wait.h>
  44 #include <pwd.h>  // for username in xterm title
  45 
  46 #include "lib/global.h"
  47 #include "lib/fileloc.h"  // MC_HINT, MC_FILEPOS_FILE
  48 #include "lib/tty/tty.h"
  49 #include "lib/tty/key.h"  // KEY_M_* masks
  50 #include "lib/skin.h"
  51 #include "lib/util.h"
  52 #include "lib/vfs/vfs.h"
  53 
  54 #include "src/args.h"
  55 #ifdef ENABLE_SUBSHELL
  56 #    include "src/subshell/subshell.h"
  57 #endif
  58 #include "src/execute.h"  // toggle_subshell
  59 #include "src/setup.h"    // variables
  60 #include "src/learn.h"    // learn_keys()
  61 #include "src/keymap.h"
  62 #include "src/usermenu.h"  // user_file_menu_cmd()
  63 
  64 #include "lib/keybind.h"
  65 #include "lib/event.h"
  66 
  67 #include "tree.h"
  68 #include "boxes.h"  // sort_box(), tree_box()
  69 #include "layout.h"
  70 #include "cmd.h"  // commands
  71 #include "hotlist.h"
  72 #include "panelize.h"
  73 #include "command.h"  // cmdline
  74 #include "dir.h"      // dir_list_clean()
  75 
  76 #ifdef USE_INTERNAL_EDIT
  77 #    include "src/editor/edit.h"
  78 #endif
  79 
  80 #ifdef USE_DIFF_VIEW
  81 #    include "src/diffviewer/ydiff.h"
  82 #endif
  83 
  84 #include "src/consaver/cons.saver.h"  // show_console_contents
  85 #include "src/file_history.h"         // show_file_history()
  86 
  87 #include "filemanager.h"
  88 
  89 /*** global variables ****************************************************************************/
  90 
  91 /* When the modes are active, left_panel, right_panel and tree_panel */
  92 /* point to a proper data structure.  You should check with the functions */
  93 /* get_current_type and get_other_type the types of the panels before using */
  94 /* this pointer variables */
  95 
  96 /* The structures for the panels */
  97 WPanel *left_panel = NULL;
  98 WPanel *right_panel = NULL;
  99 /* Pointer to the selected and unselected panel */
 100 WPanel *current_panel = NULL;
 101 
 102 /* The Menubar */
 103 WMenuBar *the_menubar = NULL;
 104 /* The widget where we draw the prompt */
 105 WLabel *the_prompt;
 106 /* The hint bar */
 107 WLabel *the_hint;
 108 /* The button bar */
 109 WButtonBar *the_bar;
 110 
 111 /* The prompt */
 112 const char *mc_prompt = NULL;
 113 
 114 /*** file scope macro definitions ****************************************************************/
 115 
 116 #ifdef HAVE_CHARSET
 117 /*
 118  * Don't restrict the output on the screen manager level,
 119  * the translation tables take care of it.
 120  */
 121 #endif
 122 
 123 /*** file scope type declarations ****************************************************************/
 124 
 125 /*** forward declarations (file scope functions) *************************************************/
 126 
 127 /*** file scope variables ************************************************************************/
 128 
 129 static menu_t *left_menu, *right_menu;
 130 
 131 /* --------------------------------------------------------------------------------------------- */
 132 /*** file scope functions ************************************************************************/
 133 /* --------------------------------------------------------------------------------------------- */
 134 
 135 /** Stop MC main dialog and the current dialog if it exists.
 136  * Needed to provide fast exit from MC viewer or editor on shell exit */
 137 static void
 138 stop_dialogs (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 139 {
 140     dlg_close (filemanager);
 141 
 142     if (top_dlg != NULL)
 143         dlg_close (DIALOG (top_dlg->data));
 144 }
 145 
 146 /* --------------------------------------------------------------------------------------------- */
 147 
 148 static void
 149 treebox_cmd (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 150 {
 151     const file_entry_t *fe;
 152     char *sel_dir;
 153 
 154     fe = panel_current_entry (current_panel);
 155     if (fe == NULL)
 156         return;
 157 
 158     sel_dir = tree_box (fe->fname->str);
 159     if (sel_dir != NULL)
 160     {
 161         vfs_path_t *sel_vdir;
 162 
 163         sel_vdir = vfs_path_from_str (sel_dir);
 164         panel_cd (current_panel, sel_vdir, cd_exact);
 165         vfs_path_free (sel_vdir, TRUE);
 166         g_free (sel_dir);
 167     }
 168 }
 169 
 170 /* --------------------------------------------------------------------------------------------- */
 171 
 172 #ifdef LISTMODE_EDITOR
 173 static void
 174 listmode_cmd (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 175 {
 176     char *newmode;
 177 
 178     if (get_current_type () != view_listing)
 179         return;
 180 
 181     newmode = listmode_edit (current_panel->user_format);
 182     if (!newmode)
 183         return;
 184 
 185     g_free (current_panel->user_format);
 186     current_panel->list_format = list_user;
 187     current_panel->user_format = newmode;
 188     set_panel_formats (current_panel);
 189 
 190     do_refresh ();
 191 }
 192 #endif
 193 
 194 /* --------------------------------------------------------------------------------------------- */
 195 
 196 static GList *
 197 create_panel_menu (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 198 {
 199     GList *entries = NULL;
 200 
 201     entries = g_list_prepend (entries, menu_entry_new (_ ("File listin&g"), CK_PanelListing));
 202     entries = g_list_prepend (entries, menu_entry_new (_ ("&Quick view"), CK_PanelQuickView));
 203     entries = g_list_prepend (entries, menu_entry_new (_ ("&Info"), CK_PanelInfo));
 204     entries = g_list_prepend (entries, menu_entry_new (_ ("&Tree"), CK_PanelTree));
 205     entries = g_list_prepend (entries, menu_separator_new ());
 206     entries =
 207         g_list_prepend (entries, menu_entry_new (_ ("&Listing format..."), CK_SetupListingFormat));
 208     entries = g_list_prepend (entries, menu_entry_new (_ ("&Sort order..."), CK_Sort));
 209     entries = g_list_prepend (entries, menu_entry_new (_ ("&Filter..."), CK_Filter));
 210 #ifdef HAVE_CHARSET
 211     entries = g_list_prepend (entries, menu_entry_new (_ ("&Encoding..."), CK_SelectCodepage));
 212 #endif
 213     entries = g_list_prepend (entries, menu_separator_new ());
 214 #ifdef ENABLE_VFS_FTP
 215     entries = g_list_prepend (entries, menu_entry_new (_ ("FT&P link..."), CK_ConnectFtp));
 216 #endif
 217 #ifdef ENABLE_VFS_SHELL
 218     entries = g_list_prepend (entries, menu_entry_new (_ ("S&hell link..."), CK_ConnectShell));
 219 #endif
 220 #ifdef ENABLE_VFS_SFTP
 221     entries = g_list_prepend (entries, menu_entry_new (_ ("SFTP li&nk..."), CK_ConnectSftp));
 222 #endif
 223     entries = g_list_prepend (entries, menu_entry_new (_ ("Paneli&ze"), CK_Panelize));
 224     entries = g_list_prepend (entries, menu_separator_new ());
 225     entries = g_list_prepend (entries, menu_entry_new (_ ("&Rescan"), CK_Reread));
 226 
 227     return g_list_reverse (entries);
 228 }
 229 
 230 /* --------------------------------------------------------------------------------------------- */
 231 
 232 static GList *
 233 create_file_menu (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 234 {
 235     GList *entries = NULL;
 236 
 237     entries = g_list_prepend (entries, menu_entry_new (_ ("&View"), CK_View));
 238     entries = g_list_prepend (entries, menu_entry_new (_ ("Vie&w file..."), CK_ViewFile));
 239     entries = g_list_prepend (entries, menu_entry_new (_ ("&Filtered view"), CK_ViewFiltered));
 240     entries = g_list_prepend (entries, menu_entry_new (_ ("&Edit"), CK_Edit));
 241     entries = g_list_prepend (entries, menu_entry_new (_ ("&Copy"), CK_Copy));
 242     entries = g_list_prepend (entries, menu_entry_new (_ ("C&hmod"), CK_ChangeMode));
 243     entries = g_list_prepend (entries, menu_entry_new (_ ("&Link"), CK_Link));
 244     entries = g_list_prepend (entries, menu_entry_new (_ ("&Symlink"), CK_LinkSymbolic));
 245     entries =
 246         g_list_prepend (entries, menu_entry_new (_ ("Relative symlin&k"), CK_LinkSymbolicRelative));
 247     entries = g_list_prepend (entries, menu_entry_new (_ ("Edit s&ymlink"), CK_LinkSymbolicEdit));
 248     entries = g_list_prepend (entries, menu_entry_new (_ ("Ch&own"), CK_ChangeOwn));
 249     entries =
 250         g_list_prepend (entries, menu_entry_new (_ ("&Advanced chown"), CK_ChangeOwnAdvanced));
 251 #ifdef ENABLE_EXT2FS_ATTR
 252     entries = g_list_prepend (entries, menu_entry_new (_ ("Cha&ttr"), CK_ChangeAttributes));
 253 #endif
 254     entries = g_list_prepend (entries, menu_entry_new (_ ("&Rename/Move"), CK_Move));
 255     entries = g_list_prepend (entries, menu_entry_new (_ ("&Mkdir"), CK_MakeDir));
 256     entries = g_list_prepend (entries, menu_entry_new (_ ("&Delete"), CK_Delete));
 257     entries = g_list_prepend (entries, menu_entry_new (_ ("&Quick cd"), CK_CdQuick));
 258     entries = g_list_prepend (entries, menu_separator_new ());
 259     entries = g_list_prepend (entries, menu_entry_new (_ ("Select &group"), CK_Select));
 260     entries = g_list_prepend (entries, menu_entry_new (_ ("U&nselect group"), CK_Unselect));
 261     entries = g_list_prepend (entries, menu_entry_new (_ ("&Invert selection"), CK_SelectInvert));
 262     entries = g_list_prepend (entries, menu_separator_new ());
 263     entries = g_list_prepend (entries, menu_entry_new (_ ("E&xit"), CK_Quit));
 264 
 265     return g_list_reverse (entries);
 266 }
 267 
 268 /* --------------------------------------------------------------------------------------------- */
 269 
 270 static GList *
 271 create_command_menu (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 272 {
 273     /* I know, I'm lazy, but the tree widget when it's not running
 274      * as a panel still has some problems, I have not yet finished
 275      * the WTree widget port, sorry.
 276      */
 277     GList *entries = NULL;
 278 
 279     entries = g_list_prepend (entries, menu_entry_new (_ ("&User menu"), CK_UserMenu));
 280     entries = g_list_prepend (entries, menu_entry_new (_ ("&Directory tree"), CK_Tree));
 281     entries = g_list_prepend (entries, menu_entry_new (_ ("&Find file"), CK_Find));
 282     entries = g_list_prepend (entries, menu_entry_new (_ ("S&wap panels"), CK_Swap));
 283     entries = g_list_prepend (entries, menu_entry_new (_ ("Switch &panels on/off"), CK_Shell));
 284     entries = g_list_prepend (entries, menu_entry_new (_ ("&Compare directories"), CK_CompareDirs));
 285 #ifdef USE_DIFF_VIEW
 286     entries = g_list_prepend (entries, menu_entry_new (_ ("C&ompare files"), CK_CompareFiles));
 287 #endif
 288     entries =
 289         g_list_prepend (entries, menu_entry_new (_ ("E&xternal panelize"), CK_ExternalPanelize));
 290     entries = g_list_prepend (entries, menu_entry_new (_ ("Show directory s&izes"), CK_DirSize));
 291     entries = g_list_prepend (entries, menu_separator_new ());
 292     entries = g_list_prepend (entries, menu_entry_new (_ ("Command &history"), CK_History));
 293     entries = g_list_prepend (
 294         entries, menu_entry_new (_ ("Viewed/edited files hi&story"), CK_EditorViewerHistory));
 295     entries = g_list_prepend (entries, menu_entry_new (_ ("Di&rectory hotlist"), CK_HotList));
 296 #ifdef ENABLE_VFS
 297     entries = g_list_prepend (entries, menu_entry_new (_ ("&Active VFS list"), CK_VfsList));
 298 #endif
 299 #ifdef ENABLE_BACKGROUND
 300     entries = g_list_prepend (entries, menu_entry_new (_ ("&Background jobs"), CK_Jobs));
 301 #endif
 302     entries = g_list_prepend (entries, menu_entry_new (_ ("Screen lis&t"), CK_ScreenList));
 303     entries = g_list_prepend (entries, menu_separator_new ());
 304 #ifdef ENABLE_VFS_UNDELFS
 305     entries =
 306         g_list_prepend (entries, menu_entry_new (_ ("&Undelete files (ext2fs only)"), CK_Undelete));
 307 #endif
 308 #ifdef LISTMODE_EDITOR
 309     entries = g_list_prepend (entries, menu_entry_new (_ ("&Listing format edit"), CK_ListMode));
 310 #endif
 311 #if defined(ENABLE_VFS_UNDELFS) || defined(LISTMODE_EDITOR)
 312     entries = g_list_prepend (entries, menu_separator_new ());
 313 #endif
 314     entries = g_list_prepend (entries,
 315                               menu_entry_new (_ ("Edit &extension file"), CK_EditExtensionsFile));
 316     entries = g_list_prepend (entries, menu_entry_new (_ ("Edit &menu file"), CK_EditUserMenu));
 317     entries = g_list_prepend (
 318         entries, menu_entry_new (_ ("Edit hi&ghlighting group file"), CK_EditFileHighlightFile));
 319 
 320     return g_list_reverse (entries);
 321 }
 322 
 323 /* --------------------------------------------------------------------------------------------- */
 324 
 325 static GList *
 326 create_options_menu (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 327 {
 328     GList *entries = NULL;
 329 
 330     entries = g_list_prepend (entries, menu_entry_new (_ ("&Configuration..."), CK_Options));
 331     entries = g_list_prepend (entries, menu_entry_new (_ ("&Layout..."), CK_OptionsLayout));
 332     entries = g_list_prepend (entries, menu_entry_new (_ ("&Panel options..."), CK_OptionsPanel));
 333     entries = g_list_prepend (entries, menu_entry_new (_ ("C&onfirmation..."), CK_OptionsConfirm));
 334     entries = g_list_prepend (entries, menu_entry_new (_ ("&Appearance..."), CK_OptionsAppearance));
 335     entries =
 336         g_list_prepend (entries, menu_entry_new (_ ("&Display bits..."), CK_OptionsDisplayBits));
 337     entries = g_list_prepend (entries, menu_entry_new (_ ("Learn &keys..."), CK_LearnKeys));
 338 #ifdef ENABLE_VFS
 339     entries = g_list_prepend (entries, menu_entry_new (_ ("&Virtual FS..."), CK_OptionsVfs));
 340 #endif
 341     entries = g_list_prepend (entries, menu_separator_new ());
 342     entries = g_list_prepend (entries, menu_entry_new (_ ("&Save setup"), CK_SaveSetup));
 343 
 344     return g_list_reverse (entries);
 345 }
 346 
 347 /* --------------------------------------------------------------------------------------------- */
 348 
 349 static void
 350 init_menu (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 351 {
 352     left_menu = menu_new ("", create_panel_menu (), "[Left and Right Menus]");
 353     menubar_add_menu (the_menubar, left_menu);
 354     menubar_add_menu (the_menubar, menu_new (_ ("&File"), create_file_menu (), "[File Menu]"));
 355     menubar_add_menu (the_menubar,
 356                       menu_new (_ ("&Command"), create_command_menu (), "[Command Menu]"));
 357     menubar_add_menu (the_menubar,
 358                       menu_new (_ ("&Options"), create_options_menu (), "[Options Menu]"));
 359     right_menu = menu_new ("", create_panel_menu (), "[Left and Right Menus]");
 360     menubar_add_menu (the_menubar, right_menu);
 361     update_menu ();
 362 }
 363 
 364 /* --------------------------------------------------------------------------------------------- */
 365 
 366 static void
 367 menu_last_selected_cmd (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 368 {
 369     menubar_activate (the_menubar, drop_menus, -1);
 370 }
 371 
 372 /* --------------------------------------------------------------------------------------------- */
 373 
 374 static void
 375 menu_cmd (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 376 {
 377     int selected;
 378 
 379     if ((get_current_index () == 0) == current_panel->active)
 380         selected = 0;
 381     else
 382         selected = g_list_length (the_menubar->menu) - 1;
 383 
 384     menubar_activate (the_menubar, drop_menus, selected);
 385 }
 386 
 387 /* --------------------------------------------------------------------------------------------- */
 388 
 389 static void
 390 sort_cmd (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 391 {
 392     WPanel *p;
 393     const panel_field_t *sort_order;
 394 
 395     if (!SELECTED_IS_PANEL)
 396         return;
 397 
 398     p = MENU_PANEL;
 399     sort_order = sort_box (&p->sort_info, p->sort_field);
 400     panel_set_sort_order (p, sort_order);
 401 }
 402 
 403 /* --------------------------------------------------------------------------------------------- */
 404 
 405 static char *
 406 midnight_get_shortcut (long command)
     /* [previous][next][first][last][top][bottom][index][help]  */
 407 {
 408     const char *ext_map;
 409     const char *shortcut = NULL;
 410 
 411     shortcut = keybind_lookup_keymap_shortcut (filemanager_map, command);
 412     if (shortcut != NULL)
 413         return g_strdup (shortcut);
 414 
 415     shortcut = keybind_lookup_keymap_shortcut (panel_map, command);
 416     if (shortcut != NULL)
 417         return g_strdup (shortcut);
 418 
 419     ext_map = keybind_lookup_keymap_shortcut (filemanager_map, CK_ExtendedKeyMap);
 420     if (ext_map != NULL)
 421         shortcut = keybind_lookup_keymap_shortcut (filemanager_x_map, command);
 422     if (shortcut != NULL)
 423         return g_strdup_printf ("%s %s", ext_map, shortcut);
 424 
 425     return NULL;
 426 }
 427 
 428 /* --------------------------------------------------------------------------------------------- */
 429 
 430 static char *
 431 midnight_get_title (const WDialog *h, size_t len)
     /* [previous][next][first][last][top][bottom][index][help]  */
 432 {
 433     char *path;
 434     char *login;
 435     char *p;
 436 
 437     (void) h;
 438 
 439     title_path_prepare (&path, &login);
 440 
 441     p = g_strdup_printf ("%s [%s]:%s", _ ("Panels:"), login, path);
 442     g_free (path);
 443     g_free (login);
 444     path = g_strdup (str_trunc (p, len - 4));
 445     g_free (p);
 446 
 447     return path;
 448 }
 449 
 450 /* --------------------------------------------------------------------------------------------- */
 451 
 452 static void
 453 toggle_panels_split (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 454 {
 455     panels_layout.horizontal_split = !panels_layout.horizontal_split;
 456     layout_change ();
 457     do_refresh ();
 458 }
 459 
 460 /* --------------------------------------------------------------------------------------------- */
 461 
 462 #ifdef ENABLE_VFS
 463 /* event helper */
 464 static gboolean
 465 check_panel_timestamp (const WPanel *panel, panel_view_mode_t mode, const struct vfs_class *vclass,
     /* [previous][next][first][last][top][bottom][index][help]  */
 466                        const vfsid id)
 467 {
 468     return (mode != view_listing
 469             || (vfs_path_get_last_path_vfs (panel->cwd_vpath) == vclass
 470                 && vfs_getid (panel->cwd_vpath) == id));
 471 }
 472 
 473 /* --------------------------------------------------------------------------------------------- */
 474 
 475 /* event callback */
 476 static gboolean
 477 check_current_panel_timestamp (const gchar *event_group_name, const gchar *event_name,
     /* [previous][next][first][last][top][bottom][index][help]  */
 478                                gpointer init_data, gpointer data)
 479 {
 480     ev_vfs_stamp_create_t *event_data = (ev_vfs_stamp_create_t *) data;
 481 
 482     (void) event_group_name;
 483     (void) event_name;
 484     (void) init_data;
 485 
 486     event_data->ret = check_panel_timestamp (current_panel, get_current_type (), event_data->vclass,
 487                                              event_data->id);
 488     return !event_data->ret;
 489 }
 490 
 491 /* --------------------------------------------------------------------------------------------- */
 492 
 493 /* event callback */
 494 static gboolean
 495 check_other_panel_timestamp (const gchar *event_group_name, const gchar *event_name,
     /* [previous][next][first][last][top][bottom][index][help]  */
 496                              gpointer init_data, gpointer data)
 497 {
 498     ev_vfs_stamp_create_t *event_data = (ev_vfs_stamp_create_t *) data;
 499 
 500     (void) event_group_name;
 501     (void) event_name;
 502     (void) init_data;
 503 
 504     event_data->ret =
 505         check_panel_timestamp (other_panel, get_other_type (), event_data->vclass, event_data->id);
 506     return !event_data->ret;
 507 }
 508 #endif
 509 
 510 /* --------------------------------------------------------------------------------------------- */
 511 
 512 /* event callback */
 513 static gboolean
 514 print_vfs_message (const gchar *event_group_name, const gchar *event_name, gpointer init_data,
     /* [previous][next][first][last][top][bottom][index][help]  */
 515                    gpointer data)
 516 {
 517     ev_vfs_print_message_t *event_data = (ev_vfs_print_message_t *) data;
 518 
 519     (void) event_group_name;
 520     (void) event_name;
 521     (void) init_data;
 522 
 523     if (mc_global.midnight_shutdown)
 524         goto ret;
 525 
 526     if (!mc_global.message_visible || the_hint == NULL || WIDGET (the_hint)->owner == NULL)
 527     {
 528         int col, row;
 529 
 530         if (!nice_rotating_dash || (ok_to_refresh <= 0))
 531             goto ret;
 532 
 533         // Preserve current cursor position
 534         tty_getyx (&row, &col);
 535 
 536         tty_gotoyx (0, 0);
 537         tty_setcolor (NORMAL_COLOR);
 538         tty_print_string (str_fit_to_term (event_data->msg, COLS - 1, J_LEFT));
 539 
 540         // Restore cursor position
 541         tty_gotoyx (row, col);
 542         mc_refresh ();
 543         goto ret;
 544     }
 545 
 546     if (mc_global.message_visible)
 547         set_hintbar (event_data->msg);
 548 
 549 ret:
 550     MC_PTR_FREE (event_data->msg);
 551     return TRUE;
 552 }
 553 
 554 /* --------------------------------------------------------------------------------------------- */
 555 
 556 static void
 557 create_panels (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 558 {
 559     int current_index, other_index;
 560     panel_view_mode_t current_mode, other_mode;
 561     char *current_dir, *other_dir;
 562     vfs_path_t *original_dir;
 563 
 564     /*
 565      * Following cases from command line are possible:
 566      * 'mc' (no arguments):            mc_run_param0 == NULL, mc_run_param1 == NULL
 567      *                                 active panel uses current directory
 568      *                                 passive panel uses "other_dir" from panels.ini
 569      *
 570      * 'mc dir1 dir2' (two arguments): mc_run_param0 != NULL, mc_run_param1 != NULL
 571      *                                 active panel uses mc_run_param0
 572      *                                 passive panel uses mc_run_param1
 573      *
 574      * 'mc dir1' (single argument):    mc_run_param0 != NULL, mc_run_param1 == NULL
 575      *                                 active panel uses mc_run_param0
 576      *                                 passive panel uses "other_dir" from panels.ini
 577      */
 578 
 579     // Set up panel directories
 580     if (boot_current_is_left)
 581     {
 582         // left panel is active
 583         current_index = 0;
 584         other_index = 1;
 585         current_mode = startup_left_mode;
 586         other_mode = startup_right_mode;
 587 
 588         if (mc_run_param0 == NULL && mc_run_param1 == NULL)
 589         {
 590             // no arguments
 591             current_dir = NULL;           // assume current dir
 592             other_dir = saved_other_dir;  // from ini
 593         }
 594         else if (mc_run_param0 != NULL && mc_run_param1 != NULL)
 595         {
 596             // two arguments
 597             current_dir = (char *) mc_run_param0;
 598             other_dir = mc_run_param1;
 599         }
 600         else  // mc_run_param0 != NULL && mc_run_param1 == NULL
 601         {
 602             // one argument
 603             current_dir = (char *) mc_run_param0;
 604             other_dir = saved_other_dir;  // from ini
 605         }
 606     }
 607     else
 608     {
 609         // right panel is active
 610         current_index = 1;
 611         other_index = 0;
 612         current_mode = startup_right_mode;
 613         other_mode = startup_left_mode;
 614 
 615         if (mc_run_param0 == NULL && mc_run_param1 == NULL)
 616         {
 617             // no arguments
 618             current_dir = NULL;           // assume current dir
 619             other_dir = saved_other_dir;  // from ini
 620         }
 621         else if (mc_run_param0 != NULL && mc_run_param1 != NULL)
 622         {
 623             // two arguments
 624             current_dir = (char *) mc_run_param0;
 625             other_dir = mc_run_param1;
 626         }
 627         else  // mc_run_param0 != NULL && mc_run_param1 == NULL
 628         {
 629             // one argument
 630             current_dir = (char *) mc_run_param0;
 631             other_dir = saved_other_dir;  // from ini
 632         }
 633     }
 634 
 635     // 1. Get current dir
 636     original_dir = vfs_path_clone (vfs_get_raw_current_dir ());
 637 
 638     // 2. Create passive panel
 639     if (other_dir != NULL)
 640     {
 641         vfs_path_t *vpath;
 642 
 643         if (g_path_is_absolute (other_dir))
 644             vpath = vfs_path_from_str (other_dir);
 645         else
 646             vpath = vfs_path_append_new (original_dir, other_dir, (char *) NULL);
 647         mc_chdir (vpath);
 648         vfs_path_free (vpath, TRUE);
 649     }
 650     create_panel (other_index, other_mode);
 651 
 652     // 3. Create active panel
 653     if (current_dir == NULL)
 654         mc_chdir (original_dir);
 655     else
 656     {
 657         vfs_path_t *vpath;
 658 
 659         if (g_path_is_absolute (current_dir))
 660             vpath = vfs_path_from_str (current_dir);
 661         else
 662             vpath = vfs_path_append_new (original_dir, current_dir, (char *) NULL);
 663         mc_chdir (vpath);
 664         vfs_path_free (vpath, TRUE);
 665     }
 666     create_panel (current_index, current_mode);
 667 
 668     if (startup_left_mode == view_listing)
 669         current_panel = left_panel;
 670     else if (right_panel != NULL)
 671         current_panel = right_panel;
 672     else
 673         current_panel = left_panel;
 674 
 675     vfs_path_free (original_dir, TRUE);
 676 
 677 #ifdef ENABLE_VFS
 678     mc_event_add (MCEVENT_GROUP_CORE, "vfs_timestamp", check_other_panel_timestamp, NULL, NULL);
 679     mc_event_add (MCEVENT_GROUP_CORE, "vfs_timestamp", check_current_panel_timestamp, NULL, NULL);
 680 #endif
 681 
 682     mc_event_add (MCEVENT_GROUP_CORE, "vfs_print_message", print_vfs_message, NULL, NULL);
 683 }
 684 
 685 /* --------------------------------------------------------------------------------------------- */
 686 
 687 static void
 688 midnight_put_panel_path (WPanel *panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
 689 {
 690     vfs_path_t *cwd_vpath;
 691     const char *cwd_vpath_str;
 692 
 693     if (!command_prompt)
 694         return;
 695 
 696 #ifdef HAVE_CHARSET
 697     cwd_vpath = remove_encoding_from_path (panel->cwd_vpath);
 698 #else
 699     cwd_vpath = vfs_path_clone (panel->cwd_vpath);
 700 #endif
 701 
 702     cwd_vpath_str = vfs_path_as_str (cwd_vpath);
 703 
 704     command_insert (cmdline, cwd_vpath_str, FALSE);
 705 
 706     if (!IS_PATH_SEP (cwd_vpath_str[strlen (cwd_vpath_str) - 1]))
 707         command_insert (cmdline, PATH_SEP_STR, FALSE);
 708 
 709     vfs_path_free (cwd_vpath, TRUE);
 710 }
 711 
 712 /* --------------------------------------------------------------------------------------------- */
 713 
 714 static void
 715 put_link (WPanel *panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
 716 {
 717     const file_entry_t *fe;
 718 
 719     if (!command_prompt)
 720         return;
 721 
 722     fe = panel_current_entry (panel);
 723 
 724     if (fe != NULL && S_ISLNK (fe->st.st_mode))
 725     {
 726         char buffer[MC_MAXPATHLEN];
 727         vfs_path_t *vpath;
 728         int i;
 729 
 730         vpath = vfs_path_append_new (panel->cwd_vpath, fe->fname->str, (char *) NULL);
 731         i = mc_readlink (vpath, buffer, sizeof (buffer) - 1);
 732         vfs_path_free (vpath, TRUE);
 733 
 734         if (i > 0)
 735         {
 736             buffer[i] = '\0';
 737             command_insert (cmdline, buffer, TRUE);
 738         }
 739     }
 740 }
 741 
 742 /* --------------------------------------------------------------------------------------------- */
 743 
 744 static void
 745 put_current_link (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 746 {
 747     put_link (current_panel);
 748 }
 749 
 750 /* --------------------------------------------------------------------------------------------- */
 751 
 752 static void
 753 put_other_link (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 754 {
 755     if (get_other_type () == view_listing)
 756         put_link (other_panel);
 757 }
 758 
 759 /* --------------------------------------------------------------------------------------------- */
 760 
 761 /** Insert the selected file name into the input line */
 762 static void
 763 put_current_selected (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 764 {
 765     if (!command_prompt)
 766         return;
 767 
 768     if (get_current_type () == view_tree)
 769     {
 770         WTree *tree;
 771         const vfs_path_t *selected_name;
 772 
 773         tree = (WTree *) get_panel_widget (get_current_index ());
 774         selected_name = tree_selected_name (tree);
 775         command_insert (cmdline, vfs_path_as_str (selected_name), TRUE);
 776     }
 777     else
 778     {
 779         const file_entry_t *fe;
 780 
 781         fe = panel_current_entry (current_panel);
 782         if (fe != NULL)
 783             command_insert (cmdline, fe->fname->str, TRUE);
 784     }
 785 }
 786 
 787 /* --------------------------------------------------------------------------------------------- */
 788 
 789 static void
 790 put_tagged (WPanel *panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
 791 {
 792     if (!command_prompt)
 793         return;
 794 
 795     input_disable_update (cmdline);
 796 
 797     if (panel->marked == 0)
 798     {
 799         const file_entry_t *fe;
 800 
 801         fe = panel_current_entry (current_panel);
 802         if (fe != NULL)
 803             command_insert (cmdline, fe->fname->str, TRUE);
 804     }
 805     else
 806     {
 807         int i;
 808 
 809         for (i = 0; i < panel->dir.len; i++)
 810             if (panel->dir.list[i].f.marked != 0)
 811                 command_insert (cmdline, panel->dir.list[i].fname->str, TRUE);
 812     }
 813 
 814     input_enable_update (cmdline);
 815 }
 816 
 817 /* --------------------------------------------------------------------------------------------- */
 818 
 819 static void
 820 put_current_tagged (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 821 {
 822     put_tagged (current_panel);
 823 }
 824 
 825 /* --------------------------------------------------------------------------------------------- */
 826 
 827 static void
 828 put_other_tagged (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 829 {
 830     if (get_other_type () == view_listing)
 831         put_tagged (other_panel);
 832 }
 833 
 834 /* --------------------------------------------------------------------------------------------- */
 835 
 836 static void
 837 setup_mc (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 838 {
 839 #ifdef HAVE_SLANG
 840 #    ifdef HAVE_CHARSET
 841     tty_display_8bit (TRUE);
 842 #    else
 843     tty_display_8bit (mc_global.full_eight_bits);
 844 #    endif
 845 
 846 #else  // HAVE_SLANG
 847 
 848 #    ifdef HAVE_CHARSET
 849     tty_display_8bit (TRUE);
 850 #    else
 851     tty_display_8bit (mc_global.eight_bit_clean);
 852 #    endif
 853 #endif
 854 
 855     const int baudrate = tty_baudrate ();
 856     if ((baudrate > 0 && baudrate < 9600) || mc_global.tty.slow_terminal)
 857         verbose = FALSE;
 858 }
 859 
 860 /* --------------------------------------------------------------------------------------------- */
 861 
 862 static void
 863 setup_dummy_mc (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 864 {
 865     vfs_path_t *vpath;
 866     char *d;
 867     int ret;
 868 
 869     d = vfs_get_cwd ();
 870     setup_mc ();
 871     vpath = vfs_path_from_str (d);
 872     ret = mc_chdir (vpath);
 873     (void) ret;
 874     vfs_path_free (vpath, TRUE);
 875     g_free (d);
 876 }
 877 
 878 /* --------------------------------------------------------------------------------------------- */
 879 
 880 static void
 881 done_mc (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 882 {
 883     /* Setup shutdown
 884      *
 885      * We sync the profiles since the hotlist may have changed, while
 886      * we only change the setup data if we have the auto save feature set
 887      */
 888 
 889     save_setup (auto_save_setup, panels_options.auto_save_setup);
 890 
 891     vfs_stamp_path (vfs_get_raw_current_dir ());
 892 }
 893 
 894 /* --------------------------------------------------------------------------------------------- */
 895 
 896 static void
 897 create_file_manager (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 898 {
 899     Widget *w = WIDGET (filemanager);
 900     WGroup *g = GROUP (filemanager);
 901 
 902     w->keymap = filemanager_map;
 903     w->ext_keymap = filemanager_x_map;
 904 
 905     filemanager->get_shortcut = midnight_get_shortcut;
 906     filemanager->get_title = midnight_get_title;
 907     // allow rebind tab
 908     widget_want_tab (w, TRUE);
 909 
 910     the_menubar = menubar_new (NULL);
 911     group_add_widget (g, the_menubar);
 912     init_menu ();
 913 
 914     create_panels ();
 915     group_add_widget (g, get_panel_widget (0));
 916     group_add_widget (g, get_panel_widget (1));
 917 
 918     the_hint = label_new (0, 0, NULL);
 919     the_hint->transparent = TRUE;
 920     the_hint->auto_adjust_cols = 0;
 921     WIDGET (the_hint)->rect.cols = COLS;
 922     group_add_widget (g, the_hint);
 923 
 924     cmdline = command_new (0, 0, 0);
 925     group_add_widget (g, cmdline);
 926 
 927     the_prompt = label_new (0, 0, mc_prompt);
 928     the_prompt->transparent = TRUE;
 929     group_add_widget (g, the_prompt);
 930 
 931     the_bar = buttonbar_new ();
 932     group_add_widget (g, the_bar);
 933     midnight_set_buttonbar (the_bar);
 934 
 935 #ifdef ENABLE_SUBSHELL
 936     /* Must be done after creation of cmdline and prompt widgets to avoid potential
 937        NULL dereference in load_prompt() -> ... -> setup_cmdline() -> label_set_text(). */
 938     if (mc_global.tty.use_subshell)
 939         add_select_channel (mc_global.tty.subshell_pty, load_prompt, NULL);
 940 #endif
 941 }
 942 
 943 /* --------------------------------------------------------------------------------------------- */
 944 
 945 /** result must be free'd (I think this should go in util.c) */
 946 static vfs_path_t *
 947 prepend_cwd_on_local (const char *filename)
     /* [previous][next][first][last][top][bottom][index][help]  */
 948 {
 949     vfs_path_t *vpath;
 950 
 951     vpath = vfs_path_from_str (filename);
 952     if (!vfs_file_is_local (vpath) || g_path_is_absolute (filename))
 953         return vpath;
 954 
 955     vfs_path_free (vpath, TRUE);
 956 
 957     return vfs_path_append_new (vfs_get_raw_current_dir (), filename, (char *) NULL);
 958 }
 959 
 960 /* --------------------------------------------------------------------------------------------- */
 961 
 962 /** Invoke the internal view/edit routine with:
 963  * the default processing and forcing the internal viewer/editor
 964  */
 965 static gboolean
 966 mc_maybe_editor_or_viewer (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 967 {
 968     gboolean ret;
 969 
 970     switch (mc_global.mc_run_mode)
 971     {
 972 #ifdef USE_INTERNAL_EDIT
 973     case MC_RUN_EDITOR:
 974         ret = edit_files ((GList *) mc_run_param0);
 975         break;
 976 #endif
 977     case MC_RUN_VIEWER:
 978     {
 979         vfs_path_t *vpath = NULL;
 980 
 981         if (mc_run_param0 != NULL && *(char *) mc_run_param0 != '\0')
 982             vpath = prepend_cwd_on_local ((char *) mc_run_param0);
 983 
 984         ret = view_file (vpath, FALSE, TRUE);
 985         vfs_path_free (vpath, TRUE);
 986         break;
 987     }
 988 #ifdef USE_DIFF_VIEW
 989     case MC_RUN_DIFFVIEWER:
 990         ret = dview_diff_cmd (mc_run_param0, mc_run_param1);
 991         break;
 992 #endif
 993     default:
 994         ret = FALSE;
 995     }
 996 
 997     return ret;
 998 }
 999 
1000 /* --------------------------------------------------------------------------------------------- */
1001 
1002 static void
1003 show_editor_viewer_history (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1004 {
1005     char *s;
1006     int act;
1007 
1008     s = show_file_history (WIDGET (filemanager), &act);
1009     if (s != NULL)
1010     {
1011         vfs_path_t *s_vpath;
1012 
1013         switch (act)
1014         {
1015         case CK_Edit:
1016             s_vpath = vfs_path_from_str (s);
1017             edit_file_at_line (s_vpath, use_internal_edit, 0);
1018             break;
1019 
1020         case CK_View:
1021             s_vpath = vfs_path_from_str (s);
1022             view_file (s_vpath, use_internal_view, FALSE);
1023             break;
1024 
1025         default:
1026         {
1027             char *d;
1028 
1029             d = g_path_get_dirname (s);
1030             s_vpath = vfs_path_from_str (d);
1031             panel_cd (current_panel, s_vpath, cd_exact);
1032             panel_set_current_by_name (current_panel, s);
1033             g_free (d);
1034         }
1035         }
1036 
1037         g_free (s);
1038         vfs_path_free (s_vpath, TRUE);
1039     }
1040 }
1041 
1042 /* --------------------------------------------------------------------------------------------- */
1043 
1044 static gboolean
1045 quit_cmd_internal (int quiet)
     /* [previous][next][first][last][top][bottom][index][help]  */
1046 {
1047     int q = quit;
1048     size_t n;
1049 
1050     n = dialog_switch_num () - 1;
1051     if (n != 0)
1052     {
1053         char msg[BUF_MEDIUM];
1054 
1055         g_snprintf (msg, sizeof (msg),
1056                     ngettext ("You have %zu opened screen. Quit anyway?",
1057                               "You have %zu opened screens. Quit anyway?", n),
1058                     n);
1059 
1060         if (query_dialog (_ ("The Midnight Commander"), msg, D_NORMAL, 2, _ ("&Yes"), _ ("&No"))
1061             != 0)
1062             return FALSE;
1063         q = 1;
1064     }
1065     else if (quiet || !confirm_exit)
1066         q = 1;
1067     else if (query_dialog (_ ("The Midnight Commander"),
1068                            _ ("Do you really want to quit the Midnight Commander?"), D_NORMAL, 2,
1069                            _ ("&Yes"), _ ("&No"))
1070              == 0)
1071         q = 1;
1072 
1073     if (q != 0)
1074     {
1075 #ifdef ENABLE_SUBSHELL
1076         if (!mc_global.tty.use_subshell)
1077             stop_dialogs ();
1078         else if ((q = exit_subshell () ? 1 : 0) != 0)
1079 #endif
1080             stop_dialogs ();
1081     }
1082 
1083     if (q != 0)
1084         quit |= 1;
1085     return (quit != 0);
1086 }
1087 
1088 /* --------------------------------------------------------------------------------------------- */
1089 
1090 static gboolean
1091 quit_cmd (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1092 {
1093     return quit_cmd_internal (0);
1094 }
1095 
1096 /* --------------------------------------------------------------------------------------------- */
1097 
1098 /**
1099  * Repaint the contents of the panels without frames.  To schedule panel
1100  * for repainting, set panel->dirty to TRUE.  There are many reasons why
1101  * the panels need to be repainted, and this is a costly operation, so
1102  * it's done once per event.
1103  */
1104 
1105 static void
1106 update_dirty_panels (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1107 {
1108     if (get_current_type () == view_listing && current_panel->dirty)
1109         widget_draw (WIDGET (current_panel));
1110 
1111     if (get_other_type () == view_listing && other_panel->dirty)
1112         widget_draw (WIDGET (other_panel));
1113 }
1114 
1115 /* --------------------------------------------------------------------------------------------- */
1116 
1117 static void
1118 toggle_show_hidden (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1119 {
1120     panels_options.show_dot_files = !panels_options.show_dot_files;
1121     update_panels (UP_RELOAD, UP_KEEPSEL);
1122     // redraw panels forced
1123     update_dirty_panels ();
1124 }
1125 
1126 /* --------------------------------------------------------------------------------------------- */
1127 
1128 static cb_ret_t
1129 midnight_execute_cmd (Widget *sender, long command)
     /* [previous][next][first][last][top][bottom][index][help]  */
1130 {
1131     cb_ret_t res = MSG_HANDLED;
1132 
1133     (void) sender;
1134 
1135     // stop quick search before executing any command
1136     send_message (current_panel, NULL, MSG_ACTION, CK_SearchStop, NULL);
1137 
1138     switch (command)
1139     {
1140     case CK_ChangePanel:
1141         (void) change_panel ();
1142         break;
1143     case CK_HotListAdd:
1144         add2hotlist_cmd (current_panel);
1145         break;
1146     case CK_SetupListingFormat:
1147         setup_listing_format_cmd ();
1148         break;
1149     case CK_ChangeMode:
1150         chmod_cmd (current_panel);
1151         break;
1152     case CK_ChangeOwn:
1153         chown_cmd (current_panel);
1154         break;
1155     case CK_ChangeOwnAdvanced:
1156         advanced_chown_cmd (current_panel);
1157         break;
1158 #ifdef ENABLE_EXT2FS_ATTR
1159     case CK_ChangeAttributes:
1160         chattr_cmd (current_panel);
1161         break;
1162 #endif
1163     case CK_CompareDirs:
1164         compare_dirs_cmd ();
1165         break;
1166     case CK_Options:
1167         configure_box ();
1168         break;
1169 #ifdef ENABLE_VFS
1170     case CK_OptionsVfs:
1171         configure_vfs_box ();
1172         break;
1173 #endif
1174     case CK_OptionsConfirm:
1175         confirm_box ();
1176         break;
1177     case CK_Copy:
1178         copy_cmd (current_panel);
1179         break;
1180     case CK_PutCurrentPath:
1181         midnight_put_panel_path (current_panel);
1182         break;
1183     case CK_PutCurrentSelected:
1184         put_current_selected ();
1185         break;
1186     case CK_PutCurrentFullSelected:
1187         midnight_put_panel_path (current_panel);
1188         put_current_selected ();
1189         break;
1190     case CK_PutCurrentLink:
1191         put_current_link ();
1192         break;
1193     case CK_PutCurrentTagged:
1194         put_current_tagged ();
1195         break;
1196     case CK_PutOtherPath:
1197         if (get_other_type () == view_listing)
1198             midnight_put_panel_path (other_panel);
1199         break;
1200     case CK_PutOtherLink:
1201         put_other_link ();
1202         break;
1203     case CK_PutOtherTagged:
1204         put_other_tagged ();
1205         break;
1206     case CK_Delete:
1207         delete_cmd (current_panel);
1208         break;
1209     case CK_ScreenList:
1210         dialog_switch_list ();
1211         break;
1212 #ifdef USE_DIFF_VIEW
1213     case CK_CompareFiles:
1214         diff_view_cmd ();
1215         break;
1216 #endif
1217     case CK_OptionsDisplayBits:
1218         display_bits_box ();
1219         break;
1220     case CK_Edit:
1221         edit_cmd (current_panel);
1222         break;
1223 #ifdef USE_INTERNAL_EDIT
1224     case CK_EditForceInternal:
1225         edit_cmd_force_internal (current_panel);
1226         break;
1227 #endif
1228     case CK_EditExtensionsFile:
1229         ext_cmd ();
1230         break;
1231     case CK_EditFileHighlightFile:
1232         edit_fhl_cmd ();
1233         break;
1234     case CK_EditUserMenu:
1235         edit_mc_menu_cmd ();
1236         break;
1237     case CK_LinkSymbolicEdit:
1238         edit_symlink_cmd ();
1239         break;
1240     case CK_ExternalPanelize:
1241         external_panelize_cmd ();
1242         break;
1243     case CK_ViewFiltered:
1244         view_filtered_cmd (current_panel);
1245         break;
1246     case CK_Find:
1247         find_cmd (current_panel);
1248         break;
1249 #ifdef ENABLE_VFS_SHELL
1250     case CK_ConnectShell:
1251         shelllink_cmd ();
1252         break;
1253 #endif
1254 #ifdef ENABLE_VFS_FTP
1255     case CK_ConnectFtp:
1256         ftplink_cmd ();
1257         break;
1258 #endif
1259 #ifdef ENABLE_VFS_SFTP
1260     case CK_ConnectSftp:
1261         sftplink_cmd ();
1262         break;
1263 #endif
1264     case CK_Panelize:
1265         panel_panelize_cd ();
1266         break;
1267     case CK_Help:
1268         help_cmd ();
1269         break;
1270     case CK_History:
1271         // show the history of command line widget
1272         send_message (cmdline, NULL, MSG_ACTION, CK_History, NULL);
1273         break;
1274     case CK_PanelInfo:
1275         if (sender == WIDGET (the_menubar))
1276             info_cmd ();  // menu
1277         else
1278             info_cmd_no_menu ();  // shortcut or buttonbar
1279         break;
1280 #ifdef ENABLE_BACKGROUND
1281     case CK_Jobs:
1282         jobs_box ();
1283         break;
1284 #endif
1285     case CK_OptionsLayout:
1286         layout_box ();
1287         break;
1288     case CK_OptionsAppearance:
1289         appearance_box ();
1290         break;
1291     case CK_LearnKeys:
1292         learn_keys ();
1293         break;
1294     case CK_Link:
1295         link_cmd (LINK_HARDLINK);
1296         break;
1297     case CK_PanelListing:
1298         listing_cmd ();
1299         break;
1300 #ifdef LISTMODE_EDITOR
1301     case CK_ListMode:
1302         listmode_cmd ();
1303         break;
1304 #endif
1305     case CK_Menu:
1306         menu_cmd ();
1307         break;
1308     case CK_MenuLastSelected:
1309         menu_last_selected_cmd ();
1310         break;
1311     case CK_MakeDir:
1312         mkdir_cmd (current_panel);
1313         break;
1314     case CK_OptionsPanel:
1315         panel_options_box ();
1316         break;
1317 #ifdef HAVE_CHARSET
1318     case CK_SelectCodepage:
1319         encoding_cmd ();
1320         break;
1321 #endif
1322     case CK_CdQuick:
1323         quick_cd_cmd (current_panel);
1324         break;
1325     case CK_HotList:
1326         hotlist_cmd (current_panel);
1327         break;
1328     case CK_PanelQuickView:
1329         if (sender == WIDGET (the_menubar))
1330             quick_view_cmd ();  // menu
1331         else
1332             quick_cmd_no_menu ();  // shortcut or buttonabr
1333         break;
1334     case CK_QuitQuiet:
1335         quiet_quit_cmd ();
1336         break;
1337     case CK_Quit:
1338         quit_cmd ();
1339         break;
1340     case CK_LinkSymbolicRelative:
1341         link_cmd (LINK_SYMLINK_RELATIVE);
1342         break;
1343     case CK_Move:
1344         rename_cmd (current_panel);
1345         break;
1346     case CK_Reread:
1347         reread_cmd ();
1348         break;
1349 #ifdef ENABLE_VFS
1350     case CK_VfsList:
1351         vfs_list (current_panel);
1352         break;
1353 #endif
1354     case CK_SaveSetup:
1355         save_setup_cmd ();
1356         break;
1357     case CK_Select:
1358     case CK_Unselect:
1359     case CK_SelectInvert:
1360     case CK_Filter:
1361         res = send_message (current_panel, filemanager, MSG_ACTION, command, NULL);
1362         break;
1363     case CK_Shell:
1364         toggle_subshell ();
1365         break;
1366     case CK_DirSize:
1367         smart_dirsize_cmd (current_panel);
1368         break;
1369     case CK_Sort:
1370         sort_cmd ();
1371         break;
1372     case CK_ExtendedKeyMap:
1373         WIDGET (filemanager)->ext_mode = TRUE;
1374         break;
1375     case CK_Suspend:
1376         mc_event_raise (MCEVENT_GROUP_CORE, "suspend", NULL);
1377         break;
1378     case CK_Swap:
1379         swap_cmd ();
1380         break;
1381     case CK_LinkSymbolic:
1382         link_cmd (LINK_SYMLINK_ABSOLUTE);
1383         break;
1384     case CK_ShowHidden:
1385         toggle_show_hidden ();
1386         break;
1387     case CK_SplitVertHoriz:
1388         toggle_panels_split ();
1389         break;
1390     case CK_SplitEqual:
1391         panels_split_equal ();
1392         break;
1393     case CK_SplitMore:
1394         panels_split_more ();
1395         break;
1396     case CK_SplitLess:
1397         panels_split_less ();
1398         break;
1399     case CK_PanelTree:
1400         panel_tree_cmd ();
1401         break;
1402     case CK_Tree:
1403         treebox_cmd ();
1404         break;
1405 #ifdef ENABLE_VFS_UNDELFS
1406     case CK_Undelete:
1407         undelete_cmd ();
1408         break;
1409 #endif
1410     case CK_UserMenu:
1411         user_file_menu_cmd ();
1412         break;
1413     case CK_View:
1414         view_cmd (current_panel);
1415         break;
1416     case CK_ViewFile:
1417         view_file_cmd (current_panel);
1418         break;
1419     case CK_EditorViewerHistory:
1420         show_editor_viewer_history ();
1421         break;
1422     case CK_Cancel:
1423         // don't close panels due to SIGINT
1424         break;
1425     default:
1426         res = MSG_NOT_HANDLED;
1427     }
1428 
1429     return res;
1430 }
1431 
1432 /* --------------------------------------------------------------------------------------------- */
1433 
1434 /**
1435  * Whether the command-line should not respond to key events.
1436  *
1437  * This is TRUE if a QuickView or TreeView have the focus, as they're going
1438  * to consume some keys and there's no sense in passing to the command-line
1439  * just the leftovers.
1440  */
1441 static gboolean
1442 is_cmdline_mute (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1443 {
1444     /* When one of panels is other than view_listing,
1445        current_panel points to view_listing panel all time independently of
1446        it's activity. Thus, we can't use get_current_type() here.
1447        current_panel should point to actually current active panel
1448        independently of it's type. */
1449     return (!current_panel->active
1450             && (get_other_type () == view_quick || get_other_type () == view_tree));
1451 }
1452 
1453 /* --------------------------------------------------------------------------------------------- */
1454 
1455 /**
1456  * Handles the Enter key on the command-line.
1457  *
1458  * Returns TRUE if non-whitespace was indeed processed.
1459  */
1460 static gboolean
1461 handle_cmdline_enter (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1462 {
1463     const char *s;
1464 
1465     for (s = input_get_ctext (cmdline); *s != '\0' && whitespace (*s); s++)
1466         ;
1467 
1468     if (*s != '\0')
1469     {
1470         send_message (cmdline, NULL, MSG_KEY, '\n', NULL);
1471         return TRUE;
1472     }
1473 
1474     input_insert (cmdline, "", FALSE);
1475     cmdline->point = 0;
1476 
1477     return FALSE;
1478 }
1479 
1480 /* --------------------------------------------------------------------------------------------- */
1481 
1482 static cb_ret_t
1483 midnight_callback (Widget *w, Widget *sender, widget_msg_t msg, int parm, void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
1484 {
1485     long command;
1486 
1487     switch (msg)
1488     {
1489     case MSG_INIT:
1490         panel_init ();
1491         setup_panels ();
1492         return MSG_HANDLED;
1493 
1494     case MSG_DRAW:
1495         load_hint (TRUE);
1496         group_default_callback (w, NULL, MSG_DRAW, 0, NULL);
1497         // We handle the special case of the output lines
1498         if (mc_global.tty.console_flag != '\0' && output_lines != 0)
1499         {
1500             unsigned char end_line;
1501 
1502             end_line = LINES - (mc_global.keybar_visible ? 1 : 0) - 1;
1503             show_console_contents (output_start_y, end_line - output_lines, end_line);
1504         }
1505         return MSG_HANDLED;
1506 
1507     case MSG_RESIZE:
1508         widget_adjust_position (w->pos_flags, &w->rect);
1509         setup_panels ();
1510         menubar_arrange (the_menubar);
1511         return MSG_HANDLED;
1512 
1513     case MSG_IDLE:
1514         // We only need the first idle event to show user menu after start
1515         widget_idle (w, FALSE);
1516 
1517         if (boot_current_is_left)
1518             widget_select (get_panel_widget (0));
1519         else
1520             widget_select (get_panel_widget (1));
1521 
1522         if (auto_menu)
1523             midnight_execute_cmd (NULL, CK_UserMenu);
1524         return MSG_HANDLED;
1525 
1526     case MSG_KEY:
1527         if (w->ext_mode)
1528         {
1529             command = widget_lookup_key (w, parm);
1530             if (command != CK_IgnoreKey)
1531                 return midnight_execute_cmd (NULL, command);
1532         }
1533 
1534         // FIXME: should handle all menu shortcuts before this point
1535         if (widget_get_state (WIDGET (the_menubar), WST_FOCUSED))
1536             return MSG_NOT_HANDLED;
1537 
1538         if (parm == '\n' && !is_cmdline_mute ())
1539         {
1540             if (handle_cmdline_enter ())
1541                 return MSG_HANDLED;
1542             // Else: the panel will handle it.
1543         }
1544 
1545         if ((!mc_global.tty.alternate_plus_minus
1546              || !(mc_global.tty.console_flag != '\0' || mc_global.tty.xterm_flag))
1547             && !quote && !current_panel->quick_search.active)
1548         {
1549             if (!only_leading_plus_minus)
1550             {
1551                 // Special treatment, since the input line will eat them
1552                 if (parm == '+')
1553                     return send_message (current_panel, filemanager, MSG_ACTION, CK_Select, NULL);
1554 
1555                 if (parm == '\\' || parm == '-')
1556                     return send_message (current_panel, filemanager, MSG_ACTION, CK_Unselect, NULL);
1557 
1558                 if (parm == '*')
1559                     return send_message (current_panel, filemanager, MSG_ACTION, CK_SelectInvert,
1560                                          NULL);
1561             }
1562             else if (!command_prompt || input_is_empty (cmdline))
1563             {
1564                 /* Special treatment '+', '-', '\', '*' only when this is
1565                  * first char on input line
1566                  */
1567                 if (parm == '+')
1568                     return send_message (current_panel, filemanager, MSG_ACTION, CK_Select, NULL);
1569 
1570                 if (parm == '\\' || parm == '-')
1571                     return send_message (current_panel, filemanager, MSG_ACTION, CK_Unselect, NULL);
1572 
1573                 if (parm == '*')
1574                     return send_message (current_panel, filemanager, MSG_ACTION, CK_SelectInvert,
1575                                          NULL);
1576             }
1577         }
1578         return MSG_NOT_HANDLED;
1579 
1580     case MSG_HOTKEY_HANDLED:
1581         if ((get_current_type () == view_listing) && current_panel->quick_search.active)
1582         {
1583             current_panel->dirty = TRUE;  // FIXME: unneeded?
1584             send_message (current_panel, NULL, MSG_ACTION, CK_SearchStop, NULL);
1585         }
1586         return MSG_HANDLED;
1587 
1588     case MSG_UNHANDLED_KEY:
1589     {
1590         cb_ret_t v = MSG_NOT_HANDLED;
1591 
1592         command = widget_lookup_key (w, parm);
1593         if (command != CK_IgnoreKey)
1594             v = midnight_execute_cmd (NULL, command);
1595 
1596         if (v == MSG_NOT_HANDLED && command_prompt && !is_cmdline_mute ())
1597             v = send_message (cmdline, NULL, MSG_KEY, parm, NULL);
1598 
1599         return v;
1600     }
1601 
1602     case MSG_POST_KEY:
1603         if (!widget_get_state (WIDGET (the_menubar), WST_FOCUSED))
1604             update_dirty_panels ();
1605         return MSG_HANDLED;
1606 
1607     case MSG_ACTION:
1608         // Handle shortcuts, menu, and buttonbar.
1609         return midnight_execute_cmd (sender, parm);
1610 
1611     case MSG_DESTROY:
1612         panel_deinit ();
1613         return MSG_HANDLED;
1614 
1615     default:
1616         return dlg_default_callback (w, sender, msg, parm, data);
1617     }
1618 }
1619 
1620 /* --------------------------------------------------------------------------------------------- */
1621 /*** public functions ****************************************************************************/
1622 /* --------------------------------------------------------------------------------------------- */
1623 
1624 void
1625 update_menu (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1626 {
1627     menu_set_name (left_menu, panels_layout.horizontal_split ? _ ("&Above") : _ ("&Left"));
1628     menu_set_name (right_menu, panels_layout.horizontal_split ? _ ("&Below") : _ ("&Right"));
1629     menubar_arrange (the_menubar);
1630     widget_set_visibility (WIDGET (the_menubar), menubar_visible);
1631 }
1632 
1633 /* --------------------------------------------------------------------------------------------- */
1634 
1635 void
1636 midnight_set_buttonbar (WButtonBar *b)
     /* [previous][next][first][last][top][bottom][index][help]  */
1637 {
1638     Widget *w = WIDGET (filemanager);
1639 
1640     buttonbar_set_label (b, 1, Q_ ("ButtonBar|Help"), w->keymap, NULL);
1641     buttonbar_set_label (b, 2, Q_ ("ButtonBar|Menu"), w->keymap, NULL);
1642     buttonbar_set_label (b, 3, Q_ ("ButtonBar|View"), w->keymap, NULL);
1643     buttonbar_set_label (b, 4, Q_ ("ButtonBar|Edit"), w->keymap, NULL);
1644     buttonbar_set_label (b, 5, Q_ ("ButtonBar|Copy"), w->keymap, NULL);
1645     buttonbar_set_label (b, 6, Q_ ("ButtonBar|RenMov"), w->keymap, NULL);
1646     buttonbar_set_label (b, 7, Q_ ("ButtonBar|Mkdir"), w->keymap, NULL);
1647     buttonbar_set_label (b, 8, Q_ ("ButtonBar|Delete"), w->keymap, NULL);
1648     buttonbar_set_label (b, 9, Q_ ("ButtonBar|PullDn"), w->keymap, NULL);
1649     buttonbar_set_label (b, 10, Q_ ("ButtonBar|Quit"), w->keymap, NULL);
1650 }
1651 
1652 /* --------------------------------------------------------------------------------------------- */
1653 /**
1654  * Return a random hint.  If force is TRUE, ignore the timeout.
1655  */
1656 
1657 char *
1658 get_random_hint (gboolean force)
     /* [previous][next][first][last][top][bottom][index][help]  */
1659 {
1660     static const gint64 update_period = 60 * G_USEC_PER_SEC;
1661     static gint64 tv = 0;
1662 
1663     char *data, *result, *eop;
1664     size_t len, start;
1665     GIConv conv;
1666 
1667     // Do not change hints more often than one minute
1668     if (!force && !mc_time_elapsed (&tv, update_period))
1669         return g_strdup ("");
1670 
1671     data = load_mc_home_file (mc_global.share_data_dir, MC_HINT, NULL, &len);
1672     if (data == NULL)
1673         return NULL;
1674 
1675     // get a random entry
1676     srand ((unsigned int) (tv / G_USEC_PER_SEC));
1677     start = ((size_t) rand ()) % (len - 1);
1678 
1679     // Search the start of paragraph
1680     for (; start != 0; start--)
1681         if (data[start] == '\n' && data[start + 1] == '\n')
1682         {
1683             start += 2;
1684             break;
1685         }
1686 
1687     // Search the end of paragraph
1688     for (eop = data + start; *eop != '\0'; eop++)
1689     {
1690         if (*eop == '\n' && *(eop + 1) == '\n')
1691         {
1692             *eop = '\0';
1693             break;
1694         }
1695         if (*eop == '\n')
1696             *eop = ' ';
1697     }
1698 
1699     // hint files are stored in utf-8
1700     // try convert hint file from utf-8 to terminal encoding
1701     conv = str_crt_conv_from ("UTF-8");
1702     if (conv == INVALID_CONV)
1703         result = g_strndup (data + start, len - start);
1704     else
1705     {
1706         GString *buffer;
1707         gboolean nok;
1708 
1709         buffer = g_string_sized_new (len - start);
1710         nok = (str_convert (conv, data + start, buffer) == ESTR_FAILURE);
1711         result = g_string_free (buffer, nok);
1712         str_close_conv (conv);
1713     }
1714 
1715     g_free (data);
1716     return result;
1717 }
1718 
1719 /* --------------------------------------------------------------------------------------------- */
1720 /**
1721  * Load new hint and display it.
1722  * IF force is not 0, ignore the timeout.
1723  */
1724 
1725 void
1726 load_hint (gboolean force)
     /* [previous][next][first][last][top][bottom][index][help]  */
1727 {
1728     char *hint;
1729 
1730     if (WIDGET (the_hint)->owner == NULL)
1731         return;
1732 
1733     if (!mc_global.message_visible)
1734     {
1735         label_set_text (the_hint, NULL);
1736         return;
1737     }
1738 
1739     hint = get_random_hint (force);
1740 
1741     if (hint != NULL)
1742     {
1743         if (*hint != '\0')
1744             set_hintbar (hint);
1745         g_free (hint);
1746     }
1747     else
1748     {
1749         char text[BUF_SMALL];
1750 
1751         g_snprintf (text, sizeof (text), _ ("GNU Midnight Commander %s\n"), mc_global.mc_version);
1752         set_hintbar (text);
1753     }
1754 }
1755 
1756 /* --------------------------------------------------------------------------------------------- */
1757 /**
1758  * Change current panel in the file manager.
1759  *
1760  * @return current_panel
1761  */
1762 
1763 WPanel *
1764 change_panel (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1765 {
1766     input_complete_free (cmdline);
1767     group_select_next_widget (GROUP (filemanager));
1768     return current_panel;
1769 }
1770 
1771 /* --------------------------------------------------------------------------------------------- */
1772 
1773 /** Save current stat of directories to avoid reloading the panels
1774  * when no modifications have taken place
1775  */
1776 void
1777 save_cwds_stat (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1778 {
1779     if (panels_options.fast_reload)
1780     {
1781         mc_stat (current_panel->cwd_vpath, &(current_panel->dir_stat));
1782         if (get_other_type () == view_listing)
1783             mc_stat (other_panel->cwd_vpath, &(other_panel->dir_stat));
1784     }
1785 }
1786 
1787 /* --------------------------------------------------------------------------------------------- */
1788 
1789 gboolean
1790 quiet_quit_cmd (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1791 {
1792     print_last_revert = TRUE;
1793     return quit_cmd_internal (1);
1794 }
1795 
1796 /* --------------------------------------------------------------------------------------------- */
1797 
1798 /** Run the main dialog that occupies the whole screen */
1799 gboolean
1800 do_nc (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1801 {
1802     gboolean ret;
1803 
1804 #ifdef USE_INTERNAL_EDIT
1805     edit_stack_init ();
1806 #endif
1807 
1808     filemanager = dlg_create (FALSE, 0, 0, 1, 1, WPOS_FULLSCREEN, FALSE, dialog_colors,
1809                               midnight_callback, NULL, "[main]", NULL);
1810 
1811     // Check if we were invoked as an editor or file viewer
1812     if (mc_global.mc_run_mode != MC_RUN_FULL)
1813     {
1814         setup_dummy_mc ();
1815         ret = mc_maybe_editor_or_viewer ();
1816     }
1817     else
1818     {
1819         // We only need the first idle event to show user menu after start
1820         widget_idle (WIDGET (filemanager), TRUE);
1821 
1822         setup_mc ();
1823         mc_filehighlight = mc_fhl_new (TRUE);
1824 
1825         create_file_manager ();
1826         (void) dlg_run (filemanager);
1827 
1828         mc_fhl_free (&mc_filehighlight);
1829 
1830         ret = TRUE;
1831 
1832         // widget_destroy destroys even current_panel->cwd_vpath, so we have to save a copy :)
1833         if (mc_args__last_wd_file != NULL && vfs_current_is_local ())
1834             last_wd_string = g_strdup (vfs_path_as_str (current_panel->cwd_vpath));
1835 
1836         // don't handle VFS timestamps for dirs opened in panels
1837         mc_event_destroy (MCEVENT_GROUP_CORE, "vfs_timestamp");
1838     }
1839 
1840     // Program end
1841     mc_global.midnight_shutdown = TRUE;
1842     dialog_switch_shutdown ();
1843     done_mc ();
1844     widget_destroy (WIDGET (filemanager));
1845     current_panel = NULL;
1846 
1847 #ifdef USE_INTERNAL_EDIT
1848     edit_stack_free ();
1849 #endif
1850 
1851     if ((quit & SUBSHELL_EXIT) == 0)
1852         tty_clear_screen ();
1853 
1854     return ret;
1855 }
1856 
1857 /* --------------------------------------------------------------------------------------------- */

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