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_dialog
  78. panel_select_unselect_files
  79. panel_select_files
  80. panel_unselect_files
  81. panel_select_invert_files
  82. panel_do_set_filter
  83. do_search
  84. start_search
  85. stop_search
  86. do_enter_on_file_entry
  87. do_enter
  88. panel_cycle_listing_format
  89. chdir_other_panel
  90. panel_sync_other
  91. chdir_to_readlink
  92. panel_get_format_field_index_by_name
  93. panel_get_sortable_field_by_format
  94. panel_toggle_sort_order_prev
  95. panel_toggle_sort_order_next
  96. panel_select_sort_order
  97. panel_content_scroll_left
  98. panel_content_scroll_right
  99. panel_set_sort_type_by_id
  100. get_parent_dir_name
  101. subshell_chdir
  102. panel_do_cd_int
  103. directory_history_next
  104. directory_history_prev
  105. directory_history_list
  106. panel_execute_cmd
  107. panel_key
  108. panel_callback
  109. mouse_toggle_mark
  110. mouse_set_mark
  111. mark_if_marking
  112. mouse_sort_col
  113. panel_mouse_is_on_item
  114. panel_mouse_callback
  115. reload_panelized
  116. update_one_panel_widget
  117. update_one_panel
  118. do_select
  119. do_try_to_select
  120. event_update_panels
  121. panel_save_current_file_to_clip_file
  122. panel_recursive_cd_to_parent
  123. panel_dir_list_callback
  124. try_to_select
  125. panel_clean_dir
  126. panel_set_cwd
  127. panel_set_lwd
  128. panel_sized_empty_new
  129. panel_sized_with_dir_new
  130. panel_reload
  131. set_panel_formats
  132. panel_set_filter
  133. select_item
  134. unmark_files
  135. recalculate_panel_summary
  136. do_file_mark
  137. panel_do_cd
  138. file_mark
  139. panel_re_sort
  140. panel_set_sort_order
  141. panel_change_encoding
  142. remove_encoding_from_path
  143. update_panels
  144. panel_get_num_of_sortable_fields
  145. panel_get_sortable_fields
  146. panel_get_field_by_id
  147. panel_get_field_by_title_hotkey
  148. panel_get_field_by_title
  149. panel_get_num_of_user_possible_fields
  150. panel_get_user_possible_fields
  151. panel_init
  152. panel_deinit
  153. panel_cd

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