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-2021
   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/keymap.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->str, fe->fname->len);
 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->str))
 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->str))
 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->str,
1014                                  (char *) NULL);
1015         len = mc_readlink (lc_link_vpath, link_target, MC_MAXPATHLEN - 1);
1016         vfs_path_free (lc_link_vpath, TRUE);
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->str))
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, TRUE);
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, TRUE);
1510     vfs_path_free (p->cwd_vpath, TRUE);
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, TRUE);
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->str);
2010             panel_cd (panel, vpath, cd_exact);
2011             vfs_path_free (vpath, TRUE);
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     GString *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->str));
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->str) || S_ISDIR (file_entry->st.st_mode))
2077             continue;
2078 
2079         if (!mc_search_run (search, file_entry->fname->str, 0, file_entry->fname->len, 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         GString *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->str))
2276             fname = mc_g_string_dup (fname);
2277         else
2278         {
2279             char *fname2;
2280 
2281             fname2 =
2282                 mc_build_filename (vfs_path_as_str (panelized_panel.root_vpath), fname->str,
2283                                    (char *) NULL);
2284 
2285             fname = g_string_new (fname2);
2286             g_free (fname2);
2287         }
2288 
2289         bname = x_basename (fname->str);
2290 
2291         if (bname == fname->str)
2292             dname_vpath = vfs_path_from_str (".");
2293         else
2294         {
2295             g_string_truncate (fname, bname - fname->str);
2296             dname_vpath = vfs_path_from_str (fname->str);
2297         }
2298 
2299         panel_cd (panel, dname_vpath, cd_exact);
2300         try_to_select (panel, bname);
2301 
2302         vfs_path_free (dname_vpath, TRUE);
2303         g_string_free (fname, TRUE);
2304     }
2305 }
2306 
2307 /* --------------------------------------------------------------------------------------------- */
2308 
2309 static void
2310 next_page (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
2311 {
2312     int items;
2313 
2314     if (panel->selected == panel->dir.len - 1)
2315         return;
2316 
2317     unselect_item (panel);
2318     items = panel_items (panel);
2319     if (panel->top_file > panel->dir.len - 2 * items)
2320         items = panel->dir.len - items - panel->top_file;
2321     if (panel->top_file + items < 0)
2322         items = -panel->top_file;
2323     if (items == 0)
2324         panel->selected = panel->dir.len - 1;
2325     else
2326         panel->selected += items;
2327     panel->top_file += items;
2328 
2329     select_item (panel);
2330     paint_dir (panel);
2331 }
2332 
2333 /* --------------------------------------------------------------------------------------------- */
2334 
2335 static void
2336 goto_child_dir (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
2337 {
2338     if ((S_ISDIR (selection (panel)->st.st_mode) || link_isdir (selection (panel))))
2339     {
2340         vfs_path_t *vpath;
2341 
2342         vpath = vfs_path_from_str (selection (panel)->fname->str);
2343         panel_cd (panel, vpath, cd_exact);
2344         vfs_path_free (vpath, TRUE);
2345     }
2346 }
2347 
2348 /* --------------------------------------------------------------------------------------------- */
2349 
2350 static void
2351 goto_top_file (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
2352 {
2353     unselect_item (panel);
2354     panel->selected = panel->top_file;
2355     select_item (panel);
2356 }
2357 
2358 /* --------------------------------------------------------------------------------------------- */
2359 
2360 static void
2361 goto_middle_file (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
2362 {
2363     unselect_item (panel);
2364     panel->selected = panel->top_file + panel_items (panel) / 2;
2365     select_item (panel);
2366 }
2367 
2368 /* --------------------------------------------------------------------------------------------- */
2369 
2370 static void
2371 goto_bottom_file (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
2372 {
2373     unselect_item (panel);
2374     panel->selected = panel->top_file + panel_items (panel) - 1;
2375     select_item (panel);
2376 }
2377 
2378 /* --------------------------------------------------------------------------------------------- */
2379 
2380 static void
2381 move_home (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
2382 {
2383     if (panel->selected == 0)
2384         return;
2385 
2386     unselect_item (panel);
2387 
2388     if (panels_options.torben_fj_mode)
2389     {
2390         int middle_pos;
2391 
2392         middle_pos = panel->top_file + panel_items (panel) / 2;
2393 
2394         if (panel->selected > middle_pos)
2395         {
2396             goto_middle_file (panel);
2397             return;
2398         }
2399         if (panel->selected != panel->top_file)
2400         {
2401             goto_top_file (panel);
2402             return;
2403         }
2404     }
2405 
2406     panel->top_file = 0;
2407     panel->selected = 0;
2408 
2409     paint_dir (panel);
2410     select_item (panel);
2411 }
2412 
2413 /* --------------------------------------------------------------------------------------------- */
2414 
2415 static void
2416 move_end (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
2417 {
2418     if (panel->selected == panel->dir.len - 1)
2419         return;
2420 
2421     unselect_item (panel);
2422 
2423     if (panels_options.torben_fj_mode)
2424     {
2425         int items, middle_pos;
2426 
2427         items = panel_items (panel);
2428         middle_pos = panel->top_file + items / 2;
2429 
2430         if (panel->selected < middle_pos)
2431         {
2432             goto_middle_file (panel);
2433             return;
2434         }
2435         if (panel->selected != panel->top_file + items - 1)
2436         {
2437             goto_bottom_file (panel);
2438             return;
2439         }
2440     }
2441 
2442     panel->selected = panel->dir.len - 1;
2443     paint_dir (panel);
2444     select_item (panel);
2445 }
2446 
2447 /* --------------------------------------------------------------------------------------------- */
2448 
2449 static void
2450 do_mark_file (WPanel * panel, mark_act_t do_move)
     /* [previous][next][first][last][top][bottom][index][help]  */
2451 {
2452     do_file_mark (panel, panel->selected, selection (panel)->f.marked ? 0 : 1);
2453 
2454     if ((panels_options.mark_moves_down && do_move == MARK_DOWN) || do_move == MARK_FORCE_DOWN)
2455         move_down (panel);
2456     else if (do_move == MARK_FORCE_UP)
2457         move_up (panel);
2458 }
2459 
2460 /* --------------------------------------------------------------------------------------------- */
2461 
2462 static inline void
2463 mark_file (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
2464 {
2465     do_mark_file (panel, MARK_DOWN);
2466 }
2467 
2468 /* --------------------------------------------------------------------------------------------- */
2469 
2470 static inline void
2471 mark_file_up (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
2472 {
2473     do_mark_file (panel, MARK_FORCE_UP);
2474 }
2475 
2476 /* --------------------------------------------------------------------------------------------- */
2477 
2478 static inline void
2479 mark_file_down (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
2480 {
2481     do_mark_file (panel, MARK_FORCE_DOWN);
2482 }
2483 
2484 /* --------------------------------------------------------------------------------------------- */
2485 
2486 static void
2487 mark_file_right (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
2488 {
2489     int lines;
2490 
2491     if (state_mark < 0)
2492         state_mark = selection (panel)->f.marked ? 0 : 1;
2493 
2494     lines = panel_lines (panel);
2495     lines = MIN (lines, panel->dir.len - panel->selected - 1);
2496     for (; lines != 0; lines--)
2497     {
2498         do_file_mark (panel, panel->selected, state_mark);
2499         move_down (panel);
2500     }
2501     do_file_mark (panel, panel->selected, state_mark);
2502 }
2503 
2504 /* --------------------------------------------------------------------------------------------- */
2505 
2506 static void
2507 mark_file_left (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
2508 {
2509     int lines;
2510 
2511     if (state_mark < 0)
2512         state_mark = selection (panel)->f.marked ? 0 : 1;
2513 
2514     lines = panel_lines (panel);
2515     lines = MIN (lines, panel->selected + 1);
2516     for (; lines != 0; lines--)
2517     {
2518         do_file_mark (panel, panel->selected, state_mark);
2519         move_up (panel);
2520     }
2521     do_file_mark (panel, panel->selected, state_mark);
2522 }
2523 
2524 /* --------------------------------------------------------------------------------------------- */
2525 
2526 static void
2527 panel_select_unselect_files (WPanel * panel, const char *title, const char *history_name,
     /* [previous][next][first][last][top][bottom][index][help]  */
2528                              gboolean do_select)
2529 {
2530     gboolean files_only = (panels_options.select_flags & SELECT_FILES_ONLY) != 0;
2531     gboolean case_sens = (panels_options.select_flags & SELECT_MATCH_CASE) != 0;
2532     gboolean shell_patterns = (panels_options.select_flags & SELECT_SHELL_PATTERNS) != 0;
2533 
2534     char *reg_exp;
2535     mc_search_t *search;
2536     int i;
2537 
2538     quick_widget_t quick_widgets[] = {
2539         /* *INDENT-OFF* */
2540         QUICK_INPUT (INPUT_LAST_TEXT, history_name, &reg_exp, NULL,
2541                      FALSE, FALSE, INPUT_COMPLETE_FILENAMES),
2542         QUICK_START_COLUMNS,
2543             QUICK_CHECKBOX (N_("&Files only"), &files_only, NULL),
2544             QUICK_CHECKBOX (N_("&Using shell patterns"), &shell_patterns, NULL),
2545         QUICK_NEXT_COLUMN,
2546             QUICK_CHECKBOX (N_("&Case sensitive"), &case_sens, NULL),
2547         QUICK_STOP_COLUMNS,
2548         QUICK_END
2549         /* *INDENT-ON* */
2550     };
2551 
2552     quick_dialog_t qdlg = {
2553         -1, -1, 50,
2554         title, "[Select/Unselect Files]",
2555         quick_widgets, NULL, NULL
2556     };
2557 
2558     if (quick_dialog (&qdlg) == B_CANCEL)
2559         return;
2560 
2561     if (reg_exp == NULL || *reg_exp == '\0')
2562     {
2563         g_free (reg_exp);
2564         return;
2565     }
2566 
2567     search = mc_search_new (reg_exp, NULL);
2568     search->search_type = shell_patterns ? MC_SEARCH_T_GLOB : MC_SEARCH_T_REGEX;
2569     search->is_entire_line = TRUE;
2570     search->is_case_sensitive = case_sens;
2571 
2572     for (i = 0; i < panel->dir.len; i++)
2573     {
2574         if (DIR_IS_DOTDOT (panel->dir.list[i].fname->str))
2575             continue;
2576         if (S_ISDIR (panel->dir.list[i].st.st_mode) && files_only)
2577             continue;
2578 
2579         if (mc_search_run
2580             (search, panel->dir.list[i].fname->str, 0, panel->dir.list[i].fname->len, NULL))
2581             do_file_mark (panel, i, do_select ? 1 : 0);
2582     }
2583 
2584     mc_search_free (search);
2585     g_free (reg_exp);
2586 
2587     /* result flags */
2588     panels_options.select_flags = 0;
2589     if (case_sens)
2590         panels_options.select_flags |= SELECT_MATCH_CASE;
2591     if (files_only)
2592         panels_options.select_flags |= SELECT_FILES_ONLY;
2593     if (shell_patterns)
2594         panels_options.select_flags |= SELECT_SHELL_PATTERNS;
2595 }
2596 
2597 /* --------------------------------------------------------------------------------------------- */
2598 
2599 static void
2600 panel_select_files (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
2601 {
2602     panel_select_unselect_files (panel, _("Select"), ":select_cmd: Select ", TRUE);
2603 }
2604 
2605 /* --------------------------------------------------------------------------------------------- */
2606 
2607 static void
2608 panel_unselect_files (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
2609 {
2610     panel_select_unselect_files (panel, _("Unselect"), ":unselect_cmd: Unselect ", FALSE);
2611 }
2612 
2613 /* --------------------------------------------------------------------------------------------- */
2614 
2615 static void
2616 panel_select_invert_files (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
2617 {
2618     int i;
2619 
2620     for (i = 0; i < panel->dir.len; i++)
2621     {
2622         file_entry_t *file = &panel->dir.list[i];
2623 
2624         if (!panels_options.reverse_files_only || !S_ISDIR (file->st.st_mode))
2625             do_file_mark (panel, i, file->f.marked ? 0 : 1);
2626     }
2627 }
2628 
2629 /* --------------------------------------------------------------------------------------------- */
2630 /** Incremental search of a file name in the panel.
2631   * @param panel instance of WPanel structure
2632   * @param c_code key code
2633   */
2634 
2635 static void
2636 do_search (WPanel * panel, int c_code)
     /* [previous][next][first][last][top][bottom][index][help]  */
2637 {
2638     int i, sel;
2639     gboolean wrapped = FALSE;
2640     char *act;
2641     mc_search_t *search;
2642     char *reg_exp, *esc_str;
2643     gboolean is_found = FALSE;
2644 
2645     if (c_code == KEY_BACKSPACE)
2646     {
2647         if (panel->quick_search.buffer->len != 0)
2648         {
2649             act = panel->quick_search.buffer->str + panel->quick_search.buffer->len;
2650             str_prev_noncomb_char (&act, panel->quick_search.buffer->str);
2651             g_string_set_size (panel->quick_search.buffer, act - panel->quick_search.buffer->str);
2652         }
2653         panel->quick_search.chpoint = 0;
2654     }
2655     else
2656     {
2657         if (c_code != 0 && (gsize) panel->quick_search.chpoint < sizeof (panel->quick_search.ch))
2658         {
2659             panel->quick_search.ch[panel->quick_search.chpoint] = c_code;
2660             panel->quick_search.chpoint++;
2661         }
2662 
2663         if (panel->quick_search.chpoint > 0)
2664         {
2665             switch (str_is_valid_char (panel->quick_search.ch, panel->quick_search.chpoint))
2666             {
2667             case -2:
2668                 return;
2669             case -1:
2670                 panel->quick_search.chpoint = 0;
2671                 return;
2672             default:
2673                 g_string_append_len (panel->quick_search.buffer, panel->quick_search.ch,
2674                                      panel->quick_search.chpoint);
2675                 panel->quick_search.chpoint = 0;
2676             }
2677         }
2678     }
2679 
2680     reg_exp = g_strdup_printf ("%s*", panel->quick_search.buffer->str);
2681     esc_str = strutils_escape (reg_exp, -1, ",|\\{}[]", TRUE);
2682     search = mc_search_new (esc_str, NULL);
2683     search->search_type = MC_SEARCH_T_GLOB;
2684     search->is_entire_line = TRUE;
2685 
2686     switch (panels_options.qsearch_mode)
2687     {
2688     case QSEARCH_CASE_SENSITIVE:
2689         search->is_case_sensitive = TRUE;
2690         break;
2691     case QSEARCH_CASE_INSENSITIVE:
2692         search->is_case_sensitive = FALSE;
2693         break;
2694     default:
2695         search->is_case_sensitive = panel->sort_info.case_sensitive;
2696         break;
2697     }
2698 
2699     sel = panel->selected;
2700 
2701     for (i = panel->selected; !wrapped || i != panel->selected; i++)
2702     {
2703         if (i >= panel->dir.len)
2704         {
2705             i = 0;
2706             if (wrapped)
2707                 break;
2708             wrapped = TRUE;
2709         }
2710         if (mc_search_run
2711             (search, panel->dir.list[i].fname->str, 0, panel->dir.list[i].fname->len, NULL))
2712         {
2713             sel = i;
2714             is_found = TRUE;
2715             break;
2716         }
2717     }
2718     if (is_found)
2719     {
2720         unselect_item (panel);
2721         panel->selected = sel;
2722         select_item (panel);
2723         widget_draw (WIDGET (panel));
2724     }
2725     else if (c_code != KEY_BACKSPACE)
2726     {
2727         act = panel->quick_search.buffer->str + panel->quick_search.buffer->len;
2728         str_prev_noncomb_char (&act, panel->quick_search.buffer->str);
2729         g_string_set_size (panel->quick_search.buffer, act - panel->quick_search.buffer->str);
2730     }
2731     mc_search_free (search);
2732     g_free (reg_exp);
2733     g_free (esc_str);
2734 }
2735 
2736 /* --------------------------------------------------------------------------------------------- */
2737 /** Start new search.
2738   * @param panel instance of WPanel structure
2739   */
2740 
2741 static void
2742 start_search (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
2743 {
2744     if (panel->quick_search.active)
2745     {
2746         if (panel->selected == panel->dir.len - 1)
2747             panel->selected = 0;
2748         else
2749             move_down (panel);
2750 
2751         /* in case if there was no search string we need to recall
2752            previous string, with which we ended previous searching */
2753         if (panel->quick_search.buffer->len == 0)
2754             mc_g_string_copy (panel->quick_search.buffer, panel->quick_search.prev_buffer);
2755 
2756         do_search (panel, 0);
2757     }
2758     else
2759     {
2760         panel->quick_search.active = TRUE;
2761         g_string_set_size (panel->quick_search.buffer, 0);
2762         panel->quick_search.ch[0] = '\0';
2763         panel->quick_search.chpoint = 0;
2764         display_mini_info (panel);
2765         mc_refresh ();
2766     }
2767 }
2768 
2769 /* --------------------------------------------------------------------------------------------- */
2770 
2771 static void
2772 stop_search (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
2773 {
2774     panel->quick_search.active = FALSE;
2775 
2776     /* if user overrdied search string, we need to store it
2777        to the quick_search.prev_buffer */
2778     if (panel->quick_search.buffer->len != 0)
2779         mc_g_string_copy (panel->quick_search.prev_buffer, panel->quick_search.buffer);
2780 
2781     display_mini_info (panel);
2782 }
2783 
2784 /* --------------------------------------------------------------------------------------------- */
2785 /** Return TRUE if the Enter key has been processed, FALSE otherwise */
2786 
2787 static gboolean
2788 do_enter_on_file_entry (WPanel * panel, file_entry_t * fe)
     /* [previous][next][first][last][top][bottom][index][help]  */
2789 {
2790     const char *fname = fe->fname->str;
2791     vfs_path_t *full_name_vpath;
2792     gboolean ok;
2793 
2794     /*
2795      * Directory or link to directory - change directory.
2796      * Try the same for the entries on which mc_lstat() has failed.
2797      */
2798     if (S_ISDIR (fe->st.st_mode) || link_isdir (fe) || (fe->st.st_mode == 0))
2799     {
2800         vfs_path_t *fname_vpath;
2801 
2802         fname_vpath = vfs_path_from_str (fname);
2803         if (!panel_cd (panel, fname_vpath, cd_exact))
2804             message (D_ERROR, MSG_ERROR, _("Cannot change directory"));
2805         vfs_path_free (fname_vpath, TRUE);
2806         return TRUE;
2807     }
2808 
2809     full_name_vpath = vfs_path_append_new (panel->cwd_vpath, fname, (char *) NULL);
2810 
2811     /* Try associated command */
2812     ok = regex_command (full_name_vpath, "Open") != 0;
2813     vfs_path_free (full_name_vpath, TRUE);
2814     if (ok)
2815         return TRUE;
2816 
2817     /* Check if the file is executable */
2818     full_name_vpath = vfs_path_append_new (panel->cwd_vpath, fname, (char *) NULL);
2819     ok = (is_exe (fe->st.st_mode) && if_link_is_exe (full_name_vpath, fe));
2820     vfs_path_free (full_name_vpath, TRUE);
2821     if (!ok)
2822         return FALSE;
2823 
2824     if (confirm_execute
2825         && query_dialog (_("The Midnight Commander"), _("Do you really want to execute?"), D_NORMAL,
2826                          2, _("&Yes"), _("&No")) != 0)
2827         return TRUE;
2828 
2829     if (!vfs_current_is_local ())
2830     {
2831         int ret;
2832         vfs_path_t *tmp_vpath;
2833 
2834         tmp_vpath = vfs_path_append_new (vfs_get_raw_current_dir (), fname, (char *) NULL);
2835         ret = mc_setctl (tmp_vpath, VFS_SETCTL_RUN, NULL);
2836         vfs_path_free (tmp_vpath, TRUE);
2837         /* We took action only if the dialog was shown or the execution was successful */
2838         return confirm_execute || (ret == 0);
2839     }
2840 
2841     {
2842         char *tmp, *cmd;
2843 
2844         tmp = name_quote (fname, FALSE);
2845         cmd = g_strconcat (".", PATH_SEP_STR, tmp, (char *) NULL);
2846         g_free (tmp);
2847         shell_execute (cmd, 0);
2848         g_free (cmd);
2849     }
2850 
2851 #ifdef HAVE_CHARSET
2852     mc_global.source_codepage = default_source_codepage;
2853 #endif
2854 
2855     return TRUE;
2856 }
2857 
2858 /* --------------------------------------------------------------------------------------------- */
2859 
2860 static inline gboolean
2861 do_enter (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
2862 {
2863     return do_enter_on_file_entry (panel, selection (panel));
2864 }
2865 
2866 
2867 /* --------------------------------------------------------------------------------------------- */
2868 
2869 static void
2870 panel_cycle_listing_format (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
2871 {
2872     panel->list_format = (panel->list_format + 1) % LIST_FORMATS;
2873 
2874     if (set_panel_formats (panel) == 0)
2875         do_refresh ();
2876 }
2877 
2878 /* --------------------------------------------------------------------------------------------- */
2879 
2880 static void
2881 chdir_other_panel (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
2882 {
2883     const file_entry_t *entry = &panel->dir.list[panel->selected];
2884     vfs_path_t *new_dir_vpath;
2885     char *sel_entry = NULL;
2886     WPanel *p;
2887 
2888     if (get_other_type () != view_listing)
2889         create_panel (get_other_index (), view_listing);
2890 
2891     if (S_ISDIR (entry->st.st_mode) || link_isdir (entry))
2892         new_dir_vpath = vfs_path_append_new (panel->cwd_vpath, entry->fname->str, (char *) NULL);
2893     else
2894     {
2895         new_dir_vpath = vfs_path_append_new (panel->cwd_vpath, "..", (char *) NULL);
2896         sel_entry = strrchr (vfs_path_get_last_path_str (panel->cwd_vpath), PATH_SEP);
2897     }
2898 
2899     p = change_panel ();
2900     panel_cd (p, new_dir_vpath, cd_exact);
2901     vfs_path_free (new_dir_vpath, TRUE);
2902 
2903     if (sel_entry)
2904         try_to_select (p, sel_entry);
2905     (void) change_panel ();
2906 
2907     move_down (panel);
2908 }
2909 
2910 /* --------------------------------------------------------------------------------------------- */
2911 /**
2912  * Make the current directory of the current panel also the current
2913  * directory of the other panel.  Put the other panel to the listing
2914  * mode if needed.  If the current panel is panelized, the other panel
2915  * doesn't become panelized.
2916  */
2917 
2918 static void
2919 panel_sync_other (const WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
2920 {
2921     if (get_other_type () != view_listing)
2922         create_panel (get_other_index (), view_listing);
2923 
2924     panel_do_cd (other_panel, panel->cwd_vpath, cd_exact);
2925 
2926     /* try to select current filename on the other panel */
2927     if (!panel->is_panelized)
2928         try_to_select (other_panel, selection (panel)->fname->str);
2929 }
2930 
2931 /* --------------------------------------------------------------------------------------------- */
2932 
2933 static void
2934 chdir_to_readlink (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
2935 {
2936     vfs_path_t *new_dir_vpath;
2937     char buffer[MC_MAXPATHLEN];
2938     int i;
2939     struct stat st;
2940     vfs_path_t *panel_fname_vpath;
2941     gboolean ok;
2942     WPanel *cpanel;
2943 
2944     if (get_other_type () != view_listing)
2945         return;
2946 
2947     if (!S_ISLNK (panel->dir.list[panel->selected].st.st_mode))
2948         return;
2949 
2950     i = readlink (selection (panel)->fname->str, buffer, MC_MAXPATHLEN - 1);
2951     if (i < 0)
2952         return;
2953 
2954     panel_fname_vpath = vfs_path_from_str (selection (panel)->fname->str);
2955     ok = (mc_stat (panel_fname_vpath, &st) >= 0);
2956     vfs_path_free (panel_fname_vpath, TRUE);
2957     if (!ok)
2958         return;
2959 
2960     buffer[i] = '\0';
2961     if (!S_ISDIR (st.st_mode))
2962     {
2963         char *p;
2964 
2965         p = strrchr (buffer, PATH_SEP);
2966         if (p != NULL && p[1] == '\0')
2967         {
2968             *p = '\0';
2969             p = strrchr (buffer, PATH_SEP);
2970         }
2971         if (p == NULL)
2972             return;
2973 
2974         p[1] = '\0';
2975     }
2976     if (IS_PATH_SEP (*buffer))
2977         new_dir_vpath = vfs_path_from_str (buffer);
2978     else
2979         new_dir_vpath = vfs_path_append_new (panel->cwd_vpath, buffer, (char *) NULL);
2980 
2981     cpanel = change_panel ();
2982     panel_cd (cpanel, new_dir_vpath, cd_exact);
2983     vfs_path_free (new_dir_vpath, TRUE);
2984     (void) change_panel ();
2985 
2986     move_down (panel);
2987 }
2988 
2989 /* --------------------------------------------------------------------------------------------- */
2990 /**
2991    function return 0 if not found and REAL_INDEX+1 if found
2992  */
2993 
2994 static gsize
2995 panel_get_format_field_index_by_name (const WPanel * panel, const char *name)
     /* [previous][next][first][last][top][bottom][index][help]  */
2996 {
2997     GSList *format;
2998     gsize lc_index;
2999 
3000     for (lc_index = 1, format = panel->format;
3001          format != NULL && strcmp (((format_item_t *) format->data)->title, name) != 0;
3002          format = g_slist_next (format), lc_index++)
3003         ;
3004 
3005     if (format == NULL)
3006         lc_index = 0;
3007 
3008     return lc_index;
3009 }
3010 
3011 /* --------------------------------------------------------------------------------------------- */
3012 
3013 static const panel_field_t *
3014 panel_get_sortable_field_by_format (const WPanel * panel, gsize lc_index)
     /* [previous][next][first][last][top][bottom][index][help]  */
3015 {
3016     const panel_field_t *pfield;
3017     const format_item_t *format;
3018 
3019     format = (const format_item_t *) g_slist_nth_data (panel->format, lc_index);
3020     if (format == NULL)
3021         return NULL;
3022 
3023     pfield = panel_get_field_by_title (format->title);
3024     if (pfield == NULL)
3025         return NULL;
3026     if (pfield->sort_routine == NULL)
3027         return NULL;
3028     return pfield;
3029 }
3030 
3031 /* --------------------------------------------------------------------------------------------- */
3032 
3033 static void
3034 panel_toggle_sort_order_prev (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
3035 {
3036     gsize lc_index, i;
3037     const char *title;
3038     const panel_field_t *pfield = NULL;
3039 
3040     title = panel_get_title_without_hotkey (panel->sort_field->title_hotkey);
3041     lc_index = panel_get_format_field_index_by_name (panel, title);
3042 
3043     if (lc_index > 1)
3044     {
3045         /* search for prev sortable column in panel format */
3046         for (i = lc_index - 1;
3047              i != 0 && (pfield = panel_get_sortable_field_by_format (panel, i - 1)) == NULL; i--)
3048             ;
3049     }
3050 
3051     if (pfield == NULL)
3052     {
3053         /* Sortable field not found. Try to search in each array */
3054         for (i = g_slist_length (panel->format);
3055              i != 0 && (pfield = panel_get_sortable_field_by_format (panel, i - 1)) == NULL; i--)
3056             ;
3057     }
3058 
3059     if (pfield != NULL)
3060     {
3061         panel->sort_field = pfield;
3062         panel_set_sort_order (panel, pfield);
3063     }
3064 }
3065 
3066 /* --------------------------------------------------------------------------------------------- */
3067 
3068 static void
3069 panel_toggle_sort_order_next (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
3070 {
3071     gsize lc_index, i;
3072     const panel_field_t *pfield = NULL;
3073     gsize format_field_count;
3074     const char *title;
3075 
3076     format_field_count = g_slist_length (panel->format);
3077     title = panel_get_title_without_hotkey (panel->sort_field->title_hotkey);
3078     lc_index = panel_get_format_field_index_by_name (panel, title);
3079 
3080     if (lc_index != 0 && lc_index != format_field_count)
3081     {
3082         /* search for prev sortable column in panel format */
3083         for (i = lc_index;
3084              i != format_field_count
3085              && (pfield = panel_get_sortable_field_by_format (panel, i)) == NULL; i++)
3086             ;
3087     }
3088 
3089     if (pfield == NULL)
3090     {
3091         /* Sortable field not found. Try to search in each array */
3092         for (i = 0;
3093              i != format_field_count
3094              && (pfield = panel_get_sortable_field_by_format (panel, i)) == NULL; i++)
3095             ;
3096     }
3097 
3098     if (pfield != NULL)
3099     {
3100         panel->sort_field = pfield;
3101         panel_set_sort_order (panel, pfield);
3102     }
3103 }
3104 
3105 /* --------------------------------------------------------------------------------------------- */
3106 
3107 static void
3108 panel_select_sort_order (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
3109 {
3110     const panel_field_t *sort_order;
3111 
3112     sort_order = sort_box (&panel->sort_info, panel->sort_field);
3113     if (sort_order != NULL)
3114     {
3115         panel->sort_field = sort_order;
3116         panel_set_sort_order (panel, sort_order);
3117     }
3118 }
3119 
3120 /* --------------------------------------------------------------------------------------------- */
3121 
3122 /**
3123  * panel_content_scroll_left:
3124  * @param panel the pointer to the panel on which we operate
3125  *
3126  * scroll long filename to the left (decrement scroll pointer)
3127  *
3128  */
3129 
3130 static void
3131 panel_content_scroll_left (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
3132 {
3133     if (panel->content_shift > -1)
3134     {
3135         if (panel->content_shift > panel->max_shift)
3136             panel->content_shift = panel->max_shift;
3137 
3138         panel->content_shift--;
3139         show_dir (panel);
3140         paint_dir (panel);
3141     }
3142 }
3143 
3144 /* --------------------------------------------------------------------------------------------- */
3145 /**
3146  * panel_content_scroll_right:
3147  * @param panel the pointer to the panel on which we operate
3148  *
3149  * scroll long filename to the right (increment scroll pointer)
3150  *
3151  */
3152 
3153 static void
3154 panel_content_scroll_right (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
3155 {
3156     if (panel->content_shift < 0 || panel->content_shift < panel->max_shift)
3157     {
3158         panel->content_shift++;
3159         show_dir (panel);
3160         paint_dir (panel);
3161     }
3162 }
3163 
3164 /* --------------------------------------------------------------------------------------------- */
3165 
3166 static void
3167 panel_set_sort_type_by_id (WPanel * panel, const char *name)
     /* [previous][next][first][last][top][bottom][index][help]  */
3168 {
3169     if (strcmp (panel->sort_field->id, name) == 0)
3170         panel->sort_info.reverse = !panel->sort_info.reverse;
3171     else
3172     {
3173         const panel_field_t *sort_order;
3174 
3175         sort_order = panel_get_field_by_id (name);
3176         if (sort_order == NULL)
3177             return;
3178 
3179         panel->sort_field = sort_order;
3180     }
3181 
3182     panel_set_sort_order (panel, panel->sort_field);
3183 }
3184 
3185 /* --------------------------------------------------------------------------------------------- */
3186 /**
3187  *  If we moved to the parent directory move the selection pointer to
3188  *  the old directory name; If we leave VFS dir, remove FS specificator.
3189  *
3190  *  You do _NOT_ want to add any vfs aware code here. <pavel@ucw.cz>
3191  */
3192 
3193 static const char *
3194 get_parent_dir_name (const vfs_path_t * cwd_vpath, const vfs_path_t * lwd_vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
3195 {
3196     size_t llen, clen;
3197     const char *p, *lwd;
3198 
3199     llen = vfs_path_len (lwd_vpath);
3200     clen = vfs_path_len (cwd_vpath);
3201 
3202     if (llen <= clen)
3203         return NULL;
3204 
3205     lwd = vfs_path_as_str (lwd_vpath);
3206 
3207     p = g_strrstr (lwd, VFS_PATH_URL_DELIMITER);
3208 
3209     if (p == NULL)
3210     {
3211         const char *cwd;
3212 
3213         cwd = vfs_path_as_str (cwd_vpath);
3214 
3215         p = strrchr (lwd, PATH_SEP);
3216 
3217         if (p != NULL && strncmp (cwd, lwd, (size_t) (p - lwd)) == 0
3218             && (clen == (size_t) (p - lwd) || (p == lwd && IS_PATH_SEP (cwd[0]) && cwd[1] == '\0')))
3219             return (p + 1);
3220 
3221         return NULL;
3222     }
3223 
3224     /* skip VFS prefix */
3225     while (--p > lwd && !IS_PATH_SEP (*p))
3226         ;
3227     /* get last component */
3228     while (--p > lwd && !IS_PATH_SEP (*p))
3229         ;
3230 
3231     /* return last component */
3232     return (p != lwd || IS_PATH_SEP (*p)) ? p + 1 : p;
3233 }
3234 
3235 /* --------------------------------------------------------------------------------------------- */
3236 /** Wrapper for do_subshell_chdir, check for availability of subshell */
3237 
3238 static void
3239 subshell_chdir (const vfs_path_t * vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
3240 {
3241 #ifdef ENABLE_SUBSHELL
3242     if (mc_global.tty.use_subshell && vfs_current_is_local ())
3243         do_subshell_chdir (vpath, FALSE);
3244 #else /* ENABLE_SUBSHELL */
3245     (void) vpath;
3246 #endif /* ENABLE_SUBSHELL */
3247 }
3248 
3249 /* --------------------------------------------------------------------------------------------- */
3250 /**
3251  * Changes the current directory of the panel.
3252  * Don't record change in the directory history.
3253  */
3254 
3255 static gboolean
3256 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]  */
3257 {
3258     vfs_path_t *olddir_vpath;
3259 
3260     /* Convert *new_path to a suitable pathname, handle ~user */
3261     if (cd_type == cd_parse_command)
3262     {
3263         const vfs_path_element_t *element;
3264 
3265         element = vfs_path_get_by_index (new_dir_vpath, 0);
3266         if (strcmp (element->path, "-") == 0)
3267             new_dir_vpath = panel->lwd_vpath;
3268     }
3269 
3270     if (mc_chdir (new_dir_vpath) == -1)
3271         return FALSE;
3272 
3273     /* Success: save previous directory, shutdown status of previous dir */
3274     olddir_vpath = vfs_path_clone (panel->cwd_vpath);
3275     panel_set_lwd (panel, panel->cwd_vpath);
3276     input_complete_free (cmdline);
3277 
3278     vfs_path_free (panel->cwd_vpath, TRUE);
3279     vfs_setup_cwd ();
3280     panel->cwd_vpath = vfs_path_clone (vfs_get_raw_current_dir ());
3281 
3282     vfs_release_path (olddir_vpath);
3283 
3284     subshell_chdir (panel->cwd_vpath);
3285 
3286     /* Reload current panel */
3287     panel_clean_dir (panel);
3288 
3289     if (!dir_list_load (&panel->dir, panel->cwd_vpath, panel->sort_field->sort_routine,
3290                         &panel->sort_info, panel->filter))
3291         message (D_ERROR, MSG_ERROR, _("Cannot read directory contents"));
3292 
3293     try_to_select (panel, get_parent_dir_name (panel->cwd_vpath, olddir_vpath));
3294 
3295     load_hint (FALSE);
3296     panel->dirty = TRUE;
3297     update_xterm_title_path ();
3298 
3299     vfs_path_free (olddir_vpath, TRUE);
3300 
3301     return TRUE;
3302 }
3303 
3304 /* --------------------------------------------------------------------------------------------- */
3305 
3306 static void
3307 directory_history_next (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
3308 {
3309     gboolean ok;
3310 
3311     do
3312     {
3313         GList *next;
3314 
3315         ok = TRUE;
3316         next = g_list_next (panel->dir_history.current);
3317         if (next != NULL)
3318         {
3319             vfs_path_t *data_vpath;
3320 
3321             data_vpath = vfs_path_from_str ((char *) next->data);
3322             ok = panel_do_cd_int (panel, data_vpath, cd_exact);
3323             vfs_path_free (data_vpath, TRUE);
3324             panel->dir_history.current = next;
3325         }
3326         /* skip directories that present in history but absent in file system */
3327     }
3328     while (!ok);
3329 }
3330 
3331 /* --------------------------------------------------------------------------------------------- */
3332 
3333 static void
3334 directory_history_prev (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
3335 {
3336     gboolean ok;
3337 
3338     do
3339     {
3340         GList *prev;
3341 
3342         ok = TRUE;
3343         prev = g_list_previous (panel->dir_history.current);
3344         if (prev != NULL)
3345         {
3346             vfs_path_t *data_vpath;
3347 
3348             data_vpath = vfs_path_from_str ((char *) prev->data);
3349             ok = panel_do_cd_int (panel, data_vpath, cd_exact);
3350             vfs_path_free (data_vpath, TRUE);
3351             panel->dir_history.current = prev;
3352         }
3353         /* skip directories that present in history but absent in file system */
3354     }
3355     while (!ok);
3356 }
3357 
3358 /* --------------------------------------------------------------------------------------------- */
3359 
3360 static void
3361 directory_history_list (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
3362 {
3363     history_descriptor_t hd;
3364     gboolean ok = FALSE;
3365     size_t pos;
3366 
3367     pos = g_list_position (panel->dir_history.current, panel->dir_history.list);
3368 
3369     history_descriptor_init (&hd, WIDGET (panel)->y, WIDGET (panel)->x, panel->dir_history.list,
3370                              (int) pos);
3371     history_show (&hd);
3372 
3373     panel->dir_history.list = hd.list;
3374     if (hd.text != NULL)
3375     {
3376         vfs_path_t *s_vpath;
3377 
3378         s_vpath = vfs_path_from_str (hd.text);
3379         ok = panel_do_cd_int (panel, s_vpath, cd_exact);
3380         if (ok)
3381             directory_history_add (panel, panel->cwd_vpath);
3382         else
3383             message (D_ERROR, MSG_ERROR, _("Cannot change directory"));
3384         vfs_path_free (s_vpath, TRUE);
3385         g_free (hd.text);
3386     }
3387 
3388     if (!ok)
3389     {
3390         /* Since history is fully modified in history_show(), panel->dir_history actually
3391          * points to the invalid place. Try restore current postition here. */
3392 
3393         size_t i;
3394 
3395         panel->dir_history.current = panel->dir_history.list;
3396 
3397         for (i = 0; i <= pos; i++)
3398         {
3399             GList *prev;
3400 
3401             prev = g_list_previous (panel->dir_history.current);
3402             if (prev == NULL)
3403                 break;
3404 
3405             panel->dir_history.current = prev;
3406         }
3407     }
3408 }
3409 
3410 /* --------------------------------------------------------------------------------------------- */
3411 
3412 static cb_ret_t
3413 panel_execute_cmd (WPanel * panel, long command)
     /* [previous][next][first][last][top][bottom][index][help]  */
3414 {
3415     int res = MSG_HANDLED;
3416 
3417     if (command != CK_Search)
3418         stop_search (panel);
3419 
3420     switch (command)
3421     {
3422     case CK_Up:
3423     case CK_Down:
3424     case CK_Left:
3425     case CK_Right:
3426     case CK_Bottom:
3427     case CK_Top:
3428     case CK_PageDown:
3429     case CK_PageUp:
3430         /* reset state of marks flag */
3431         state_mark = -1;
3432         break;
3433     default:
3434         break;
3435     }
3436 
3437     switch (command)
3438     {
3439     case CK_CycleListingFormat:
3440         panel_cycle_listing_format (panel);
3441         break;
3442     case CK_PanelOtherCd:
3443         chdir_other_panel (panel);
3444         break;
3445     case CK_PanelOtherCdLink:
3446         chdir_to_readlink (panel);
3447         break;
3448     case CK_CopySingle:
3449         copy_cmd_local (panel);
3450         break;
3451     case CK_DeleteSingle:
3452         delete_cmd_local (panel);
3453         break;
3454     case CK_Enter:
3455         do_enter (panel);
3456         break;
3457     case CK_ViewRaw:
3458         view_raw_cmd (panel);
3459         break;
3460     case CK_EditNew:
3461         edit_cmd_new ();
3462         break;
3463     case CK_MoveSingle:
3464         rename_cmd_local (panel);
3465         break;
3466     case CK_SelectInvert:
3467         panel_select_invert_files (panel);
3468         break;
3469     case CK_Select:
3470         panel_select_files (panel);
3471         break;
3472     case CK_SelectExt:
3473         panel_select_ext_cmd (panel);
3474         break;
3475     case CK_Unselect:
3476         panel_unselect_files (panel);
3477         break;
3478     case CK_PageDown:
3479         next_page (panel);
3480         break;
3481     case CK_PageUp:
3482         prev_page (panel);
3483         break;
3484     case CK_CdChild:
3485         goto_child_dir (panel);
3486         break;
3487     case CK_CdParent:
3488         goto_parent_dir (panel);
3489         break;
3490     case CK_History:
3491         directory_history_list (panel);
3492         break;
3493     case CK_HistoryNext:
3494         directory_history_next (panel);
3495         break;
3496     case CK_HistoryPrev:
3497         directory_history_prev (panel);
3498         break;
3499     case CK_BottomOnScreen:
3500         goto_bottom_file (panel);
3501         break;
3502     case CK_MiddleOnScreen:
3503         goto_middle_file (panel);
3504         break;
3505     case CK_TopOnScreen:
3506         goto_top_file (panel);
3507         break;
3508     case CK_Mark:
3509         mark_file (panel);
3510         break;
3511     case CK_MarkUp:
3512         mark_file_up (panel);
3513         break;
3514     case CK_MarkDown:
3515         mark_file_down (panel);
3516         break;
3517     case CK_MarkLeft:
3518         mark_file_left (panel);
3519         break;
3520     case CK_MarkRight:
3521         mark_file_right (panel);
3522         break;
3523     case CK_CdParentSmart:
3524         res = force_maybe_cd (panel);
3525         break;
3526     case CK_Up:
3527         move_up (panel);
3528         break;
3529     case CK_Down:
3530         move_down (panel);
3531         break;
3532     case CK_Left:
3533         res = move_left (panel);
3534         break;
3535     case CK_Right:
3536         res = move_right (panel);
3537         break;
3538     case CK_Bottom:
3539         move_end (panel);
3540         break;
3541     case CK_Top:
3542         move_home (panel);
3543         break;
3544 #ifdef HAVE_CHARSET
3545     case CK_SelectCodepage:
3546         panel_change_encoding (panel);
3547         break;
3548 #endif
3549     case CK_ScrollLeft:
3550         panel_content_scroll_left (panel);
3551         break;
3552     case CK_ScrollRight:
3553         panel_content_scroll_right (panel);
3554         break;
3555     case CK_Search:
3556         start_search (panel);
3557         break;
3558     case CK_SearchStop:
3559         break;
3560     case CK_PanelOtherSync:
3561         panel_sync_other (panel);
3562         break;
3563     case CK_Sort:
3564         panel_select_sort_order (panel);
3565         break;
3566     case CK_SortPrev:
3567         panel_toggle_sort_order_prev (panel);
3568         break;
3569     case CK_SortNext:
3570         panel_toggle_sort_order_next (panel);
3571         break;
3572     case CK_SortReverse:
3573         panel->sort_info.reverse = !panel->sort_info.reverse;
3574         panel_set_sort_order (panel, panel->sort_field);
3575         break;
3576     case CK_SortByName:
3577         panel_set_sort_type_by_id (panel, "name");
3578         break;
3579     case CK_SortByExt:
3580         panel_set_sort_type_by_id (panel, "extension");
3581         break;
3582     case CK_SortBySize:
3583         panel_set_sort_type_by_id (panel, "size");
3584         break;
3585     case CK_SortByMTime:
3586         panel_set_sort_type_by_id (panel, "mtime");
3587         break;
3588     default:
3589         res = MSG_NOT_HANDLED;
3590         break;
3591     }
3592 
3593     return res;
3594 }
3595 
3596 /* --------------------------------------------------------------------------------------------- */
3597 
3598 static cb_ret_t
3599 panel_key (WPanel * panel, int key)
     /* [previous][next][first][last][top][bottom][index][help]  */
3600 {
3601     long command;
3602 
3603     if (is_abort_char (key))
3604     {
3605         stop_search (panel);
3606         return MSG_HANDLED;
3607     }
3608 
3609     if (panel->quick_search.active && ((key >= ' ' && key <= 255) || key == KEY_BACKSPACE))
3610     {
3611         do_search (panel, key);
3612         return MSG_HANDLED;
3613     }
3614 
3615     command = widget_lookup_key (WIDGET (panel), key);
3616     if (command != CK_IgnoreKey)
3617         return panel_execute_cmd (panel, command);
3618 
3619     if (panels_options.torben_fj_mode && key == ALT ('h'))
3620     {
3621         goto_middle_file (panel);
3622         return MSG_HANDLED;
3623     }
3624 
3625     if (!command_prompt && ((key >= ' ' && key <= 255) || key == KEY_BACKSPACE))
3626     {
3627         start_search (panel);
3628         do_search (panel, key);
3629         return MSG_HANDLED;
3630     }
3631 
3632     return MSG_NOT_HANDLED;
3633 }
3634 
3635 /* --------------------------------------------------------------------------------------------- */
3636 
3637 static cb_ret_t
3638 panel_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
3639 {
3640     WPanel *panel = PANEL (w);
3641     WDialog *h = DIALOG (w->owner);
3642     WButtonBar *bb;
3643 
3644     switch (msg)
3645     {
3646     case MSG_INIT:
3647         /* subscribe to "history_load" event */
3648         mc_event_add (h->event_group, MCEVENT_HISTORY_LOAD, panel_load_history, w, NULL);
3649         /* subscribe to "history_save" event */
3650         mc_event_add (h->event_group, MCEVENT_HISTORY_SAVE, panel_save_history, w, NULL);
3651         return MSG_HANDLED;
3652 
3653     case MSG_DRAW:
3654         /* Repaint everything, including frame and separator */
3655         widget_erase (w);
3656         show_dir (panel);
3657         panel_print_header (panel);
3658         adjust_top_file (panel);
3659         paint_dir (panel);
3660         mini_info_separator (panel);
3661         display_mini_info (panel);
3662         panel->dirty = FALSE;
3663         return MSG_HANDLED;
3664 
3665     case MSG_FOCUS:
3666         state_mark = -1;
3667         current_panel = panel;
3668         panel->active = TRUE;
3669 
3670         if (mc_chdir (panel->cwd_vpath) != 0)
3671         {
3672             char *cwd;
3673 
3674             cwd = vfs_path_to_str_flags (panel->cwd_vpath, 0, VPF_STRIP_PASSWORD);
3675             message (D_ERROR, MSG_ERROR, _("Cannot chdir to \"%s\"\n%s"),
3676                      cwd, unix_error_string (errno));
3677             g_free (cwd);
3678         }
3679         else
3680             subshell_chdir (panel->cwd_vpath);
3681 
3682         update_xterm_title_path ();
3683         select_item (panel);
3684 
3685         bb = find_buttonbar (h);
3686         midnight_set_buttonbar (bb);
3687         widget_draw (WIDGET (bb));
3688         return MSG_HANDLED;
3689 
3690     case MSG_UNFOCUS:
3691         /* Janne: look at this for the multiple panel options */
3692         stop_search (panel);
3693         panel->active = FALSE;
3694         unselect_item (panel);
3695         return MSG_HANDLED;
3696 
3697     case MSG_KEY:
3698         return panel_key (panel, parm);
3699 
3700     case MSG_ACTION:
3701         return panel_execute_cmd (panel, parm);
3702 
3703     case MSG_DESTROY:
3704         vfs_stamp_path (panel->cwd_vpath);
3705         /* unsubscribe from "history_load" event */
3706         mc_event_del (h->event_group, MCEVENT_HISTORY_LOAD, panel_load_history, w);
3707         /* unsubscribe from "history_save" event */
3708         mc_event_del (h->event_group, MCEVENT_HISTORY_SAVE, panel_save_history, w);
3709         panel_destroy (panel);
3710         free_my_statfs ();
3711         return MSG_HANDLED;
3712 
3713     default:
3714         return widget_default_callback (w, sender, msg, parm, data);
3715     }
3716 }
3717 
3718 /* --------------------------------------------------------------------------------------------- */
3719 /*                                     */
3720 /* Panel mouse events support routines */
3721 /*                                     */
3722 
3723 static void
3724 mouse_toggle_mark (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
3725 {
3726     do_mark_file (panel, MARK_DONT_MOVE);
3727     mouse_marking = selection (panel)->f.marked;
3728     mouse_mark_panel = current_panel;
3729 }
3730 
3731 /* --------------------------------------------------------------------------------------------- */
3732 
3733 static void
3734 mouse_set_mark (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
3735 {
3736     if (mouse_mark_panel == panel)
3737     {
3738         if (mouse_marking && !selection (panel)->f.marked)
3739             do_mark_file (panel, MARK_DONT_MOVE);
3740         else if (!mouse_marking && selection (panel)->f.marked)
3741             do_mark_file (panel, MARK_DONT_MOVE);
3742     }
3743 }
3744 
3745 /* --------------------------------------------------------------------------------------------- */
3746 
3747 static gboolean
3748 mark_if_marking (WPanel * panel, const mouse_event_t * event)
     /* [previous][next][first][last][top][bottom][index][help]  */
3749 {
3750     if ((event->buttons & GPM_B_RIGHT) != 0)
3751     {
3752         if (event->msg == MSG_MOUSE_DOWN)
3753             mouse_toggle_mark (panel);
3754         else
3755             mouse_set_mark (panel);
3756         return TRUE;
3757     }
3758 
3759     return FALSE;
3760 }
3761 
3762 /* --------------------------------------------------------------------------------------------- */
3763 /** Determine which column was clicked, and sort the panel on
3764  * that column, or reverse sort on that column if already
3765  * sorted on that column.
3766  */
3767 
3768 static void
3769 mouse_sort_col (WPanel * panel, int x)
     /* [previous][next][first][last][top][bottom][index][help]  */
3770 {
3771     int i = 0;
3772     GSList *format;
3773     const char *lc_sort_name = NULL;
3774     panel_field_t *col_sort_format = NULL;
3775 
3776     for (format = panel->format; format != NULL; format = g_slist_next (format))
3777     {
3778         format_item_t *fi = (format_item_t *) format->data;
3779 
3780         i += fi->field_len;
3781         if (x < i + 1)
3782         {
3783             /* found column */
3784             lc_sort_name = fi->title;
3785             break;
3786         }
3787     }
3788 
3789     if (lc_sort_name == NULL)
3790         return;
3791 
3792     for (i = 0; panel_fields[i].id != NULL; i++)
3793     {
3794         const char *title;
3795 
3796         title = panel_get_title_without_hotkey (panel_fields[i].title_hotkey);
3797         if (panel_fields[i].sort_routine != NULL && strcmp (title, lc_sort_name) == 0)
3798         {
3799             col_sort_format = &panel_fields[i];
3800             break;
3801         }
3802     }
3803 
3804     if (col_sort_format != NULL)
3805     {
3806         if (panel->sort_field == col_sort_format)
3807             /* reverse the sort if clicked column is already the sorted column */
3808             panel->sort_info.reverse = !panel->sort_info.reverse;
3809         else
3810             /* new sort is forced to be ascending */
3811             panel->sort_info.reverse = FALSE;
3812 
3813         panel_set_sort_order (panel, col_sort_format);
3814     }
3815 }
3816 
3817 /* --------------------------------------------------------------------------------------------- */
3818 
3819 static int
3820 panel_mouse_is_on_item (const WPanel * panel, int y, int x)
     /* [previous][next][first][last][top][bottom][index][help]  */
3821 {
3822     int last;
3823 
3824     if (y < 0)
3825         return (-1);
3826 
3827     last = panel->dir.len - 1;
3828     y += panel->top_file;
3829 
3830     if (y > last)
3831         return (-1);
3832 
3833     if (panel->list_cols > 1)
3834     {
3835         int width, lines;
3836 
3837         width = (WIDGET (panel)->cols - 2) / panel->list_cols;
3838         lines = panel_lines (panel);
3839         y += lines * (x / width);
3840     }
3841 
3842     return (y > last ? -1 : y);
3843 }
3844 
3845 /* --------------------------------------------------------------------------------------------- */
3846 
3847 static void
3848 panel_mouse_callback (Widget * w, mouse_msg_t msg, mouse_event_t * event)
     /* [previous][next][first][last][top][bottom][index][help]  */
3849 {
3850     WPanel *panel = PANEL (w);
3851     gboolean is_active;
3852 
3853     is_active = widget_is_active (w);
3854 
3855     switch (msg)
3856     {
3857     case MSG_MOUSE_DOWN:
3858         if (event->y == 0)
3859         {
3860             /* top frame */
3861             if (event->x == 1)
3862                 /* "<" button */
3863                 directory_history_prev (panel);
3864             else if (event->x == w->cols - 2)
3865                 /* ">" button */
3866                 directory_history_next (panel);
3867             else if (event->x >= w->cols - 5 && event->x <= w->cols - 3)
3868                 /* "^" button */
3869                 directory_history_list (panel);
3870             else if (event->x == w->cols - 6)
3871                 /* "." button show/hide hidden files */
3872                 send_message (midnight_dlg, NULL, MSG_ACTION, CK_ShowHidden, NULL);
3873             else
3874             {
3875                 /* no other events on 1st line, return MOU_UNHANDLED */
3876                 event->result.abort = TRUE;
3877                 /* avoid extra panel redraw */
3878                 panel->dirty = FALSE;
3879             }
3880             break;
3881         }
3882 
3883         if (event->y == 1)
3884         {
3885             /* sort on clicked column */
3886             mouse_sort_col (panel, event->x + 1);
3887             break;
3888         }
3889 
3890         if (!is_active)
3891             (void) change_panel ();
3892         MC_FALLTHROUGH;
3893 
3894     case MSG_MOUSE_DRAG:
3895         {
3896             int my_index;
3897 
3898             my_index = panel_mouse_is_on_item (panel, event->y - 2, event->x);
3899             if (my_index >= 0)
3900             {
3901                 if (my_index != panel->selected)
3902                 {
3903                     unselect_item (panel);
3904                     panel->selected = my_index;
3905                     select_item (panel);
3906                 }
3907 
3908                 /* This one is new */
3909                 mark_if_marking (panel, event);
3910             }
3911         }
3912         break;
3913 
3914     case MSG_MOUSE_UP:
3915         break;
3916 
3917     case MSG_MOUSE_CLICK:
3918         if ((event->count & GPM_DOUBLE) != 0 && (event->buttons & GPM_B_LEFT) != 0 &&
3919             panel_mouse_is_on_item (panel, event->y - 2, event->x) >= 0)
3920             do_enter (panel);
3921         break;
3922 
3923     case MSG_MOUSE_MOVE:
3924         break;
3925 
3926     case MSG_MOUSE_SCROLL_UP:
3927         if (is_active)
3928         {
3929             if (panels_options.mouse_move_pages && panel->top_file > 0)
3930                 prev_page (panel);
3931             else                /* We are in first page */
3932                 move_up (panel);
3933         }
3934         break;
3935 
3936     case MSG_MOUSE_SCROLL_DOWN:
3937         if (is_active)
3938         {
3939             if (panels_options.mouse_move_pages
3940                 && panel->top_file + panel_items (panel) < panel->dir.len)
3941                 next_page (panel);
3942             else                /* We are in last page */
3943                 move_down (panel);
3944         }
3945         break;
3946 
3947     default:
3948         break;
3949     }
3950 
3951     if (panel->dirty)
3952         widget_draw (w);
3953 }
3954 
3955 /* --------------------------------------------------------------------------------------------- */
3956 
3957 static void
3958 reload_panelized (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
3959 {
3960     int i, j;
3961     dir_list *list = &panel->dir;
3962 
3963     /* refresh current VFS directory required for vfs_path_from_str() */
3964     (void) mc_chdir (panel->cwd_vpath);
3965 
3966     for (i = 0, j = 0; i < list->len; i++)
3967     {
3968         vfs_path_t *vpath;
3969 
3970         vpath = vfs_path_from_str (list->list[i].fname->str);
3971         if (mc_lstat (vpath, &list->list[i].st) != 0)
3972             g_string_free (list->list[i].fname, TRUE);
3973         else
3974         {
3975             if (j != i)
3976                 list->list[j] = list->list[i];
3977             j++;
3978         }
3979         vfs_path_free (vpath, TRUE);
3980     }
3981     if (j == 0)
3982         dir_list_init (list);
3983     else
3984         list->len = j;
3985 
3986     recalculate_panel_summary (panel);
3987 
3988     if (panel != current_panel)
3989         (void) mc_chdir (current_panel->cwd_vpath);
3990 }
3991 
3992 /* --------------------------------------------------------------------------------------------- */
3993 
3994 static void
3995 update_one_panel_widget (WPanel * panel, panel_update_flags_t flags, const char *current_file)
     /* [previous][next][first][last][top][bottom][index][help]  */
3996 {
3997     gboolean free_pointer;
3998     char *my_current_file = NULL;
3999 
4000     if ((flags & UP_RELOAD) != 0)
4001     {
4002         panel->is_panelized = FALSE;
4003         mc_setctl (panel->cwd_vpath, VFS_SETCTL_FLUSH, NULL);
4004         memset (&(panel->dir_stat), 0, sizeof (panel->dir_stat));
4005     }
4006 
4007     /* If current_file == -1 (an invalid pointer) then preserve selection */
4008     free_pointer = current_file == UP_KEEPSEL;
4009 
4010     if (free_pointer)
4011     {
4012         my_current_file = g_strndup (panel->dir.list[panel->selected].fname->str,
4013                                      panel->dir.list[panel->selected].fname->len);
4014         current_file = my_current_file;
4015     }
4016 
4017     if (panel->is_panelized)
4018         reload_panelized (panel);
4019     else
4020         panel_reload (panel);
4021 
4022     try_to_select (panel, current_file);
4023     panel->dirty = TRUE;
4024 
4025     if (free_pointer)
4026         g_free (my_current_file);
4027 }
4028 
4029 /* --------------------------------------------------------------------------------------------- */
4030 
4031 static void
4032 update_one_panel (int which, panel_update_flags_t flags, const char *current_file)
     /* [previous][next][first][last][top][bottom][index][help]  */
4033 {
4034     if (get_panel_type (which) == view_listing)
4035     {
4036         WPanel *panel;
4037 
4038         panel = PANEL (get_panel_widget (which));
4039         if (panel->is_panelized)
4040             flags &= ~UP_RELOAD;
4041         update_one_panel_widget (panel, flags, current_file);
4042     }
4043 }
4044 
4045 /* --------------------------------------------------------------------------------------------- */
4046 
4047 static void
4048 do_select (WPanel * panel, int i)
     /* [previous][next][first][last][top][bottom][index][help]  */
4049 {
4050     if (i != panel->selected)
4051     {
4052         panel->dirty = TRUE;
4053         panel->selected = i;
4054         panel->top_file = panel->selected - (WIDGET (panel)->lines - 2) / 2;
4055         if (panel->top_file < 0)
4056             panel->top_file = 0;
4057     }
4058 }
4059 
4060 /* --------------------------------------------------------------------------------------------- */
4061 
4062 static void
4063 do_try_to_select (WPanel * panel, const char *name)
     /* [previous][next][first][last][top][bottom][index][help]  */
4064 {
4065     int i;
4066     char *subdir;
4067 
4068     if (name == NULL)
4069     {
4070         do_select (panel, 0);
4071         return;
4072     }
4073 
4074     /* We only want the last component of the directory,
4075      * and from this only the name without suffix.
4076      * Cut prefix if the panel is not panelized */
4077 
4078     if (panel->is_panelized)
4079         subdir = vfs_strip_suffix_from_filename (name);
4080     else
4081         subdir = vfs_strip_suffix_from_filename (x_basename (name));
4082 
4083     /* Search that subdir or filename without prefix (if not panelized panel), select it if found */
4084     for (i = 0; i < panel->dir.len; i++)
4085     {
4086         if (strcmp (subdir, panel->dir.list[i].fname->str) == 0)
4087         {
4088             do_select (panel, i);
4089             g_free (subdir);
4090             return;
4091         }
4092     }
4093 
4094     /* Try to select a file near the file that is missing */
4095     if (panel->selected >= panel->dir.len)
4096         do_select (panel, panel->dir.len - 1);
4097     g_free (subdir);
4098 }
4099 
4100 /* --------------------------------------------------------------------------------------------- */
4101 
4102 /* event callback */
4103 static gboolean
4104 event_update_panels (const gchar * event_group_name, const gchar * event_name,
     /* [previous][next][first][last][top][bottom][index][help]  */
4105                      gpointer init_data, gpointer data)
4106 {
4107     (void) event_group_name;
4108     (void) event_name;
4109     (void) init_data;
4110     (void) data;
4111 
4112     update_panels (UP_RELOAD, UP_KEEPSEL);
4113 
4114     return TRUE;
4115 }
4116 
4117 /* --------------------------------------------------------------------------------------------- */
4118 
4119 /* event callback */
4120 static gboolean
4121 panel_save_current_file_to_clip_file (const gchar * event_group_name, const gchar * event_name,
     /* [previous][next][first][last][top][bottom][index][help]  */
4122                                       gpointer init_data, gpointer data)
4123 {
4124     (void) event_group_name;
4125     (void) event_name;
4126     (void) init_data;
4127     (void) data;
4128 
4129     if (current_panel->marked == 0)
4130         mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_text_to_file",
4131                         (gpointer) selection (current_panel)->fname->str);
4132     else
4133     {
4134         int i;
4135         gboolean first = TRUE;
4136         char *flist = NULL;
4137 
4138         for (i = 0; i < current_panel->dir.len; i++)
4139             if (current_panel->dir.list[i].f.marked != 0)
4140             {                   /* Skip the unmarked ones */
4141                 if (first)
4142                 {
4143                     flist = g_strndup (current_panel->dir.list[i].fname->str,
4144                                        current_panel->dir.list[i].fname->len);
4145                     first = FALSE;
4146                 }
4147                 else
4148                 {
4149                     /* Add empty lines after the file */
4150                     char *tmp;
4151 
4152                     tmp =
4153                         g_strconcat (flist, "\n", current_panel->dir.list[i].fname->str,
4154                                      (char *) NULL);
4155                     g_free (flist);
4156                     flist = tmp;
4157                 }
4158             }
4159 
4160         mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_text_to_file", (gpointer) flist);
4161         g_free (flist);
4162     }
4163     return TRUE;
4164 }
4165 
4166 /* --------------------------------------------------------------------------------------------- */
4167 
4168 static vfs_path_t *
4169 panel_recursive_cd_to_parent (const vfs_path_t * vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
4170 {
4171     vfs_path_t *cwd_vpath;
4172 
4173     cwd_vpath = vfs_path_clone (vpath);
4174 
4175     while (mc_chdir (cwd_vpath) < 0)
4176     {
4177         const char *panel_cwd_path;
4178         vfs_path_t *tmp_vpath;
4179 
4180         /* check if path contains only '/' */
4181         panel_cwd_path = vfs_path_as_str (cwd_vpath);
4182         if (panel_cwd_path != NULL && IS_PATH_SEP (panel_cwd_path[0]) && panel_cwd_path[1] == '\0')
4183             return NULL;
4184 
4185         tmp_vpath = vfs_path_vtokens_get (cwd_vpath, 0, -1);
4186         vfs_path_free (cwd_vpath, TRUE);
4187         cwd_vpath =
4188             vfs_path_build_filename (PATH_SEP_STR, vfs_path_as_str (tmp_vpath), (char *) NULL);
4189         vfs_path_free (tmp_vpath, TRUE);
4190     }
4191 
4192     return cwd_vpath;
4193 }
4194 
4195 /* --------------------------------------------------------------------------------------------- */
4196 
4197 static void
4198 panel_dir_list_callback (dir_list_cb_state_t state, void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
4199 {
4200     static int count = 0;
4201 
4202     (void) data;
4203 
4204     switch (state)
4205     {
4206     case DIR_OPEN:
4207         count = 0;
4208         break;
4209 
4210     case DIR_READ:
4211         count++;
4212         if ((count & 15) == 0)
4213             rotate_dash (TRUE);
4214         break;
4215 
4216     case DIR_CLOSE:
4217         rotate_dash (FALSE);
4218         break;
4219 
4220     default:
4221         g_assert_not_reached ();
4222     }
4223 }
4224 
4225 /* --------------------------------------------------------------------------------------------- */
4226 /*** public functions ****************************************************************************/
4227 /* --------------------------------------------------------------------------------------------- */
4228 
4229 void
4230 try_to_select (WPanel * panel, const char *name)
     /* [previous][next][first][last][top][bottom][index][help]  */
4231 {
4232     do_try_to_select (panel, name);
4233     select_item (panel);
4234 }
4235 
4236 /* --------------------------------------------------------------------------------------------- */
4237 
4238 void
4239 panel_clean_dir (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
4240 {
4241     panel->top_file = 0;
4242     panel->selected = 0;
4243     panel->marked = 0;
4244     panel->dirs_marked = 0;
4245     panel->total = 0;
4246     panel->quick_search.active = FALSE;
4247     panel->is_panelized = FALSE;
4248     panel->dirty = TRUE;
4249     panel->content_shift = -1;
4250     panel->max_shift = -1;
4251 
4252     dir_list_free_list (&panel->dir);
4253 }
4254 
4255 /* --------------------------------------------------------------------------------------------- */
4256 /**
4257  * Set Up panel's current dir object
4258  *
4259  * @param panel panel object
4260  * @param path_str string contain path
4261  */
4262 
4263 void
4264 panel_set_cwd (WPanel * panel, const vfs_path_t * vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
4265 {
4266     if (vpath != panel->cwd_vpath)      /* check if new vpath is not the panel->cwd_vpath object */
4267     {
4268         vfs_path_free (panel->cwd_vpath, TRUE);
4269         panel->cwd_vpath = vfs_path_clone (vpath);
4270     }
4271 }
4272 
4273 /* --------------------------------------------------------------------------------------------- */
4274 /**
4275  * Set Up panel's last working dir object
4276  *
4277  * @param panel panel object
4278  * @param path_str string contain path
4279  */
4280 
4281 void
4282 panel_set_lwd (WPanel * panel, const vfs_path_t * vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
4283 {
4284     if (vpath != panel->lwd_vpath)      /* check if new vpath is not the panel->lwd_vpath object */
4285     {
4286         vfs_path_free (panel->lwd_vpath, TRUE);
4287         panel->lwd_vpath = vfs_path_clone (vpath);
4288     }
4289 }
4290 
4291 /* --------------------------------------------------------------------------------------------- */
4292 /**
4293  * Creatie an empty panel with specified size.
4294  *
4295  * @param panel_name name of panel for setup retieving
4296  *
4297  * @return new instance of WPanel
4298  */
4299 
4300 WPanel *
4301 panel_sized_empty_new (const char *panel_name, int y, int x, int lines, int cols)
     /* [previous][next][first][last][top][bottom][index][help]  */
4302 {
4303     WPanel *panel;
4304     Widget *w;
4305     char *section;
4306     int i, err;
4307 
4308     panel = g_new0 (WPanel, 1);
4309     w = WIDGET (panel);
4310     widget_init (w, y, x, lines, cols, panel_callback, panel_mouse_callback);
4311     w->options |= WOP_SELECTABLE | WOP_TOP_SELECT;
4312     w->keymap = panel_map;
4313 
4314     panel->dir.size = DIR_LIST_MIN_SIZE;
4315     panel->dir.list = g_new (file_entry_t, panel->dir.size);
4316     panel->dir.len = 0;
4317     panel->dir.callback = panel_dir_list_callback;
4318 
4319     panel->list_cols = 1;
4320     panel->brief_cols = 2;
4321     panel->dirty = TRUE;
4322     panel->content_shift = -1;
4323     panel->max_shift = -1;
4324 
4325     panel->list_format = list_full;
4326     panel->user_format = g_strdup (DEFAULT_USER_FORMAT);
4327 
4328     for (i = 0; i < LIST_FORMATS; i++)
4329         panel->user_status_format[i] = g_strdup (DEFAULT_USER_FORMAT);
4330 
4331 #ifdef HAVE_CHARSET
4332     panel->codepage = SELECT_CHARSET_NO_TRANSLATE;
4333 #endif
4334 
4335     panel->frame_size = frame_half;
4336 
4337     panel->quick_search.buffer = g_string_sized_new (MC_MAXFILENAMELEN);
4338     panel->quick_search.prev_buffer = g_string_sized_new (MC_MAXFILENAMELEN);
4339 
4340     panel->name = g_strdup (panel_name);
4341     panel->dir_history.name = g_strconcat ("Dir Hist ", panel->name, (char *) NULL);
4342     /* directories history will be get later */
4343 
4344     section = g_strconcat ("Temporal:", panel->name, (char *) NULL);
4345     if (!mc_config_has_group (mc_global.main_config, section))
4346     {
4347         g_free (section);
4348         section = g_strdup (panel->name);
4349     }
4350     panel_load_setup (panel, section);
4351     g_free (section);
4352 
4353     /* Load format strings */
4354     err = set_panel_formats (panel);
4355     if (err != 0)
4356         set_panel_formats (panel);
4357 
4358     return panel;
4359 }
4360 
4361 /* --------------------------------------------------------------------------------------------- */
4362 /**
4363  * Panel creation for specified size and directory.
4364  *
4365  * @param panel_name name of panel for setup retieving
4366  * @param y y coordinate of top-left corner
4367  * @param x x coordinate of top-left corner
4368  * @param lines vertical size
4369  * @param cols horizontal size
4370  * @param vpath working panel directory. If NULL then current directory is used
4371  *
4372  * @return new instance of WPanel
4373  */
4374 
4375 WPanel *
4376 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]  */
4377                           const vfs_path_t * vpath)
4378 {
4379     WPanel *panel;
4380     char *curdir = NULL;
4381 #ifdef HAVE_CHARSET
4382     const vfs_path_element_t *path_element;
4383 #endif
4384 
4385     panel = panel_sized_empty_new (panel_name, y, x, lines, cols);
4386 
4387     if (vpath != NULL)
4388     {
4389         curdir = _vfs_get_cwd ();
4390         panel_set_cwd (panel, vpath);
4391     }
4392     else
4393     {
4394         vfs_setup_cwd ();
4395         panel->cwd_vpath = vfs_path_clone (vfs_get_raw_current_dir ());
4396     }
4397 
4398     panel_set_lwd (panel, vfs_get_raw_current_dir ());
4399 
4400 #ifdef HAVE_CHARSET
4401     path_element = vfs_path_get_by_index (panel->cwd_vpath, -1);
4402     if (path_element->encoding != NULL)
4403         panel->codepage = get_codepage_index (path_element->encoding);
4404 #endif
4405 
4406     if (mc_chdir (panel->cwd_vpath) != 0)
4407     {
4408 #ifdef HAVE_CHARSET
4409         panel->codepage = SELECT_CHARSET_NO_TRANSLATE;
4410 #endif
4411         vfs_setup_cwd ();
4412         vfs_path_free (panel->cwd_vpath, TRUE);
4413         panel->cwd_vpath = vfs_path_clone (vfs_get_raw_current_dir ());
4414     }
4415 
4416     /* Load the default format */
4417     if (!dir_list_load (&panel->dir, panel->cwd_vpath, panel->sort_field->sort_routine,
4418                         &panel->sort_info, panel->filter))
4419         message (D_ERROR, MSG_ERROR, _("Cannot read directory contents"));
4420 
4421     /* Restore old right path */
4422     if (curdir != NULL)
4423     {
4424         vfs_path_t *tmp_vpath;
4425         int err;
4426 
4427         tmp_vpath = vfs_path_from_str (curdir);
4428         mc_chdir (tmp_vpath);
4429         vfs_path_free (tmp_vpath, TRUE);
4430         (void) err;
4431     }
4432     g_free (curdir);
4433 
4434     return panel;
4435 }
4436 
4437 /* --------------------------------------------------------------------------------------------- */
4438 
4439 void
4440 panel_reload (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
4441 {
4442     struct stat current_stat;
4443     vfs_path_t *cwd_vpath;
4444 
4445     if (panels_options.fast_reload && stat (vfs_path_as_str (panel->cwd_vpath), &current_stat) == 0
4446         && current_stat.st_ctime == panel->dir_stat.st_ctime
4447         && current_stat.st_mtime == panel->dir_stat.st_mtime)
4448         return;
4449 
4450     cwd_vpath = panel_recursive_cd_to_parent (panel->cwd_vpath);
4451     vfs_path_free (panel->cwd_vpath, TRUE);
4452 
4453     if (cwd_vpath == NULL)
4454     {
4455         panel->cwd_vpath = vfs_path_from_str (PATH_SEP_STR);
4456         panel_clean_dir (panel);
4457         dir_list_init (&panel->dir);
4458         return;
4459     }
4460 
4461     panel->cwd_vpath = cwd_vpath;
4462     memset (&(panel->dir_stat), 0, sizeof (panel->dir_stat));
4463     show_dir (panel);
4464 
4465     if (!dir_list_reload (&panel->dir, panel->cwd_vpath, panel->sort_field->sort_routine,
4466                           &panel->sort_info, panel->filter))
4467         message (D_ERROR, MSG_ERROR, _("Cannot read directory contents"));
4468 
4469     panel->dirty = TRUE;
4470     if (panel->selected >= panel->dir.len)
4471         do_select (panel, panel->dir.len - 1);
4472 
4473     recalculate_panel_summary (panel);
4474 }
4475 
4476 /* --------------------------------------------------------------------------------------------- */
4477 /* Switches the panel to the mode specified in the format           */
4478 /* Seting up both format and status string. Return: 0 - on success; */
4479 /* 1 - format error; 2 - status error; 3 - errors in both formats.  */
4480 
4481 int
4482 set_panel_formats (WPanel * p)
     /* [previous][next][first][last][top][bottom][index][help]  */
4483 {
4484     GSList *form;
4485     char *err = NULL;
4486     int retcode = 0;
4487 
4488     form = use_display_format (p, panel_format (p), &err, FALSE);
4489 
4490     if (err != NULL)
4491     {
4492         g_free (err);
4493         retcode = 1;
4494     }
4495     else
4496     {
4497         g_slist_free_full (p->format, (GDestroyNotify) format_item_free);
4498         p->format = form;
4499     }
4500 
4501     if (panels_options.show_mini_info)
4502     {
4503         form = use_display_format (p, mini_status_format (p), &err, TRUE);
4504 
4505         if (err != NULL)
4506         {
4507             g_free (err);
4508             retcode += 2;
4509         }
4510         else
4511         {
4512             g_slist_free_full (p->status_format, (GDestroyNotify) format_item_free);
4513             p->status_format = form;
4514         }
4515     }
4516 
4517     panel_update_cols (WIDGET (p), p->frame_size);
4518 
4519     if (retcode)
4520         message (D_ERROR, _("Warning"),
4521                  _("User supplied format looks invalid, reverting to default."));
4522     if (retcode & 0x01)
4523     {
4524         g_free (p->user_format);
4525         p->user_format = g_strdup (DEFAULT_USER_FORMAT);
4526     }
4527     if (retcode & 0x02)
4528     {
4529         g_free (p->user_status_format[p->list_format]);
4530         p->user_status_format[p->list_format] = g_strdup (DEFAULT_USER_FORMAT);
4531     }
4532 
4533     return retcode;
4534 }
4535 
4536 /* --------------------------------------------------------------------------------------------- */
4537 
4538 /* Select current item and readjust the panel */
4539 void
4540 select_item (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
4541 {
4542     adjust_top_file (panel);
4543 
4544     panel->dirty = TRUE;
4545 
4546     execute_hooks (select_file_hook);
4547 }
4548 
4549 /* --------------------------------------------------------------------------------------------- */
4550 /** Clears all files in the panel, used only when one file was marked */
4551 void
4552 unmark_files (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
4553 {
4554     if (panel->marked != 0)
4555     {
4556         int i;
4557 
4558         for (i = 0; i < panel->dir.len; i++)
4559             file_mark (panel, i, 0);
4560 
4561         panel->dirs_marked = 0;
4562         panel->marked = 0;
4563         panel->total = 0;
4564     }
4565 }
4566 
4567 /* --------------------------------------------------------------------------------------------- */
4568 /** Recalculate the panels summary information, used e.g. when marked
4569    files might have been removed by an external command */
4570 
4571 void
4572 recalculate_panel_summary (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
4573 {
4574     int i;
4575 
4576     panel->marked = 0;
4577     panel->dirs_marked = 0;
4578     panel->total = 0;
4579 
4580     for (i = 0; i < panel->dir.len; i++)
4581         if (panel->dir.list[i].f.marked)
4582         {
4583             /* do_file_mark will return immediately if newmark == oldmark.
4584                So we have to first unmark it to get panel's summary information
4585                updated. (Norbert) */
4586             panel->dir.list[i].f.marked = 0;
4587             do_file_mark (panel, i, 1);
4588         }
4589 }
4590 
4591 /* --------------------------------------------------------------------------------------------- */
4592 /** This routine marks a file or a directory */
4593 
4594 void
4595 do_file_mark (WPanel * panel, int idx, int mark)
     /* [previous][next][first][last][top][bottom][index][help]  */
4596 {
4597     if (panel->dir.list[idx].f.marked == mark)
4598         return;
4599 
4600     /* Only '..' can't be marked, '.' isn't visible */
4601     if (DIR_IS_DOTDOT (panel->dir.list[idx].fname->str))
4602         return;
4603 
4604     file_mark (panel, idx, mark);
4605     if (panel->dir.list[idx].f.marked)
4606     {
4607         panel->marked++;
4608 
4609         if (S_ISDIR (panel->dir.list[idx].st.st_mode))
4610         {
4611             if (panel->dir.list[idx].f.dir_size_computed)
4612                 panel->total += (uintmax_t) panel->dir.list[idx].st.st_size;
4613             panel->dirs_marked++;
4614         }
4615         else
4616             panel->total += (uintmax_t) panel->dir.list[idx].st.st_size;
4617 
4618         set_colors (panel);
4619     }
4620     else
4621     {
4622         if (S_ISDIR (panel->dir.list[idx].st.st_mode))
4623         {
4624             if (panel->dir.list[idx].f.dir_size_computed)
4625                 panel->total -= (uintmax_t) panel->dir.list[idx].st.st_size;
4626             panel->dirs_marked--;
4627         }
4628         else
4629             panel->total -= (uintmax_t) panel->dir.list[idx].st.st_size;
4630 
4631         panel->marked--;
4632     }
4633 }
4634 
4635 /* --------------------------------------------------------------------------------------------- */
4636 /**
4637  * Changes the current directory of the panel.
4638  * Record change in the directory history.
4639  */
4640 gboolean
4641 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]  */
4642 {
4643     gboolean r;
4644 
4645     r = panel_do_cd_int (panel, new_dir_vpath, cd_type);
4646     if (r)
4647         directory_history_add (panel, panel->cwd_vpath);
4648     return r;
4649 }
4650 
4651 /* --------------------------------------------------------------------------------------------- */
4652 
4653 void
4654 file_mark (WPanel * panel, int lc_index, int val)
     /* [previous][next][first][last][top][bottom][index][help]  */
4655 {
4656     if (panel->dir.list[lc_index].f.marked != val)
4657     {
4658         panel->dir.list[lc_index].f.marked = val;
4659         panel->dirty = TRUE;
4660     }
4661 }
4662 
4663 /* --------------------------------------------------------------------------------------------- */
4664 
4665 void
4666 panel_re_sort (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
4667 {
4668     char *filename;
4669     file_entry_t *fe;
4670     int i;
4671 
4672     if (panel == NULL)
4673         return;
4674 
4675     fe = selection (panel);
4676     filename = g_strndup (fe->fname->str, fe->fname->len);
4677     unselect_item (panel);
4678     dir_list_sort (&panel->dir, panel->sort_field->sort_routine, &panel->sort_info);
4679     panel->selected = -1;
4680 
4681     for (i = panel->dir.len; i != 0; i--)
4682         if (strcmp (panel->dir.list[i - 1].fname->str, filename) == 0)
4683         {
4684             panel->selected = i - 1;
4685             break;
4686         }
4687 
4688     g_free (filename);
4689     panel->top_file = panel->selected - panel_items (panel) / 2;
4690     select_item (panel);
4691     panel->dirty = TRUE;
4692 }
4693 
4694 /* --------------------------------------------------------------------------------------------- */
4695 
4696 void
4697 panel_set_sort_order (WPanel * panel, const panel_field_t * sort_order)
     /* [previous][next][first][last][top][bottom][index][help]  */
4698 {
4699     if (sort_order == NULL)
4700         return;
4701 
4702     panel->sort_field = sort_order;
4703 
4704     /* The directory is already sorted, we have to load the unsorted stuff */
4705     if (sort_order->sort_routine == (GCompareFunc) unsorted)
4706     {
4707         char *current_file;
4708 
4709         current_file = g_strndup (panel->dir.list[panel->selected].fname->str,
4710                                   panel->dir.list[panel->selected].fname->len);
4711         panel_reload (panel);
4712         try_to_select (panel, current_file);
4713         g_free (current_file);
4714     }
4715     panel_re_sort (panel);
4716 }
4717 
4718 /* --------------------------------------------------------------------------------------------- */
4719 
4720 #ifdef HAVE_CHARSET
4721 
4722 /**
4723  * Change panel encoding.
4724  * @param panel WPanel object
4725  */
4726 
4727 void
4728 panel_change_encoding (WPanel * panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
4729 {
4730     const char *encoding = NULL;
4731     char *errmsg;
4732     int r;
4733 
4734     r = select_charset (-1, -1, panel->codepage, FALSE);
4735 
4736     if (r == SELECT_CHARSET_CANCEL)
4737         return;                 /* Cancel */
4738 
4739     panel->codepage = r;
4740 
4741     if (panel->codepage == SELECT_CHARSET_NO_TRANSLATE)
4742     {
4743         /* No translation */
4744         vfs_path_t *cd_path_vpath;
4745 
4746         g_free (init_translation_table (mc_global.display_codepage, mc_global.display_codepage));
4747         cd_path_vpath = remove_encoding_from_path (panel->cwd_vpath);
4748         panel_do_cd (panel, cd_path_vpath, cd_parse_command);
4749         show_dir (panel);
4750         vfs_path_free (cd_path_vpath, TRUE);
4751         return;
4752     }
4753 
4754     errmsg = init_translation_table (panel->codepage, mc_global.display_codepage);
4755     if (errmsg != NULL)
4756     {
4757         message (D_ERROR, MSG_ERROR, "%s", errmsg);
4758         g_free (errmsg);
4759         return;
4760     }
4761 
4762     encoding = get_codepage_id (panel->codepage);
4763     if (encoding != NULL)
4764     {
4765         vfs_path_change_encoding (panel->cwd_vpath, encoding);
4766 
4767         if (!panel_do_cd (panel, panel->cwd_vpath, cd_parse_command))
4768             message (D_ERROR, MSG_ERROR, _("Cannot chdir to \"%s\""),
4769                      vfs_path_as_str (panel->cwd_vpath));
4770     }
4771 }
4772 
4773 /* --------------------------------------------------------------------------------------------- */
4774 
4775 /**
4776  * Remove encode info from last path element.
4777  *
4778  */
4779 vfs_path_t *
4780 remove_encoding_from_path (const vfs_path_t * vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
4781 {
4782     vfs_path_t *ret_vpath;
4783     GString *tmp_conv;
4784     int indx;
4785 
4786     ret_vpath = vfs_path_new ();
4787 
4788     tmp_conv = g_string_new ("");
4789 
4790     for (indx = 0; indx < vfs_path_elements_count (vpath); indx++)
4791     {
4792         GIConv converter;
4793         vfs_path_element_t *path_element;
4794 
4795         path_element = vfs_path_element_clone (vfs_path_get_by_index (vpath, indx));
4796 
4797         if (path_element->encoding == NULL)
4798         {
4799             vfs_path_add_element (ret_vpath, path_element);
4800             continue;
4801         }
4802 
4803         converter = str_crt_conv_to (path_element->encoding);
4804         if (converter == INVALID_CONV)
4805         {
4806             vfs_path_add_element (ret_vpath, path_element);
4807             continue;
4808         }
4809 
4810         MC_PTR_FREE (path_element->encoding);
4811 
4812         str_vfs_convert_from (converter, path_element->path, tmp_conv);
4813 
4814         g_free (path_element->path);
4815         path_element->path = g_strndup (tmp_conv->str, tmp_conv->len);
4816 
4817         g_string_set_size (tmp_conv, 0);
4818 
4819         str_close_conv (converter);
4820         str_close_conv (path_element->dir.converter);
4821         path_element->dir.converter = INVALID_CONV;
4822         vfs_path_add_element (ret_vpath, path_element);
4823     }
4824     g_string_free (tmp_conv, TRUE);
4825     return ret_vpath;
4826 }
4827 #endif /* HAVE_CHARSET */
4828 
4829 /* --------------------------------------------------------------------------------------------- */
4830 
4831 /**
4832  * This routine reloads the directory in both panels. It tries to
4833  * select current_file in current_panel and other_file in other_panel.
4834  * If current_file == -1 then it automatically sets current_file and
4835  * other_file to the currently selected files in the panels.
4836  *
4837  * If flags has the UP_ONLY_CURRENT bit toggled on, then it
4838  * will not reload the other panel.
4839  *
4840  * @param flags for reload panel
4841  * @param current_file name of the current file
4842  */
4843 
4844 void
4845 update_panels (panel_update_flags_t flags, const char *current_file)
     /* [previous][next][first][last][top][bottom][index][help]  */
4846 {
4847     WPanel *panel;
4848 
4849     /* first, update other panel... */
4850     if ((flags & UP_ONLY_CURRENT) == 0)
4851         update_one_panel (get_other_index (), flags, UP_KEEPSEL);
4852     /* ...then current one */
4853     update_one_panel (get_current_index (), flags, current_file);
4854 
4855     if (get_current_type () == view_listing)
4856         panel = PANEL (get_panel_widget (get_current_index ()));
4857     else
4858         panel = PANEL (get_panel_widget (get_other_index ()));
4859 
4860     if (!panel->is_panelized)
4861         (void) mc_chdir (panel->cwd_vpath);
4862 }
4863 
4864 /* --------------------------------------------------------------------------------------------- */
4865 
4866 gsize
4867 panel_get_num_of_sortable_fields (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
4868 {
4869     gsize ret = 0, lc_index;
4870 
4871     for (lc_index = 0; panel_fields[lc_index].id != NULL; lc_index++)
4872         if (panel_fields[lc_index].is_user_choice)
4873             ret++;
4874     return ret;
4875 }
4876 
4877 /* --------------------------------------------------------------------------------------------- */
4878 
4879 char **
4880 panel_get_sortable_fields (gsize * array_size)
     /* [previous][next][first][last][top][bottom][index][help]  */
4881 {
4882     char **ret;
4883     gsize lc_index, i;
4884 
4885     lc_index = panel_get_num_of_sortable_fields ();
4886 
4887     ret = g_try_new0 (char *, lc_index + 1);
4888     if (ret == NULL)
4889         return NULL;
4890 
4891     if (array_size != NULL)
4892         *array_size = lc_index;
4893 
4894     lc_index = 0;
4895 
4896     for (i = 0; panel_fields[i].id != NULL; i++)
4897         if (panel_fields[i].is_user_choice)
4898             ret[lc_index++] = g_strdup (_(panel_fields[i].title_hotkey));
4899 
4900     return ret;
4901 }
4902 
4903 /* --------------------------------------------------------------------------------------------- */
4904 
4905 const panel_field_t *
4906 panel_get_field_by_id (const char *name)
     /* [previous][next][first][last][top][bottom][index][help]  */
4907 {
4908     gsize lc_index;
4909 
4910     for (lc_index = 0; panel_fields[lc_index].id != NULL; lc_index++)
4911         if (panel_fields[lc_index].id != NULL && strcmp (name, panel_fields[lc_index].id) == 0)
4912             return &panel_fields[lc_index];
4913 
4914     return NULL;
4915 }
4916 
4917 /* --------------------------------------------------------------------------------------------- */
4918 
4919 const panel_field_t *
4920 panel_get_field_by_title_hotkey (const char *name)
     /* [previous][next][first][last][top][bottom][index][help]  */
4921 {
4922     gsize lc_index;
4923 
4924     for (lc_index = 0; panel_fields[lc_index].id != NULL; lc_index++)
4925         if (panel_fields[lc_index].title_hotkey != NULL &&
4926             strcmp (name, _(panel_fields[lc_index].title_hotkey)) == 0)
4927             return &panel_fields[lc_index];
4928 
4929     return NULL;
4930 }
4931 
4932 /* --------------------------------------------------------------------------------------------- */
4933 
4934 const panel_field_t *
4935 panel_get_field_by_title (const char *name)
     /* [previous][next][first][last][top][bottom][index][help]  */
4936 {
4937     gsize lc_index;
4938 
4939     for (lc_index = 0; panel_fields[lc_index].id != NULL; lc_index++)
4940     {
4941         const char *title;
4942 
4943         title = panel_get_title_without_hotkey (panel_fields[lc_index].title_hotkey);
4944         if (strcmp (title, name) == 0)
4945             return &panel_fields[lc_index];
4946     }
4947 
4948     return NULL;
4949 }
4950 
4951 /* --------------------------------------------------------------------------------------------- */
4952 
4953 gsize
4954 panel_get_num_of_user_possible_fields (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
4955 {
4956     gsize ret = 0, lc_index;
4957 
4958     for (lc_index = 0; panel_fields[lc_index].id != NULL; lc_index++)
4959         if (panel_fields[lc_index].use_in_user_format)
4960             ret++;
4961 
4962     return ret;
4963 }
4964 
4965 /* --------------------------------------------------------------------------------------------- */
4966 
4967 char **
4968 panel_get_user_possible_fields (gsize * array_size)
     /* [previous][next][first][last][top][bottom][index][help]  */
4969 {
4970     char **ret;
4971     gsize lc_index, i;
4972 
4973     lc_index = panel_get_num_of_user_possible_fields ();
4974 
4975     ret = g_try_new0 (char *, lc_index + 1);
4976     if (ret == NULL)
4977         return NULL;
4978 
4979     if (array_size != NULL)
4980         *array_size = lc_index;
4981 
4982     lc_index = 0;
4983 
4984     for (i = 0; panel_fields[i].id != NULL; i++)
4985         if (panel_fields[i].use_in_user_format)
4986             ret[lc_index++] = g_strdup (_(panel_fields[i].title_hotkey));
4987 
4988     return ret;
4989 }
4990 
4991 /* --------------------------------------------------------------------------------------------- */
4992 
4993 void
4994 panel_init (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
4995 {
4996     panel_sort_up_char = mc_skin_get ("widget-panel", "sort-up-char", "'");
4997     panel_sort_down_char = mc_skin_get ("widget-panel", "sort-down-char", ".");
4998     panel_hiddenfiles_show_char = mc_skin_get ("widget-panel", "hiddenfiles-show-char", ".");
4999     panel_hiddenfiles_hide_char = mc_skin_get ("widget-panel", "hiddenfiles-hide-char", ".");
5000     panel_history_prev_item_char = mc_skin_get ("widget-panel", "history-prev-item-char", "<");
5001     panel_history_next_item_char = mc_skin_get ("widget-panel", "history-next-item-char", ">");
5002     panel_history_show_list_char = mc_skin_get ("widget-panel", "history-show-list-char", "^");
5003     panel_filename_scroll_left_char =
5004         mc_skin_get ("widget-panel", "filename-scroll-left-char", "{");
5005     panel_filename_scroll_right_char =
5006         mc_skin_get ("widget-panel", "filename-scroll-right-char", "}");
5007 
5008     string_file_name_buffer = g_string_sized_new (MC_MAXFILENAMELEN);
5009 
5010     mc_event_add (MCEVENT_GROUP_FILEMANAGER, "update_panels", event_update_panels, NULL, NULL);
5011     mc_event_add (MCEVENT_GROUP_FILEMANAGER, "panel_save_current_file_to_clip_file",
5012                   panel_save_current_file_to_clip_file, NULL, NULL);
5013 }
5014 
5015 /* --------------------------------------------------------------------------------------------- */
5016 
5017 void
5018 panel_deinit (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
5019 {
5020     g_free (panel_sort_up_char);
5021     g_free (panel_sort_down_char);
5022     g_free (panel_hiddenfiles_show_char);
5023