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