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

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