Ticket #3497: panel.c

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