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

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