root/src/filemanager/panel.c

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

DEFINITIONS

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

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