root/src/filemanager/panel.c

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

DEFINITIONS

This source file includes following definitions.
  1. set_colors
  2. format_item_free
  3. panel_lines
  4. add_permission_string
  5. string_file_name
  6. ilog10
  7. format_device_number
  8. string_file_size
  9. string_file_size_brief
  10. string_file_type
  11. string_file_mtime
  12. string_file_atime
  13. string_file_ctime
  14. string_file_permission
  15. string_file_perm_octal
  16. string_file_nlinks
  17. string_inode
  18. string_file_nuid
  19. string_file_ngid
  20. string_file_owner
  21. string_file_group
  22. string_marked
  23. string_space
  24. string_dot
  25. file_compute_color
  26. panel_items
  27. format_file
  28. repaint_file
  29. display_mini_info
  30. paint_dir
  31. display_total_marked_size
  32. mini_info_separator
  33. show_free_space
  34. panel_correct_path_to_show
  35. panel_get_encoding_info_str
  36. show_dir
  37. adjust_top_file
  38. panel_save_name
  39. directory_history_add
  40. panel_load_history
  41. panel_save_history
  42. panel_destroy
  43. panel_paint_sort_info
  44. panel_get_title_without_hotkey
  45. panel_print_header
  46. parse_panel_size
  47. parse_display_format
  48. use_display_format
  49. panel_format
  50. mini_status_format
  51. cd_up_dir
  52. maybe_cd
  53. force_maybe_cd
  54. unselect_item
  55. panel_select_ext_cmd
  56. panel_selected_at_half
  57. move_down
  58. move_up
  59. move_selection
  60. move_left
  61. move_right
  62. prev_page
  63. goto_parent_dir
  64. next_page
  65. goto_child_dir
  66. goto_top_file
  67. goto_middle_file
  68. goto_bottom_file
  69. move_home
  70. move_end
  71. do_mark_file
  72. mark_file
  73. mark_file_up
  74. mark_file_down
  75. mark_file_right
  76. mark_file_left
  77. panel_select_unselect_files
  78. panel_select_files
  79. panel_unselect_files
  80. panel_select_invert_files
  81. do_search
  82. start_search
  83. stop_search
  84. do_enter_on_file_entry
  85. do_enter
  86. panel_cycle_listing_format
  87. chdir_other_panel
  88. panel_sync_other
  89. chdir_to_readlink
  90. panel_get_format_field_index_by_name
  91. panel_get_sortable_field_by_format
  92. panel_toggle_sort_order_prev
  93. panel_toggle_sort_order_next
  94. panel_select_sort_order
  95. panel_content_scroll_left
  96. panel_content_scroll_right
  97. panel_set_sort_type_by_id
  98. get_parent_dir_name
  99. subshell_chdir
  100. _do_panel_cd
  101. directory_history_next
  102. directory_history_prev
  103. directory_history_list
  104. panel_execute_cmd
  105. panel_key
  106. panel_callback
  107. mouse_toggle_mark
  108. mouse_set_mark
  109. mark_if_marking
  110. mouse_sort_col
  111. panel_mouse_is_on_item
  112. panel_mouse_callback
  113. reload_panelized
  114. update_one_panel_widget
  115. update_one_panel
  116. do_select
  117. do_try_to_select
  118. event_update_panels
  119. panel_save_current_file_to_clip_file
  120. panel_recursive_cd_to_parent
  121. panel_dir_list_callback
  122. try_to_select
  123. panel_clean_dir
  124. panel_set_cwd
  125. panel_set_lwd
  126. panel_sized_empty_new
  127. panel_sized_with_dir_new
  128. panel_reload
  129. set_panel_formats
  130. select_item
  131. unmark_files
  132. recalculate_panel_summary
  133. do_file_mark
  134. do_panel_cd
  135. file_mark
  136. panel_re_sort
  137. panel_set_sort_order
  138. panel_change_encoding
  139. remove_encoding_from_path
  140. update_panels
  141. panel_get_num_of_sortable_fields
  142. panel_get_sortable_fields
  143. panel_get_field_by_id
  144. panel_get_field_by_title_hotkey
  145. panel_get_field_by_title
  146. panel_get_num_of_user_possible_fields
  147. panel_get_user_possible_fields
  148. panel_init
  149. panel_deinit
  150. do_cd

   1 /*
   2    Panel managing.
   3 
   4    Copyright (C) 1994-2020
   5    Free Software Foundation, Inc.
   6 
   7    Written by:
   8    Miguel de Icaza, 1995
   9    Timur Bakeyev, 1997, 1999
  10    Slava Zanko <slavazanko@gmail.com>, 2013
  11    Andrew Borodin <aborodin@vmail.ru>, 2013-2016
  12 
  13    This file is part of the Midnight Commander.
  14 
  15    The Midnight Commander is free software: you can redistribute it
  16    and/or modify it under the terms of the GNU General Public License as
  17    published by the Free Software Foundation, either version 3 of the License,
  18    or (at your option) any later version.
  19 
  20    The Midnight Commander is distributed in the hope that it will be useful,
  21    but WITHOUT ANY WARRANTY; without even the implied warranty of
  22    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  23    GNU General Public License for more details.
  24 
  25    You should have received a copy of the GNU General Public License
  26    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  27  */
  28 
  29 /** \file panel.c
  30  *  \brief Source: panel managin module
  31  */
  32 
  33 #include <config.h>
  34 
  35 #include <errno.h>
  36 #include <stdio.h>
  37 #include <stdlib.h>
  38 #include <string.h>
  39 
  40 #include "lib/global.h"
  41 
  42 #include "lib/tty/tty.h"
  43 #include "lib/tty/key.h"        /* XCTRL and ALT macros  */
  44 #include "lib/skin.h"
  45 #include "lib/strescape.h"
  46 #include "lib/mcconfig.h"
  47 #include "lib/vfs/vfs.h"
  48 #include "lib/unixcompat.h"
  49 #include "lib/search.h"
  50 #include "lib/timefmt.h"        /* file_date() */
  51 #include "lib/util.h"
  52 #include "lib/widget.h"
  53 #ifdef HAVE_CHARSET
  54 #include "lib/charsets.h"       /* get_codepage_id () */
  55 #endif
  56 #include "lib/event.h"
  57 
  58 #include "src/setup.h"          /* For loading/saving panel options */
  59 #include "src/execute.h"
  60 #ifdef HAVE_CHARSET
  61 #include "src/selcodepage.h"    /* select_charset (), SELECT_CHARSET_NO_TRANSLATE */
  62 #endif
  63 #include "src/keybind-defaults.h"       /* global_keymap_t */
  64 #ifdef ENABLE_SUBSHELL
  65 #include "src/subshell/subshell.h"      /* do_subshell_chdir() */
  66 #endif
  67 
  68 #include "src/usermenu.h"
  69 
  70 #include "dir.h"
  71 #include "boxes.h"
  72 #include "tree.h"
  73 #include "ext.h"                /* regexp_command */
  74 #include "layout.h"             /* Most layout variables are here */
  75 #include "cmd.h"
  76 #include "command.h"            /* cmdline */
  77 #include "midnight.h"
  78 #include "mountlist.h"          /* my_statfs */
  79 
  80 #include "panel.h"
  81 
  82 /*** global variables ****************************************************************************/
  83 
  84 /* The hook list for the select file function */
  85 hook_t *select_file_hook = NULL;
  86 
  87 /* *INDENT-OFF* */
  88 panelized_panel_t panelized_panel = { {NULL, 0, -1, NULL}, NULL };
  89 /* *INDENT-ON* */
  90 
  91 static const char *string_file_name (file_entry_t *, int);
  92 static const char *string_file_size (file_entry_t *, int);
  93 static const char *string_file_size_brief (file_entry_t *, int);
  94 static const char *string_file_type (file_entry_t *, int);
  95 static const char *string_file_mtime (file_entry_t *, int);
  96 static const char *string_file_atime (file_entry_t *, int);
  97 static const char *string_file_ctime (file_entry_t *, int);
  98 static const char *string_file_permission (file_entry_t *, int);
  99 static const char *string_file_perm_octal (file_entry_t *, int);
 100 static const char *string_file_nlinks (file_entry_t *, int);
 101 static const char *string_inode (file_entry_t *, int);
 102 static const char *string_file_nuid (file_entry_t *, int);
 103 static const char *string_file_ngid (file_entry_t *, int);
 104 static const char *string_file_owner (file_entry_t *, int);
 105 static const char *string_file_group (file_entry_t *, int);
 106 static const char *string_marked (file_entry_t *, int);
 107 static const char *string_space (file_entry_t *, int);
 108 static const char *string_dot (file_entry_t *, int);
 109 
 110 mc_fhl_t *mc_filehighlight = NULL;
 111 
 112 /*** file scope macro definitions ****************************************************************/
 113 
 114 #define NORMAL          0
 115 #define SELECTED        1
 116 #define MARKED          2
 117 #define MARKED_SELECTED 3
 118 #define STATUS          5
 119 
 120 /*** file scope type declarations ****************************************************************/
 121 
 122 typedef enum
 123 {
 124     MARK_DONT_MOVE = 0,
 125     MARK_DOWN = 1,
 126     MARK_FORCE_DOWN = 2,
 127     MARK_FORCE_UP = 3
 128 } mark_act_t;
 129 
 130 /*
 131  * This describes a format item.  The parse_display_format routine parses
 132  * the user specified format and creates a linked list of format_item_t structures.
 133  */
 134 typedef struct format_item_t
 135 {
 136     int requested_field_len;
 137     int field_len;
 138     align_crt_t just_mode;
 139     gboolean expand;
 140     const char *(*string_fn) (file_entry_t *, int len);
 141     char *title;
 142     const char *id;
 143 } format_item_t;
 144 
 145 /* File name scroll states */
 146 typedef enum
 147 {
 148     FILENAME_NOSCROLL = 1,
 149     FILENAME_SCROLL_LEFT = 2,
 150     FILENAME_SCROLL_RIGHT = 4
 151 } filename_scroll_flag_t;
 152 
 153 /*** file scope variables ************************************************************************/
 154 
 155 /* *INDENT-OFF* */
 156 static panel_field_t panel_fields[] = {
 157     {
 158      "unsorted", 12, TRUE, J_LEFT_FIT,
 159      /* TRANSLATORS: one single character to represent 'unsorted' sort mode  */
 160      /* TRANSLATORS: no need to translate 'sort', it's just a context prefix  */
 161      N_("sort|u"),
 162      N_("&Unsorted"), TRUE, FALSE,
 163      string_file_name,
 164      (GCompareFunc) unsorted
 165     }
 166     ,
 167     {
 168      "name", 12, TRUE, J_LEFT_FIT,
 169      /* TRANSLATORS: one single character to represent 'name' sort mode  */
 170      /* TRANSLATORS: no need to translate 'sort', it's just a context prefix  */
 171      N_("sort|n"),
 172      N_("&Name"), TRUE, TRUE,
 173      string_file_name,
 174      (GCompareFunc) sort_name
 175     }
 176     ,
 177     {
 178      "version", 12, TRUE, J_LEFT_FIT,
 179      /* TRANSLATORS: one single character to represent 'version' sort mode  */
 180      /* TRANSLATORS: no need to translate 'sort', it's just a context prefix  */
 181      N_("sort|v"),
 182      N_("&Version"), TRUE, FALSE,
 183      string_file_name,
 184      (GCompareFunc) sort_vers
 185     }
 186     ,
 187     {
 188      "extension", 12, TRUE, J_LEFT_FIT,
 189      /* TRANSLATORS: one single character to represent 'extension' sort mode  */
 190      /* TRANSLATORS: no need to translate 'sort', it's just a context prefix  */
 191      N_("sort|e"),
 192      N_("E&xtension"), TRUE, FALSE,
 193      string_file_name,          /* TODO: string_file_ext */
 194      (GCompareFunc) sort_ext
 195     }
 196     ,
 197     {
 198      "size", 7, FALSE, J_RIGHT,
 199      /* TRANSLATORS: one single character to represent 'size' sort mode  */
 200      /* TRANSLATORS: no need to translate 'sort', it's just a context prefix  */
 201      N_("sort|s"),
 202      N_("&Size"), TRUE, TRUE,
 203      string_file_size,
 204      (GCompareFunc) sort_size
 205     }
 206     ,
 207     {
 208      "bsize", 7, FALSE, J_RIGHT,
 209      "",
 210      N_("Block Size"), FALSE, FALSE,
 211      string_file_size_brief,
 212      (GCompareFunc) sort_size
 213     }
 214     ,
 215     {
 216      "type", 1, FALSE, J_LEFT,
 217      "",
 218      "", FALSE, TRUE,
 219      string_file_type,
 220      NULL
 221     }
 222     ,
 223     {
 224      "mtime", 12, FALSE, J_RIGHT,
 225      /* TRANSLATORS: one single character to represent 'Modify time' sort mode  */
 226      /* TRANSLATORS: no need to translate 'sort', it's just a context prefix  */
 227      N_("sort|m"),
 228      N_("&Modify time"), TRUE, TRUE,
 229      string_file_mtime,
 230      (GCompareFunc) sort_time
 231     }
 232     ,
 233     {
 234      "atime", 12, FALSE, J_RIGHT,
 235      /* TRANSLATORS: one single character to represent 'Access time' sort mode  */
 236      /* TRANSLATORS: no need to translate 'sort', it's just a context prefix  */
 237      N_("sort|a"),
 238      N_("&Access time"), TRUE, TRUE,
 239      string_file_atime,
 240      (GCompareFunc) sort_atime
 241     }
 242     ,
 243     {
 244      "ctime", 12, FALSE, J_RIGHT,
 245      /* TRANSLATORS: one single character to represent 'Change time' sort mode  */
 246      /* TRANSLATORS: no need to translate 'sort', it's just a context prefix  */
 247      N_("sort|h"),
 248      N_("C&hange time"), TRUE, TRUE,
 249      string_file_ctime,
 250      (GCompareFunc) sort_ctime
 251     }
 252     ,
 253     {
 254      "perm", 10, FALSE, J_LEFT,
 255      "",
 256      N_("Permission"), FALSE, TRUE,
 257      string_file_permission,
 258      NULL
 259     }
 260     ,
 261     {
 262      "mode", 6, FALSE, J_RIGHT,
 263      "",
 264      N_("Perm"), FALSE, TRUE,
 265      string_file_perm_octal,
 266      NULL
 267     }
 268     ,
 269     {
 270      "nlink", 2, FALSE, J_RIGHT,
 271      "",
 272      N_("Nl"), FALSE, TRUE,
 273      string_file_nlinks, NULL
 274     }
 275     ,
 276     {
 277      "inode", 5, FALSE, J_RIGHT,
 278      /* TRANSLATORS: one single character to represent 'inode' sort mode  */
 279      /* TRANSLATORS: no need to translate 'sort', it's just a context prefix  */
 280      N_("sort|i"),
 281      N_("&Inode"), TRUE, TRUE,
 282      string_inode,
 283      (GCompareFunc) sort_inode
 284     }
 285     ,
 286     {
 287      "nuid", 5, FALSE, J_RIGHT,
 288      "",
 289      N_("UID"), FALSE, FALSE,
 290      string_file_nuid,
 291      NULL
 292     }
 293     ,
 294     {
 295      "ngid", 5, FALSE, J_RIGHT,
 296      "",
 297      N_("GID"), FALSE, FALSE,
 298      string_file_ngid,
 299      NULL
 300     }
 301     ,
 302     {
 303      "owner", 8, FALSE, J_LEFT_FIT,
 304      "",
 305      N_("Owner"), FALSE, TRUE,
 306      string_file_owner,
 307      NULL
 308     }
 309     ,
 310     {
 311      "group", 8, FALSE, J_LEFT_FIT,
 312      "",
 313      N_("Group"), FALSE, TRUE,
 314      string_file_group,
 315      NULL
 316     }
 317     ,
 318     {
 319      "mark", 1, FALSE, J_RIGHT,
 320      "",
 321      " ", FALSE, TRUE,
 322      string_marked,
 323      NULL
 324     }
 325     ,
 326     {
 327      "|", 1, FALSE, J_RIGHT,
 328      "",
 329      " ", FALSE, TRUE,
 330      NULL,
 331      NULL
 332     }
 333     ,
 334     {
 335      "space", 1, FALSE, J_RIGHT,
 336      "",
 337      " ", FALSE, TRUE,
 338      string_space,
 339      NULL
 340     }
 341     ,
 342     {
 343      "dot", 1, FALSE, J_RIGHT,
 344      "",
 345      " ", FALSE, FALSE,
 346      string_dot,
 347      NULL
 348     }
 349     ,
 350     {
 351      NULL, 0, FALSE, J_RIGHT, NULL, NULL, FALSE, FALSE, NULL, NULL
 352     }
 353 };
 354 /* *INDENT-ON* */
 355 
 356 static char *panel_sort_up_char = NULL;
 357 static char *panel_sort_down_char = NULL;
 358 
 359 static char *panel_hiddenfiles_show_char = NULL;
 360 static char *panel_hiddenfiles_hide_char = NULL;
 361 static char *panel_history_prev_item_char = NULL;
 362 static char *panel_history_next_item_char = NULL;
 363 static char *panel_history_show_list_char = NULL;
 364 static char *panel_filename_scroll_left_char = NULL;
 365 static char *panel_filename_scroll_right_char = NULL;
 366 
 367 /* Panel that selection started */
 368 static WPanel *mouse_mark_panel = NULL;
 369 
 370 static gboolean mouse_marking = FALSE;
 371 static int state_mark = 0;
 372 
 373 /* --------------------------------------------------------------------------------------------- */
 374 /*** file scope functions ************************************************************************/
 375 /* --------------------------------------------------------------------------------------------- */
 376 
 377 static void
 378 set_colors (const WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
 379 {
 380     (void) panel;
 381 
 382     tty_set_normal_attrs ();
 383     tty_setcolor (NORMAL_COLOR);
 384 }
 385 
 386 /* --------------------------------------------------------------------------------------------- */
 387 /** Delete format_item_t object */
 388 
 389 static void
 390 format_item_free (format_item_t * format)
     /* [previous][next][first][last][top][bottom][index][help]  */
 391 {
 392     g_free (format->title);
 393     g_free (format);
 394 }
 395 
 396 /* --------------------------------------------------------------------------------------------- */
 397 /** Extract the number of available lines in a panel */
 398 
 399 static int
 400 panel_lines (const WPanel * p)
     /* [previous][next][first][last][top][bottom][index][help]  */
 401 {
 402     /* 3 lines are: top frame, column header, botton frame */
 403     return (CONST_WIDGET (p)->lines - 3 - (panels_options.show_mini_info ? 2 : 0));
 404 }
 405 
 406 /* --------------------------------------------------------------------------------------------- */
 407 /** This code relies on the default justification!!! */
 408 
 409 static void
 410 add_permission_string (const char *dest, int width, file_entry_t * fe, int attr, int color,
     /* [previous][next][first][last][top][bottom][index][help]  */
 411                        gboolean is_octal)
 412 {
 413     int i, r, l;
 414 
 415     l = get_user_permissions (&fe->st);
 416 
 417     if (is_octal)
 418     {
 419         /* Place of the access bit in octal mode */
 420         l = width + l - 3;
 421         r = l + 1;
 422     }
 423     else
 424     {
 425         /* The same to the triplet in string mode */
 426         l = l * 3 + 1;
 427         r = l + 3;
 428     }
 429 
 430     for (i = 0; i < width; i++)
 431     {
 432         if (i >= l && i < r)
 433         {
 434             if (attr == SELECTED || attr == MARKED_SELECTED)
 435                 tty_setcolor (MARKED_SELECTED_COLOR);
 436             else
 437                 tty_setcolor (MARKED_COLOR);
 438         }
 439         else if (color >= 0)
 440             tty_setcolor (color);
 441         else
 442             tty_lowlevel_setcolor (-color);
 443 
 444         tty_print_char (dest[i]);
 445     }
 446 }
 447 
 448 /* --------------------------------------------------------------------------------------------- */
 449 /** String representations of various file attributes name */
 450 
 451 static const char *
 452 string_file_name (file_entry_t * fe, int len)
     /* [previous][next][first][last][top][bottom][index][help]  */
 453 {
 454     static char buffer[MC_MAXPATHLEN * MB_LEN_MAX + 1];
 455 
 456     (void) len;
 457 
 458     g_strlcpy (buffer, fe->fname, sizeof (buffer));
 459     return buffer;
 460 }
 461 
 462 /* --------------------------------------------------------------------------------------------- */
 463 
 464 static unsigned int
 465 ilog10 (dev_t n)
     /* [previous][next][first][last][top][bottom][index][help]  */
 466 {
 467     unsigned int digits = 0;
 468 
 469     do
 470     {
 471         digits++;
 472         n /= 10;
 473     }
 474     while (n != 0);
 475 
 476     return digits;
 477 }
 478 
 479 /* --------------------------------------------------------------------------------------------- */
 480 
 481 static void
 482 format_device_number (char *buf, size_t bufsize, dev_t dev)
     /* [previous][next][first][last][top][bottom][index][help]  */
 483 {
 484     dev_t major_dev, minor_dev;
 485     unsigned int major_digits, minor_digits;
 486 
 487     major_dev = major (dev);
 488     major_digits = ilog10 (major_dev);
 489 
 490     minor_dev = minor (dev);
 491     minor_digits = ilog10 (minor_dev);
 492 
 493     g_assert (bufsize >= 1);
 494 
 495     if (major_digits + 1 + minor_digits + 1 <= bufsize)
 496         g_snprintf (buf, bufsize, "%lu,%lu", (unsigned long) major_dev, (unsigned long) minor_dev);
 497     else
 498         g_strlcpy (buf, _("[dev]"), bufsize);
 499 }
 500 
 501 /* --------------------------------------------------------------------------------------------- */
 502 /** size */
 503 
 504 static const char *
 505 string_file_size (file_entry_t * fe, int len)
     /* [previous][next][first][last][top][bottom][index][help]  */
 506 {
 507     static char buffer[BUF_TINY];
 508 
 509     /* Don't ever show size of ".." since we don't calculate it */
 510     if (DIR_IS_DOTDOT (fe->fname))
 511         return _("UP--DIR");
 512 
 513 #ifdef HAVE_STRUCT_STAT_ST_RDEV
 514     if (S_ISBLK (fe->st.st_mode) || S_ISCHR (fe->st.st_mode))
 515         format_device_number (buffer, len + 1, fe->st.st_rdev);
 516     else
 517 #endif
 518         size_trunc_len (buffer, (unsigned int) len, fe->st.st_size, 0, panels_options.kilobyte_si);
 519 
 520     return buffer;
 521 }
 522 
 523 /* --------------------------------------------------------------------------------------------- */
 524 /** bsize */
 525 
 526 static const char *
 527 string_file_size_brief (file_entry_t * fe, int len)
     /* [previous][next][first][last][top][bottom][index][help]  */
 528 {
 529     if (S_ISLNK (fe->st.st_mode) && !link_isdir (fe))
 530         return _("SYMLINK");
 531 
 532     if ((S_ISDIR (fe->st.st_mode) || link_isdir (fe)) && !DIR_IS_DOTDOT (fe->fname))
 533         return _("SUB-DIR");
 534 
 535     return string_file_size (fe, len);
 536 }
 537 
 538 /* --------------------------------------------------------------------------------------------- */
 539 /** This functions return a string representation of a file entry type */
 540 
 541 static const char *
 542 string_file_type (file_entry_t * fe, int len)
     /* [previous][next][first][last][top][bottom][index][help]  */
 543 {
 544     static char buffer[2];
 545 
 546     (void) len;
 547 
 548     if (S_ISDIR (fe->st.st_mode))
 549         buffer[0] = PATH_SEP;
 550     else if (S_ISLNK (fe->st.st_mode))
 551     {
 552         if (link_isdir (fe))
 553             buffer[0] = '~';
 554         else if (fe->f.stale_link)
 555             buffer[0] = '!';
 556         else
 557             buffer[0] = '@';
 558     }
 559     else if (S_ISCHR (fe->st.st_mode))
 560         buffer[0] = '-';
 561     else if (S_ISSOCK (fe->st.st_mode))
 562         buffer[0] = '=';
 563     else if (S_ISDOOR (fe->st.st_mode))
 564         buffer[0] = '>';
 565     else if (S_ISBLK (fe->st.st_mode))
 566         buffer[0] = '+';
 567     else if (S_ISFIFO (fe->st.st_mode))
 568         buffer[0] = '|';
 569     else if (S_ISNAM (fe->st.st_mode))
 570         buffer[0] = '#';
 571     else if (!S_ISREG (fe->st.st_mode))
 572         buffer[0] = '?';        /* non-regular of unknown kind */
 573     else if (is_exe (fe->st.st_mode))
 574         buffer[0] = '*';
 575     else
 576         buffer[0] = ' ';
 577     buffer[1] = '\0';
 578     return buffer;
 579 }
 580 
 581 /* --------------------------------------------------------------------------------------------- */
 582 /** mtime */
 583 
 584 static const char *
 585 string_file_mtime (file_entry_t * fe, int len)
     /* [previous][next][first][last][top][bottom][index][help]  */
 586 {
 587     (void) len;
 588 
 589     return file_date (fe->st.st_mtime);
 590 }
 591 
 592 /* --------------------------------------------------------------------------------------------- */
 593 /** atime */
 594 
 595 static const char *
 596 string_file_atime (file_entry_t * fe, int len)
     /* [previous][next][first][last][top][bottom][index][help]  */
 597 {
 598     (void) len;
 599 
 600     return file_date (fe->st.st_atime);
 601 }
 602 
 603 /* --------------------------------------------------------------------------------------------- */
 604 /** ctime */
 605 
 606 static const char *
 607 string_file_ctime (file_entry_t * fe, int len)
     /* [previous][next][first][last][top][bottom][index][help]  */
 608 {
 609     (void) len;
 610 
 611     return file_date (fe->st.st_ctime);
 612 }
 613 
 614 /* --------------------------------------------------------------------------------------------- */
 615 /** perm */
 616 
 617 static const char *
 618 string_file_permission (file_entry_t * fe, int len)
     /* [previous][next][first][last][top][bottom][index][help]  */
 619 {
 620     (void) len;
 621 
 622     return string_perm (fe->st.st_mode);
 623 }
 624 
 625 /* --------------------------------------------------------------------------------------------- */
 626 /** mode */
 627 
 628 static const char *
 629 string_file_perm_octal (file_entry_t * fe, int len)
     /* [previous][next][first][last][top][bottom][index][help]  */
 630 {
 631     static char buffer[10];
 632 
 633     (void) len;
 634 
 635     g_snprintf (buffer, sizeof (buffer), "0%06lo", (unsigned long) fe->st.st_mode);
 636     return buffer;
 637 }
 638 
 639 /* --------------------------------------------------------------------------------------------- */
 640 /** nlink */
 641 
 642 static const char *
 643 string_file_nlinks (file_entry_t * fe, int len)
     /* [previous][next][first][last][top][bottom][index][help]  */
 644 {
 645     static char buffer[BUF_TINY];
 646 
 647     (void) len;
 648 
 649     g_snprintf (buffer, sizeof (buffer), "%16d", (int) fe->st.st_nlink);
 650     return buffer;
 651 }
 652 
 653 /* --------------------------------------------------------------------------------------------- */
 654 /** inode */
 655 
 656 static const char *
 657 string_inode (file_entry_t * fe, int len)
     /* [previous][next][first][last][top][bottom][index][help]  */
 658 {
 659     static char buffer[10];
 660 
 661     (void) len;
 662 
 663     g_snprintf (buffer, sizeof (buffer), "%lu", (unsigned long) fe->st.st_ino);
 664     return buffer;
 665 }
 666 
 667 /* --------------------------------------------------------------------------------------------- */
 668 /** nuid */
 669 
 670 static const char *
 671 string_file_nuid (file_entry_t * fe, int len)
     /* [previous][next][first][last][top][bottom][index][help]  */
 672 {
 673     static char buffer[10];
 674 
 675     (void) len;
 676 
 677     g_snprintf (buffer, sizeof (buffer), "%lu", (unsigned long) fe->st.st_uid);
 678     return buffer;
 679 }
 680 
 681 /* --------------------------------------------------------------------------------------------- */
 682 /** ngid */
 683 
 684 static const char *
 685 string_file_ngid (file_entry_t * fe, int len)
     /* [previous][next][first][last][top][bottom][index][help]  */
 686 {
 687     static char buffer[10];
 688 
 689     (void) len;
 690 
 691     g_snprintf (buffer, sizeof (buffer), "%lu", (unsigned long) fe->st.st_gid);
 692     return buffer;
 693 }
 694 
 695 /* --------------------------------------------------------------------------------------------- */
 696 /** owner */
 697 
 698 static const char *
 699 string_file_owner (file_entry_t * fe, int len)
     /* [previous][next][first][last][top][bottom][index][help]  */
 700 {
 701     (void) len;
 702 
 703     return get_owner (fe->st.st_uid);
 704 }
 705 
 706 /* --------------------------------------------------------------------------------------------- */
 707 /** group */
 708 
 709 static const char *
 710 string_file_group (file_entry_t * fe, int len)
     /* [previous][next][first][last][top][bottom][index][help]  */
 711 {
 712     (void) len;
 713 
 714     return get_group (fe->st.st_gid);
 715 }
 716 
 717 /* --------------------------------------------------------------------------------------------- */
 718 /** mark */
 719 
 720 static const char *
 721 string_marked (file_entry_t * fe, int len)
     /* [previous][next][first][last][top][bottom][index][help]  */
 722 {
 723     (void) len;
 724 
 725     return fe->f.marked ? "*" : " ";
 726 }
 727 
 728 /* --------------------------------------------------------------------------------------------- */
 729 /** space */
 730 
 731 static const char *
 732 string_space (file_entry_t * fe, int len)
     /* [previous][next][first][last][top][bottom][index][help]  */
 733 {
 734     (void) fe;
 735     (void) len;
 736 
 737     return " ";
 738 }
 739 
 740 /* --------------------------------------------------------------------------------------------- */
 741 /** dot */
 742 
 743 static const char *
 744 string_dot (file_entry_t * fe, int len)
     /* [previous][next][first][last][top][bottom][index][help]  */
 745 {
 746     (void) fe;
 747     (void) len;
 748 
 749     return ".";
 750 }
 751 
 752 /* --------------------------------------------------------------------------------------------- */
 753 
 754 static int
 755 file_compute_color (int attr, file_entry_t * fe)
     /* [previous][next][first][last][top][bottom][index][help]  */
 756 {
 757     switch (attr)
 758     {
 759     case SELECTED:
 760         return (SELECTED_COLOR);
 761     case MARKED:
 762         return (MARKED_COLOR);
 763     case MARKED_SELECTED:
 764         return (MARKED_SELECTED_COLOR);
 765     case STATUS:
 766         return (NORMAL_COLOR);
 767     case NORMAL:
 768     default:
 769         if (!panels_options.filetype_mode)
 770             return (NORMAL_COLOR);
 771     }
 772 
 773     return mc_fhl_get_color (mc_filehighlight, fe);
 774 }
 775 
 776 /* --------------------------------------------------------------------------------------------- */
 777 /** Returns the number of items in the given panel */
 778 
 779 static int
 780 panel_items (const WPanel * p)
     /* [previous][next][first][last][top][bottom][index][help]  */
 781 {
 782     return panel_lines (p) * p->list_cols;
 783 }
 784 
 785 /* --------------------------------------------------------------------------------------------- */
 786 /** Formats the file number file_index of panel in the buffer dest */
 787 
 788 static filename_scroll_flag_t
 789 format_file (WPanel * panel, int file_index, int width, int attr, gboolean isstatus,
     /* [previous][next][first][last][top][bottom][index][help]  */
 790              int *field_length)
 791 {
 792     int color = NORMAL_COLOR;
 793     int length = 0;
 794     GSList *format, *home;
 795     file_entry_t *fe = NULL;
 796     filename_scroll_flag_t res = FILENAME_NOSCROLL;
 797 
 798     *field_length = 0;
 799 
 800     if (file_index < panel->dir.len)
 801     {
 802         fe = &panel->dir.list[file_index];
 803         color = file_compute_color (attr, fe);
 804     }
 805 
 806     home = isstatus ? panel->status_format : panel->format;
 807 
 808     for (format = home; format != NULL && length != width; format = g_slist_next (format))
 809     {
 810         format_item_t *fi = (format_item_t *) format->data;
 811 
 812         if (fi->string_fn != NULL)
 813         {
 814             const char *txt = " ";
 815             int len, perm = 0;
 816             const char *prepared_text;
 817             int name_offset = 0;
 818 
 819             if (fe != NULL)
 820                 txt = fi->string_fn (fe, fi->field_len);
 821 
 822             len = fi->field_len;
 823             if (len + length > width)
 824                 len = width - length;
 825             if (len <= 0)
 826                 break;
 827 
 828             if (!isstatus && panel->content_shift > -1 && strcmp (fi->id, "name") == 0)
 829             {
 830                 int str_len;
 831                 int i;
 832 
 833                 *field_length = len + 1;
 834 
 835                 str_len = str_length (txt);
 836                 i = MAX (0, str_len - len);
 837                 panel->max_shift = MAX (panel->max_shift, i);
 838                 i = MIN (panel->content_shift, i);
 839 
 840                 if (i > -1)
 841                 {
 842                     name_offset = str_offset_to_pos (txt, i);
 843                     if (str_len > len)
 844                     {
 845                         res = FILENAME_SCROLL_LEFT;
 846                         if (str_length (txt + name_offset) > len)
 847                             res |= FILENAME_SCROLL_RIGHT;
 848                     }
 849                 }
 850             }
 851 
 852             if (panels_options.permission_mode)
 853             {
 854                 if (strcmp (fi->id, "perm") == 0)
 855                     perm = 1;
 856                 else if (strcmp (fi->id, "mode") == 0)
 857                     perm = 2;
 858             }
 859 
 860             if (color >= 0)
 861                 tty_setcolor (color);
 862             else
 863                 tty_lowlevel_setcolor (-color);
 864 
 865             if (!isstatus && panel->content_shift > -1)
 866                 prepared_text = str_fit_to_term (txt + name_offset, len, HIDE_FIT (fi->just_mode));
 867             else
 868                 prepared_text = str_fit_to_term (txt, len, fi->just_mode);
 869 
 870             if (perm != 0 && fe != NULL)
 871                 add_permission_string (prepared_text, fi->field_len, fe, attr, color, perm != 1);
 872             else
 873                 tty_print_string (prepared_text);
 874 
 875             length += len;
 876         }
 877         else
 878         {
 879             if (attr == SELECTED || attr == MARKED_SELECTED)
 880                 tty_setcolor (SELECTED_COLOR);
 881             else
 882                 tty_setcolor (NORMAL_COLOR);
 883             tty_print_one_vline (TRUE);
 884             length++;
 885         }
 886     }
 887 
 888     if (length < width)
 889     {
 890         int y, x;
 891 
 892         tty_getyx (&y, &x);
 893         tty_draw_hline (y, x, ' ', width - length);
 894     }
 895 
 896     return res;
 897 }
 898 
 899 /* --------------------------------------------------------------------------------------------- */
 900 
 901 static void
 902 repaint_file (WPanel * panel, int file_index, gboolean mv, int attr, gboolean isstatus)
     /* [previous][next][first][last][top][bottom][index][help]  */
 903 {
 904     Widget *w = WIDGET (panel);
 905 
 906     int nth_column = 0;
 907     int width;
 908     int offset = 0;
 909     filename_scroll_flag_t ret_frm;
 910     int ypos = 0;
 911     gboolean panel_is_split;
 912     int fln = 0;
 913 
 914     panel_is_split = !isstatus && panel->list_cols > 1;
 915     width = w->cols - 2;
 916 
 917     if (panel_is_split)
 918     {
 919         nth_column = (file_index - panel->top_file) / panel_lines (panel);
 920         width /= panel->list_cols;
 921 
 922         offset = width * nth_column;
 923 
 924         if (nth_column + 1 >= panel->list_cols)
 925             width = w->cols - offset - 2;
 926     }
 927 
 928     /* Nothing to paint */
 929     if (width <= 0)
 930         return;
 931 
 932     if (mv)
 933     {
 934         ypos = file_index - panel->top_file;
 935 
 936         if (panel_is_split)
 937             ypos %= panel_lines (panel);
 938 
 939         ypos += 2;              /* top frame and header */
 940         widget_gotoyx (w, ypos, offset + 1);
 941     }
 942 
 943     ret_frm = format_file (panel, file_index, width, attr, isstatus, &fln);
 944 
 945     if (panel_is_split && nth_column + 1 < panel->list_cols)
 946     {
 947         tty_setcolor (NORMAL_COLOR);
 948         tty_print_one_vline (TRUE);
 949     }
 950 
 951     if (ret_frm != FILENAME_NOSCROLL && mv)
 952     {
 953         if (!panel_is_split && fln > 0)
 954         {
 955             if (panel->list_format != list_long)
 956                 width = fln;
 957             else
 958             {
 959                 offset = width - fln + 1;
 960                 width = fln - 1;
 961             }
 962         }
 963 
 964         widget_gotoyx (w, ypos, offset);
 965         tty_setcolor (NORMAL_COLOR);
 966         tty_print_string (panel_filename_scroll_left_char);
 967 
 968         if ((ret_frm & FILENAME_SCROLL_RIGHT) != 0)
 969         {
 970             offset += width;
 971             if (nth_column + 1 >= panel->list_cols)
 972                 offset++;
 973 
 974             widget_gotoyx (w, ypos, offset);
 975             tty_setcolor (NORMAL_COLOR);
 976             tty_print_string (panel_filename_scroll_right_char);
 977         }
 978     }
 979 }
 980 
 981 /* --------------------------------------------------------------------------------------------- */
 982 
 983 static void
 984 display_mini_info (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
 985 {
 986     Widget *w = WIDGET (panel);
 987 
 988     if (!panels_options.show_mini_info)
 989         return;
 990 
 991     widget_gotoyx (w, panel_lines (panel) + 3, 1);
 992 
 993     if (panel->searching)
 994     {
 995         tty_setcolor (INPUT_COLOR);
 996         tty_print_char ('/');
 997         tty_print_string (str_fit_to_term (panel->search_buffer, w->cols - 3, J_LEFT));
 998         return;
 999     }
1000 
1001     /* Status resolves links and show them */
1002     set_colors (panel);
1003 
1004     if (S_ISLNK (panel->dir.list[panel->selected].st.st_mode))
1005     {
1006         char link_target[MC_MAXPATHLEN];
1007         vfs_path_t *lc_link_vpath;
1008         int len;
1009 
1010         lc_link_vpath =
1011             vfs_path_append_new (panel->cwd_vpath, panel->dir.list[panel->selected].fname,
1012                                  (char *) NULL);
1013         len = mc_readlink (lc_link_vpath, link_target, MC_MAXPATHLEN - 1);
1014         vfs_path_free (lc_link_vpath);
1015         if (len > 0)
1016         {
1017             link_target[len] = 0;
1018             tty_print_string ("-> ");
1019             tty_print_string (str_fit_to_term (link_target, w->cols - 5, J_LEFT_FIT));
1020         }
1021         else
1022             tty_print_string (str_fit_to_term (_("<readlink failed>"), w->cols - 2, J_LEFT));
1023     }
1024     else if (DIR_IS_DOTDOT (panel->dir.list[panel->selected].fname))
1025     {
1026         /* FIXME:
1027          * while loading directory (dir_list_load() and dir_list_reload()),
1028          * the actual stat info about ".." directory isn't got;
1029          * so just don't display incorrect info about ".." directory */
1030         tty_print_string (str_fit_to_term (_("UP--DIR"), w->cols - 2, J_LEFT));
1031     }
1032     else
1033         /* Default behavior */
1034         repaint_file (panel, panel->selected, FALSE, STATUS, TRUE);
1035 }
1036 
1037 /* --------------------------------------------------------------------------------------------- */
1038 
1039 static void
1040 paint_dir (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
1041 {
1042     int i;
1043     int items;                  /* Number of items */
1044 
1045     items = panel_items (panel);
1046     /* reset max len of filename because we have the new max length for the new file list */
1047     panel->max_shift = -1;
1048 
1049     for (i = 0; i < items; i++)
1050     {
1051         int color = 0;          /* Color value of the line */
1052 
1053         if (i + panel->top_file < panel->dir.len)
1054         {
1055             color = 2 * (panel->dir.list[i + panel->top_file].f.marked);
1056             color += (panel->selected == i + panel->top_file && panel->active);
1057         }
1058 
1059         repaint_file (panel, i + panel->top_file, TRUE, color, FALSE);
1060     }
1061 
1062     tty_set_normal_attrs ();
1063 }
1064 
1065 /* --------------------------------------------------------------------------------------------- */
1066 
1067 static void
1068 display_total_marked_size (const WPanel * panel, int y, int x, gboolean size_only)
     /* [previous][next][first][last][top][bottom][index][help]  */
1069 {
1070     const Widget *w = CONST_WIDGET (panel);
1071 
1072     char buffer[BUF_SMALL], b_bytes[BUF_SMALL];
1073     const char *buf;
1074     int cols;
1075 
1076     if (panel->marked <= 0)
1077         return;
1078 
1079     buf = size_only ? b_bytes : buffer;
1080     cols = w->cols - 2;
1081 
1082     g_strlcpy (b_bytes, size_trunc_sep (panel->total, panels_options.kilobyte_si),
1083                sizeof (b_bytes));
1084 
1085     if (!size_only)
1086         g_snprintf (buffer, sizeof (buffer),
1087                     ngettext ("%s in %d file", "%s in %d files", panel->marked),
1088                     b_bytes, panel->marked);
1089 
1090     /* don't forget spaces around buffer content */
1091     buf = str_trunc (buf, cols - 4);
1092 
1093     if (x < 0)
1094         /* center in panel */
1095         x = (w->cols - str_term_width1 (buf)) / 2 - 1;
1096 
1097     /*
1098      * y == panel_lines (panel) + 2  for mini_info_separator
1099      * y == w->lines - 1             for panel bottom frame
1100      */
1101     widget_gotoyx (w, y, x);
1102     tty_setcolor (MARKED_COLOR);
1103     tty_printf (" %s ", buf);
1104 }
1105 
1106 /* --------------------------------------------------------------------------------------------- */
1107 
1108 static void
1109 mini_info_separator (const WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
1110 {
1111     if (panels_options.show_mini_info)
1112     {
1113         const Widget *w = CONST_WIDGET (panel);
1114         int y;
1115 
1116         y = panel_lines (panel) + 2;
1117 
1118         tty_setcolor (NORMAL_COLOR);
1119         tty_draw_hline (w->y + y, w->x + 1, ACS_HLINE, w->cols - 2);
1120         /* Status displays total marked size.
1121          * Centered in panel, full format. */
1122         display_total_marked_size (panel, y, -1, FALSE);
1123     }
1124 }
1125 
1126 /* --------------------------------------------------------------------------------------------- */
1127 
1128 static void
1129 show_free_space (const WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
1130 {
1131     /* Used to figure out how many free space we have */
1132     static struct my_statfs myfs_stats;
1133     /* Old current working directory for displaying free space */
1134     static char *old_cwd = NULL;
1135 
1136     /* Don't try to stat non-local fs */
1137     if (!vfs_file_is_local (panel->cwd_vpath) || !free_space)
1138         return;
1139 
1140     if (old_cwd == NULL || strcmp (old_cwd, vfs_path_as_str (panel->cwd_vpath)) != 0)
1141     {
1142         char rpath[PATH_MAX];
1143 
1144         init_my_statfs ();
1145         g_free (old_cwd);
1146         old_cwd = g_strdup (vfs_path_as_str (panel->cwd_vpath));
1147 
1148         if (mc_realpath (old_cwd, rpath) == NULL)
1149             return;
1150 
1151         my_statfs (&myfs_stats, rpath);
1152     }
1153 
1154     if (myfs_stats.avail != 0 || myfs_stats.total != 0)
1155     {
1156         const Widget *w = CONST_WIDGET (panel);
1157         char buffer1[6], buffer2[6], tmp[BUF_SMALL];
1158 
1159         size_trunc_len (buffer1, sizeof (buffer1) - 1, myfs_stats.avail, 1,
1160                         panels_options.kilobyte_si);
1161         size_trunc_len (buffer2, sizeof (buffer2) - 1, myfs_stats.total, 1,
1162                         panels_options.kilobyte_si);
1163         g_snprintf (tmp, sizeof (tmp), " %s/%s (%d%%) ", buffer1, buffer2,
1164                     myfs_stats.total == 0 ? 0 :
1165                     (int) (100 * (long double) myfs_stats.avail / myfs_stats.total));
1166         widget_gotoyx (w, w->lines - 1, w->cols - 2 - (int) strlen (tmp));
1167         tty_setcolor (NORMAL_COLOR);
1168         tty_print_string (tmp);
1169     }
1170 }
1171 
1172 /* --------------------------------------------------------------------------------------------- */
1173 /**
1174  * Prepare path string for showing in panel's header.
1175  * Passwords will removed, also home dir will replaced by ~
1176  *
1177  * @param panel WPanel object
1178  *
1179  * @return newly allocated string.
1180  */
1181 
1182 static char *
1183 panel_correct_path_to_show (const WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
1184 {
1185     vfs_path_t *last_vpath;
1186     const vfs_path_element_t *path_element;
1187     char *return_path;
1188     int elements_count;
1189 
1190     elements_count = vfs_path_elements_count (panel->cwd_vpath);
1191 
1192     /* get last path element */
1193     path_element = vfs_path_element_clone (vfs_path_get_by_index (panel->cwd_vpath, -1));
1194 
1195     if (elements_count > 1 && (strcmp (path_element->class->name, "cpiofs") == 0 ||
1196                                strcmp (path_element->class->name, "extfs") == 0 ||
1197                                strcmp (path_element->class->name, "tarfs") == 0))
1198     {
1199         const char *archive_name;
1200         const vfs_path_element_t *prev_path_element;
1201 
1202         /* get previous path element for catching archive name */
1203         prev_path_element = vfs_path_get_by_index (panel->cwd_vpath, -2);
1204         archive_name = strrchr (prev_path_element->path, PATH_SEP);
1205         if (archive_name != NULL)
1206             last_vpath = vfs_path_from_str_flags (archive_name + 1, VPF_NO_CANON);
1207         else
1208         {
1209             last_vpath = vfs_path_from_str_flags (prev_path_element->path, VPF_NO_CANON);
1210             last_vpath->relative = TRUE;
1211         }
1212     }
1213     else
1214     {
1215         last_vpath = vfs_path_new ();
1216         last_vpath->relative = TRUE;
1217     }
1218 
1219     vfs_path_add_element (last_vpath, path_element);
1220     return_path =
1221         vfs_path_to_str_flags (last_vpath, 0,
1222                                VPF_STRIP_HOME | VPF_STRIP_PASSWORD | VPF_HIDE_CHARSET);
1223     vfs_path_free (last_vpath);
1224 
1225     return return_path;
1226 }
1227 
1228 /* --------------------------------------------------------------------------------------------- */
1229 /**
1230  * Get Current path element encoding
1231  *
1232  * @param panel WPanel object
1233  *
1234  * @return newly allocated string or NULL if path charset is same as system charset
1235  */
1236 
1237 #ifdef HAVE_CHARSET
1238 static char *
1239 panel_get_encoding_info_str (const WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
1240 {
1241     char *ret_str = NULL;
1242     const vfs_path_element_t *path_element;
1243 
1244     path_element = vfs_path_get_by_index (panel->cwd_vpath, -1);
1245     if (path_element->encoding != NULL)
1246         ret_str = g_strdup_printf ("[%s]", path_element->encoding);
1247 
1248     return ret_str;
1249 }
1250 #endif
1251 
1252 /* --------------------------------------------------------------------------------------------- */
1253 
1254 static void
1255 show_dir (const WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
1256 {
1257     const Widget *w = CONST_WIDGET (panel);
1258     gchar *tmp;
1259 
1260     set_colors (panel);
1261     tty_draw_box (w->y, w->x, w->lines, w->cols, FALSE);
1262 
1263     if (panels_options.show_mini_info)
1264     {
1265         int y;
1266 
1267         y = panel_lines (panel) + 2;
1268 
1269         widget_gotoyx (w, y, 0);
1270         tty_print_alt_char (ACS_LTEE, FALSE);
1271         widget_gotoyx (w, y, w->cols - 1);
1272         tty_print_alt_char (ACS_RTEE, FALSE);
1273     }
1274 
1275     widget_gotoyx (w, 0, 1);
1276     tty_print_string (panel_history_prev_item_char);
1277 
1278     tmp = panels_options.show_dot_files ? panel_hiddenfiles_show_char : panel_hiddenfiles_hide_char;
1279     tmp = g_strdup_printf ("%s[%s]%s", tmp, panel_history_show_list_char,
1280                            panel_history_next_item_char);
1281 
1282     widget_gotoyx (w, 0, w->cols - 6);
1283     tty_print_string (tmp);
1284 
1285     g_free (tmp);
1286 
1287     widget_gotoyx (w, 0, 3);
1288 
1289     if (panel->is_panelized)
1290         tty_printf (" %s ", _("Panelize"));
1291 #ifdef HAVE_CHARSET
1292     else
1293     {
1294         tmp = panel_get_encoding_info_str (panel);
1295         if (tmp != NULL)
1296         {
1297             tty_printf ("%s", tmp);
1298             widget_gotoyx (w, 0, 3 + strlen (tmp));
1299             g_free (tmp);
1300         }
1301     }
1302 #endif
1303 
1304     if (panel->active)
1305         tty_setcolor (REVERSE_COLOR);
1306 
1307     tmp = panel_correct_path_to_show (panel);
1308     tty_printf (" %s ", str_term_trim (tmp, MIN (MAX (w->cols - 12, 0), w->cols)));
1309     g_free (tmp);
1310 
1311     if (!panels_options.show_mini_info)
1312     {
1313         if (panel->marked == 0)
1314         {
1315             /* Show size of curret file in the bottom of panel */
1316             if (S_ISREG (panel->dir.list[panel->selected].st.st_mode))
1317             {
1318                 char buffer[BUF_SMALL];
1319 
1320                 g_snprintf (buffer, sizeof (buffer), " %s ",
1321                             size_trunc_sep (panel->dir.list[panel->selected].st.st_size,
1322                                             panels_options.kilobyte_si));
1323                 tty_setcolor (NORMAL_COLOR);
1324                 widget_gotoyx (w, w->lines - 1, 4);
1325                 tty_print_string (buffer);
1326             }
1327         }
1328         else
1329         {
1330             /* Show total size of marked files
1331              * In the bottom of panel, display size only. */
1332             display_total_marked_size (panel, w->lines - 1, 2, TRUE);
1333         }
1334     }
1335 
1336     show_free_space (panel);
1337 
1338     if (panel->active)
1339         tty_set_normal_attrs ();
1340 }
1341 
1342 /* --------------------------------------------------------------------------------------------- */
1343 
1344 static void
1345 adjust_top_file (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
1346 {
1347     int items;
1348 
1349     /* Update panel->selected to avoid out of range in panel->dir.list[panel->selected]
1350      * when panel is redrawing when directory is reloading, for example in path:
1351      * dir_list_reload() -> mc_refresh() -> dialog_change_screen_size() ->
1352      * midnight_callback (MSG_RESIZE) -> setup_panels() -> panel_callback(MSG_DRAW) ->
1353      * display_mini_info()
1354      */
1355     panel->selected = CLAMP (panel->selected, 0, panel->dir.len - 1);
1356 
1357     items = panel_items (panel);
1358 
1359     if (panel->dir.len <= items)
1360     {
1361         /* If all files fit, show them all. */
1362         panel->top_file = 0;
1363     }
1364     else
1365     {
1366         int i;
1367 
1368         /* top_file has to be in the range [selected-items+1, selected] so that
1369            the selected file is visible.
1370            top_file should be in the range [0, count-items] so that there's
1371            no empty space wasted.
1372            Within these ranges, adjust it by as little as possible. */
1373 
1374         if (panel->top_file < 0)
1375             panel->top_file = 0;
1376 
1377         i = panel->selected - items + 1;
1378         if (panel->top_file < i)
1379             panel->top_file = i;
1380 
1381         i = panel->dir.len - items;
1382         if (panel->top_file > i)
1383             panel->top_file = i;
1384 
1385         if (panel->top_file > panel->selected)
1386             panel->top_file = panel->selected;
1387     }
1388 }
1389 
1390 /* --------------------------------------------------------------------------------------------- */
1391 /** add "#enc:encodning" to end of path */
1392 /* if path end width a previous #enc:, only encoding is changed no additional 
1393  * #enc: is appended
1394  * retun new string
1395  */
1396 
1397 static char *
1398 panel_save_name (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
1399 {
1400     /* If the program is shuting down */
1401     if ((mc_global.midnight_shutdown && auto_save_setup) || saving_setup)
1402         return g_strdup (panel->panel_name);
1403 
1404     return g_strconcat ("Temporal:", panel->panel_name, (char *) NULL);
1405 }
1406 
1407 /* --------------------------------------------------------------------------------------------- */
1408 
1409 static void
1410 directory_history_add (WPanel * panel, const vfs_path_t * vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
1411 {
1412     char *tmp;
1413 
1414     tmp = vfs_path_to_str_flags (vpath, 0, VPF_STRIP_PASSWORD);
1415     panel->dir_history = list_append_unique (panel->dir_history, tmp);
1416     panel->dir_history_current = panel->dir_history;
1417 }
1418 
1419 /* --------------------------------------------------------------------------------------------- */
1420 
1421 /* "history_load" event handler */
1422 static gboolean
1423 panel_load_history (const gchar * event_group_name, const gchar * event_name,
     /* [previous][next][first][last][top][bottom][index][help]  */
1424                     gpointer init_data, gpointer data)
1425 {
1426     WPanel *p = PANEL (init_data);
1427     ev_history_load_save_t *ev = (ev_history_load_save_t *) data;
1428 
1429     (void) event_group_name;
1430     (void) event_name;
1431 
1432     if (ev->receiver == NULL || ev->receiver == WIDGET (p))
1433     {
1434         if (ev->cfg != NULL)
1435             p->dir_history = mc_config_history_load (ev->cfg, p->hist_name);
1436         else
1437             p->dir_history = mc_config_history_get (p->hist_name);
1438 
1439         directory_history_add (p, p->cwd_vpath);
1440     }
1441 
1442     return TRUE;
1443 }
1444 
1445 /* --------------------------------------------------------------------------------------------- */
1446 
1447 /* "history_save" event handler */
1448 static gboolean
1449 panel_save_history (const gchar * event_group_name, const gchar * event_name,
     /* [previous][next][first][last][top][bottom][index][help]  */
1450                     gpointer init_data, gpointer data)
1451 {
1452     WPanel *p = PANEL (init_data);
1453 
1454     (void) event_group_name;
1455     (void) event_name;
1456 
1457     if (p->dir_history != NULL)
1458     {
1459         ev_history_load_save_t *ev = (ev_history_load_save_t *) data;
1460 
1461         mc_config_history_save (ev->cfg, p->hist_name, p->dir_history);
1462     }
1463 
1464     return TRUE;
1465 }
1466 
1467 /* --------------------------------------------------------------------------------------------- */
1468 
1469 static void
1470 panel_destroy (WPanel * p)
     /* [previous][next][first][last][top][bottom][index][help]  */
1471 {
1472     size_t i;
1473 
1474     if (panels_options.auto_save_setup)
1475     {
1476         char *name;
1477 
1478         name = panel_save_name (p);
1479         panel_save_setup (p, name);
1480         g_free (name);
1481     }
1482 
1483     panel_clean_dir (p);
1484 
1485     /* clean history */
1486     if (p->dir_history != NULL)
1487     {
1488         /* directory history is already saved before this moment */
1489         p->dir_history = g_list_first (p->dir_history);
1490         g_list_free_full (p->dir_history, g_free);
1491     }
1492     g_free (p->hist_name);
1493 
1494     g_slist_free_full (p->format, (GDestroyNotify) format_item_free);
1495     g_slist_free_full (p->status_format, (GDestroyNotify) format_item_free);
1496 
1497     g_free (p->user_format);
1498     for (i = 0; i < LIST_FORMATS; i++)
1499         g_free (p->user_status_format[i]);
1500 
1501     g_free (p->dir.list);
1502     g_free (p->panel_name);
1503 
1504     vfs_path_free (p->lwd_vpath);
1505     vfs_path_free (p->cwd_vpath);
1506 }
1507 
1508 /* --------------------------------------------------------------------------------------------- */
1509 
1510 static void
1511 panel_paint_sort_info (const WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
1512 {
1513     if (*panel->sort_field->hotkey != '\0')
1514     {
1515         const char *sort_sign =
1516             panel->sort_info.reverse ? panel_sort_up_char : panel_sort_down_char;
1517         char *str;
1518 
1519         str = g_strdup_printf ("%s%s", sort_sign, Q_ (panel->sort_field->hotkey));
1520         widget_gotoyx (panel, 1, 1);
1521         tty_print_string (str);
1522         g_free (str);
1523     }
1524 }
1525 
1526 /* --------------------------------------------------------------------------------------------- */
1527 
1528 static const char *
1529 panel_get_title_without_hotkey (const char *title)
     /* [previous][next][first][last][top][bottom][index][help]  */
1530 {
1531     static char translated_title[BUF_TINY];
1532 
1533     if (title == NULL || title[0] == '\0')
1534         translated_title[0] = '\0';
1535     else
1536     {
1537         char *hkey;
1538 
1539         g_snprintf (translated_title, sizeof (translated_title), "%s", _(title));
1540 
1541         hkey = strchr (translated_title, '&');
1542         if (hkey != NULL && hkey[1] != '\0')
1543             memmove (hkey, hkey + 1, strlen (hkey));
1544     }
1545 
1546     return translated_title;
1547 }
1548 
1549 /* --------------------------------------------------------------------------------------------- */
1550 
1551 static void
1552 panel_print_header (const WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
1553 {
1554     const Widget *w = CONST_WIDGET (panel);
1555 
1556     int y, x;
1557     int i;
1558     GString *format_txt;
1559 
1560     widget_gotoyx (w, 1, 1);
1561     tty_getyx (&y, &x);
1562     tty_setcolor (NORMAL_COLOR);
1563     tty_draw_hline (y, x, ' ', w->cols - 2);
1564 
1565     format_txt = g_string_new ("");
1566 
1567     for (i = 0; i < panel->list_cols; i++)
1568     {
1569         GSList *format;
1570 
1571         for (format = panel->format; format != NULL; format = g_slist_next (format))
1572         {
1573             format_item_t *fi = (format_item_t *) format->data;
1574 
1575             if (fi->string_fn != NULL)
1576             {
1577                 g_string_set_size (format_txt, 0);
1578 
1579                 if (panel->list_format == list_long && strcmp (fi->id, panel->sort_field->id) == 0)
1580                     g_string_append (format_txt,
1581                                      panel->sort_info.reverse
1582                                      ? panel_sort_up_char : panel_sort_down_char);
1583 
1584                 g_string_append (format_txt, fi->title);
1585 
1586                 if (panel->filter != NULL && strcmp (fi->id, "name") == 0)
1587                 {
1588                     g_string_append (format_txt, " [");
1589                     g_string_append (format_txt, panel->filter);
1590                     g_string_append (format_txt, "]");
1591                 }
1592 
1593                 tty_setcolor (HEADER_COLOR);
1594                 tty_print_string (str_fit_to_term (format_txt->str, fi->field_len, J_CENTER_LEFT));
1595             }
1596             else
1597             {
1598                 tty_setcolor (NORMAL_COLOR);
1599                 tty_print_one_vline (TRUE);
1600             }
1601         }
1602 
1603         if (i < panel->list_cols - 1)
1604         {
1605             tty_setcolor (NORMAL_COLOR);
1606             tty_print_one_vline (TRUE);
1607         }
1608     }
1609 
1610     g_string_free (format_txt, TRUE);
1611 
1612     if (panel->list_format != list_long)
1613         panel_paint_sort_info (panel);
1614 }
1615 
1616 /* --------------------------------------------------------------------------------------------- */
1617 
1618 static const char *
1619 parse_panel_size (WPanel * panel, const char *format, gboolean isstatus)
     /* [previous][next][first][last][top][bottom][index][help]  */
1620 {
1621     panel_display_t frame = frame_half;
1622 
1623     format = skip_separators (format);
1624 
1625     if (strncmp (format, "full", 4) == 0)
1626     {
1627         frame = frame_full;
1628         format += 4;
1629     }
1630     else if (strncmp (format, "half", 4) == 0)
1631     {
1632         frame = frame_half;
1633         format += 4;
1634     }
1635 
1636     if (!isstatus)
1637     {
1638         panel->frame_size = frame;
1639         panel->list_cols = 1;
1640     }
1641 
1642     /* Now, the optional column specifier */
1643     format = skip_separators (format);
1644 
1645     if (g_ascii_isdigit (*format))
1646     {
1647         if (!isstatus)
1648         {
1649             panel->list_cols = g_ascii_digit_value (*format);
1650             if (panel->list_cols < 1)
1651                 panel->list_cols = 1;
1652         }
1653 
1654         format++;
1655     }
1656 
1657     if (!isstatus)
1658         panel_update_cols (WIDGET (panel), panel->frame_size);
1659 
1660     return skip_separators (format);
1661 }
1662 
1663 /* --------------------------------------------------------------------------------------------- */
1664 
1665 /* *INDENT-OFF* */
1666 /* Format is:
1667 
1668    all              := panel_format? format
1669    panel_format     := [full|half] [1-9]
1670    format           := one_format_item_t
1671                      | format , one_format_item_t
1672 
1673    one_format_item_t := just format.id [opt_size]
1674    just              := [<=>]
1675    opt_size          := : size [opt_expand]
1676    size              := [0-9]+
1677    opt_expand        := +
1678 
1679  */
1680 /* *INDENT-ON* */
1681 
1682 static GSList *
1683 parse_display_format (WPanel * panel, const char *format, char **error, gboolean isstatus,
     /* [previous][next][first][last][top][bottom][index][help]  */
1684                       int *res_total_cols)
1685 {
1686     GSList *home = NULL;        /* The formats we return */
1687     int total_cols = 0;         /* Used columns by the format */
1688     size_t i;
1689 
1690     static size_t i18n_timelength = 0;  /* flag: check ?Time length at startup */
1691 
1692     *error = NULL;
1693 
1694     if (i18n_timelength == 0)
1695     {
1696         i18n_timelength = i18n_checktimelength ();      /* Musn't be 0 */
1697 
1698         for (i = 0; panel_fields[i].id != NULL; i++)
1699             if (strcmp ("time", panel_fields[i].id + 1) == 0)
1700                 panel_fields[i].min_size = i18n_timelength;
1701     }
1702 
1703     /*
1704      * This makes sure that the panel and mini status full/half mode
1705      * setting is equal
1706      */
1707     format = parse_panel_size (panel, format, isstatus);
1708 
1709     while (*format != '\0')
1710     {                           /* format can be an empty string */
1711         format_item_t *darr;
1712         align_crt_t justify;    /* Which mode. */
1713         gboolean set_justify = TRUE;    /* flag: set justification mode? */
1714         gboolean found = FALSE;
1715         size_t klen = 0;
1716 
1717         darr = g_new0 (format_item_t, 1);
1718         home = g_slist_append (home, darr);
1719 
1720         format = skip_separators (format);
1721 
1722         switch (*format)
1723         {
1724         case '<':
1725             justify = J_LEFT;
1726             format = skip_separators (format + 1);
1727             break;
1728         case '=':
1729             justify = J_CENTER;
1730             format = skip_separators (format + 1);
1731             break;
1732         case '>':
1733             justify = J_RIGHT;
1734             format = skip_separators (format + 1);
1735             break;
1736         default:
1737             justify = J_LEFT;
1738             set_justify = FALSE;
1739             break;
1740         }
1741 
1742         for (i = 0; !found && panel_fields[i].id != NULL; i++)
1743         {
1744             klen = strlen (panel_fields[i].id);
1745             found = strncmp (format, panel_fields[i].id, klen) == 0;
1746         }
1747 
1748         if (found)
1749         {
1750             i--;
1751             format += klen;
1752 
1753             darr->requested_field_len = panel_fields[i].min_size;
1754             darr->string_fn = panel_fields[i].string_fn;
1755             darr->title = g_strdup (panel_get_title_without_hotkey (panel_fields[i].title_hotkey));
1756             darr->id = panel_fields[i].id;
1757             darr->expand = panel_fields[i].expands;
1758             darr->just_mode = panel_fields[i].default_just;
1759 
1760             if (set_justify)
1761             {
1762                 if (IS_FIT (darr->just_mode))
1763                     darr->just_mode = MAKE_FIT (justify);
1764                 else
1765                     darr->just_mode = justify;
1766             }
1767 
1768             format = skip_separators (format);
1769 
1770             /* If we have a size specifier */
1771             if (*format == ':')
1772             {
1773                 int req_length;
1774 
1775                 /* If the size was specified, we don't want
1776                  * auto-expansion by default
1777                  */
1778                 darr->expand = FALSE;
1779                 format++;
1780                 req_length = atoi (format);
1781                 darr->requested_field_len = req_length;
1782 
1783                 format = skip_numbers (format);
1784 
1785                 /* Now, if they insist on expansion */
1786                 if (*format == '+')
1787                 {
1788                     darr->expand = TRUE;
1789                     format++;
1790                 }
1791             }
1792         }
1793         else
1794         {
1795             size_t pos;
1796             char *tmp_format;
1797 
1798             pos = strlen (format);
1799             if (pos > 8)
1800                 pos = 8;
1801 
1802             tmp_format = g_strndup (format, pos);
1803             g_slist_free_full (home, (GDestroyNotify) format_item_free);
1804             *error =
1805                 g_strconcat (_("Unknown tag on display format:"), " ", tmp_format, (char *) NULL);
1806             g_free (tmp_format);
1807 
1808             return NULL;
1809         }
1810 
1811         total_cols += darr->requested_field_len;
1812     }
1813 
1814     *res_total_cols = total_cols;
1815     return home;
1816 }
1817 
1818 /* --------------------------------------------------------------------------------------------- */
1819 
1820 static GSList *
1821 use_display_format (WPanel * panel, const char *format, char **error, gboolean isstatus)
     /* [previous][next][first][last][top][bottom][index][help]  */
1822 {
1823 #define MAX_EXPAND 4
1824     int expand_top = 0;         /* Max used element in expand */
1825     int usable_columns;         /* Usable columns in the panel */
1826     int total_cols = 0;
1827     GSList *darr, *home;
1828 
1829     if (format == NULL)
1830         format = DEFAULT_USER_FORMAT;
1831 
1832     home = parse_display_format (panel, format, error, isstatus, &total_cols);
1833 
1834     if (*error != NULL)
1835         return NULL;
1836 
1837     panel->dirty = 1;
1838 
1839     usable_columns = WIDGET (panel)->cols - 2;
1840     /* Status needn't to be split */
1841     if (!isstatus)
1842     {
1843         usable_columns /= panel->list_cols;
1844         if (panel->list_cols > 1)
1845             usable_columns--;
1846     }
1847 
1848     /* Look for the expandable fields and set field_len based on the requested field len */
1849     for (darr = home; darr != NULL && expand_top < MAX_EXPAND; darr = g_slist_next (darr))
1850     {
1851         format_item_t *fi = (format_item_t *) darr->data;
1852 
1853         fi->field_len = fi->requested_field_len;
1854         if (fi->expand)
1855             expand_top++;
1856     }
1857 
1858     /* If we used more columns than the available columns, adjust that */
1859     if (total_cols > usable_columns)
1860     {
1861         int dif;
1862         int pdif = 0;
1863 
1864         dif = total_cols - usable_columns;
1865 
1866         while (dif != 0 && pdif != dif)
1867         {
1868             pdif = dif;
1869 
1870             for (darr = home; darr != NULL; darr = g_slist_next (darr))
1871             {
1872                 format_item_t *fi = (format_item_t *) darr->data;
1873 
1874                 if (dif != 0 && fi->field_len != 1)
1875                 {
1876                     fi->field_len--;
1877                     dif--;
1878                 }
1879             }
1880         }
1881 
1882         total_cols = usable_columns;    /* give up, the rest should be truncated */
1883     }
1884 
1885     /* Expand the available space */
1886     if (usable_columns > total_cols && expand_top != 0)
1887     {
1888         int i;
1889         int spaces;
1890 
1891         spaces = (usable_columns - total_cols) / expand_top;
1892 
1893         for (i = 0, darr = home; darr != NULL && i < expand_top; darr = g_slist_next (darr))
1894         {
1895             format_item_t *fi = (format_item_t *) darr->data;
1896 
1897             if (fi->expand)
1898             {
1899                 fi->field_len += spaces;
1900                 if (i == 0)
1901                     fi->field_len += (usable_columns - total_cols) % expand_top;
1902                 i++;
1903             }
1904         }
1905     }
1906 
1907     return home;
1908 }
1909 
1910 /* --------------------------------------------------------------------------------------------- */
1911 /** Given the panel->view_type returns the format string to be parsed */
1912 
1913 static const char *
1914 panel_format (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
1915 {
1916     switch (panel->list_format)
1917     {
1918     case list_long:
1919         return "full perm space nlink space owner space group space size space mtime space name";
1920 
1921     case list_brief:
1922         {
1923             static char format[BUF_TINY];
1924             int brief_cols = panel->brief_cols;
1925 
1926             if (brief_cols < 1)
1927                 brief_cols = 2;
1928 
1929             if (brief_cols > 9)
1930                 brief_cols = 9;
1931 
1932             g_snprintf (format, sizeof (format), "half %d type name", brief_cols);
1933             return format;
1934         }
1935 
1936     case list_user:
1937         return panel->user_format;
1938 
1939     default:
1940     case list_full:
1941         return "half type name | size | mtime";
1942     }
1943 }
1944 
1945 /* --------------------------------------------------------------------------------------------- */
1946 
1947 static const char *
1948 mini_status_format (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
1949 {
1950     if (panel->user_mini_status)
1951         return panel->user_status_format[panel->list_format];
1952 
1953     switch (panel->list_format)
1954     {
1955     case list_long:
1956         return "full perm space nlink space owner space group space size space mtime space name";
1957 
1958     case list_brief:
1959         return "half type name space bsize space perm space";
1960 
1961     case list_full:
1962         return "half type name";
1963 
1964     default:
1965     case list_user:
1966         return panel->user_format;
1967     }
1968 }
1969 
1970 /*                          */
1971 /* Panel operation commands */
1972 /*                          */
1973 
1974 /* --------------------------------------------------------------------------------------------- */
1975 
1976 static void
1977 cd_up_dir (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1978 {
1979     vfs_path_t *up_dir;
1980 
1981     up_dir = vfs_path_from_str ("..");
1982     do_cd (up_dir, cd_exact);
1983     vfs_path_free (up_dir);
1984 }
1985 
1986 /* --------------------------------------------------------------------------------------------- */
1987 /** Used to emulate Lynx's entering leaving a directory with the arrow keys */
1988 
1989 static cb_ret_t
1990 maybe_cd (gboolean move_up_dir)
     /* [previous][next][first][last][top][bottom][index][help]  */
1991 {
1992     if (panels_options.navigate_with_arrows && input_is_empty (cmdline))
1993     {
1994         if (move_up_dir)
1995         {
1996             cd_up_dir ();
1997             return MSG_HANDLED;
1998         }
1999 
2000         if (S_ISDIR (selection (current_panel)->st.st_mode)
2001             || link_isdir (selection (current_panel)))
2002         {
2003             vfs_path_t *vpath;
2004 
2005             vpath = vfs_path_from_str (selection (current_panel)->fname);
2006             do_cd (vpath, cd_exact);
2007             vfs_path_free (vpath);
2008             return MSG_HANDLED;
2009         }
2010     }
2011 
2012     return MSG_NOT_HANDLED;
2013 }
2014 
2015 /* --------------------------------------------------------------------------------------------- */
2016 
2017 /* if command line is empty then do 'cd ..' */
2018 static cb_ret_t
2019 force_maybe_cd (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
2020 {
2021     if (input_is_empty (cmdline))
2022     {
2023         cd_up_dir ();
2024         return MSG_HANDLED;
2025     }
2026 
2027     return MSG_NOT_HANDLED;
2028 }
2029 
2030 /* --------------------------------------------------------------------------------------------- */
2031 
2032 static inline void
2033 unselect_item (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
2034 {
2035     repaint_file (panel, panel->selected, TRUE, 2 * selection (panel)->f.marked, FALSE);
2036 }
2037 
2038 /* --------------------------------------------------------------------------------------------- */
2039 /** Select/unselect all the files like a current file by extension */
2040 
2041 static void
2042 panel_select_ext_cmd (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
2043 {
2044     char *filename;
2045     gboolean do_select;
2046     char *reg_exp, *cur_file_ext;
2047     mc_search_t *search;
2048     int i;
2049 
2050     filename = selection (current_panel)->fname;
2051     if (filename == NULL)
2052         return;
2053 
2054     do_select = !selection (current_panel)->f.marked;
2055 
2056     cur_file_ext = strutils_regex_escape (extension (filename));
2057     if (cur_file_ext[0] != '\0')
2058         reg_exp = g_strconcat ("^.*\\.", cur_file_ext, "$", (char *) NULL);
2059     else
2060         reg_exp = g_strdup ("^[^\\.]+$");
2061 
2062     g_free (cur_file_ext);
2063 
2064     search = mc_search_new (reg_exp, NULL);
2065     search->search_type = MC_SEARCH_T_REGEX;
2066     search->is_case_sensitive = FALSE;
2067 
2068     for (i = 0; i < current_panel->dir.len; i++)
2069     {
2070         file_entry_t *file_entry = &current_panel->dir.list[i];
2071 
2072         if (DIR_IS_DOTDOT (file_entry->fname) || S_ISDIR (file_entry->st.st_mode))
2073             continue;
2074 
2075         if (!mc_search_run (search, file_entry->fname, 0, file_entry->fnamelen, NULL))
2076             continue;
2077 
2078         do_file_mark (current_panel, i, do_select ? 1 : 0);
2079     }
2080 
2081     mc_search_free (search);
2082     g_free (reg_exp);
2083 }
2084 
2085 /* --------------------------------------------------------------------------------------------- */
2086 
2087 static int
2088 panel_selected_at_half (const WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
2089 {
2090     int lines, top;
2091 
2092     lines = panel_lines (panel);
2093 
2094     /* define top file of column */
2095     top = panel->top_file;
2096     if (panel->list_cols > 1)
2097         top += lines * ((panel->selected - top) / lines);
2098 
2099     return (panel->selected - top - lines / 2);
2100 }
2101 
2102 /* --------------------------------------------------------------------------------------------- */
2103 
2104 static void
2105 move_down (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
2106 {
2107     int items;
2108 
2109     if (panel->selected + 1 == panel->dir.len)
2110         return;
2111 
2112     unselect_item (panel);
2113     panel->selected++;
2114 
2115     items = panel_items (panel);
2116 
2117     if (panels_options.scroll_pages && panel->selected - panel->top_file == items)
2118     {
2119         /* Scroll window half screen */
2120         panel->top_file += items / 2;
2121         if (panel->top_file > panel->dir.len - items)
2122             panel->top_file = panel->dir.len - items;
2123         paint_dir (panel);
2124     }
2125     else if (panels_options.scroll_center && panel_selected_at_half (panel) > 0)
2126     {
2127         /* Scroll window when cursor is halfway down */
2128         panel->top_file++;
2129         if (panel->top_file > panel->dir.len - items)
2130             panel->top_file = panel->dir.len - items;
2131     }
2132     select_item (panel);
2133 }
2134 
2135 /* --------------------------------------------------------------------------------------------- */
2136 
2137 static void
2138 move_up (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
2139 {
2140     if (panel->selected == 0)
2141         return;
2142 
2143     unselect_item (panel);
2144     panel->selected--;
2145 
2146     if (panels_options.scroll_pages && panel->selected < panel->top_file)
2147     {
2148         /* Scroll window half screen */
2149         panel->top_file -= panel_items (panel) / 2;
2150         if (panel->top_file < 0)
2151             panel->top_file = 0;
2152         paint_dir (panel);
2153     }
2154     else if (panels_options.scroll_center && panel_selected_at_half (panel) < 0)
2155     {
2156         /* Scroll window when cursor is halfway up */
2157         panel->top_file--;
2158         if (panel->top_file < 0)
2159             panel->top_file = 0;
2160     }
2161     select_item (panel);
2162 }
2163 
2164 /* --------------------------------------------------------------------------------------------- */
2165 /** Changes the selection by lines (may be negative) */
2166 
2167 static void
2168 move_selection (WPanel * panel, int lines)
     /* [previous][next][first][last][top][bottom][index][help]  */
2169 {
2170     int new_pos;
2171     gboolean adjust = FALSE;
2172 
2173     new_pos = panel->selected + lines;
2174     if (new_pos >= panel->dir.len)
2175         new_pos = panel->dir.len - 1;
2176 
2177     if (new_pos < 0)
2178         new_pos = 0;
2179 
2180     unselect_item (panel);
2181     panel->selected = new_pos;
2182 
2183     if (panel->selected - panel->top_file >= panel_items (panel))
2184     {
2185         panel->top_file += lines;
2186         adjust = TRUE;
2187     }
2188 
2189     if (panel->selected - panel->top_file < 0)
2190     {
2191         panel->top_file += lines;
2192         adjust = TRUE;
2193     }
2194 
2195     if (adjust)
2196     {
2197         if (panel->top_file > panel->selected)
2198             panel->top_file = panel->selected;
2199         if (panel->top_file < 0)
2200             panel->top_file = 0;
2201         paint_dir (panel);
2202     }
2203     select_item (panel);
2204 }
2205 
2206 /* --------------------------------------------------------------------------------------------- */
2207 
2208 static cb_ret_t
2209 move_left (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
2210 {
2211     if (panel->list_cols > 1)
2212     {
2213         move_selection (panel, -panel_lines (panel));
2214         return MSG_HANDLED;
2215     }
2216 
2217     return maybe_cd (TRUE);     /* cd .. */
2218 }
2219 
2220 /* --------------------------------------------------------------------------------------------- */
2221 
2222 static cb_ret_t
2223 move_right (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
2224 {
2225     if (panel->list_cols > 1)
2226     {
2227         move_selection (panel, panel_lines (panel));
2228         return MSG_HANDLED;
2229     }
2230 
2231     return maybe_cd (FALSE);    /* cd (selection) */
2232 }
2233 
2234 /* --------------------------------------------------------------------------------------------- */
2235 
2236 static void
2237 prev_page (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
2238 {
2239     int items;
2240 
2241     if (panel->selected == 0 && panel->top_file == 0)
2242         return;
2243 
2244     unselect_item (panel);
2245     items = panel_items (panel);
2246     if (panel->top_file < items)
2247         items = panel->top_file;
2248     if (items == 0)
2249         panel->selected = 0;
2250     else
2251         panel->selected -= items;
2252     panel->top_file -= items;
2253 
2254     select_item (panel);
2255     paint_dir (panel);
2256 }
2257 
2258 /* --------------------------------------------------------------------------------------------- */
2259 
2260 static void
2261 goto_parent_dir (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
2262 {
2263     if (!panel->is_panelized)
2264         cd_up_dir ();
2265     else
2266     {
2267         char *fname = panel->dir.list[panel->selected].fname;
2268         const char *bname;
2269         vfs_path_t *dname_vpath;
2270 
2271         if (g_path_is_absolute (fname))
2272             fname = g_strdup (fname);
2273         else
2274             fname =
2275                 mc_build_filename (vfs_path_as_str (panelized_panel.root_vpath), fname,
2276                                    (char *) NULL);
2277 
2278         bname = x_basename (fname);
2279 
2280         if (bname == fname)
2281             dname_vpath = vfs_path_from_str (".");
2282         else
2283         {
2284             char *dname;
2285 
2286             dname = g_strndup (fname, bname - fname);
2287             dname_vpath = vfs_path_from_str (dname);
2288             g_free (dname);
2289         }
2290 
2291         do_cd (dname_vpath, cd_exact);
2292         try_to_select (panel, bname);
2293 
2294         vfs_path_free (dname_vpath);
2295         g_free (fname);
2296     }
2297 }
2298 
2299 /* --------------------------------------------------------------------------------------------- */
2300 
2301 static void
2302 next_page (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
2303 {
2304     int items;
2305 
2306     if (panel->selected == panel->dir.len - 1)
2307         return;
2308 
2309     unselect_item (panel);
2310     items = panel_items (panel);
2311     if (panel->top_file > panel->dir.len - 2 * items)
2312         items = panel->dir.len - items - panel->top_file;
2313     if (panel->top_file + items < 0)
2314         items = -panel->top_file;
2315     if (items == 0)
2316         panel->selected = panel->dir.len - 1;
2317     else
2318         panel->selected += items;
2319     panel->top_file += items;
2320 
2321     select_item (panel);
2322     paint_dir (panel);
2323 }
2324 
2325 /* --------------------------------------------------------------------------------------------- */
2326 
2327 static void
2328 goto_child_dir (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
2329 {
2330     if ((S_ISDIR (selection (panel)->st.st_mode) || link_isdir (selection (panel))))
2331     {
2332         vfs_path_t *vpath;
2333 
2334         vpath = vfs_path_from_str (selection (panel)->fname);
2335         do_cd (vpath, cd_exact);
2336         vfs_path_free (vpath);
2337     }
2338 }
2339 
2340 /* --------------------------------------------------------------------------------------------- */
2341 
2342 static void
2343 goto_top_file (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
2344 {
2345     unselect_item (panel);
2346     panel->selected = panel->top_file;
2347     select_item (panel);
2348 }
2349 
2350 /* --------------------------------------------------------------------------------------------- */
2351 
2352 static void
2353 goto_middle_file (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
2354 {
2355     unselect_item (panel);
2356     panel->selected = panel->top_file + panel_items (panel) / 2;
2357     select_item (panel);
2358 }
2359 
2360 /* --------------------------------------------------------------------------------------------- */
2361 
2362 static void
2363 goto_bottom_file (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
2364 {
2365     unselect_item (panel);
2366     panel->selected = panel->top_file + panel_items (panel) - 1;
2367     select_item (panel);
2368 }
2369 
2370 /* --------------------------------------------------------------------------------------------- */
2371 
2372 static void
2373 move_home (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
2374 {
2375     if (panel->selected == 0)
2376         return;
2377 
2378     unselect_item (panel);
2379 
2380     if (panels_options.torben_fj_mode)
2381     {
2382         int middle_pos;
2383 
2384         middle_pos = panel->top_file + panel_items (panel) / 2;
2385 
2386         if (panel->selected > middle_pos)
2387         {
2388             goto_middle_file (panel);
2389             return;
2390         }
2391         if (panel->selected != panel->top_file)
2392         {
2393             goto_top_file (panel);
2394             return;
2395         }
2396     }
2397 
2398     panel->top_file = 0;
2399     panel->selected = 0;
2400 
2401     paint_dir (panel);
2402     select_item (panel);
2403 }
2404 
2405 /* --------------------------------------------------------------------------------------------- */
2406 
2407 static void
2408 move_end (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
2409 {
2410     if (panel->selected == panel->dir.len - 1)
2411         return;
2412 
2413     unselect_item (panel);
2414 
2415     if (panels_options.torben_fj_mode)
2416     {
2417         int items, middle_pos;
2418 
2419         items = panel_items (panel);
2420         middle_pos = panel->top_file + items / 2;
2421 
2422         if (panel->selected < middle_pos)
2423         {
2424             goto_middle_file (panel);
2425             return;
2426         }
2427         if (panel->selected != panel->top_file + items - 1)
2428         {
2429             goto_bottom_file (panel);
2430             return;
2431         }
2432     }
2433 
2434     panel->selected = panel->dir.len - 1;
2435     paint_dir (panel);
2436     select_item (panel);
2437 }
2438 
2439 /* --------------------------------------------------------------------------------------------- */
2440 
2441 static void
2442 do_mark_file (WPanel * panel, mark_act_t do_move)
     /* [previous][next][first][last][top][bottom][index][help]  */
2443 {
2444     do_file_mark (panel, panel->selected, selection (panel)->f.marked ? 0 : 1);
2445 
2446     if ((panels_options.mark_moves_down && do_move == MARK_DOWN) || do_move == MARK_FORCE_DOWN)
2447         move_down (panel);
2448     else if (do_move == MARK_FORCE_UP)
2449         move_up (panel);
2450 }
2451 
2452 /* --------------------------------------------------------------------------------------------- */
2453 
2454 static inline void
2455 mark_file (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
2456 {
2457     do_mark_file (panel, MARK_DOWN);
2458 }
2459 
2460 /* --------------------------------------------------------------------------------------------- */
2461 
2462 static inline void
2463 mark_file_up (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
2464 {
2465     do_mark_file (panel, MARK_FORCE_UP);
2466 }
2467 
2468 /* --------------------------------------------------------------------------------------------- */
2469 
2470 static inline void
2471 mark_file_down (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
2472 {
2473     do_mark_file (panel, MARK_FORCE_DOWN);
2474 }
2475 
2476 /* --------------------------------------------------------------------------------------------- */
2477 
2478 static void
2479 mark_file_right (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
2480 {
2481     int lines;
2482 
2483     if (state_mark < 0)
2484         state_mark = selection (panel)->f.marked ? 0 : 1;
2485 
2486     lines = panel_lines (panel);
2487     lines = MIN (lines, panel->dir.len - panel->selected - 1);
2488     for (; lines != 0; lines--)
2489     {
2490         do_file_mark (panel, panel->selected, state_mark);
2491         move_down (panel);
2492     }
2493     do_file_mark (panel, panel->selected, state_mark);
2494 }
2495 
2496 /* --------------------------------------------------------------------------------------------- */
2497 
2498 static void
2499 mark_file_left (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
2500 {
2501     int lines;
2502 
2503     if (state_mark < 0)
2504         state_mark = selection (panel)->f.marked ? 0 : 1;
2505 
2506     lines = panel_lines (panel);
2507     lines = MIN (lines, panel->selected + 1);
2508     for (; lines != 0; lines--)
2509     {
2510         do_file_mark (panel, panel->selected, state_mark);
2511         move_up (panel);
2512     }
2513     do_file_mark (panel, panel->selected, state_mark);
2514 }
2515 
2516 /* --------------------------------------------------------------------------------------------- */
2517 
2518 static void
2519 panel_select_unselect_files (WPanel * panel, const char *title, const char *history_name,
     /* [previous][next][first][last][top][bottom][index][help]  */
2520                              gboolean do_select)
2521 {
2522     gboolean files_only = (panels_options.select_flags & SELECT_FILES_ONLY) != 0;
2523     gboolean case_sens = (panels_options.select_flags & SELECT_MATCH_CASE) != 0;
2524     gboolean shell_patterns = (panels_options.select_flags & SELECT_SHELL_PATTERNS) != 0;
2525 
2526     char *reg_exp;
2527     mc_search_t *search;
2528     int i;
2529 
2530     quick_widget_t quick_widgets[] = {
2531         /* *INDENT-OFF* */
2532         QUICK_INPUT (INPUT_LAST_TEXT, history_name, &reg_exp, NULL,
2533                      FALSE, FALSE, INPUT_COMPLETE_FILENAMES),
2534         QUICK_START_COLUMNS,
2535             QUICK_CHECKBOX (N_("&Files only"), &files_only, NULL),
2536             QUICK_CHECKBOX (N_("&Using shell patterns"), &shell_patterns, NULL),
2537         QUICK_NEXT_COLUMN,
2538             QUICK_CHECKBOX (N_("&Case sensitive"), &case_sens, NULL),
2539         QUICK_STOP_COLUMNS,
2540         QUICK_END
2541         /* *INDENT-ON* */
2542     };
2543 
2544     quick_dialog_t qdlg = {
2545         -1, -1, 50,
2546         title, "[Select/Unselect Files]",
2547         quick_widgets, NULL, NULL
2548     };
2549 
2550     if (quick_dialog (&qdlg) == B_CANCEL)
2551         return;
2552 
2553     if (reg_exp == NULL || *reg_exp == '\0')
2554     {
2555         g_free (reg_exp);
2556         return;
2557     }
2558 
2559     search = mc_search_new (reg_exp, NULL);
2560     search->search_type = shell_patterns ? MC_SEARCH_T_GLOB : MC_SEARCH_T_REGEX;
2561     search->is_entire_line = TRUE;
2562     search->is_case_sensitive = case_sens;
2563 
2564     for (i = 0; i < panel->dir.len; i++)
2565     {
2566         if (DIR_IS_DOTDOT (panel->dir.list[i].fname))
2567             continue;
2568         if (S_ISDIR (panel->dir.list[i].st.st_mode) && files_only)
2569             continue;
2570 
2571         if (mc_search_run (search, panel->dir.list[i].fname, 0, panel->dir.list[i].fnamelen, NULL))
2572             do_file_mark (panel, i, do_select ? 1 : 0);
2573     }
2574 
2575     mc_search_free (search);
2576     g_free (reg_exp);
2577 
2578     /* result flags */
2579     panels_options.select_flags = 0;
2580     if (case_sens)
2581         panels_options.select_flags |= SELECT_MATCH_CASE;
2582     if (files_only)
2583         panels_options.select_flags |= SELECT_FILES_ONLY;
2584     if (shell_patterns)
2585         panels_options.select_flags |= SELECT_SHELL_PATTERNS;
2586 }
2587 
2588 /* --------------------------------------------------------------------------------------------- */
2589 
2590 static void
2591 panel_select_files (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
2592 {
2593     panel_select_unselect_files (panel, _("Select"), ":select_cmd: Select ", TRUE);
2594 }
2595 
2596 /* --------------------------------------------------------------------------------------------- */
2597 
2598 static void
2599 panel_unselect_files (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
2600 {
2601     panel_select_unselect_files (panel, _("Unselect"), ":unselect_cmd: Unselect ", FALSE);
2602 }
2603 
2604 /* --------------------------------------------------------------------------------------------- */
2605 
2606 static void
2607 panel_select_invert_files (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
2608 {
2609     int i;
2610 
2611     for (i = 0; i < panel->dir.len; i++)
2612     {
2613         file_entry_t *file = &panel->dir.list[i];
2614 
2615         if (!panels_options.reverse_files_only || !S_ISDIR (file->st.st_mode))
2616             do_file_mark (panel, i, file->f.marked ? 0 : 1);
2617     }
2618 }
2619 
2620 /* --------------------------------------------------------------------------------------------- */
2621 /** Incremental search of a file name in the panel.
2622   * @param panel instance of WPanel structure
2623   * @param c_code key code
2624   */
2625 
2626 static void
2627 do_search (WPanel * panel, int c_code)
     /* [previous][next][first][last][top][bottom][index][help]  */
2628 {
2629     size_t l;
2630     int i, sel;
2631     gboolean wrapped = FALSE;
2632     char *act;
2633     mc_search_t *search;
2634     char *reg_exp, *esc_str;
2635     gboolean is_found = FALSE;
2636 
2637     l = strlen (panel->search_buffer);
2638     if (c_code == KEY_BACKSPACE)
2639     {
2640         if (l != 0)
2641         {
2642             act = panel->search_buffer + l;
2643             str_prev_noncomb_char (&act, panel->search_buffer);
2644             act[0] = '\0';
2645         }
2646         panel->search_chpoint = 0;
2647     }
2648     else
2649     {
2650         if (c_code != 0 && (gsize) panel->search_chpoint < sizeof (panel->search_char))
2651         {
2652             panel->search_char[panel->search_chpoint] = c_code;
2653             panel->search_chpoint++;
2654         }
2655 
2656         if (panel->search_chpoint > 0)
2657         {
2658             switch (str_is_valid_char (panel->search_char, panel->search_chpoint))
2659             {
2660             case -2:
2661                 return;
2662             case -1:
2663                 panel->search_chpoint = 0;
2664                 return;
2665             default:
2666                 if (l + panel->search_chpoint < sizeof (panel->search_buffer))
2667                 {
2668                     memcpy (panel->search_buffer + l, panel->search_char, panel->search_chpoint);
2669                     l += panel->search_chpoint;
2670                     panel->search_buffer[l] = '\0';
2671                     panel->search_chpoint = 0;
2672                 }
2673             }
2674         }
2675     }
2676 
2677     reg_exp = g_strdup_printf ("%s*", panel->search_buffer);
2678     esc_str = strutils_escape (reg_exp, -1, ",|\\{}[]", TRUE);
2679     search = mc_search_new (esc_str, NULL);
2680     search->search_type = MC_SEARCH_T_GLOB;
2681     search->is_entire_line = TRUE;
2682 
2683     switch (panels_options.qsearch_mode)
2684     {
2685     case QSEARCH_CASE_SENSITIVE:
2686         search->is_case_sensitive = TRUE;
2687         break;
2688     case QSEARCH_CASE_INSENSITIVE:
2689         search->is_case_sensitive = FALSE;
2690         break;
2691     default:
2692         search->is_case_sensitive = panel->sort_info.case_sensitive;
2693         break;
2694     }
2695 
2696     sel = panel->selected;
2697 
2698     for (i = panel->selected; !wrapped || i != panel->selected; i++)
2699     {
2700         if (i >= panel->dir.len)
2701         {
2702             i = 0;
2703             if (wrapped)
2704                 break;
2705             wrapped = TRUE;
2706         }
2707         if (mc_search_run (search, panel->dir.list[i].fname, 0, panel->dir.list[i].fnamelen, NULL))
2708         {
2709             sel = i;
2710             is_found = TRUE;
2711             break;
2712         }
2713     }
2714     if (is_found)
2715     {
2716         unselect_item (panel);
2717         panel->selected = sel;
2718         select_item (panel);
2719         widget_draw (WIDGET (panel));
2720     }
2721     else if (c_code != KEY_BACKSPACE)
2722     {
2723         act = panel->search_buffer + l;
2724         str_prev_noncomb_char (&act, panel->search_buffer);
2725         act[0] = '\0';
2726     }
2727     mc_search_free (search);
2728     g_free (reg_exp);
2729     g_free (esc_str);
2730 }
2731 
2732 /* --------------------------------------------------------------------------------------------- */
2733 /** Start new search.
2734   * @param panel instance of WPanel structure
2735   */
2736 
2737 static void
2738 start_search (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
2739 {
2740     if (panel->searching)
2741     {
2742         if (panel->selected == panel->dir.len - 1)
2743             panel->selected = 0;
2744         else
2745             move_down (panel);
2746 
2747         /* in case if there was no search string we need to recall
2748            previous string, with which we ended previous searching */
2749         if (panel->search_buffer[0] == '\0')
2750             g_strlcpy (panel->search_buffer, panel->prev_search_buffer,
2751                        sizeof (panel->search_buffer));
2752 
2753         do_search (panel, 0);
2754     }
2755     else
2756     {
2757         panel->searching = TRUE;
2758         panel->search_buffer[0] = '\0';
2759         panel->search_char[0] = '\0';
2760         panel->search_chpoint = 0;
2761         display_mini_info (panel);
2762         mc_refresh ();
2763     }
2764 }
2765 
2766 /* --------------------------------------------------------------------------------------------- */
2767 
2768 static void
2769 stop_search (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
2770 {
2771     panel->searching = FALSE;
2772 
2773     /* if user had overrdied search string, we need to store it
2774        to the previous_search_buffer */
2775     if (panel->search_buffer[0] != '\0')
2776         g_strlcpy (panel->prev_search_buffer, panel->search_buffer,
2777                    sizeof (panel->prev_search_buffer));
2778 
2779     display_mini_info (panel);
2780 }
2781 
2782 /* --------------------------------------------------------------------------------------------- */
2783 /** Return TRUE if the Enter key has been processed, FALSE otherwise */
2784 
2785 static gboolean
2786 do_enter_on_file_entry (file_entry_t * fe)
     /* [previous][next][first][last][top][bottom][index][help]  */
2787 {
2788     vfs_path_t *full_name_vpath;
2789     gboolean ok;
2790 
2791     /*
2792      * Directory or link to directory - change directory.
2793      * Try the same for the entries on which mc_lstat() has failed.
2794      */
2795     if (S_ISDIR (fe->st.st_mode) || link_isdir (fe) || (fe->st.st_mode == 0))
2796     {
2797         vfs_path_t *fname_vpath;
2798 
2799         fname_vpath = vfs_path_from_str (fe->fname);
2800         if (!do_cd (fname_vpath, cd_exact))
2801             message (D_ERROR, MSG_ERROR, _("Cannot change directory"));
2802         vfs_path_free (fname_vpath);
2803         return TRUE;
2804     }
2805 
2806     full_name_vpath = vfs_path_append_new (current_panel->cwd_vpath, fe->fname, (char *) NULL);
2807 
2808     /* Try associated command */
2809     ok = regex_command (full_name_vpath, "Open") != 0;
2810     vfs_path_free (full_name_vpath);
2811     if (ok)
2812         return TRUE;
2813 
2814     /* Check if the file is executable */
2815     full_name_vpath = vfs_path_append_new (current_panel->cwd_vpath, fe->fname, (char *) NULL);
2816     ok = (is_exe (fe->st.st_mode) && if_link_is_exe (full_name_vpath, fe));
2817     vfs_path_free (full_name_vpath);
2818     if (!ok)
2819         return FALSE;
2820 
2821     if (confirm_execute
2822         && query_dialog (_("The Midnight Commander"), _("Do you really want to execute?"), D_NORMAL,
2823                          2, _("&Yes"), _("&No")) != 0)
2824         return TRUE;
2825 
2826     if (!vfs_current_is_local ())
2827     {
2828         int ret;
2829         vfs_path_t *tmp_vpath;
2830 
2831         tmp_vpath = vfs_path_append_new (vfs_get_raw_current_dir (), fe->fname, (char *) NULL);
2832         ret = mc_setctl (tmp_vpath, VFS_SETCTL_RUN, NULL);
2833         vfs_path_free (tmp_vpath);
2834         /* We took action only if the dialog was shown or the execution was successful */
2835         return confirm_execute || (ret == 0);
2836     }
2837 
2838     {
2839         char *tmp, *cmd;
2840 
2841         tmp = name_quote (fe->fname, FALSE);
2842         cmd = g_strconcat (".", PATH_SEP_STR, tmp, (char *) NULL);
2843         g_free (tmp);
2844         shell_execute (cmd, 0);
2845         g_free (cmd);
2846     }
2847 
2848 #ifdef HAVE_CHARSET
2849     mc_global.source_codepage = default_source_codepage;
2850 #endif
2851 
2852     return TRUE;
2853 }
2854 
2855 /* --------------------------------------------------------------------------------------------- */
2856 
2857 static inline gboolean
2858 do_enter (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
2859 {
2860     return do_enter_on_file_entry (selection (panel));
2861 }
2862 
2863 
2864 /* --------------------------------------------------------------------------------------------- */
2865 
2866 static void
2867 panel_cycle_listing_format (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
2868 {
2869     panel->list_format = (panel->list_format + 1) % LIST_FORMATS;
2870 
2871     if (set_panel_formats (panel) == 0)
2872         do_refresh ();
2873 }
2874 
2875 /* --------------------------------------------------------------------------------------------- */
2876 
2877 static void
2878 chdir_other_panel (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
2879 {
2880     const file_entry_t *entry = &panel->dir.list[panel->selected];
2881     vfs_path_t *new_dir_vpath;
2882     char *sel_entry = NULL;
2883 
2884     if (get_other_type () != view_listing)
2885         create_panel (get_other_index (), view_listing);
2886 
2887     if (S_ISDIR (entry->st.st_mode) || link_isdir (entry))
2888         new_dir_vpath = vfs_path_append_new (panel->cwd_vpath, entry->fname, (char *) NULL);
2889     else
2890     {
2891         new_dir_vpath = vfs_path_append_new (panel->cwd_vpath, "..", (char *) NULL);
2892         sel_entry = strrchr (vfs_path_get_last_path_str (panel->cwd_vpath), PATH_SEP);
2893     }
2894 
2895     change_panel ();
2896     do_cd (new_dir_vpath, cd_exact);
2897     vfs_path_free (new_dir_vpath);
2898 
2899     if (sel_entry)
2900         try_to_select (current_panel, sel_entry);
2901     change_panel ();
2902 
2903     move_down (panel);
2904 }
2905 
2906 /* --------------------------------------------------------------------------------------------- */
2907 /**
2908  * Make the current directory of the current panel also the current
2909  * directory of the other panel.  Put the other panel to the listing
2910  * mode if needed.  If the current panel is panelized, the other panel
2911  * doesn't become panelized.
2912  */
2913 
2914 static void
2915 panel_sync_other (const WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
2916 {
2917     if (get_other_type () != view_listing)
2918         create_panel (get_other_index (), view_listing);
2919 
2920     do_panel_cd (other_panel, current_panel->cwd_vpath, cd_exact);
2921 
2922     /* try to select current filename on the other panel */
2923     if (!panel->is_panelized)
2924         try_to_select (other_panel, selection (panel)->fname);
2925 }
2926 
2927 /* --------------------------------------------------------------------------------------------- */
2928 
2929 static void
2930 chdir_to_readlink (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
2931 {
2932     vfs_path_t *new_dir_vpath;
2933     char buffer[MC_MAXPATHLEN];
2934     int i;
2935     struct stat st;
2936     vfs_path_t *panel_fname_vpath;
2937     gboolean ok;
2938 
2939     if (get_other_type () != view_listing)
2940         return;
2941 
2942     if (!S_ISLNK (panel->dir.list[panel->selected].st.st_mode))
2943         return;
2944 
2945     i = readlink (selection (panel)->fname, buffer, MC_MAXPATHLEN - 1);
2946     if (i < 0)
2947         return;
2948 
2949     panel_fname_vpath = vfs_path_from_str (selection (panel)->fname);
2950     ok = (mc_stat (panel_fname_vpath, &st) >= 0);
2951     vfs_path_free (panel_fname_vpath);
2952     if (!ok)
2953         return;
2954 
2955     buffer[i] = '\0';
2956     if (!S_ISDIR (st.st_mode))
2957     {
2958         char *p;
2959 
2960         p = strrchr (buffer, PATH_SEP);
2961         if (p != NULL && p[1] == '\0')
2962         {
2963             *p = '\0';
2964             p = strrchr (buffer, PATH_SEP);
2965         }
2966         if (p == NULL)
2967             return;
2968 
2969         p[1] = '\0';
2970     }
2971     if (IS_PATH_SEP (*buffer))
2972         new_dir_vpath = vfs_path_from_str (buffer);
2973     else
2974         new_dir_vpath = vfs_path_append_new (panel->cwd_vpath, buffer, (char *) NULL);
2975 
2976     change_panel ();
2977     do_cd (new_dir_vpath, cd_exact);
2978     vfs_path_free (new_dir_vpath);
2979     change_panel ();
2980 
2981     move_down (panel);
2982 }
2983 
2984 /* --------------------------------------------------------------------------------------------- */
2985 /**
2986    function return 0 if not found and REAL_INDEX+1 if found
2987  */
2988 
2989 static gsize
2990 panel_get_format_field_index_by_name (const WPanel * panel, const char *name)
     /* [previous][next][first][last][top][bottom][index][help]  */
2991 {
2992     GSList *format;
2993     gsize lc_index;
2994 
2995     for (lc_index = 1, format = panel->format;
2996          format != NULL && strcmp (((format_item_t *) format->data)->title, name) != 0;
2997          format = g_slist_next (format), lc_index++)
2998         ;
2999 
3000     if (format == NULL)
3001         lc_index = 0;
3002 
3003     return lc_index;
3004 }
3005 
3006 /* --------------------------------------------------------------------------------------------- */
3007 
3008 static const panel_field_t *
3009 panel_get_sortable_field_by_format (const WPanel * panel, gsize lc_index)
     /* [previous][next][first][last][top][bottom][index][help]  */
3010 {
3011     const panel_field_t *pfield;
3012     const format_item_t *format;
3013 
3014     format = (const format_item_t *) g_slist_nth_data (panel->format, lc_index);
3015     if (format == NULL)
3016         return NULL;
3017 
3018     pfield = panel_get_field_by_title (format->title);
3019     if (pfield == NULL)
3020         return NULL;
3021     if (pfield->sort_routine == NULL)
3022         return NULL;
3023     return pfield;
3024 }
3025 
3026 /* --------------------------------------------------------------------------------------------- */
3027 
3028 static void
3029 panel_toggle_sort_order_prev (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
3030 {
3031     gsize lc_index, i;
3032     const char *title;
3033     const panel_field_t *pfield = NULL;
3034 
3035     title = panel_get_title_without_hotkey (panel->sort_field->title_hotkey);
3036     lc_index = panel_get_format_field_index_by_name (panel, title);
3037 
3038     if (lc_index > 1)
3039     {
3040         /* search for prev sortable column in panel format */
3041         for (i = lc_index - 1;
3042              i != 0 && (pfield = panel_get_sortable_field_by_format (panel, i - 1)) == NULL; i--)
3043             ;
3044     }
3045 
3046     if (pfield == NULL)
3047     {
3048         /* Sortable field not found. Try to search in each array */
3049         for (i = g_slist_length (panel->format);
3050              i != 0 && (pfield = panel_get_sortable_field_by_format (panel, i - 1)) == NULL; i--)
3051             ;
3052     }
3053 
3054     if (pfield != NULL)
3055     {
3056         panel->sort_field = pfield;
3057         panel_set_sort_order (panel, pfield);
3058     }
3059 }
3060 
3061 /* --------------------------------------------------------------------------------------------- */
3062 
3063 static void
3064 panel_toggle_sort_order_next (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
3065 {
3066     gsize lc_index, i;
3067     const panel_field_t *pfield = NULL;
3068     gsize format_field_count;
3069     const char *title;
3070 
3071     format_field_count = g_slist_length (panel->format);
3072     title = panel_get_title_without_hotkey (panel->sort_field->title_hotkey);
3073     lc_index = panel_get_format_field_index_by_name (panel, title);
3074 
3075     if (lc_index != 0 && lc_index != format_field_count)
3076     {
3077         /* search for prev sortable column in panel format */
3078         for (i = lc_index;
3079              i != format_field_count
3080              && (pfield = panel_get_sortable_field_by_format (panel, i)) == NULL; i++)
3081             ;
3082     }
3083 
3084     if (pfield == NULL)
3085     {
3086         /* Sortable field not found. Try to search in each array */
3087         for (i = 0;
3088              i != format_field_count
3089              && (pfield = panel_get_sortable_field_by_format (panel, i)) == NULL; i++)
3090             ;
3091     }
3092 
3093     if (pfield != NULL)
3094     {
3095         panel->sort_field = pfield;
3096         panel_set_sort_order (panel, pfield);
3097     }
3098 }
3099 
3100 /* --------------------------------------------------------------------------------------------- */
3101 
3102 static void
3103 panel_select_sort_order (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
3104 {
3105     const panel_field_t *sort_order;
3106 
3107     sort_order = sort_box (&panel->sort_info, panel->sort_field);
3108     if (sort_order != NULL)
3109     {
3110         panel->sort_field = sort_order;
3111         panel_set_sort_order (panel, sort_order);
3112     }
3113 }
3114 
3115 /* --------------------------------------------------------------------------------------------- */
3116 
3117 /**
3118  * panel_content_scroll_left:
3119  * @param panel the pointer to the panel on which we operate
3120  *
3121  * scroll long filename to the left (decrement scroll pointer)
3122  *
3123  */
3124 
3125 static void
3126 panel_content_scroll_left (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
3127 {
3128     if (panel->content_shift > -1)
3129     {
3130         if (panel->content_shift > panel->max_shift)
3131             panel->content_shift = panel->max_shift;
3132 
3133         panel->content_shift--;
3134         show_dir (panel);
3135         paint_dir (panel);
3136     }
3137 }
3138 
3139 /* --------------------------------------------------------------------------------------------- */
3140 /**
3141  * panel_content_scroll_right:
3142  * @param panel the pointer to the panel on which we operate
3143  *
3144  * scroll long filename to the right (increment scroll pointer)
3145  *
3146  */
3147 
3148 static void
3149 panel_content_scroll_right (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
3150 {
3151     if (panel->content_shift < 0 || panel->content_shift < panel->max_shift)
3152     {
3153         panel->content_shift++;
3154         show_dir (panel);
3155         paint_dir (panel);
3156     }
3157 }
3158 
3159 /* --------------------------------------------------------------------------------------------- */
3160 
3161 static void
3162 panel_set_sort_type_by_id (WPanel * panel, const char *name)
     /* [previous][next][first][last][top][bottom][index][help]  */
3163 {
3164     if (strcmp (panel->sort_field->id, name) == 0)
3165         panel->sort_info.reverse = !panel->sort_info.reverse;
3166     else
3167     {
3168         const panel_field_t *sort_order;
3169 
3170         sort_order = panel_get_field_by_id (name);
3171         if (sort_order == NULL)
3172             return;
3173 
3174         panel->sort_field = sort_order;
3175     }
3176 
3177     panel_set_sort_order (panel, panel->sort_field);
3178 }
3179 
3180 /* --------------------------------------------------------------------------------------------- */
3181 /**
3182  *  If we moved to the parent directory move the selection pointer to
3183  *  the old directory name; If we leave VFS dir, remove FS specificator.
3184  *
3185  *  You do _NOT_ want to add any vfs aware code here. <pavel@ucw.cz>
3186  */
3187 
3188 static const char *
3189 get_parent_dir_name (const vfs_path_t * cwd_vpath, const vfs_path_t * lwd_vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
3190 {
3191     size_t llen, clen;
3192     const char *p, *lwd;
3193 
3194     llen = vfs_path_len (lwd_vpath);
3195     clen = vfs_path_len (cwd_vpath);
3196 
3197     if (llen <= clen)
3198         return NULL;
3199 
3200     lwd = vfs_path_as_str (lwd_vpath);
3201 
3202     p = g_strrstr (lwd, VFS_PATH_URL_DELIMITER);
3203 
3204     if (p == NULL)
3205     {
3206         const char *cwd;
3207 
3208         cwd = vfs_path_as_str (cwd_vpath);
3209 
3210         p = strrchr (lwd, PATH_SEP);
3211 
3212         if (p != NULL && strncmp (cwd, lwd, (size_t) (p - lwd)) == 0
3213             && (clen == (size_t) (p - lwd) || (p == lwd && IS_PATH_SEP (cwd[0]) && cwd[1] == '\0')))
3214             return (p + 1);
3215 
3216         return NULL;
3217     }
3218 
3219     /* skip VFS prefix */
3220     while (--p > lwd && !IS_PATH_SEP (*p))
3221         ;
3222     /* get last component */
3223     while (--p > lwd && !IS_PATH_SEP (*p))
3224         ;
3225 
3226     /* return last component */
3227     return (p != lwd || IS_PATH_SEP (*p)) ? p + 1 : p;
3228 }
3229 
3230 /* --------------------------------------------------------------------------------------------- */
3231 /** Wrapper for do_subshell_chdir, check for availability of subshell */
3232 
3233 static void
3234 subshell_chdir (const vfs_path_t * vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
3235 {
3236 #ifdef ENABLE_SUBSHELL
3237     if (mc_global.tty.use_subshell && vfs_current_is_local ())
3238         do_subshell_chdir (vpath, FALSE);
3239 #else /* ENABLE_SUBSHELL */
3240     (void) vpath;
3241 #endif /* ENABLE_SUBSHELL */
3242 }
3243 
3244 /* --------------------------------------------------------------------------------------------- */
3245 /**
3246  * Changes the current directory of the panel.
3247  * Don't record change in the directory history.
3248  */
3249 
3250 static gboolean
3251 _do_panel_cd (WPanel * panel, const vfs_path_t * new_dir_vpath, enum cd_enum cd_type)
     /* [previous][next][first][last][top][bottom][index][help]  */
3252 {
3253     vfs_path_t *olddir_vpath;
3254 
3255     /* Convert *new_path to a suitable pathname, handle ~user */
3256     if (cd_type == cd_parse_command)
3257     {
3258         const vfs_path_element_t *element;
3259 
3260         element = vfs_path_get_by_index (new_dir_vpath, 0);
3261         if (strcmp (element->path, "-") == 0)
3262             new_dir_vpath = panel->lwd_vpath;
3263     }
3264 
3265     if (mc_chdir (new_dir_vpath) == -1)
3266         return FALSE;
3267 
3268     /* Success: save previous directory, shutdown status of previous dir */
3269     olddir_vpath = vfs_path_clone (panel->cwd_vpath);
3270     panel_set_lwd (panel, panel->cwd_vpath);
3271     input_free_completions (cmdline);
3272 
3273     vfs_path_free (panel->cwd_vpath);
3274     vfs_setup_cwd ();
3275     panel->cwd_vpath = vfs_path_clone (vfs_get_raw_current_dir ());
3276 
3277     vfs_release_path (olddir_vpath);
3278 
3279     subshell_chdir (panel->cwd_vpath);
3280 
3281     /* Reload current panel */
3282     panel_clean_dir (panel);
3283 
3284     if (!dir_list_load (&panel->dir, panel->cwd_vpath, panel->sort_field->sort_routine,
3285                         &panel->sort_info, panel->filter))
3286         message (D_ERROR, MSG_ERROR, _("Cannot read directory contents"));
3287 
3288     try_to_select (panel, get_parent_dir_name (panel->cwd_vpath, olddir_vpath));
3289 
3290     load_hint (FALSE);
3291     panel->dirty = 1;
3292     update_xterm_title_path ();
3293 
3294     vfs_path_free (olddir_vpath);
3295 
3296     return TRUE;
3297 }
3298 
3299 /* --------------------------------------------------------------------------------------------- */
3300 
3301 static void
3302 directory_history_next (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
3303 {
3304     gboolean ok;
3305 
3306     do
3307     {
3308         GList *next;
3309 
3310         ok = TRUE;
3311         next = g_list_next (panel->dir_history_current);
3312         if (next != NULL)
3313         {
3314             vfs_path_t *data_vpath;
3315 
3316             data_vpath = vfs_path_from_str ((char *) next->data);
3317             ok = _do_panel_cd (panel, data_vpath, cd_exact);
3318             vfs_path_free (data_vpath);
3319             panel->dir_history_current = next;
3320         }
3321         /* skip directories that present in history but absent in file system */
3322     }
3323     while (!ok);
3324 }
3325 
3326 /* --------------------------------------------------------------------------------------------- */
3327 
3328 static void
3329 directory_history_prev (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
3330 {
3331     gboolean ok;
3332 
3333     do
3334     {
3335         GList *prev;
3336 
3337         ok = TRUE;
3338         prev = g_list_previous (panel->dir_history_current);
3339         if (prev != NULL)
3340         {
3341             vfs_path_t *data_vpath;
3342 
3343             data_vpath = vfs_path_from_str ((char *) prev->data);
3344             ok = _do_panel_cd (panel, data_vpath, cd_exact);
3345             vfs_path_free (data_vpath);
3346             panel->dir_history_current = prev;
3347         }
3348         /* skip directories that present in history but absent in file system */
3349     }
3350     while (!ok);
3351 }
3352 
3353 /* --------------------------------------------------------------------------------------------- */
3354 
3355 static void
3356 directory_history_list (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
3357 {
3358     history_descriptor_t hd;
3359     gboolean ok = FALSE;
3360     size_t pos;
3361 
3362     pos = g_list_position (panel->dir_history_current, panel->dir_history);
3363 
3364     history_descriptor_init (&hd, WIDGET (panel)->y, WIDGET (panel)->x, panel->dir_history,
3365                              (int) pos);
3366     history_show (&hd);
3367 
3368     panel->dir_history = hd.list;
3369     if (hd.text != NULL)
3370     {
3371         vfs_path_t *s_vpath;
3372 
3373         s_vpath = vfs_path_from_str (hd.text);
3374         ok = _do_panel_cd (panel, s_vpath, cd_exact);
3375         if (ok)
3376             directory_history_add (panel, panel->cwd_vpath);
3377         else
3378             message (D_ERROR, MSG_ERROR, _("Cannot change directory"));
3379         vfs_path_free (s_vpath);
3380         g_free (hd.text);
3381     }
3382 
3383     if (!ok)
3384     {
3385         /* Since history is fully modified in history_show(), panel->dir_history actually
3386          * points to the invalid place. Try restore current postition here. */
3387 
3388         size_t i;
3389 
3390         panel->dir_history_current = panel->dir_history;
3391 
3392         for (i = 0; i <= pos; i++)
3393         {
3394             GList *prev;
3395 
3396             prev = g_list_previous (panel->dir_history_current);
3397             if (prev == NULL)
3398                 break;
3399 
3400             panel->dir_history_current = prev;
3401         }
3402     }
3403 }
3404 
3405 /* --------------------------------------------------------------------------------------------- */
3406 
3407 static cb_ret_t
3408 panel_execute_cmd (WPanel * panel, long command)
     /* [previous][next][first][last][top][bottom][index][help]  */
3409 {
3410     int res = MSG_HANDLED;
3411 
3412     if (command != CK_Search)
3413         stop_search (panel);
3414 
3415     switch (command)
3416     {
3417     case CK_Up:
3418     case CK_Down:
3419     case CK_Left:
3420     case CK_Right:
3421     case CK_Bottom:
3422     case CK_Top:
3423     case CK_PageDown:
3424     case CK_PageUp:
3425         /* reset state of marks flag */
3426         state_mark = -1;
3427         break;
3428     default:
3429         break;
3430     }
3431 
3432     switch (command)
3433     {
3434     case CK_CycleListingFormat:
3435         panel_cycle_listing_format (panel);
3436         break;
3437     case CK_PanelOtherCd:
3438         chdir_other_panel (panel);
3439         break;
3440     case CK_PanelOtherCdLink:
3441         chdir_to_readlink (panel);
3442         break;
3443     case CK_CopySingle:
3444         copy_cmd_local ();
3445         break;
3446     case CK_DeleteSingle:
3447         delete_cmd_local ();
3448         break;
3449     case CK_Enter:
3450         do_enter (panel);
3451         break;
3452     case CK_ViewRaw:
3453         view_raw_cmd ();
3454         break;
3455     case CK_EditNew:
3456         edit_cmd_new ();
3457         break;
3458     case CK_MoveSingle:
3459         rename_cmd_local ();
3460         break;
3461     case CK_SelectInvert:
3462         panel_select_invert_files (panel);
3463         break;
3464     case CK_Select:
3465         panel_select_files (panel);
3466         break;
3467     case CK_SelectExt:
3468         panel_select_ext_cmd ();
3469         break;
3470     case CK_Unselect:
3471         panel_unselect_files (panel);
3472         break;
3473     case CK_PageDown:
3474         next_page (panel);
3475         break;
3476     case CK_PageUp:
3477         prev_page (panel);
3478         break;
3479     case CK_CdChild:
3480         goto_child_dir (panel);
3481         break;
3482     case CK_CdParent:
3483         goto_parent_dir (panel);
3484         break;
3485     case CK_History:
3486         directory_history_list (panel);
3487         break;
3488     case CK_HistoryNext:
3489         directory_history_next (panel);
3490         break;
3491     case CK_HistoryPrev:
3492         directory_history_prev (panel);
3493         break;
3494     case CK_BottomOnScreen:
3495         goto_bottom_file (panel);
3496         break;
3497     case CK_MiddleOnScreen:
3498         goto_middle_file (panel);
3499         break;
3500     case CK_TopOnScreen:
3501         goto_top_file (panel);
3502         break;
3503     case CK_Mark:
3504         mark_file (panel);
3505         break;
3506     case CK_MarkUp:
3507         mark_file_up (panel);
3508         break;
3509     case CK_MarkDown:
3510         mark_file_down (panel);
3511         break;
3512     case CK_MarkLeft:
3513         mark_file_left (panel);
3514         break;
3515     case CK_MarkRight:
3516         mark_file_right (panel);
3517         break;
3518     case CK_CdParentSmart:
3519         res = force_maybe_cd ();
3520         break;
3521     case CK_Up:
3522         move_up (panel);
3523         break;
3524     case CK_Down:
3525         move_down (panel);
3526         break;
3527     case CK_Left:
3528         res = move_left (panel);
3529         break;
3530     case CK_Right:
3531         res = move_right (panel);
3532         break;
3533     case CK_Bottom:
3534         move_end (panel);
3535         break;
3536     case CK_Top:
3537         move_home (panel);
3538         break;
3539 #ifdef HAVE_CHARSET
3540     case CK_SelectCodepage:
3541         panel_change_encoding (panel);
3542         break;
3543 #endif
3544     case CK_ScrollLeft:
3545         panel_content_scroll_left (panel);
3546         break;
3547     case CK_ScrollRight:
3548         panel_content_scroll_right (panel);
3549         break;
3550     case CK_Search:
3551         start_search (panel);
3552         break;
3553     case CK_SearchStop:
3554         break;
3555     case CK_PanelOtherSync:
3556         panel_sync_other (panel);
3557         break;
3558     case CK_Sort:
3559         panel_select_sort_order (panel);
3560         break;
3561     case CK_SortPrev:
3562         panel_toggle_sort_order_prev (panel);
3563         break;
3564     case CK_SortNext:
3565         panel_toggle_sort_order_next (panel);
3566         break;
3567     case CK_SortReverse:
3568         panel->sort_info.reverse = !panel->sort_info.reverse;
3569         panel_set_sort_order (panel, panel->sort_field);
3570         break;
3571     case CK_SortByName:
3572         panel_set_sort_type_by_id (panel, "name");
3573         break;
3574     case CK_SortByExt:
3575         panel_set_sort_type_by_id (panel, "extension");
3576         break;
3577     case CK_SortBySize:
3578         panel_set_sort_type_by_id (panel, "size");
3579         break;
3580     case CK_SortByMTime:
3581         panel_set_sort_type_by_id (panel, "mtime");
3582         break;
3583     default:
3584         res = MSG_NOT_HANDLED;
3585         break;
3586     }
3587 
3588     return res;
3589 }
3590 
3591 /* --------------------------------------------------------------------------------------------- */
3592 
3593 static cb_ret_t
3594 panel_key (WPanel * panel, int key)
     /* [previous][next][first][last][top][bottom][index][help]  */
3595 {
3596     long command;
3597 
3598     if (is_abort_char (key))
3599     {
3600         stop_search (panel);
3601         return MSG_HANDLED;
3602     }
3603 
3604     if (panel->searching && ((key >= ' ' && key <= 255) || key == KEY_BACKSPACE))
3605     {
3606         do_search (panel, key);
3607         return MSG_HANDLED;
3608     }
3609 
3610     command = widget_lookup_key (WIDGET (panel), key);
3611     if (command != CK_IgnoreKey)
3612         return panel_execute_cmd (panel, command);
3613 
3614     if (panels_options.torben_fj_mode && key == ALT ('h'))
3615     {
3616         goto_middle_file (panel);
3617         return MSG_HANDLED;
3618     }
3619 
3620     if (!command_prompt && ((key >= ' ' && key <= 255) || key == KEY_BACKSPACE))
3621     {
3622         start_search (panel);
3623         do_search (panel, key);
3624         return MSG_HANDLED;
3625     }
3626 
3627     return MSG_NOT_HANDLED;
3628 }
3629 
3630 /* --------------------------------------------------------------------------------------------- */
3631 
3632 static cb_ret_t
3633 panel_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
3634 {
3635     WPanel *panel = PANEL (w);
3636     WDialog *h = DIALOG (w->owner);
3637     WButtonBar *bb;
3638 
3639     switch (msg)
3640     {
3641     case MSG_INIT:
3642         /* subscribe to "history_load" event */
3643         mc_event_add (h->event_group, MCEVENT_HISTORY_LOAD, panel_load_history, w, NULL);
3644         /* subscribe to "history_save" event */
3645         mc_event_add (h->event_group, MCEVENT_HISTORY_SAVE, panel_save_history, w, NULL);
3646         return MSG_HANDLED;
3647 
3648     case MSG_DRAW:
3649         /* Repaint everything, including frame and separator */
3650         widget_erase (w);
3651         show_dir (panel);
3652         panel_print_header (panel);
3653         adjust_top_file (panel);
3654         paint_dir (panel);
3655         mini_info_separator (panel);
3656         display_mini_info (panel);
3657         panel->dirty = 0;
3658         return MSG_HANDLED;
3659 
3660     case MSG_FOCUS:
3661         state_mark = -1;
3662         current_panel = panel;
3663         panel->active = 1;
3664 
3665         if (mc_chdir (panel->cwd_vpath) != 0)
3666         {
3667             char *cwd;
3668 
3669             cwd = vfs_path_to_str_flags (panel->cwd_vpath, 0, VPF_STRIP_PASSWORD);
3670             message (D_ERROR, MSG_ERROR, _("Cannot chdir to \"%s\"\n%s"),
3671                      cwd, unix_error_string (errno));
3672             g_free (cwd);
3673         }
3674         else
3675             subshell_chdir (panel->cwd_vpath);
3676 
3677         update_xterm_title_path ();
3678         select_item (panel);
3679 
3680         bb = find_buttonbar (h);
3681         midnight_set_buttonbar (bb);
3682         widget_draw (WIDGET (bb));
3683         return MSG_HANDLED;
3684 
3685     case MSG_UNFOCUS:
3686         /* Janne: look at this for the multiple panel options */
3687         stop_search (panel);
3688         panel->active = 0;
3689         unselect_item (panel);
3690         return MSG_HANDLED;
3691 
3692     case MSG_KEY:
3693         return panel_key (panel, parm);
3694 
3695     case MSG_ACTION:
3696         return panel_execute_cmd (panel, parm);
3697 
3698     case MSG_DESTROY:
3699         vfs_stamp_path (panel->cwd_vpath);
3700         /* unsubscribe from "history_load" event */
3701         mc_event_del (h->event_group, MCEVENT_HISTORY_LOAD, panel_load_history, w);
3702         /* unsubscribe from "history_save" event */
3703         mc_event_del (h->event_group, MCEVENT_HISTORY_SAVE, panel_save_history, w);
3704         panel_destroy (panel);
3705         free_my_statfs ();
3706         return MSG_HANDLED;
3707 
3708     default:
3709         return widget_default_callback (w, sender, msg, parm, data);
3710     }
3711 }
3712 
3713 /* --------------------------------------------------------------------------------------------- */
3714 /*                                     */
3715 /* Panel mouse events support routines */
3716 /*                                     */
3717 
3718 static void
3719 mouse_toggle_mark (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
3720 {
3721     do_mark_file (panel, MARK_DONT_MOVE);
3722     mouse_marking = selection (panel)->f.marked;
3723     mouse_mark_panel = current_panel;
3724 }
3725 
3726 /* --------------------------------------------------------------------------------------------- */
3727 
3728 static void
3729 mouse_set_mark (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
3730 {
3731     if (mouse_mark_panel == panel)
3732     {
3733         if (mouse_marking && !selection (panel)->f.marked)
3734             do_mark_file (panel, MARK_DONT_MOVE);
3735         else if (!mouse_marking && selection (panel)->f.marked)
3736             do_mark_file (panel, MARK_DONT_MOVE);
3737     }
3738 }
3739 
3740 /* --------------------------------------------------------------------------------------------- */
3741 
3742 static gboolean
3743 mark_if_marking (WPanel * panel, const mouse_event_t * event)
     /* [previous][next][first][last][top][bottom][index][help]  */
3744 {
3745     if ((event->buttons & GPM_B_RIGHT) != 0)
3746     {
3747         if (event->msg == MSG_MOUSE_DOWN)
3748             mouse_toggle_mark (panel);
3749         else
3750             mouse_set_mark (panel);
3751         return TRUE;
3752     }
3753 
3754     return FALSE;
3755 }
3756 
3757 /* --------------------------------------------------------------------------------------------- */
3758 /** Determine which column was clicked, and sort the panel on
3759  * that column, or reverse sort on that column if already
3760  * sorted on that column.
3761  */
3762 
3763 static void
3764 mouse_sort_col (WPanel * panel, int x)
     /* [previous][next][first][last][top][bottom][index][help]  */
3765 {
3766     int i = 0;
3767     GSList *format;
3768     const char *lc_sort_name = NULL;
3769     panel_field_t *col_sort_format = NULL;
3770 
3771     for (format = panel->format; format != NULL; format = g_slist_next (format))
3772     {
3773         format_item_t *fi = (format_item_t *) format->data;
3774 
3775         i += fi->field_len;
3776         if (x < i + 1)
3777         {
3778             /* found column */
3779             lc_sort_name = fi->title;
3780             break;
3781         }
3782     }
3783 
3784     if (lc_sort_name == NULL)
3785         return;
3786 
3787     for (i = 0; panel_fields[i].id != NULL; i++)
3788     {
3789         const char *title;
3790 
3791         title = panel_get_title_without_hotkey (panel_fields[i].title_hotkey);
3792         if (panel_fields[i].sort_routine != NULL && strcmp (title, lc_sort_name) == 0)
3793         {
3794             col_sort_format = &panel_fields[i];
3795             break;
3796         }
3797     }
3798 
3799     if (col_sort_format != NULL)
3800     {
3801         if (panel->sort_field == col_sort_format)
3802             /* reverse the sort if clicked column is already the sorted column */
3803             panel->sort_info.reverse = !panel->sort_info.reverse;
3804         else
3805             /* new sort is forced to be ascending */
3806             panel->sort_info.reverse = FALSE;
3807 
3808         panel_set_sort_order (panel, col_sort_format);
3809     }
3810 }
3811 
3812 /* --------------------------------------------------------------------------------------------- */
3813 
3814 static int
3815 panel_mouse_is_on_item (const WPanel * panel, int y, int x)
     /* [previous][next][first][last][top][bottom][index][help]  */
3816 {
3817     int last;
3818 
3819     if (y < 0)
3820         return (-1);
3821 
3822     last = panel->dir.len - 1;
3823     y += panel->top_file;
3824 
3825     if (y > last)
3826         return (-1);
3827 
3828     if (panel->list_cols > 1)
3829     {
3830         int width, lines;
3831 
3832         width = (WIDGET (panel)->cols - 2) / panel->list_cols;
3833         lines = panel_lines (panel);
3834         y += lines * (x / width);
3835     }
3836 
3837     return (y > last ? -1 : y);
3838 }
3839 
3840 /* --------------------------------------------------------------------------------------------- */
3841 
3842 static void
3843 panel_mouse_callback (Widget * w, mouse_msg_t msg, mouse_event_t * event)
     /* [previous][next][first][last][top][bottom][index][help]  */
3844 {
3845     WPanel *panel = PANEL (w);
3846     gboolean is_active;
3847 
3848     is_active = widget_is_active (w);
3849 
3850     switch (msg)
3851     {
3852     case MSG_MOUSE_DOWN:
3853         if (event->y == 0)
3854         {
3855             /* top frame */
3856             if (event->x == 1)
3857                 /* "<" button */
3858                 directory_history_prev (panel);
3859             else if (event->x == w->cols - 2)
3860                 /* ">" button */
3861                 directory_history_next (panel);
3862             else if (event->x >= w->cols - 5 && event->x <= w->cols - 3)
3863                 /* "^" button */
3864                 directory_history_list (panel);
3865             else if (event->x == w->cols - 6)
3866                 /* "." button show/hide hidden files */
3867                 send_message (midnight_dlg, NULL, MSG_ACTION, CK_ShowHidden, NULL);
3868             else
3869             {
3870                 /* no other events on 1st line, return MOU_UNHANDLED */
3871                 event->result.abort = TRUE;
3872                 /* avoid extra panel redraw */
3873                 panel->dirty = 0;
3874             }
3875             break;
3876         }
3877 
3878         if (event->y == 1)
3879         {
3880             /* sort on clicked column */
3881             mouse_sort_col (panel, event->x + 1);
3882             break;
3883         }
3884 
3885         if (!is_active)
3886             change_panel ();
3887         MC_FALLTHROUGH;
3888 
3889     case MSG_MOUSE_DRAG:
3890         {
3891             int my_index;
3892 
3893             my_index = panel_mouse_is_on_item (panel, event->y - 2, event->x);
3894             if (my_index >= 0)
3895             {
3896                 if (my_index != panel->selected)
3897                 {
3898                     unselect_item (panel);
3899                     panel->selected = my_index;
3900                     select_item (panel);
3901                 }
3902 
3903                 /* This one is new */
3904                 mark_if_marking (panel, event);
3905             }
3906         }
3907         break;
3908 
3909     case MSG_MOUSE_UP:
3910         break;
3911 
3912     case MSG_MOUSE_CLICK:
3913         if ((event->count & GPM_DOUBLE) != 0 && (event->buttons & GPM_B_LEFT) != 0 &&
3914             panel_mouse_is_on_item (panel, event->y - 2, event->x) >= 0)
3915             do_enter (panel);
3916         break;
3917 
3918     case MSG_MOUSE_MOVE:
3919         break;
3920 
3921     case MSG_MOUSE_SCROLL_UP:
3922         if (is_active)
3923         {
3924             if (panels_options.mouse_move_pages && panel->top_file > 0)
3925                 prev_page (panel);
3926             else                /* We are in first page */
3927                 move_up (panel);
3928         }
3929         break;
3930 
3931     case MSG_MOUSE_SCROLL_DOWN:
3932         if (is_active)
3933         {
3934             if (panels_options.mouse_move_pages
3935                 && panel->top_file + panel_items (panel) < panel->dir.len)
3936                 next_page (panel);
3937             else                /* We are in last page */
3938                 move_down (panel);
3939         }
3940         break;
3941 
3942     default:
3943         break;
3944     }
3945 
3946     if (panel->dirty)
3947         widget_draw (w);
3948 }
3949 
3950 /* --------------------------------------------------------------------------------------------- */
3951 
3952 static void
3953 reload_panelized (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
3954 {
3955     int i, j;
3956     dir_list *list = &panel->dir;
3957 
3958     /* refresh current VFS directory required for vfs_path_from_str() */
3959     (void) mc_chdir (panel->cwd_vpath);
3960 
3961     for (i = 0, j = 0; i < list->len; i++)
3962     {
3963         vfs_path_t *vpath;
3964 
3965         vpath = vfs_path_from_str (list->list[i].fname);
3966         if (mc_lstat (vpath, &list->list[i].st) != 0)
3967             g_free (list->list[i].fname);
3968         else
3969         {
3970             if (j != i)
3971                 list->list[j] = list->list[i];
3972             j++;
3973         }
3974         vfs_path_free (vpath);
3975     }
3976     if (j == 0)
3977         dir_list_init (list);
3978     else
3979         list->len = j;
3980 
3981     recalculate_panel_summary (panel);
3982 
3983     if (panel != current_panel)
3984         (void) mc_chdir (current_panel->cwd_vpath);
3985 }
3986 
3987 /* --------------------------------------------------------------------------------------------- */
3988 
3989 static void
3990 update_one_panel_widget (WPanel * panel, panel_update_flags_t flags, const char *current_file)
     /* [previous][next][first][last][top][bottom][index][help]  */
3991 {
3992     gboolean free_pointer;
3993     char *my_current_file = NULL;
3994 
3995     if ((flags & UP_RELOAD) != 0)
3996     {
3997         panel->is_panelized = FALSE;
3998         mc_setctl (panel->cwd_vpath, VFS_SETCTL_FLUSH, NULL);
3999         memset (&(panel->dir_stat), 0, sizeof (panel->dir_stat));
4000     }
4001 
4002     /* If current_file == -1 (an invalid pointer) then preserve selection */
4003     free_pointer = current_file == UP_KEEPSEL;
4004 
4005     if (free_pointer)
4006     {
4007         my_current_file = g_strdup (panel->dir.list[panel->selected].fname);
4008         current_file = my_current_file;
4009     }
4010 
4011     if (panel->is_panelized)
4012         reload_panelized (panel);
4013     else
4014         panel_reload (panel);
4015 
4016     try_to_select (panel, current_file);
4017     panel->dirty = 1;
4018 
4019     if (free_pointer)
4020         g_free (my_current_file);
4021 }
4022 
4023 /* --------------------------------------------------------------------------------------------- */
4024 
4025 static void
4026 update_one_panel (int which, panel_update_flags_t flags, const char *current_file)
     /* [previous][next][first][last][top][bottom][index][help]  */
4027 {
4028     if (get_panel_type (which) == view_listing)
4029     {
4030         WPanel *panel;
4031 
4032         panel = PANEL (get_panel_widget (which));
4033         if (panel->is_panelized)
4034             flags &= ~UP_RELOAD;
4035         update_one_panel_widget (panel, flags, current_file);
4036     }
4037 }
4038 
4039 /* --------------------------------------------------------------------------------------------- */
4040 
4041 static void
4042 do_select (WPanel * panel, int i)
     /* [previous][next][first][last][top][bottom][index][help]  */
4043 {
4044     if (i != panel->selected)
4045     {
4046         panel->dirty = 1;
4047         panel->selected = i;
4048         panel->top_file = panel->selected - (WIDGET (panel)->lines - 2) / 2;
4049         if (panel->top_file < 0)
4050             panel->top_file = 0;
4051     }
4052 }
4053 
4054 /* --------------------------------------------------------------------------------------------- */
4055 
4056 static void
4057 do_try_to_select (WPanel * panel, const char *name)
     /* [previous][next][first][last][top][bottom][index][help]  */
4058 {
4059     int i;
4060     char *subdir;
4061 
4062     if (name == NULL)
4063     {
4064         do_select (panel, 0);
4065         return;
4066     }
4067 
4068     /* We only want the last component of the directory,
4069      * and from this only the name without suffix.
4070      * Cut prefix if the panel is not panelized */
4071 
4072     if (panel->is_panelized)
4073         subdir = vfs_strip_suffix_from_filename (name);
4074     else
4075         subdir = vfs_strip_suffix_from_filename (x_basename (name));
4076 
4077     /* Search that subdir or filename without prefix (if not panelized panel), select it if found */
4078     for (i = 0; i < panel->dir.len; i++)
4079     {
4080         if (strcmp (subdir, panel->dir.list[i].fname) == 0)
4081         {
4082             do_select (panel, i);
4083             g_free (subdir);
4084             return;
4085         }
4086     }
4087 
4088     /* Try to select a file near the file that is missing */
4089     if (panel->selected >= panel->dir.len)
4090         do_select (panel, panel->dir.len - 1);
4091     g_free (subdir);
4092 }
4093 
4094 /* --------------------------------------------------------------------------------------------- */
4095 
4096 /* event callback */
4097 static gboolean
4098 event_update_panels (const gchar * event_group_name, const gchar * event_name,
     /* [previous][next][first][last][top][bottom][index][help]  */
4099                      gpointer init_data, gpointer data)
4100 {
4101     (void) event_group_name;
4102     (void) event_name;
4103     (void) init_data;
4104     (void) data;
4105 
4106     update_panels (UP_RELOAD, UP_KEEPSEL);
4107 
4108     return TRUE;
4109 }
4110 
4111 /* --------------------------------------------------------------------------------------------- */
4112 
4113 /* event callback */
4114 static gboolean
4115 panel_save_current_file_to_clip_file (const gchar * event_group_name, const gchar * event_name,
     /* [previous][next][first][last][top][bottom][index][help]  */
4116                                       gpointer init_data, gpointer data)
4117 {
4118     (void) event_group_name;
4119     (void) event_name;
4120     (void) init_data;
4121     (void) data;
4122 
4123     if (current_panel->marked == 0)
4124         mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_text_to_file",
4125                         (gpointer) selection (current_panel)->fname);
4126     else
4127     {
4128         int i;
4129         gboolean first = TRUE;
4130         char *flist = NULL;
4131 
4132         for (i = 0; i < current_panel->dir.len; i++)
4133             if (current_panel->dir.list[i].f.marked != 0)
4134             {                   /* Skip the unmarked ones */
4135                 if (first)
4136                 {
4137                     flist = g_strdup (current_panel->dir.list[i].fname);
4138                     first = FALSE;
4139                 }
4140                 else
4141                 {
4142                     /* Add empty lines after the file */
4143                     char *tmp;
4144 
4145                     tmp =
4146                         g_strconcat (flist, "\n", current_panel->dir.list[i].fname, (char *) NULL);
4147                     g_free (flist);
4148                     flist = tmp;
4149                 }
4150             }
4151 
4152         mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_text_to_file", (gpointer) flist);
4153         g_free (flist);
4154     }
4155     return TRUE;
4156 }
4157 
4158 /* --------------------------------------------------------------------------------------------- */
4159 
4160 static vfs_path_t *
4161 panel_recursive_cd_to_parent (const vfs_path_t * vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
4162 {
4163     vfs_path_t *cwd_vpath;
4164 
4165     cwd_vpath = vfs_path_clone (vpath);
4166 
4167     while (mc_chdir (cwd_vpath) < 0)
4168     {
4169         const char *panel_cwd_path;
4170         vfs_path_t *tmp_vpath;
4171 
4172         /* check if path contains only '/' */
4173         panel_cwd_path = vfs_path_as_str (cwd_vpath);
4174         if (panel_cwd_path != NULL && IS_PATH_SEP (panel_cwd_path[0]) && panel_cwd_path[1] == '\0')
4175             return NULL;
4176 
4177         tmp_vpath = vfs_path_vtokens_get (cwd_vpath, 0, -1);
4178         vfs_path_free (cwd_vpath);
4179         cwd_vpath =
4180             vfs_path_build_filename (PATH_SEP_STR, vfs_path_as_str (tmp_vpath), (char *) NULL);
4181         vfs_path_free (tmp_vpath);
4182     }
4183 
4184     return cwd_vpath;
4185 }
4186 
4187 /* --------------------------------------------------------------------------------------------- */
4188 
4189 static void
4190 panel_dir_list_callback (dir_list_cb_state_t state, void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
4191 {
4192     static int count = 0;
4193 
4194     (void) data;
4195 
4196     switch (state)
4197     {
4198     case DIR_OPEN:
4199         count = 0;
4200         break;
4201 
4202     case DIR_READ:
4203         count++;
4204         if ((count & 15) == 0)
4205             rotate_dash (TRUE);
4206         break;
4207 
4208     case DIR_CLOSE:
4209         rotate_dash (FALSE);
4210         break;
4211 
4212     default:
4213         g_assert_not_reached ();
4214     }
4215 }
4216 
4217 /* --------------------------------------------------------------------------------------------- */
4218 /*** public functions ****************************************************************************/
4219 /* --------------------------------------------------------------------------------------------- */
4220 
4221 void
4222 try_to_select (WPanel * panel, const char *name)
     /* [previous][next][first][last][top][bottom][index][help]  */
4223 {
4224     do_try_to_select (panel, name);
4225     select_item (panel);
4226 }
4227 
4228 /* --------------------------------------------------------------------------------------------- */
4229 
4230 void
4231 panel_clean_dir (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
4232 {
4233     panel->top_file = 0;
4234     panel->selected = 0;
4235     panel->marked = 0;
4236     panel->dirs_marked = 0;
4237     panel->total = 0;
4238     panel->searching = FALSE;
4239     panel->is_panelized = FALSE;
4240     panel->dirty = 1;
4241     panel->content_shift = -1;
4242     panel->max_shift = -1;
4243 
4244     dir_list_free_list (&panel->dir);
4245 }
4246 
4247 /* --------------------------------------------------------------------------------------------- */
4248 /**
4249  * Set Up panel's current dir object
4250  *
4251  * @param panel panel object
4252  * @param path_str string contain path
4253  */
4254 
4255 void
4256 panel_set_cwd (WPanel * panel, const vfs_path_t * vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
4257 {
4258     if (vpath != panel->cwd_vpath)      /* check if new vpath is not the panel->cwd_vpath object */
4259     {
4260         vfs_path_free (panel->cwd_vpath);
4261         panel->cwd_vpath = vfs_path_clone (vpath);
4262     }
4263 }
4264 
4265 /* --------------------------------------------------------------------------------------------- */
4266 /**
4267  * Set Up panel's last working dir object
4268  *
4269  * @param panel panel object
4270  * @param path_str string contain path
4271  */
4272 
4273 void
4274 panel_set_lwd (WPanel * panel, const vfs_path_t * vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
4275 {
4276     if (vpath != panel->lwd_vpath)      /* check if new vpath is not the panel->lwd_vpath object */
4277     {
4278         vfs_path_free (panel->lwd_vpath);
4279         panel->lwd_vpath = vfs_path_clone (vpath);
4280     }
4281 }
4282 
4283 /* --------------------------------------------------------------------------------------------- */
4284 /**
4285  * Creatie an empty panel with specified size.
4286  *
4287  * @param panel_name name of panel for setup retieving
4288  *
4289  * @return new instance of WPanel
4290  */
4291 
4292 WPanel *
4293 panel_sized_empty_new (const char *panel_name, int y, int x, int lines, int cols)
     /* [previous][next][first][last][top][bottom][index][help]  */
4294 {
4295     WPanel *panel;
4296     Widget *w;
4297     char *section;
4298     int i, err;
4299 
4300     panel = g_new0 (WPanel, 1);
4301     w = WIDGET (panel);
4302     widget_init (w, y, x, lines, cols, panel_callback, panel_mouse_callback);
4303     w->options |= WOP_SELECTABLE | WOP_TOP_SELECT;
4304     w->keymap = panel_map;
4305 
4306     panel->dir.size = DIR_LIST_MIN_SIZE;
4307     panel->dir.list = g_new (file_entry_t, panel->dir.size);
4308     panel->dir.len = 0;
4309     panel->dir.callback = panel_dir_list_callback;
4310 
4311     panel->list_cols = 1;
4312     panel->brief_cols = 2;
4313     panel->dirty = 1;
4314     panel->content_shift = -1;
4315     panel->max_shift = -1;
4316 
4317     panel->list_format = list_full;
4318     panel->user_format = g_strdup (DEFAULT_USER_FORMAT);
4319 
4320     for (i = 0; i < LIST_FORMATS; i++)
4321         panel->user_status_format[i] = g_strdup (DEFAULT_USER_FORMAT);
4322 
4323 #ifdef HAVE_CHARSET
4324     panel->codepage = SELECT_CHARSET_NO_TRANSLATE;
4325 #endif
4326 
4327     panel->frame_size = frame_half;
4328 
4329     panel->panel_name = g_strdup (panel_name);
4330     panel->hist_name = g_strconcat ("Dir Hist ", panel->panel_name, (char *) NULL);
4331     /* directories history will be get later */
4332 
4333     section = g_strconcat ("Temporal:", panel->panel_name, (char *) NULL);
4334     if (!mc_config_has_group (mc_global.main_config, section))
4335     {
4336         g_free (section);
4337         section = g_strdup (panel->panel_name);
4338     }
4339     panel_load_setup (panel, section);
4340     g_free (section);
4341 
4342     /* Load format strings */
4343     err = set_panel_formats (panel);
4344     if (err != 0)
4345         set_panel_formats (panel);
4346 
4347     return panel;
4348 }
4349 
4350 /* --------------------------------------------------------------------------------------------- */
4351 /**
4352  * Panel creation for specified size and directory.
4353  *
4354  * @param panel_name name of panel for setup retieving
4355  * @param y y coordinate of top-left corner
4356  * @param x x coordinate of top-left corner
4357  * @param lines vertical size
4358  * @param cols horizontal size
4359  * @param vpath working panel directory. If NULL then current directory is used
4360  *
4361  * @return new instance of WPanel
4362  */
4363 
4364 WPanel *
4365 panel_sized_with_dir_new (const char *panel_name, int y, int x, int lines, int cols,
     /* [previous][next][first][last][top][bottom][index][help]  */
4366                           const vfs_path_t * vpath)
4367 {
4368     WPanel *panel;
4369     char *curdir = NULL;
4370 #ifdef HAVE_CHARSET
4371     const vfs_path_element_t *path_element;
4372 #endif
4373 
4374     panel = panel_sized_empty_new (panel_name, y, x, lines, cols);
4375 
4376     if (vpath != NULL)
4377     {
4378         curdir = _vfs_get_cwd ();
4379         panel_set_cwd (panel, vpath);
4380     }
4381     else
4382     {
4383         vfs_setup_cwd ();
4384         panel->cwd_vpath = vfs_path_clone (vfs_get_raw_current_dir ());
4385     }
4386 
4387     panel_set_lwd (panel, vfs_get_raw_current_dir ());
4388 
4389 #ifdef HAVE_CHARSET
4390     path_element = vfs_path_get_by_index (panel->cwd_vpath, -1);
4391     if (path_element->encoding != NULL)
4392         panel->codepage = get_codepage_index (path_element->encoding);
4393 #endif
4394 
4395     if (mc_chdir (panel->cwd_vpath) != 0)
4396     {
4397 #ifdef HAVE_CHARSET
4398         panel->codepage = SELECT_CHARSET_NO_TRANSLATE;
4399 #endif
4400         vfs_setup_cwd ();
4401         vfs_path_free (panel->cwd_vpath);
4402         panel->cwd_vpath = vfs_path_clone (vfs_get_raw_current_dir ());
4403     }
4404 
4405     /* Load the default format */
4406     if (!dir_list_load (&panel->dir, panel->cwd_vpath, panel->sort_field->sort_routine,
4407                         &panel->sort_info, panel->filter))
4408         message (D_ERROR, MSG_ERROR, _("Cannot read directory contents"));
4409 
4410     /* Restore old right path */
4411     if (curdir != NULL)
4412     {
4413         vfs_path_t *tmp_vpath;
4414         int err;
4415 
4416         tmp_vpath = vfs_path_from_str (curdir);
4417         mc_chdir (tmp_vpath);
4418         vfs_path_free (tmp_vpath);
4419         (void) err;
4420     }
4421     g_free (curdir);
4422 
4423     return panel;
4424 }
4425 
4426 /* --------------------------------------------------------------------------------------------- */
4427 
4428 void
4429 panel_reload (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
4430 {
4431     struct stat current_stat;
4432     vfs_path_t *cwd_vpath;
4433 
4434     if (panels_options.fast_reload && stat (vfs_path_as_str (panel->cwd_vpath), &current_stat) == 0
4435         && current_stat.st_ctime == panel->dir_stat.st_ctime
4436         && current_stat.st_mtime == panel->dir_stat.st_mtime)
4437         return;
4438 
4439     cwd_vpath = panel_recursive_cd_to_parent (panel->cwd_vpath);
4440     vfs_path_free (panel->cwd_vpath);
4441 
4442     if (cwd_vpath == NULL)
4443     {
4444         panel->cwd_vpath = vfs_path_from_str (PATH_SEP_STR);
4445         panel_clean_dir (panel);
4446         dir_list_init (&panel->dir);
4447         return;
4448     }
4449 
4450     panel->cwd_vpath = cwd_vpath;
4451     memset (&(panel->dir_stat), 0, sizeof (panel->dir_stat));
4452     show_dir (panel);
4453 
4454     if (!dir_list_reload (&panel->dir, panel->cwd_vpath, panel->sort_field->sort_routine,
4455                           &panel->sort_info, panel->filter))
4456         message (D_ERROR, MSG_ERROR, _("Cannot read directory contents"));
4457 
4458     panel->dirty = 1;
4459     if (panel->selected >= panel->dir.len)
4460         do_select (panel, panel->dir.len - 1);
4461 
4462     recalculate_panel_summary (panel);
4463 }
4464 
4465 /* --------------------------------------------------------------------------------------------- */
4466 /* Switches the panel to the mode specified in the format           */
4467 /* Seting up both format and status string. Return: 0 - on success; */
4468 /* 1 - format error; 2 - status error; 3 - errors in both formats.  */
4469 
4470 int
4471 set_panel_formats (WPanel * p)
     /* [previous][next][first][last][top][bottom][index][help]  */
4472 {
4473     GSList *form;
4474     char *err = NULL;
4475     int retcode = 0;
4476 
4477     form = use_display_format (p, panel_format (p), &err, FALSE);
4478 
4479     if (err != NULL)
4480     {
4481         g_free (err);
4482         retcode = 1;
4483     }
4484     else
4485     {
4486         g_slist_free_full (p->format, (GDestroyNotify) format_item_free);
4487         p->format = form;
4488     }
4489 
4490     if (panels_options.show_mini_info)
4491     {
4492         form = use_display_format (p, mini_status_format (p), &err, TRUE);
4493 
4494         if (err != NULL)
4495         {
4496             g_free (err);
4497             retcode += 2;
4498         }
4499         else
4500         {
4501             g_slist_free_full (p->status_format, (GDestroyNotify) format_item_free);
4502             p->status_format = form;
4503         }
4504     }
4505 
4506     panel_update_cols (WIDGET (p), p->frame_size);
4507 
4508     if (retcode)
4509         message (D_ERROR, _("Warning"),
4510                  _("User supplied format looks invalid, reverting to default."));
4511     if (retcode & 0x01)
4512     {
4513         g_free (p->user_format);
4514         p->user_format = g_strdup (DEFAULT_USER_FORMAT);
4515     }
4516     if (retcode & 0x02)
4517     {
4518         g_free (p->user_status_format[p->list_format]);
4519         p->user_status_format[p->list_format] = g_strdup (DEFAULT_USER_FORMAT);
4520     }
4521 
4522     return retcode;
4523 }
4524 
4525 /* --------------------------------------------------------------------------------------------- */
4526 
4527 /* Select current item and readjust the panel */
4528 void
4529 select_item (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
4530 {
4531     adjust_top_file (panel);
4532 
4533     panel->dirty = 1;
4534 
4535     execute_hooks (select_file_hook);
4536 }
4537 
4538 /* --------------------------------------------------------------------------------------------- */
4539 /** Clears all files in the panel, used only when one file was marked */
4540 void
4541 unmark_files (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
4542 {
4543     if (panel->marked != 0)
4544     {
4545         int i;
4546 
4547         for (i = 0; i < panel->dir.len; i++)
4548             file_mark (panel, i, 0);
4549 
4550         panel->dirs_marked = 0;
4551         panel->marked = 0;
4552         panel->total = 0;
4553     }
4554 }
4555 
4556 /* --------------------------------------------------------------------------------------------- */
4557 /** Recalculate the panels summary information, used e.g. when marked
4558    files might have been removed by an external command */
4559 
4560 void
4561 recalculate_panel_summary (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
4562 {
4563     int i;
4564 
4565     panel->marked = 0;
4566     panel->dirs_marked = 0;
4567     panel->total = 0;
4568 
4569     for (i = 0; i < panel->dir.len; i++)
4570         if (panel->dir.list[i].f.marked)
4571         {
4572             /* do_file_mark will return immediately if newmark == oldmark.
4573                So we have to first unmark it to get panel's summary information
4574                updated. (Norbert) */
4575             panel->dir.list[i].f.marked = 0;
4576             do_file_mark (panel, i, 1);
4577         }
4578 }
4579 
4580 /* --------------------------------------------------------------------------------------------- */
4581 /** This routine marks a file or a directory */
4582 
4583 void
4584 do_file_mark (WPanel * panel, int idx, int mark)
     /* [previous][next][first][last][top][bottom][index][help]  */
4585 {
4586     if (panel->dir.list[idx].f.marked == mark)
4587         return;
4588 
4589     /* Only '..' can't be marked, '.' isn't visible */
4590     if (DIR_IS_DOTDOT (panel->dir.list[idx].fname))
4591         return;
4592 
4593     file_mark (panel, idx, mark);
4594     if (panel->dir.list[idx].f.marked)
4595     {
4596         panel->marked++;
4597 
4598         if (S_ISDIR (panel->dir.list[idx].st.st_mode))
4599         {
4600             if (panel->dir.list[idx].f.dir_size_computed)
4601                 panel->total += (uintmax_t) panel->dir.list[idx].st.st_size;
4602             panel->dirs_marked++;
4603         }
4604         else
4605             panel->total += (uintmax_t) panel->dir.list[idx].st.st_size;
4606 
4607         set_colors (panel);
4608     }
4609     else
4610     {
4611         if (S_ISDIR (panel->dir.list[idx].st.st_mode))
4612         {
4613             if (panel->dir.list[idx].f.dir_size_computed)
4614                 panel->total -= (uintmax_t) panel->dir.list[idx].st.st_size;
4615             panel->dirs_marked--;
4616         }
4617         else
4618             panel->total -= (uintmax_t) panel->dir.list[idx].st.st_size;
4619 
4620         panel->marked--;
4621     }
4622 }
4623 
4624 /* --------------------------------------------------------------------------------------------- */
4625 /**
4626  * Changes the current directory of the panel.
4627  * Record change in the directory history.
4628  */
4629 gboolean
4630 do_panel_cd (WPanel * panel, const vfs_path_t * new_dir_vpath, enum cd_enum cd_type)
     /* [previous][next][first][last][top][bottom][index][help]  */
4631 {
4632     gboolean r;
4633 
4634     r = _do_panel_cd (panel, new_dir_vpath, cd_type);
4635     if (r)
4636         directory_history_add (panel, panel->cwd_vpath);
4637     return r;
4638 }
4639 
4640 /* --------------------------------------------------------------------------------------------- */
4641 
4642 void
4643 file_mark (WPanel * panel, int lc_index, int val)
     /* [previous][next][first][last][top][bottom][index][help]  */
4644 {
4645     if (panel->dir.list[lc_index].f.marked != val)
4646     {
4647         panel->dir.list[lc_index].f.marked = val;
4648         panel->dirty = 1;
4649     }
4650 }
4651 
4652 /* --------------------------------------------------------------------------------------------- */
4653 
4654 void
4655 panel_re_sort (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
4656 {
4657     char *filename;
4658     int i;
4659 
4660     if (panel == NULL)
4661         return;
4662 
4663     filename = g_strdup (selection (panel)->fname);
4664     unselect_item (panel);
4665     dir_list_sort (&panel->dir, panel->sort_field->sort_routine, &panel->sort_info);
4666     panel->selected = -1;
4667 
4668     for (i = panel->dir.len; i != 0; i--)
4669         if (strcmp (panel->dir.list[i - 1].fname, filename) == 0)
4670         {
4671             panel->selected = i - 1;
4672             break;
4673         }
4674 
4675     g_free (filename);
4676     panel->top_file = panel->selected - panel_items (panel) / 2;
4677     select_item (panel);
4678     panel->dirty = 1;
4679 }
4680 
4681 /* --------------------------------------------------------------------------------------------- */
4682 
4683 void
4684 panel_set_sort_order (WPanel * panel, const panel_field_t * sort_order)
     /* [previous][next][first][last][top][bottom][index][help]  */
4685 {
4686     if (sort_order == NULL)
4687         return;
4688 
4689     panel->sort_field = sort_order;
4690 
4691     /* The directory is already sorted, we have to load the unsorted stuff */
4692     if (sort_order->sort_routine == (GCompareFunc) unsorted)
4693     {
4694         char *current_file;
4695 
4696         current_file = g_strdup (panel->dir.list[panel->selected].fname);
4697         panel_reload (panel);
4698         try_to_select (panel, current_file);
4699         g_free (current_file);
4700     }
4701     panel_re_sort (panel);
4702 }
4703 
4704 /* --------------------------------------------------------------------------------------------- */
4705 
4706 #ifdef HAVE_CHARSET
4707 
4708 /**
4709  * Change panel encoding.
4710  * @param panel WPanel object
4711  */
4712 
4713 void
4714 panel_change_encoding (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
4715 {
4716     const char *encoding = NULL;
4717     char *errmsg;
4718     int r;
4719 
4720     r = select_charset (-1, -1, panel->codepage, FALSE);
4721 
4722     if (r == SELECT_CHARSET_CANCEL)
4723         return;                 /* Cancel */
4724 
4725     panel->codepage = r;
4726 
4727     if (panel->codepage == SELECT_CHARSET_NO_TRANSLATE)
4728     {
4729         /* No translation */
4730         vfs_path_t *cd_path_vpath;
4731 
4732         g_free (init_translation_table (mc_global.display_codepage, mc_global.display_codepage));
4733         cd_path_vpath = remove_encoding_from_path (panel->cwd_vpath);
4734         do_panel_cd (panel, cd_path_vpath, cd_parse_command);
4735         show_dir (panel);
4736         vfs_path_free (cd_path_vpath);
4737         return;
4738     }
4739 
4740     errmsg = init_translation_table (panel->codepage, mc_global.display_codepage);
4741     if (errmsg != NULL)
4742     {
4743         message (D_ERROR, MSG_ERROR, "%s", errmsg);
4744         g_free (errmsg);
4745         return;
4746     }
4747 
4748     encoding = get_codepage_id (panel->codepage);
4749     if (encoding != NULL)
4750     {
4751         vfs_path_change_encoding (panel->cwd_vpath, encoding);
4752 
4753         if (!do_panel_cd (panel, panel->cwd_vpath, cd_parse_command))
4754             message (D_ERROR, MSG_ERROR, _("Cannot chdir to \"%s\""),
4755                      vfs_path_as_str (panel->cwd_vpath));
4756     }
4757 }
4758 
4759 /* --------------------------------------------------------------------------------------------- */
4760 
4761 /**
4762  * Remove encode info from last path element.
4763  *
4764  */
4765 vfs_path_t *
4766 remove_encoding_from_path (const vfs_path_t * vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
4767 {
4768     vfs_path_t *ret_vpath;
4769     GString *tmp_conv;
4770     int indx;
4771 
4772     ret_vpath = vfs_path_new ();
4773 
4774     tmp_conv = g_string_new ("");
4775 
4776     for (indx = 0; indx < vfs_path_elements_count (vpath); indx++)
4777     {
4778         GIConv converter;
4779         vfs_path_element_t *path_element;
4780 
4781         path_element = vfs_path_element_clone (vfs_path_get_by_index (vpath, indx));
4782 
4783         if (path_element->encoding == NULL)
4784         {
4785             vfs_path_add_element (ret_vpath, path_element);
4786             continue;
4787         }
4788 
4789         converter = str_crt_conv_to (path_element->encoding);
4790         if (converter == INVALID_CONV)
4791         {
4792             vfs_path_add_element (ret_vpath, path_element);
4793             continue;
4794         }
4795 
4796         MC_PTR_FREE (path_element->encoding);
4797 
4798         str_vfs_convert_from (converter, path_element->path, tmp_conv);
4799 
4800         g_free (path_element->path);
4801         path_element->path = g_strndup (tmp_conv->str, tmp_conv->len);
4802 
4803         g_string_set_size (tmp_conv, 0);
4804 
4805         str_close_conv (converter);
4806         str_close_conv (path_element->dir.converter);
4807         path_element->dir.converter = INVALID_CONV;
4808         vfs_path_add_element (ret_vpath, path_element);
4809     }
4810     g_string_free (tmp_conv, TRUE);
4811     return ret_vpath;
4812 }
4813 #endif /* HAVE_CHARSET */
4814 
4815 /* --------------------------------------------------------------------------------------------- */
4816 
4817 /**
4818  * This routine reloads the directory in both panels. It tries to
4819  * select current_file in current_panel and other_file in other_panel.
4820  * If current_file == -1 then it automatically sets current_file and
4821  * other_file to the currently selected files in the panels.
4822  *
4823  * If flags has the UP_ONLY_CURRENT bit toggled on, then it
4824  * will not reload the other panel.
4825  *
4826  * @param flags for reload panel
4827  * @param current_file name of the current file
4828  */
4829 
4830 void
4831 update_panels (panel_update_flags_t flags, const char *current_file)
     /* [previous][next][first][last][top][bottom][index][help]  */
4832 {
4833     WPanel *panel;
4834 
4835     /* first, update other panel... */
4836     if ((flags & UP_ONLY_CURRENT) == 0)
4837         update_one_panel (get_other_index (), flags, UP_KEEPSEL);
4838     /* ...then current one */
4839     update_one_panel (get_current_index (), flags, current_file);
4840 
4841     if (get_current_type () == view_listing)
4842         panel = PANEL (get_panel_widget (get_current_index ()));
4843     else
4844         panel = PANEL (get_panel_widget (get_other_index ()));
4845 
4846     if (!panel->is_panelized)
4847         (void) mc_chdir (panel->cwd_vpath);
4848 }
4849 
4850 /* --------------------------------------------------------------------------------------------- */
4851 
4852 gsize
4853 panel_get_num_of_sortable_fields (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
4854 {
4855     gsize ret = 0, lc_index;
4856 
4857     for (lc_index = 0; panel_fields[lc_index].id != NULL; lc_index++)
4858         if (panel_fields[lc_index].is_user_choice)
4859             ret++;
4860     return ret;
4861 }
4862 
4863 /* --------------------------------------------------------------------------------------------- */
4864 
4865 char **
4866 panel_get_sortable_fields (gsize * array_size)
     /* [previous][next][first][last][top][bottom][index][help]  */
4867 {
4868     char **ret;
4869     gsize lc_index, i;
4870 
4871     lc_index = panel_get_num_of_sortable_fields ();
4872 
4873     ret = g_try_new0 (char *, lc_index + 1);
4874     if (ret == NULL)
4875         return NULL;
4876 
4877     if (array_size != NULL)
4878         *array_size = lc_index;
4879 
4880     lc_index = 0;
4881 
4882     for (i = 0; panel_fields[i].id != NULL; i++)
4883         if (panel_fields[i].is_user_choice)
4884             ret[lc_index++] = g_strdup (_(panel_fields[i].title_hotkey));
4885 
4886     return ret;
4887 }
4888 
4889 /* --------------------------------------------------------------------------------------------- */
4890 
4891 const panel_field_t *
4892 panel_get_field_by_id (const char *name)
     /* [previous][next][first][last][top][bottom][index][help]  */
4893 {
4894     gsize lc_index;
4895 
4896     for (lc_index = 0; panel_fields[lc_index].id != NULL; lc_index++)
4897         if (panel_fields[lc_index].id != NULL && strcmp (name, panel_fields[lc_index].id) == 0)
4898             return &panel_fields[lc_index];
4899 
4900     return NULL;
4901 }
4902 
4903 /* --------------------------------------------------------------------------------------------- */
4904 
4905 const panel_field_t *
4906 panel_get_field_by_title_hotkey (const char *name)
     /* [previous][next][first][last][top][bottom][index][help]  */
4907 {
4908     gsize lc_index;
4909 
4910     for (lc_index = 0; panel_fields[lc_index].id != NULL; lc_index++)
4911         if (panel_fields[lc_index].title_hotkey != NULL &&
4912             strcmp (name, _(panel_fields[lc_index].title_hotkey)) == 0)
4913             return &panel_fields[lc_index];
4914 
4915     return NULL;
4916 }
4917 
4918 /* --------------------------------------------------------------------------------------------- */
4919 
4920 const panel_field_t *
4921 panel_get_field_by_title (const char *name)
     /* [previous][next][first][last][top][bottom][index][help]  */
4922 {
4923     gsize lc_index;
4924 
4925     for (lc_index = 0; panel_fields[lc_index].id != NULL; lc_index++)
4926     {
4927         const char *title;
4928 
4929         title = panel_get_title_without_hotkey (panel_fields[lc_index].title_hotkey);
4930         if (strcmp (title, name) == 0)
4931             return &panel_fields[lc_index];
4932     }
4933 
4934     return NULL;
4935 }
4936 
4937 /* --------------------------------------------------------------------------------------------- */
4938 
4939 gsize
4940 panel_get_num_of_user_possible_fields (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
4941 {
4942     gsize ret = 0, lc_index;
4943 
4944     for (lc_index = 0; panel_fields[lc_index].id != NULL; lc_index++)
4945         if (panel_fields[lc_index].use_in_user_format)
4946             ret++;
4947 
4948     return ret;
4949 }
4950 
4951 /* --------------------------------------------------------------------------------------------- */
4952 
4953 char **
4954 panel_get_user_possible_fields (gsize * array_size)
     /* [previous][next][first][last][top][bottom][index][help]  */
4955 {
4956     char **ret;
4957     gsize lc_index, i;
4958 
4959     lc_index = panel_get_num_of_user_possible_fields ();
4960 
4961     ret = g_try_new0 (char *, lc_index + 1);
4962     if (ret == NULL)
4963         return NULL;
4964 
4965     if (array_size != NULL)
4966         *array_size = lc_index;
4967 
4968     lc_index = 0;
4969 
4970     for (i = 0; panel_fields[i].id != NULL; i++)
4971         if (panel_fields[i].use_in_user_format)
4972             ret[lc_index++] = g_strdup (_(panel_fields[i].title_hotkey));
4973 
4974     return ret;
4975 }
4976 
4977 /* --------------------------------------------------------------------------------------------- */
4978 
4979 void
4980 panel_init (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
4981 {
4982     panel_sort_up_char = mc_skin_get ("widget-panel", "sort-up-char", "'");
4983     panel_sort_down_char = mc_skin_get ("widget-panel", "sort-down-char", ".");
4984     panel_hiddenfiles_show_char = mc_skin_get ("widget-panel", "hiddenfiles-show-char", ".");
4985     panel_hiddenfiles_hide_char = mc_skin_get ("widget-panel", "hiddenfiles-hide-char", ".");
4986     panel_history_prev_item_char = mc_skin_get ("widget-panel", "history-prev-item-char", "<");
4987     panel_history_next_item_char = mc_skin_get ("widget-panel", "history-next-item-char", ">");
4988     panel_history_show_list_char = mc_skin_get ("widget-panel", "history-show-list-char", "^");
4989     panel_filename_scroll_left_char =
4990         mc_skin_get ("widget-panel", "filename-scroll-left-char", "{");
4991     panel_filename_scroll_right_char =
4992         mc_skin_get ("widget-panel", "filename-scroll-right-char", "}");
4993 
4994     mc_event_add (MCEVENT_GROUP_FILEMANAGER, "update_panels", event_update_panels, NULL, NULL);
4995     mc_event_add (MCEVENT_GROUP_FILEMANAGER, "panel_save_current_file_to_clip_file",
4996                   panel_save_current_file_to_clip_file, NULL, NULL);
4997 }
4998 
4999 /* --------------------------------------------------------------------------------------------- */
5000 
5001 void
5002 panel_deinit (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
5003 {
5004     g_free (panel_sort_up_char);
5005     g_free (panel_sort_down_char);
5006     g_free (panel_hiddenfiles_show_char);
5007     g_free (panel_hiddenfiles_hide_char);
5008     g_free (panel_history_prev_item_char);
5009     g_free (panel_history_next_item_char);
5010     g_free (panel_history_show_list_char);
5011     g_free (panel_filename_scroll_left_char);
5012     g_free (panel_filename_scroll_right_char);
5013 }
5014 
5015 /* --------------------------------------------------------------------------------------------- */
5016 
5017 gboolean
5018 do_cd (const vfs_path_t * new_dir_vpath, enum cd_enum exact)
     /* [previous][next][first][last][top][bottom][index][help]  */
5019 {
5020     gboolean res;
5021     const vfs_path_t *_new_dir_vpath = new_dir_vpath;
5022 
5023     if (current_panel->is_panelized)
5024     {
5025         size_t new_vpath_len;
5026 
5027         new_vpath_len = vfs_path_len (new_dir_vpath);
5028         if (vfs_path_equal_len (new_dir_vpath, panelized_panel.root_vpath, new_vpath_len))
5029             _new_dir_vpath = panelized_panel.root_vpath;
5030     }
5031 
5032     res = do_panel_cd (current_panel, _new_dir_vpath, exact);
5033 
5034 #ifdef HAVE_CHARSET
5035     if (res)
5036     {
5037         const vfs_path_element_t *path_element;
5038 
5039         path_element = vfs_path_get_by_index (current_panel->cwd_vpath, -1);
5040         if (path_element->encoding != NULL)
5041             current_panel->codepage = get_codepage_index (path_element->encoding);
5042         else
5043             current_panel->codepage = SELECT_CHARSET_NO_TRANSLATE;
5044     }
5045 #endif /* HAVE_CHARSET */
5046 
5047     return res;
5048 }
5049 
5050 /* --------------------------------------------------------------------------------------------- */

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