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

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