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

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