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