Manual pages: mcmcdiffmceditmcview

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

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

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