root/src/filemanager/panel.c

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

DEFINITIONS

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

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

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