Merge branch '3205_eta'
[midnight-commander.git] / src / filemanager / panel.c
blob44b25bd416ec6cb3d48bda7aafbcade80ee955cb
1 /*
2 Panel managing.
4 Copyright (C) 1994-2024
5 Free Software Foundation, Inc.
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-2023
13 This file is part of the Midnight Commander.
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.
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.
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/>.
29 /** \file panel.c
30 * \brief Source: panel managin module
33 #include <config.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
39 #include "lib/global.h"
41 #include "lib/tty/tty.h"
42 #include "lib/tty/key.h" /* XCTRL and ALT macros */
43 #include "lib/skin.h"
44 #include "lib/strutil.h"
45 #include "lib/mcconfig.h"
46 #include "lib/vfs/vfs.h"
47 #include "lib/unixcompat.h"
48 #include "lib/search.h"
49 #include "lib/timefmt.h" /* file_date() */
50 #include "lib/util.h"
51 #include "lib/widget.h"
52 #ifdef HAVE_CHARSET
53 #include "lib/charsets.h" /* get_codepage_id () */
54 #endif
55 #include "lib/event.h"
57 #include "src/setup.h" /* For loading/saving panel options */
58 #include "src/execute.h"
59 #ifdef HAVE_CHARSET
60 #include "src/selcodepage.h" /* select_charset (), SELECT_CHARSET_NO_TRANSLATE */
61 #endif
62 #include "src/keymap.h" /* global_keymap_t */
63 #include "src/history.h"
64 #ifdef ENABLE_SUBSHELL
65 #include "src/subshell/subshell.h" /* do_subshell_chdir() */
66 #endif
68 #include "src/usermenu.h"
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 #include "cd.h" /* cd_error_message() */
81 #include "panel.h"
83 /*** global variables ****************************************************************************/
85 /* The hook list for the select file function */
86 hook_t *select_file_hook = NULL;
88 mc_fhl_t *mc_filehighlight = NULL;
90 /*** file scope macro definitions ****************************************************************/
92 typedef enum
94 FATTR_NORMAL = 0,
95 FATTR_CURRENT,
96 FATTR_MARKED,
97 FATTR_MARKED_CURRENT,
98 FATTR_STATUS
99 } file_attr_t;
101 #define DEFAULT_USER_FORMAT "half type name | size | perm"
103 /* select/unselect dialog results */
104 #define SELECT_RESET ((mc_search_t *)(-1))
105 #define SELECT_ERROR ((mc_search_t *)(-2))
107 /* mouse position relative to file list */
108 #define MOUSE_UPPER_FILE_LIST (-1)
109 #define MOUSE_BELOW_FILE_LIST (-2)
110 #define MOUSE_AFTER_LAST_FILE (-3)
112 /*** file scope type declarations ****************************************************************/
114 typedef enum
116 MARK_DONT_MOVE = 0,
117 MARK_DOWN = 1,
118 MARK_FORCE_DOWN = 2,
119 MARK_FORCE_UP = 3
120 } mark_act_t;
123 * This describes a format item. The parse_display_format routine parses
124 * the user specified format and creates a linked list of format_item_t structures.
126 typedef struct format_item_t
128 int requested_field_len;
129 int field_len;
130 align_crt_t just_mode;
131 gboolean expand;
132 const char *(*string_fn) (const file_entry_t * fe, int len);
133 char *title;
134 const char *id;
135 } format_item_t;
137 /* File name scroll states */
138 typedef enum
140 FILENAME_NOSCROLL = 1,
141 FILENAME_SCROLL_LEFT = 2,
142 FILENAME_SCROLL_RIGHT = 4
143 } filename_scroll_flag_t;
145 /*** forward declarations (file scope functions) *************************************************/
147 static const char *string_file_name (const file_entry_t * fe, int len);
148 static const char *string_file_size (const file_entry_t * fe, int len);
149 static const char *string_file_size_brief (const file_entry_t * fe, int len);
150 static const char *string_file_type (const file_entry_t * fe, int len);
151 static const char *string_file_mtime (const file_entry_t * fe, int len);
152 static const char *string_file_atime (const file_entry_t * fe, int len);
153 static const char *string_file_ctime (const file_entry_t * fe, int len);
154 static const char *string_file_permission (const file_entry_t * fe, int len);
155 static const char *string_file_perm_octal (const file_entry_t * fe, int len);
156 static const char *string_file_nlinks (const file_entry_t * fe, int len);
157 static const char *string_inode (const file_entry_t * fe, int len);
158 static const char *string_file_nuid (const file_entry_t * fe, int len);
159 static const char *string_file_ngid (const file_entry_t * fe, int len);
160 static const char *string_file_owner (const file_entry_t * fe, int len);
161 static const char *string_file_group (const file_entry_t * fe, int len);
162 static const char *string_marked (const file_entry_t * fe, int len);
163 static const char *string_space (const file_entry_t * fe, int len);
164 static const char *string_dot (const file_entry_t * fe, int len);
166 /*** file scope variables ************************************************************************/
168 /* *INDENT-OFF* */
169 static panel_field_t panel_fields[] = {
171 "unsorted", 12, TRUE, J_LEFT_FIT,
172 /* TRANSLATORS: one single character to represent 'unsorted' sort mode */
173 /* TRANSLATORS: no need to translate 'sort', it's just a context prefix */
174 N_("sort|u"),
175 N_("&Unsorted"), TRUE, FALSE,
176 string_file_name,
177 (GCompareFunc) unsorted
181 "name", 12, TRUE, J_LEFT_FIT,
182 /* TRANSLATORS: one single character to represent 'name' sort mode */
183 /* TRANSLATORS: no need to translate 'sort', it's just a context prefix */
184 N_("sort|n"),
185 N_("&Name"), TRUE, TRUE,
186 string_file_name,
187 (GCompareFunc) sort_name
191 "version", 12, TRUE, J_LEFT_FIT,
192 /* TRANSLATORS: one single character to represent 'version' sort mode */
193 /* TRANSLATORS: no need to translate 'sort', it's just a context prefix */
194 N_("sort|v"),
195 N_("&Version"), TRUE, FALSE,
196 string_file_name,
197 (GCompareFunc) sort_vers
201 "extension", 12, TRUE, J_LEFT_FIT,
202 /* TRANSLATORS: one single character to represent 'extension' sort mode */
203 /* TRANSLATORS: no need to translate 'sort', it's just a context prefix */
204 N_("sort|e"),
205 N_("E&xtension"), TRUE, FALSE,
206 string_file_name, /* TODO: string_file_ext */
207 (GCompareFunc) sort_ext
211 "size", 7, FALSE, J_RIGHT,
212 /* TRANSLATORS: one single character to represent 'size' sort mode */
213 /* TRANSLATORS: no need to translate 'sort', it's just a context prefix */
214 N_("sort|s"),
215 N_("&Size"), TRUE, TRUE,
216 string_file_size,
217 (GCompareFunc) sort_size
221 "bsize", 7, FALSE, J_RIGHT,
223 N_("Block Size"), FALSE, FALSE,
224 string_file_size_brief,
225 (GCompareFunc) sort_size
229 "type", 1, FALSE, J_LEFT,
231 "", FALSE, TRUE,
232 string_file_type,
233 NULL
237 "mtime", 12, FALSE, J_RIGHT,
238 /* TRANSLATORS: one single character to represent 'Modify time' sort mode */
239 /* TRANSLATORS: no need to translate 'sort', it's just a context prefix */
240 N_("sort|m"),
241 N_("&Modify time"), TRUE, TRUE,
242 string_file_mtime,
243 (GCompareFunc) sort_time
247 "atime", 12, FALSE, J_RIGHT,
248 /* TRANSLATORS: one single character to represent 'Access time' sort mode */
249 /* TRANSLATORS: no need to translate 'sort', it's just a context prefix */
250 N_("sort|a"),
251 N_("&Access time"), TRUE, TRUE,
252 string_file_atime,
253 (GCompareFunc) sort_atime
257 "ctime", 12, FALSE, J_RIGHT,
258 /* TRANSLATORS: one single character to represent 'Change time' sort mode */
259 /* TRANSLATORS: no need to translate 'sort', it's just a context prefix */
260 N_("sort|h"),
261 N_("C&hange time"), TRUE, TRUE,
262 string_file_ctime,
263 (GCompareFunc) sort_ctime
267 "perm", 10, FALSE, J_LEFT,
269 N_("Permission"), FALSE, TRUE,
270 string_file_permission,
271 NULL
275 "mode", 6, FALSE, J_RIGHT,
277 N_("Perm"), FALSE, TRUE,
278 string_file_perm_octal,
279 NULL
283 "nlink", 2, FALSE, J_RIGHT,
285 N_("Nl"), FALSE, TRUE,
286 string_file_nlinks, NULL
290 "inode", 5, FALSE, J_RIGHT,
291 /* TRANSLATORS: one single character to represent 'inode' sort mode */
292 /* TRANSLATORS: no need to translate 'sort', it's just a context prefix */
293 N_("sort|i"),
294 N_("&Inode"), TRUE, TRUE,
295 string_inode,
296 (GCompareFunc) sort_inode
300 "nuid", 5, FALSE, J_RIGHT,
302 N_("UID"), FALSE, FALSE,
303 string_file_nuid,
304 NULL
308 "ngid", 5, FALSE, J_RIGHT,
310 N_("GID"), FALSE, FALSE,
311 string_file_ngid,
312 NULL
316 "owner", 8, FALSE, J_LEFT_FIT,
318 N_("Owner"), FALSE, TRUE,
319 string_file_owner,
320 NULL
324 "group", 8, FALSE, J_LEFT_FIT,
326 N_("Group"), FALSE, TRUE,
327 string_file_group,
328 NULL
332 "mark", 1, FALSE, J_RIGHT,
334 " ", FALSE, TRUE,
335 string_marked,
336 NULL
340 "|", 1, FALSE, J_RIGHT,
342 " ", FALSE, TRUE,
343 NULL,
344 NULL
348 "space", 1, FALSE, J_RIGHT,
350 " ", FALSE, TRUE,
351 string_space,
352 NULL
356 "dot", 1, FALSE, J_RIGHT,
358 " ", FALSE, FALSE,
359 string_dot,
360 NULL
364 NULL, 0, FALSE, J_RIGHT, NULL, NULL, FALSE, FALSE, NULL, NULL
367 /* *INDENT-ON* */
369 static char *panel_sort_up_char = NULL;
370 static char *panel_sort_down_char = NULL;
372 static char *panel_hiddenfiles_show_char = NULL;
373 static char *panel_hiddenfiles_hide_char = NULL;
374 static char *panel_history_prev_item_char = NULL;
375 static char *panel_history_next_item_char = NULL;
376 static char *panel_history_show_list_char = NULL;
377 static char *panel_filename_scroll_left_char = NULL;
378 static char *panel_filename_scroll_right_char = NULL;
380 /* Panel that selection started */
381 static WPanel *mouse_mark_panel = NULL;
383 static gboolean mouse_marking = FALSE;
384 static int state_mark = 0;
386 static GString *string_file_name_buffer;
388 /* --------------------------------------------------------------------------------------------- */
389 /*** file scope functions ************************************************************************/
390 /* --------------------------------------------------------------------------------------------- */
392 static panelized_descr_t *
393 panelized_descr_new (void)
395 panelized_descr_t *p;
397 p = g_new0 (panelized_descr_t, 1);
398 p->list.len = -1;
400 return p;
403 /* --------------------------------------------------------------------------------------------- */
405 static void
406 panelized_descr_free (panelized_descr_t *p)
408 if (p != NULL)
410 dir_list_free_list (&p->list);
411 vfs_path_free (p->root_vpath, TRUE);
412 g_free (p);
416 /* --------------------------------------------------------------------------------------------- */
418 static void
419 set_colors (const WPanel *panel)
421 (void) panel;
423 tty_set_normal_attrs ();
424 tty_setcolor (NORMAL_COLOR);
427 /* --------------------------------------------------------------------------------------------- */
428 /** Delete format_item_t object */
430 static void
431 format_item_free (format_item_t *format)
433 g_free (format->title);
434 g_free (format);
437 /* --------------------------------------------------------------------------------------------- */
438 /** Extract the number of available lines in a panel */
440 static int
441 panel_lines (const WPanel *p)
443 /* 3 lines are: top frame, column header, button frame */
444 return (CONST_WIDGET (p)->rect.lines - 3 - (panels_options.show_mini_info ? 2 : 0));
447 /* --------------------------------------------------------------------------------------------- */
448 /** This code relies on the default justification!!! */
450 static void
451 add_permission_string (const char *dest, int width, file_entry_t *fe, file_attr_t attr, int color,
452 gboolean is_octal)
454 int i, r, l;
456 l = get_user_permissions (&fe->st);
458 if (is_octal)
460 /* Place of the access bit in octal mode */
461 l = width + l - 3;
462 r = l + 1;
464 else
466 /* The same to the triplet in string mode */
467 l = l * 3 + 1;
468 r = l + 3;
471 for (i = 0; i < width; i++)
473 if (i >= l && i < r)
475 if (attr == FATTR_CURRENT || attr == FATTR_MARKED_CURRENT)
476 tty_setcolor (MARKED_SELECTED_COLOR);
477 else
478 tty_setcolor (MARKED_COLOR);
480 else if (color >= 0)
481 tty_setcolor (color);
482 else
483 tty_lowlevel_setcolor (-color);
485 tty_print_char (dest[i]);
489 /* --------------------------------------------------------------------------------------------- */
490 /** String representations of various file attributes name */
492 static const char *
493 string_file_name (const file_entry_t *fe, int len)
495 (void) len;
497 mc_g_string_copy (string_file_name_buffer, fe->fname);
499 return string_file_name_buffer->str;
502 /* --------------------------------------------------------------------------------------------- */
504 static unsigned int
505 ilog10 (dev_t n)
507 unsigned int digits = 0;
511 digits++;
512 n /= 10;
514 while (n != 0);
516 return digits;
519 /* --------------------------------------------------------------------------------------------- */
521 static void
522 format_device_number (char *buf, size_t bufsize, dev_t dev)
524 dev_t major_dev, minor_dev;
525 unsigned int major_digits, minor_digits;
527 major_dev = major (dev);
528 major_digits = ilog10 (major_dev);
530 minor_dev = minor (dev);
531 minor_digits = ilog10 (minor_dev);
533 g_assert (bufsize >= 1);
535 if (major_digits + 1 + minor_digits + 1 <= bufsize)
536 g_snprintf (buf, bufsize, "%lu,%lu", (unsigned long) major_dev, (unsigned long) minor_dev);
537 else
538 g_strlcpy (buf, _("[dev]"), bufsize);
541 /* --------------------------------------------------------------------------------------------- */
542 /** size */
544 static const char *
545 string_file_size (const file_entry_t *fe, int len)
547 static char buffer[BUF_TINY];
549 /* Don't ever show size of ".." since we don't calculate it */
550 if (DIR_IS_DOTDOT (fe->fname->str))
551 return _("UP--DIR");
553 #ifdef HAVE_STRUCT_STAT_ST_RDEV
554 if (S_ISBLK (fe->st.st_mode) || S_ISCHR (fe->st.st_mode))
555 format_device_number (buffer, len + 1, fe->st.st_rdev);
556 else
557 #endif
558 size_trunc_len (buffer, (unsigned int) len, fe->st.st_size, 0, panels_options.kilobyte_si);
560 return buffer;
563 /* --------------------------------------------------------------------------------------------- */
564 /** bsize */
566 static const char *
567 string_file_size_brief (const file_entry_t *fe, int len)
569 if (S_ISLNK (fe->st.st_mode) && !link_isdir (fe))
570 return _("SYMLINK");
572 if ((S_ISDIR (fe->st.st_mode) || link_isdir (fe)) && !DIR_IS_DOTDOT (fe->fname->str))
573 return _("SUB-DIR");
575 return string_file_size (fe, len);
578 /* --------------------------------------------------------------------------------------------- */
579 /** This functions return a string representation of a file entry type */
581 static const char *
582 string_file_type (const file_entry_t *fe, int len)
584 static char buffer[2];
586 (void) len;
588 if (S_ISDIR (fe->st.st_mode))
589 buffer[0] = PATH_SEP;
590 else if (S_ISLNK (fe->st.st_mode))
592 if (link_isdir (fe))
593 buffer[0] = '~';
594 else if (fe->f.stale_link != 0)
595 buffer[0] = '!';
596 else
597 buffer[0] = '@';
599 else if (S_ISCHR (fe->st.st_mode))
600 buffer[0] = '-';
601 else if (S_ISSOCK (fe->st.st_mode))
602 buffer[0] = '=';
603 else if (S_ISDOOR (fe->st.st_mode))
604 buffer[0] = '>';
605 else if (S_ISBLK (fe->st.st_mode))
606 buffer[0] = '+';
607 else if (S_ISFIFO (fe->st.st_mode))
608 buffer[0] = '|';
609 else if (S_ISNAM (fe->st.st_mode))
610 buffer[0] = '#';
611 else if (!S_ISREG (fe->st.st_mode))
612 buffer[0] = '?'; /* non-regular of unknown kind */
613 else if (is_exe (fe->st.st_mode))
614 buffer[0] = '*';
615 else
616 buffer[0] = ' ';
617 buffer[1] = '\0';
618 return buffer;
621 /* --------------------------------------------------------------------------------------------- */
622 /** mtime */
624 static const char *
625 string_file_mtime (const file_entry_t *fe, int len)
627 (void) len;
629 return file_date (fe->st.st_mtime);
632 /* --------------------------------------------------------------------------------------------- */
633 /** atime */
635 static const char *
636 string_file_atime (const file_entry_t *fe, int len)
638 (void) len;
640 return file_date (fe->st.st_atime);
643 /* --------------------------------------------------------------------------------------------- */
644 /** ctime */
646 static const char *
647 string_file_ctime (const file_entry_t *fe, int len)
649 (void) len;
651 return file_date (fe->st.st_ctime);
654 /* --------------------------------------------------------------------------------------------- */
655 /** perm */
657 static const char *
658 string_file_permission (const file_entry_t *fe, int len)
660 (void) len;
662 return string_perm (fe->st.st_mode);
665 /* --------------------------------------------------------------------------------------------- */
666 /** mode */
668 static const char *
669 string_file_perm_octal (const file_entry_t *fe, int len)
671 static char buffer[10];
673 (void) len;
675 g_snprintf (buffer, sizeof (buffer), "0%06lo", (unsigned long) fe->st.st_mode);
676 return buffer;
679 /* --------------------------------------------------------------------------------------------- */
680 /** nlink */
682 static const char *
683 string_file_nlinks (const file_entry_t *fe, int len)
685 static char buffer[BUF_TINY];
687 (void) len;
689 g_snprintf (buffer, sizeof (buffer), "%16d", (int) fe->st.st_nlink);
690 return buffer;
693 /* --------------------------------------------------------------------------------------------- */
694 /** inode */
696 static const char *
697 string_inode (const file_entry_t *fe, int len)
699 static char buffer[10];
701 (void) len;
703 g_snprintf (buffer, sizeof (buffer), "%lu", (unsigned long) fe->st.st_ino);
704 return buffer;
707 /* --------------------------------------------------------------------------------------------- */
708 /** nuid */
710 static const char *
711 string_file_nuid (const file_entry_t *fe, int len)
713 static char buffer[10];
715 (void) len;
717 g_snprintf (buffer, sizeof (buffer), "%lu", (unsigned long) fe->st.st_uid);
718 return buffer;
721 /* --------------------------------------------------------------------------------------------- */
722 /** ngid */
724 static const char *
725 string_file_ngid (const file_entry_t *fe, int len)
727 static char buffer[10];
729 (void) len;
731 g_snprintf (buffer, sizeof (buffer), "%lu", (unsigned long) fe->st.st_gid);
732 return buffer;
735 /* --------------------------------------------------------------------------------------------- */
736 /** owner */
738 static const char *
739 string_file_owner (const file_entry_t *fe, int len)
741 (void) len;
743 return get_owner (fe->st.st_uid);
746 /* --------------------------------------------------------------------------------------------- */
747 /** group */
749 static const char *
750 string_file_group (const file_entry_t *fe, int len)
752 (void) len;
754 return get_group (fe->st.st_gid);
757 /* --------------------------------------------------------------------------------------------- */
758 /** mark */
760 static const char *
761 string_marked (const file_entry_t *fe, int len)
763 (void) len;
765 return fe->f.marked != 0 ? "*" : " ";
768 /* --------------------------------------------------------------------------------------------- */
769 /** space */
771 static const char *
772 string_space (const file_entry_t *fe, int len)
774 (void) fe;
775 (void) len;
777 return " ";
780 /* --------------------------------------------------------------------------------------------- */
781 /** dot */
783 static const char *
784 string_dot (const file_entry_t *fe, int len)
786 (void) fe;
787 (void) len;
789 return ".";
792 /* --------------------------------------------------------------------------------------------- */
794 static int
795 file_compute_color (const file_attr_t attr, file_entry_t *fe)
797 switch (attr)
799 case FATTR_CURRENT:
800 return (SELECTED_COLOR);
801 case FATTR_MARKED:
802 return (MARKED_COLOR);
803 case FATTR_MARKED_CURRENT:
804 return (MARKED_SELECTED_COLOR);
805 case FATTR_STATUS:
806 return (NORMAL_COLOR);
807 case FATTR_NORMAL:
808 default:
809 if (!panels_options.filetype_mode)
810 return (NORMAL_COLOR);
813 return mc_fhl_get_color (mc_filehighlight, fe);
816 /* --------------------------------------------------------------------------------------------- */
817 /** Returns the number of items in the given panel */
819 static int
820 panel_items (const WPanel *p)
822 return panel_lines (p) * p->list_cols;
825 /* --------------------------------------------------------------------------------------------- */
826 /** Formats the file number file_index of panel in the buffer dest */
828 static filename_scroll_flag_t
829 format_file (WPanel *panel, int file_index, int width, file_attr_t attr, gboolean isstatus,
830 int *field_length)
832 int color = NORMAL_COLOR;
833 int length = 0;
834 GSList *format, *home;
835 file_entry_t *fe = NULL;
836 filename_scroll_flag_t res = FILENAME_NOSCROLL;
838 *field_length = 0;
840 if (panel->dir.len != 0 && file_index < panel->dir.len)
842 fe = &panel->dir.list[file_index];
843 color = file_compute_color (attr, fe);
846 home = isstatus ? panel->status_format : panel->format;
848 for (format = home; format != NULL && length != width; format = g_slist_next (format))
850 format_item_t *fi = (format_item_t *) format->data;
852 if (fi->string_fn != NULL)
854 const char *txt = " ";
855 int len, perm = 0;
856 const char *prepared_text;
857 int name_offset = 0;
859 if (fe != NULL)
860 txt = fi->string_fn (fe, fi->field_len);
862 len = fi->field_len;
863 if (len + length > width)
864 len = width - length;
865 if (len <= 0)
866 break;
868 if (!isstatus && panel->content_shift > -1 && strcmp (fi->id, "name") == 0)
870 int str_len;
871 int i;
873 *field_length = len + 1;
875 str_len = str_length (txt);
876 i = MAX (0, str_len - len);
877 panel->max_shift = MAX (panel->max_shift, i);
878 i = MIN (panel->content_shift, i);
880 if (i > -1)
882 name_offset = str_offset_to_pos (txt, i);
883 if (str_len > len)
885 res = FILENAME_SCROLL_LEFT;
886 if (str_length (txt + name_offset) > len)
887 res |= FILENAME_SCROLL_RIGHT;
892 if (panels_options.permission_mode)
894 if (strcmp (fi->id, "perm") == 0)
895 perm = 1;
896 else if (strcmp (fi->id, "mode") == 0)
897 perm = 2;
900 if (color >= 0)
901 tty_setcolor (color);
902 else
903 tty_lowlevel_setcolor (-color);
905 if (!isstatus && panel->content_shift > -1)
906 prepared_text = str_fit_to_term (txt + name_offset, len, HIDE_FIT (fi->just_mode));
907 else
908 prepared_text = str_fit_to_term (txt, len, fi->just_mode);
910 if (perm != 0 && fe != NULL)
911 add_permission_string (prepared_text, fi->field_len, fe, attr, color, perm != 1);
912 else
913 tty_print_string (prepared_text);
915 length += len;
917 else
919 if (attr == FATTR_CURRENT || attr == FATTR_MARKED_CURRENT)
920 tty_setcolor (SELECTED_COLOR);
921 else
922 tty_setcolor (NORMAL_COLOR);
923 tty_print_one_vline (TRUE);
924 length++;
928 if (length < width)
930 int y, x;
932 tty_getyx (&y, &x);
933 tty_draw_hline (y, x, ' ', width - length);
936 return res;
939 /* --------------------------------------------------------------------------------------------- */
941 static void
942 repaint_file (WPanel *panel, int file_index, file_attr_t attr)
944 Widget *w = WIDGET (panel);
946 int nth_column = 0;
947 int width;
948 int offset = 0;
949 filename_scroll_flag_t ret_frm;
950 int ypos = 0;
951 gboolean panel_is_split;
952 int fln = 0;
954 panel_is_split = panel->list_cols > 1;
955 width = w->rect.cols - 2;
957 if (panel_is_split)
959 nth_column = (file_index - panel->top) / panel_lines (panel);
960 width /= panel->list_cols;
962 offset = width * nth_column;
964 if (nth_column + 1 >= panel->list_cols)
965 width = w->rect.cols - offset - 2;
968 /* Nothing to paint */
969 if (width <= 0)
970 return;
972 ypos = file_index - panel->top;
974 if (panel_is_split)
975 ypos %= panel_lines (panel);
977 ypos += 2; /* top frame and header */
978 widget_gotoyx (w, ypos, offset + 1);
980 ret_frm = format_file (panel, file_index, width, attr, FALSE, &fln);
982 if (panel_is_split && nth_column + 1 < panel->list_cols)
984 tty_setcolor (NORMAL_COLOR);
985 tty_print_one_vline (TRUE);
988 if (ret_frm != FILENAME_NOSCROLL)
990 if (!panel_is_split && fln > 0)
992 if (panel->list_format != list_long)
993 width = fln;
994 else
996 offset = width - fln + 1;
997 width = fln - 1;
1001 widget_gotoyx (w, ypos, offset);
1002 tty_setcolor (NORMAL_COLOR);
1003 tty_print_string (panel_filename_scroll_left_char);
1005 if ((ret_frm & FILENAME_SCROLL_RIGHT) != 0)
1007 offset += width;
1008 if (nth_column + 1 >= panel->list_cols)
1009 offset++;
1011 widget_gotoyx (w, ypos, offset);
1012 tty_setcolor (NORMAL_COLOR);
1013 tty_print_string (panel_filename_scroll_right_char);
1018 /* --------------------------------------------------------------------------------------------- */
1020 static void
1021 repaint_status (WPanel *panel)
1023 int width;
1025 width = WIDGET (panel)->rect.cols - 2;
1026 if (width > 0)
1028 int fln = 0;
1030 (void) format_file (panel, panel->current, width, FATTR_STATUS, TRUE, &fln);
1034 /* --------------------------------------------------------------------------------------------- */
1036 static void
1037 display_mini_info (WPanel *panel)
1039 Widget *w = WIDGET (panel);
1040 const file_entry_t *fe;
1042 if (!panels_options.show_mini_info)
1043 return;
1045 widget_gotoyx (w, panel_lines (panel) + 3, 1);
1047 if (panel->quick_search.active)
1049 tty_setcolor (INPUT_COLOR);
1050 tty_print_char ('/');
1051 tty_print_string (str_fit_to_term
1052 (panel->quick_search.buffer->str, w->rect.cols - 3, J_LEFT));
1053 return;
1056 /* Status resolves links and show them */
1057 set_colors (panel);
1059 fe = panel_current_entry (panel);
1061 if (fe == NULL)
1062 /* NULL is in case of filter that doesn't match anything */
1063 repaint_status (panel);
1064 else if (S_ISLNK (fe->st.st_mode))
1066 char link_target[MC_MAXPATHLEN];
1067 vfs_path_t *lc_link_vpath;
1068 int len;
1070 lc_link_vpath = vfs_path_append_new (panel->cwd_vpath, fe->fname->str, (char *) NULL);
1071 len = mc_readlink (lc_link_vpath, link_target, MC_MAXPATHLEN - 1);
1072 vfs_path_free (lc_link_vpath, TRUE);
1073 if (len > 0)
1075 link_target[len] = 0;
1076 tty_print_string ("-> ");
1077 tty_print_string (str_fit_to_term (link_target, w->rect.cols - 5, J_LEFT_FIT));
1079 else
1080 tty_print_string (str_fit_to_term (_("<readlink failed>"), w->rect.cols - 2, J_LEFT));
1082 else if (DIR_IS_DOTDOT (fe->fname->str))
1084 /* FIXME:
1085 * while loading directory (dir_list_load() and dir_list_reload()),
1086 * the actual stat info about ".." directory isn't got;
1087 * so just don't display incorrect info about ".." directory */
1088 tty_print_string (str_fit_to_term (_("UP--DIR"), w->rect.cols - 2, J_LEFT));
1090 else
1091 /* Default behavior */
1092 repaint_status (panel);
1095 /* --------------------------------------------------------------------------------------------- */
1097 static void
1098 paint_dir (WPanel *panel)
1100 int i;
1101 int items; /* Number of items */
1103 items = panel_items (panel);
1104 /* reset max len of filename because we have the new max length for the new file list */
1105 panel->max_shift = -1;
1107 for (i = 0; i < items; i++)
1109 file_attr_t attr = FATTR_NORMAL; /* Color value of the line */
1110 int n;
1111 gboolean marked;
1113 n = i + panel->top;
1114 marked = (panel->dir.list[n].f.marked != 0);
1116 if (n < panel->dir.len)
1118 if (panel->current == n && panel->active)
1119 attr = marked ? FATTR_MARKED_CURRENT : FATTR_CURRENT;
1120 else if (marked)
1121 attr = FATTR_MARKED;
1124 repaint_file (panel, n, attr);
1127 tty_set_normal_attrs ();
1130 /* --------------------------------------------------------------------------------------------- */
1132 static void
1133 display_total_marked_size (const WPanel *panel, int y, int x, gboolean size_only)
1135 const Widget *w = CONST_WIDGET (panel);
1137 char buffer[BUF_SMALL], b_bytes[BUF_SMALL];
1138 const char *buf;
1139 int cols;
1141 if (panel->marked <= 0)
1142 return;
1144 buf = size_only ? b_bytes : buffer;
1145 cols = w->rect.cols - 2;
1147 g_strlcpy (b_bytes, size_trunc_sep (panel->total, panels_options.kilobyte_si),
1148 sizeof (b_bytes));
1150 if (!size_only)
1151 g_snprintf (buffer, sizeof (buffer),
1152 ngettext ("%s in %d file", "%s in %d files", panel->marked),
1153 b_bytes, panel->marked);
1155 /* don't forget spaces around buffer content */
1156 buf = str_trunc (buf, cols - 4);
1158 if (x < 0)
1159 /* center in panel */
1160 x = (w->rect.cols - str_term_width1 (buf)) / 2 - 1;
1163 * y == panel_lines (panel) + 2 for mini_info_separator
1164 * y == w->lines - 1 for panel bottom frame
1166 widget_gotoyx (w, y, x);
1167 tty_setcolor (MARKED_COLOR);
1168 tty_printf (" %s ", buf);
1171 /* --------------------------------------------------------------------------------------------- */
1173 static void
1174 mini_info_separator (const WPanel *panel)
1176 if (panels_options.show_mini_info)
1178 const Widget *w = CONST_WIDGET (panel);
1179 int y;
1181 y = panel_lines (panel) + 2;
1183 tty_setcolor (NORMAL_COLOR);
1184 tty_draw_hline (w->rect.y + y, w->rect.x + 1, ACS_HLINE, w->rect.cols - 2);
1185 /* Status displays total marked size.
1186 * Centered in panel, full format. */
1187 display_total_marked_size (panel, y, -1, FALSE);
1191 /* --------------------------------------------------------------------------------------------- */
1193 static void
1194 show_free_space (const WPanel *panel)
1196 /* Used to figure out how many free space we have */
1197 static struct my_statfs myfs_stats;
1198 /* Old current working directory for displaying free space */
1199 static char *old_cwd = NULL;
1201 /* Don't try to stat non-local fs */
1202 if (!vfs_file_is_local (panel->cwd_vpath) || !free_space)
1203 return;
1205 if (old_cwd == NULL || strcmp (old_cwd, vfs_path_as_str (panel->cwd_vpath)) != 0)
1207 char rpath[PATH_MAX];
1209 init_my_statfs ();
1210 g_free (old_cwd);
1211 old_cwd = g_strdup (vfs_path_as_str (panel->cwd_vpath));
1213 if (mc_realpath (old_cwd, rpath) == NULL)
1214 return;
1216 my_statfs (&myfs_stats, rpath);
1219 if (myfs_stats.avail != 0 || myfs_stats.total != 0)
1221 const Widget *w = CONST_WIDGET (panel);
1222 char buffer1[6], buffer2[6], tmp[BUF_SMALL];
1224 size_trunc_len (buffer1, sizeof (buffer1) - 1, myfs_stats.avail, 1,
1225 panels_options.kilobyte_si);
1226 size_trunc_len (buffer2, sizeof (buffer2) - 1, myfs_stats.total, 1,
1227 panels_options.kilobyte_si);
1228 g_snprintf (tmp, sizeof (tmp), " %s / %s (%d%%) ", buffer1, buffer2,
1229 myfs_stats.total == 0 ? 0 :
1230 (int) (100 * (long double) myfs_stats.avail / myfs_stats.total));
1231 widget_gotoyx (w, w->rect.lines - 1, w->rect.cols - 2 - (int) strlen (tmp));
1232 tty_setcolor (NORMAL_COLOR);
1233 tty_print_string (tmp);
1237 /* --------------------------------------------------------------------------------------------- */
1239 * Prepare path string for showing in panel's header.
1240 * Passwords will removed, also home dir will replaced by ~
1242 * @param panel WPanel object
1244 * @return newly allocated string.
1247 static char *
1248 panel_correct_path_to_show (const WPanel *panel)
1250 vfs_path_t *last_vpath;
1251 const vfs_path_element_t *path_element;
1252 char *return_path;
1253 int elements_count;
1255 elements_count = vfs_path_elements_count (panel->cwd_vpath);
1257 /* get last path element */
1258 path_element = vfs_path_element_clone (vfs_path_get_by_index (panel->cwd_vpath, -1));
1260 if (elements_count > 1 && (strcmp (path_element->class->name, "cpiofs") == 0 ||
1261 strcmp (path_element->class->name, "extfs") == 0 ||
1262 strcmp (path_element->class->name, "tarfs") == 0))
1264 const char *archive_name;
1265 const vfs_path_element_t *prev_path_element;
1267 /* get previous path element for catching archive name */
1268 prev_path_element = vfs_path_get_by_index (panel->cwd_vpath, -2);
1269 archive_name = strrchr (prev_path_element->path, PATH_SEP);
1270 if (archive_name != NULL)
1271 last_vpath = vfs_path_from_str_flags (archive_name + 1, VPF_NO_CANON);
1272 else
1274 last_vpath = vfs_path_from_str_flags (prev_path_element->path, VPF_NO_CANON);
1275 last_vpath->relative = TRUE;
1278 else
1279 last_vpath = vfs_path_new (TRUE);
1281 vfs_path_add_element (last_vpath, path_element);
1282 return_path =
1283 vfs_path_to_str_flags (last_vpath, 0,
1284 VPF_STRIP_HOME | VPF_STRIP_PASSWORD | VPF_HIDE_CHARSET);
1285 vfs_path_free (last_vpath, TRUE);
1287 return return_path;
1290 /* --------------------------------------------------------------------------------------------- */
1292 * Get Current path element encoding
1294 * @param panel WPanel object
1296 * @return newly allocated string or NULL if path charset is same as system charset
1299 #ifdef HAVE_CHARSET
1300 static char *
1301 panel_get_encoding_info_str (const WPanel *panel)
1303 char *ret_str = NULL;
1304 const vfs_path_element_t *path_element;
1306 path_element = vfs_path_get_by_index (panel->cwd_vpath, -1);
1307 if (path_element->encoding != NULL)
1308 ret_str = g_strdup_printf ("[%s]", path_element->encoding);
1310 return ret_str;
1312 #endif
1314 /* --------------------------------------------------------------------------------------------- */
1316 static void
1317 show_dir (const WPanel *panel)
1319 const Widget *w = CONST_WIDGET (panel);
1320 gchar *tmp;
1322 set_colors (panel);
1323 tty_draw_box (w->rect.y, w->rect.x, w->rect.lines, w->rect.cols, FALSE);
1325 if (panels_options.show_mini_info)
1327 int y;
1329 y = panel_lines (panel) + 2;
1331 widget_gotoyx (w, y, 0);
1332 tty_print_alt_char (ACS_LTEE, FALSE);
1333 widget_gotoyx (w, y, w->rect.cols - 1);
1334 tty_print_alt_char (ACS_RTEE, FALSE);
1337 widget_gotoyx (w, 0, 1);
1338 tty_print_string (panel_history_prev_item_char);
1340 tmp = panels_options.show_dot_files ? panel_hiddenfiles_show_char : panel_hiddenfiles_hide_char;
1341 tmp = g_strdup_printf ("%s[%s]%s", tmp, panel_history_show_list_char,
1342 panel_history_next_item_char);
1344 widget_gotoyx (w, 0, w->rect.cols - 6);
1345 tty_print_string (tmp);
1347 g_free (tmp);
1349 widget_gotoyx (w, 0, 3);
1351 if (panel->is_panelized)
1352 tty_printf (" %s ", _("Panelize"));
1353 #ifdef HAVE_CHARSET
1354 else
1356 tmp = panel_get_encoding_info_str (panel);
1357 if (tmp != NULL)
1359 tty_printf ("%s", tmp);
1360 widget_gotoyx (w, 0, 3 + strlen (tmp));
1361 g_free (tmp);
1364 #endif
1366 if (panel->active)
1367 tty_setcolor (REVERSE_COLOR);
1369 tmp = panel_correct_path_to_show (panel);
1370 tty_printf (" %s ", str_term_trim (tmp, MIN (MAX (w->rect.cols - 12, 0), w->rect.cols)));
1371 g_free (tmp);
1373 if (!panels_options.show_mini_info)
1375 if (panel->marked == 0)
1377 const file_entry_t *fe;
1379 fe = panel_current_entry (panel);
1381 /* Show size of current file in the bottom of panel */
1382 if (fe != NULL && S_ISREG (fe->st.st_mode))
1384 char buffer[BUF_SMALL];
1386 g_snprintf (buffer, sizeof (buffer), " %s ",
1387 size_trunc_sep (fe->st.st_size, panels_options.kilobyte_si));
1388 tty_setcolor (NORMAL_COLOR);
1389 widget_gotoyx (w, w->rect.lines - 1, 4);
1390 tty_print_string (buffer);
1393 else
1395 /* Show total size of marked files
1396 * In the bottom of panel, display size only. */
1397 display_total_marked_size (panel, w->rect.lines - 1, 2, TRUE);
1401 show_free_space (panel);
1403 if (panel->active)
1404 tty_set_normal_attrs ();
1407 /* --------------------------------------------------------------------------------------------- */
1409 static void
1410 adjust_top_file (WPanel *panel)
1412 int items;
1414 items = panel_items (panel);
1416 if (panel->dir.len <= items || panel->current < 0)
1418 /* If all files fit, show them all. */
1419 /* panel->current < 0 is in case of filter that doesn't match anything, keep it. */
1420 panel->top = 0;
1422 else
1424 int i;
1426 /* Update panel->current to avoid out of range in panel->dir.list[panel->current]. */
1427 panel->current = CLAMP (panel->current, 0, panel->dir.len - 1);
1429 /* top_file has to be in the range [current-items+1, current] so that
1430 the current file is visible.
1431 top_file should be in the range [0, count-items] so that there's
1432 no empty space wasted.
1433 Within these ranges, adjust it by as little as possible. */
1435 if (panel->top < 0)
1436 panel->top = 0;
1438 i = panel->current - items + 1;
1439 if (panel->top < i)
1440 panel->top = i;
1442 i = panel->dir.len - items;
1443 if (panel->top > i)
1444 panel->top = i;
1446 if (panel->top > panel->current)
1447 panel->top = panel->current;
1451 /* --------------------------------------------------------------------------------------------- */
1452 /** add "#enc:encoding" to end of path */
1453 /* if path ends width a previous #enc:, only encoding is changed no additional
1454 * #enc: is appended
1455 * return new string
1458 static char *
1459 panel_save_name (WPanel *panel)
1461 /* If the program is shutting down */
1462 if ((mc_global.midnight_shutdown && auto_save_setup) || saving_setup)
1463 return g_strdup (panel->name);
1465 return g_strconcat ("Temporal:", panel->name, (char *) NULL);
1468 /* --------------------------------------------------------------------------------------------- */
1470 static void
1471 directory_history_add (WPanel *panel, const vfs_path_t *vpath)
1473 char *tmp;
1475 tmp = vfs_path_to_str_flags (vpath, 0, VPF_STRIP_PASSWORD);
1476 panel->dir_history.list = list_append_unique (panel->dir_history.list, tmp);
1477 panel->dir_history.current = panel->dir_history.list;
1480 /* --------------------------------------------------------------------------------------------- */
1482 /* "history_load" event handler */
1483 static gboolean
1484 panel_load_history (const gchar *event_group_name, const gchar *event_name,
1485 gpointer init_data, gpointer data)
1487 WPanel *p = PANEL (init_data);
1488 ev_history_load_save_t *ev = (ev_history_load_save_t *) data;
1490 (void) event_group_name;
1491 (void) event_name;
1493 if (ev->receiver == NULL || ev->receiver == WIDGET (p))
1495 if (ev->cfg != NULL)
1496 p->dir_history.list = mc_config_history_load (ev->cfg, p->dir_history.name);
1497 else
1498 p->dir_history.list = mc_config_history_get (p->dir_history.name);
1500 directory_history_add (p, p->cwd_vpath);
1503 return TRUE;
1506 /* --------------------------------------------------------------------------------------------- */
1508 /* "history_save" event handler */
1509 static gboolean
1510 panel_save_history (const gchar *event_group_name, const gchar *event_name,
1511 gpointer init_data, gpointer data)
1513 WPanel *p = PANEL (init_data);
1515 (void) event_group_name;
1516 (void) event_name;
1518 if (p->dir_history.list != NULL)
1520 ev_history_load_save_t *ev = (ev_history_load_save_t *) data;
1522 mc_config_history_save (ev->cfg, p->dir_history.name, p->dir_history.list);
1525 return TRUE;
1528 /* --------------------------------------------------------------------------------------------- */
1530 static void
1531 panel_destroy (WPanel *p)
1533 size_t i;
1535 if (panels_options.auto_save_setup)
1537 char *name;
1539 name = panel_save_name (p);
1540 panel_save_setup (p, name);
1541 g_free (name);
1544 panel_clean_dir (p);
1546 /* clean history */
1547 if (p->dir_history.list != NULL)
1549 /* directory history is already saved before this moment */
1550 p->dir_history.list = g_list_first (p->dir_history.list);
1551 g_list_free_full (p->dir_history.list, g_free);
1553 g_free (p->dir_history.name);
1555 file_filter_clear (&p->filter);
1557 g_slist_free_full (p->format, (GDestroyNotify) format_item_free);
1558 g_slist_free_full (p->status_format, (GDestroyNotify) format_item_free);
1560 g_free (p->user_format);
1561 for (i = 0; i < LIST_FORMATS; i++)
1562 g_free (p->user_status_format[i]);
1564 g_free (p->name);
1566 panelized_descr_free (p->panelized_descr);
1568 g_string_free (p->quick_search.buffer, TRUE);
1569 g_string_free (p->quick_search.prev_buffer, TRUE);
1571 vfs_path_free (p->lwd_vpath, TRUE);
1572 vfs_path_free (p->cwd_vpath, TRUE);
1575 /* --------------------------------------------------------------------------------------------- */
1577 static void
1578 panel_paint_sort_info (const WPanel *panel)
1580 if (*panel->sort_field->hotkey != '\0')
1582 const char *sort_sign =
1583 panel->sort_info.reverse ? panel_sort_up_char : panel_sort_down_char;
1584 char *str;
1586 str = g_strdup_printf ("%s%s", sort_sign, Q_ (panel->sort_field->hotkey));
1587 widget_gotoyx (panel, 1, 1);
1588 tty_print_string (str);
1589 g_free (str);
1593 /* --------------------------------------------------------------------------------------------- */
1595 static const char *
1596 panel_get_title_without_hotkey (const char *title)
1598 static char translated_title[BUF_TINY];
1600 if (title == NULL || title[0] == '\0')
1601 translated_title[0] = '\0';
1602 else
1604 char *hkey;
1606 g_snprintf (translated_title, sizeof (translated_title), "%s", _(title));
1608 hkey = strchr (translated_title, '&');
1609 if (hkey != NULL && hkey[1] != '\0')
1610 memmove (hkey, hkey + 1, strlen (hkey));
1613 return translated_title;
1616 /* --------------------------------------------------------------------------------------------- */
1618 static void
1619 panel_print_header (const WPanel *panel)
1621 const Widget *w = CONST_WIDGET (panel);
1623 int y, x;
1624 int i;
1625 GString *format_txt;
1627 widget_gotoyx (w, 1, 1);
1628 tty_getyx (&y, &x);
1629 tty_setcolor (NORMAL_COLOR);
1630 tty_draw_hline (y, x, ' ', w->rect.cols - 2);
1632 format_txt = g_string_new ("");
1634 for (i = 0; i < panel->list_cols; i++)
1636 GSList *format;
1638 for (format = panel->format; format != NULL; format = g_slist_next (format))
1640 format_item_t *fi = (format_item_t *) format->data;
1642 if (fi->string_fn != NULL)
1644 g_string_set_size (format_txt, 0);
1646 if (panel->list_format == list_long && strcmp (fi->id, panel->sort_field->id) == 0)
1647 g_string_append (format_txt,
1648 panel->sort_info.reverse
1649 ? panel_sort_up_char : panel_sort_down_char);
1651 g_string_append (format_txt, fi->title);
1653 if (panel->filter.handler != NULL && strcmp (fi->id, "name") == 0)
1655 g_string_append (format_txt, " [");
1656 g_string_append (format_txt, panel->filter.value);
1657 g_string_append (format_txt, "]");
1660 tty_setcolor (HEADER_COLOR);
1661 tty_print_string (str_fit_to_term (format_txt->str, fi->field_len, J_CENTER_LEFT));
1663 else
1665 tty_setcolor (NORMAL_COLOR);
1666 tty_print_one_vline (TRUE);
1670 if (i < panel->list_cols - 1)
1672 tty_setcolor (NORMAL_COLOR);
1673 tty_print_one_vline (TRUE);
1677 g_string_free (format_txt, TRUE);
1679 if (panel->list_format != list_long)
1680 panel_paint_sort_info (panel);
1683 /* --------------------------------------------------------------------------------------------- */
1685 static const char *
1686 parse_panel_size (WPanel *panel, const char *format, gboolean isstatus)
1688 panel_display_t frame = frame_half;
1690 format = skip_separators (format);
1692 if (strncmp (format, "full", 4) == 0)
1694 frame = frame_full;
1695 format += 4;
1697 else if (strncmp (format, "half", 4) == 0)
1699 frame = frame_half;
1700 format += 4;
1703 if (!isstatus)
1705 panel->frame_size = frame;
1706 panel->list_cols = 1;
1709 /* Now, the optional column specifier */
1710 format = skip_separators (format);
1712 if (g_ascii_isdigit (*format))
1714 if (!isstatus)
1716 panel->list_cols = g_ascii_digit_value (*format);
1717 if (panel->list_cols < 1)
1718 panel->list_cols = 1;
1721 format++;
1724 if (!isstatus)
1725 panel_update_cols (WIDGET (panel), panel->frame_size);
1727 return skip_separators (format);
1730 /* --------------------------------------------------------------------------------------------- */
1732 /* *INDENT-OFF* */
1733 /* Format is:
1735 all := panel_format? format
1736 panel_format := [full|half] [1-9]
1737 format := one_format_item_t
1738 | format , one_format_item_t
1740 one_format_item_t := just format.id [opt_size]
1741 just := [<=>]
1742 opt_size := : size [opt_expand]
1743 size := [0-9]+
1744 opt_expand := +
1747 /* *INDENT-ON* */
1749 static GSList *
1750 parse_display_format (WPanel *panel, const char *format, char **error, gboolean isstatus,
1751 int *res_total_cols)
1753 GSList *home = NULL; /* The formats we return */
1754 int total_cols = 0; /* Used columns by the format */
1755 size_t i;
1757 static size_t i18n_timelength = 0; /* flag: check ?Time length at startup */
1759 *error = NULL;
1761 if (i18n_timelength == 0)
1763 i18n_timelength = i18n_checktimelength (); /* Mustn't be 0 */
1765 for (i = 0; panel_fields[i].id != NULL; i++)
1766 if (strcmp ("time", panel_fields[i].id + 1) == 0)
1767 panel_fields[i].min_size = i18n_timelength;
1771 * This makes sure that the panel and mini status full/half mode
1772 * setting is equal
1774 format = parse_panel_size (panel, format, isstatus);
1776 while (*format != '\0')
1777 { /* format can be an empty string */
1778 format_item_t *darr;
1779 align_crt_t justify; /* Which mode. */
1780 gboolean set_justify = TRUE; /* flag: set justification mode? */
1781 gboolean found = FALSE;
1782 size_t klen = 0;
1784 darr = g_new0 (format_item_t, 1);
1785 home = g_slist_append (home, darr);
1787 format = skip_separators (format);
1789 switch (*format)
1791 case '<':
1792 justify = J_LEFT;
1793 format = skip_separators (format + 1);
1794 break;
1795 case '=':
1796 justify = J_CENTER;
1797 format = skip_separators (format + 1);
1798 break;
1799 case '>':
1800 justify = J_RIGHT;
1801 format = skip_separators (format + 1);
1802 break;
1803 default:
1804 justify = J_LEFT;
1805 set_justify = FALSE;
1806 break;
1809 for (i = 0; !found && panel_fields[i].id != NULL; i++)
1811 klen = strlen (panel_fields[i].id);
1812 found = strncmp (format, panel_fields[i].id, klen) == 0;
1815 if (found)
1817 i--;
1818 format += klen;
1820 darr->requested_field_len = panel_fields[i].min_size;
1821 darr->string_fn = panel_fields[i].string_fn;
1822 darr->title = g_strdup (panel_get_title_without_hotkey (panel_fields[i].title_hotkey));
1823 darr->id = panel_fields[i].id;
1824 darr->expand = panel_fields[i].expands;
1825 darr->just_mode = panel_fields[i].default_just;
1827 if (set_justify)
1829 if (IS_FIT (darr->just_mode))
1830 darr->just_mode = MAKE_FIT (justify);
1831 else
1832 darr->just_mode = justify;
1835 format = skip_separators (format);
1837 /* If we have a size specifier */
1838 if (*format == ':')
1840 int req_length;
1842 /* If the size was specified, we don't want
1843 * auto-expansion by default
1845 darr->expand = FALSE;
1846 format++;
1847 req_length = atoi (format);
1848 darr->requested_field_len = req_length;
1850 format = skip_numbers (format);
1852 /* Now, if they insist on expansion */
1853 if (*format == '+')
1855 darr->expand = TRUE;
1856 format++;
1860 else
1862 size_t pos;
1863 char *tmp_format;
1865 pos = strlen (format);
1866 if (pos > 8)
1867 pos = 8;
1869 tmp_format = g_strndup (format, pos);
1870 g_slist_free_full (home, (GDestroyNotify) format_item_free);
1871 *error =
1872 g_strconcat (_("Unknown tag on display format:"), " ", tmp_format, (char *) NULL);
1873 g_free (tmp_format);
1875 return NULL;
1878 total_cols += darr->requested_field_len;
1881 *res_total_cols = total_cols;
1882 return home;
1885 /* --------------------------------------------------------------------------------------------- */
1887 static GSList *
1888 use_display_format (WPanel *panel, const char *format, char **error, gboolean isstatus)
1890 #define MAX_EXPAND 4
1891 int expand_top = 0; /* Max used element in expand */
1892 int usable_columns; /* Usable columns in the panel */
1893 int total_cols = 0;
1894 GSList *darr, *home;
1896 if (format == NULL)
1897 format = DEFAULT_USER_FORMAT;
1899 home = parse_display_format (panel, format, error, isstatus, &total_cols);
1901 if (*error != NULL)
1902 return NULL;
1904 panel->dirty = TRUE;
1906 usable_columns = WIDGET (panel)->rect.cols - 2;
1907 /* Status needn't to be split */
1908 if (!isstatus)
1910 usable_columns /= panel->list_cols;
1911 if (panel->list_cols > 1)
1912 usable_columns--;
1915 /* Look for the expandable fields and set field_len based on the requested field len */
1916 for (darr = home; darr != NULL && expand_top < MAX_EXPAND; darr = g_slist_next (darr))
1918 format_item_t *fi = (format_item_t *) darr->data;
1920 fi->field_len = fi->requested_field_len;
1921 if (fi->expand)
1922 expand_top++;
1925 /* If we used more columns than the available columns, adjust that */
1926 if (total_cols > usable_columns)
1928 int dif;
1929 int pdif = 0;
1931 dif = total_cols - usable_columns;
1933 while (dif != 0 && pdif != dif)
1935 pdif = dif;
1937 for (darr = home; darr != NULL; darr = g_slist_next (darr))
1939 format_item_t *fi = (format_item_t *) darr->data;
1941 if (dif != 0 && fi->field_len != 1)
1943 fi->field_len--;
1944 dif--;
1949 total_cols = usable_columns; /* give up, the rest should be truncated */
1952 /* Expand the available space */
1953 if (usable_columns > total_cols && expand_top != 0)
1955 int i;
1956 int spaces;
1958 spaces = (usable_columns - total_cols) / expand_top;
1960 for (i = 0, darr = home; darr != NULL && i < expand_top; darr = g_slist_next (darr))
1962 format_item_t *fi = (format_item_t *) darr->data;
1964 if (fi->expand)
1966 fi->field_len += spaces;
1967 if (i == 0)
1968 fi->field_len += (usable_columns - total_cols) % expand_top;
1969 i++;
1974 return home;
1977 /* --------------------------------------------------------------------------------------------- */
1978 /** Given the panel->view_type returns the format string to be parsed */
1980 static const char *
1981 panel_format (WPanel *panel)
1983 switch (panel->list_format)
1985 case list_long:
1986 return "full perm space nlink space owner space group space size space mtime space name";
1988 case list_brief:
1990 static char format[BUF_TINY];
1991 int brief_cols = panel->brief_cols;
1993 if (brief_cols < 1)
1994 brief_cols = 2;
1996 if (brief_cols > 9)
1997 brief_cols = 9;
1999 g_snprintf (format, sizeof (format), "half %d type name", brief_cols);
2000 return format;
2003 case list_user:
2004 return panel->user_format;
2006 default:
2007 case list_full:
2008 return "half type name | size | mtime";
2012 /* --------------------------------------------------------------------------------------------- */
2014 static const char *
2015 mini_status_format (WPanel *panel)
2017 if (panel->user_mini_status)
2018 return panel->user_status_format[panel->list_format];
2020 switch (panel->list_format)
2022 case list_long:
2023 return "full perm space nlink space owner space group space size space mtime space name";
2025 case list_brief:
2026 return "half type name space bsize space perm space";
2028 case list_full:
2029 return "half type name";
2031 default:
2032 case list_user:
2033 return panel->user_format;
2037 /* */
2038 /* Panel operation commands */
2039 /* */
2041 /* --------------------------------------------------------------------------------------------- */
2043 static void
2044 cd_up_dir (WPanel *panel)
2046 vfs_path_t *up_dir;
2048 up_dir = vfs_path_from_str ("..");
2049 panel_cd (panel, up_dir, cd_exact);
2050 vfs_path_free (up_dir, TRUE);
2053 /* --------------------------------------------------------------------------------------------- */
2054 /** Used to emulate Lynx's entering leaving a directory with the arrow keys */
2056 static cb_ret_t
2057 maybe_cd (WPanel *panel, gboolean move_up_dir)
2059 if (panels_options.navigate_with_arrows && input_is_empty (cmdline))
2061 const file_entry_t *fe;
2063 if (move_up_dir)
2065 cd_up_dir (panel);
2066 return MSG_HANDLED;
2069 fe = panel_current_entry (panel);
2071 if (fe != NULL && (S_ISDIR (fe->st.st_mode) || link_isdir (fe)))
2073 vfs_path_t *vpath;
2075 vpath = vfs_path_from_str (fe->fname->str);
2076 panel_cd (panel, vpath, cd_exact);
2077 vfs_path_free (vpath, TRUE);
2078 return MSG_HANDLED;
2082 return MSG_NOT_HANDLED;
2085 /* --------------------------------------------------------------------------------------------- */
2087 /* if command line is empty then do 'cd ..' */
2088 static cb_ret_t
2089 force_maybe_cd (WPanel *panel)
2091 if (input_is_empty (cmdline))
2093 cd_up_dir (panel);
2094 return MSG_HANDLED;
2097 return MSG_NOT_HANDLED;
2100 /* --------------------------------------------------------------------------------------------- */
2102 static void
2103 unselect_item (WPanel *panel)
2105 const file_entry_t *fe;
2107 fe = panel_current_entry (panel);
2108 repaint_file (panel, panel->current,
2109 fe != NULL && fe->f.marked != 0 ? FATTR_MARKED : FATTR_NORMAL);
2112 /* --------------------------------------------------------------------------------------------- */
2113 /** Select/unselect all the files like a current file by extension */
2115 static void
2116 panel_select_ext_cmd (WPanel *panel)
2118 const file_entry_t *fe;
2119 gboolean do_select;
2120 char *reg_exp, *cur_file_ext;
2121 mc_search_t *search;
2122 int i;
2124 fe = panel_current_entry (panel);
2125 if (fe == NULL)
2126 return;
2128 do_select = (fe->f.marked == 0);
2130 cur_file_ext = str_regex_escape (extension (fe->fname->str));
2131 if (cur_file_ext[0] != '\0')
2132 reg_exp = g_strconcat ("^.*\\.", cur_file_ext, "$", (char *) NULL);
2133 else
2134 reg_exp = g_strdup ("^[^\\.]+$");
2136 g_free (cur_file_ext);
2138 search = mc_search_new (reg_exp, NULL);
2139 search->search_type = MC_SEARCH_T_REGEX;
2140 search->is_case_sensitive = FALSE;
2142 for (i = 0; i < panel->dir.len; i++)
2144 fe = &panel->dir.list[i];
2146 if (DIR_IS_DOTDOT (fe->fname->str) || S_ISDIR (fe->st.st_mode))
2147 continue;
2149 if (!mc_search_run (search, fe->fname->str, 0, fe->fname->len, NULL))
2150 continue;
2152 do_file_mark (panel, i, do_select ? 1 : 0);
2155 mc_search_free (search);
2156 g_free (reg_exp);
2159 /* --------------------------------------------------------------------------------------------- */
2161 static void
2162 panel_set_current (WPanel *panel, int i)
2164 if (i != panel->current)
2166 panel->dirty = TRUE;
2167 panel->current = i;
2168 panel->top = panel->current - (WIDGET (panel)->rect.lines - 2) / 2;
2169 if (panel->top < 0)
2170 panel->top = 0;
2174 /* --------------------------------------------------------------------------------------------- */
2176 static int
2177 panel_current_at_half (const WPanel *panel)
2179 int lines, top;
2181 lines = panel_lines (panel);
2183 /* define top file of column */
2184 top = panel->top;
2185 if (panel->list_cols > 1)
2186 top += lines * ((panel->current - top) / lines);
2188 return (panel->current - top - lines / 2);
2191 /* --------------------------------------------------------------------------------------------- */
2193 static void
2194 move_down (WPanel *panel)
2196 int items;
2198 if (panel->dir.len == 0 || panel->current + 1 == panel->dir.len)
2199 return;
2201 unselect_item (panel);
2202 panel->current++;
2204 items = panel_items (panel);
2206 if (panels_options.scroll_pages && panel->current - panel->top == items)
2208 /* Scroll window half screen */
2209 panel->top += items / 2;
2210 if (panel->top > panel->dir.len - items)
2211 panel->top = panel->dir.len - items;
2212 paint_dir (panel);
2214 else if (panels_options.scroll_center && panel_current_at_half (panel) > 0)
2216 /* Scroll window when cursor is halfway down */
2217 panel->top++;
2218 if (panel->top > panel->dir.len - items)
2219 panel->top = panel->dir.len - items;
2221 select_item (panel);
2224 /* --------------------------------------------------------------------------------------------- */
2226 static void
2227 move_up (WPanel *panel)
2229 if (panel->dir.len == 0 || panel->current <= 0)
2230 return;
2232 unselect_item (panel);
2233 panel->current--;
2235 if (panels_options.scroll_pages && panel->current < panel->top)
2237 /* Scroll window half screen */
2238 panel->top -= panel_items (panel) / 2;
2239 if (panel->top < 0)
2240 panel->top = 0;
2241 paint_dir (panel);
2243 else if (panels_options.scroll_center && panel_current_at_half (panel) < 0)
2245 /* Scroll window when cursor is halfway up */
2246 panel->top--;
2247 if (panel->top < 0)
2248 panel->top = 0;
2250 select_item (panel);
2253 /* --------------------------------------------------------------------------------------------- */
2254 /** Changes the current by lines (may be negative) */
2256 static void
2257 panel_move_current (WPanel *panel, int lines)
2259 int new_pos;
2260 gboolean adjust = FALSE;
2262 if (panel->dir.len == 0 || panel->current < 0)
2263 return;
2265 new_pos = panel->current + lines;
2266 if (new_pos >= panel->dir.len)
2267 new_pos = panel->dir.len - 1;
2269 if (new_pos < 0)
2270 new_pos = 0;
2272 unselect_item (panel);
2273 panel->current = new_pos;
2275 if (panel->current - panel->top >= panel_items (panel))
2277 panel->top += lines;
2278 adjust = TRUE;
2281 if (panel->current - panel->top < 0)
2283 panel->top += lines;
2284 adjust = TRUE;
2287 if (adjust)
2289 if (panel->top > panel->current)
2290 panel->top = panel->current;
2291 if (panel->top < 0)
2292 panel->top = 0;
2293 paint_dir (panel);
2295 select_item (panel);
2298 /* --------------------------------------------------------------------------------------------- */
2300 static cb_ret_t
2301 move_left (WPanel *panel)
2303 if (panel->list_cols > 1)
2305 panel_move_current (panel, -panel_lines (panel));
2306 return MSG_HANDLED;
2309 return maybe_cd (panel, TRUE); /* cd .. */
2312 /* --------------------------------------------------------------------------------------------- */
2314 static cb_ret_t
2315 move_right (WPanel *panel)
2317 if (panel->list_cols > 1)
2319 panel_move_current (panel, panel_lines (panel));
2320 return MSG_HANDLED;
2323 return maybe_cd (panel, FALSE); /* cd (current) */
2326 /* --------------------------------------------------------------------------------------------- */
2328 static void
2329 prev_page (WPanel *panel)
2331 int items;
2333 if (panel->dir.len == 0 || panel->current < 0 || (panel->current == 0 && panel->top == 0))
2334 return;
2336 unselect_item (panel);
2337 items = panel_items (panel);
2338 if (panel->top < items)
2339 items = panel->top;
2340 if (items == 0)
2341 panel->current = 0;
2342 else
2343 panel->current -= items;
2344 panel->top -= items;
2346 select_item (panel);
2347 paint_dir (panel);
2350 /* --------------------------------------------------------------------------------------------- */
2352 static void
2353 goto_parent_dir (WPanel *panel)
2355 if (!panel->is_panelized)
2356 cd_up_dir (panel);
2357 else
2359 const file_entry_t *fe;
2360 GString *fname;
2361 const char *bname;
2362 vfs_path_t *dname_vpath;
2364 fe = panel_current_entry (panel);
2365 if (fe == NULL)
2366 return;
2368 fname = fe->fname;
2370 if (g_path_is_absolute (fname->str))
2371 fname = mc_g_string_dup (fname);
2372 else
2374 char *fname2;
2376 fname2 =
2377 mc_build_filename (vfs_path_as_str (panel->panelized_descr->root_vpath), fname->str,
2378 (char *) NULL);
2380 fname = g_string_new_take (fname2);
2383 bname = x_basename (fname->str);
2385 if (bname == fname->str)
2386 dname_vpath = vfs_path_from_str (".");
2387 else
2389 g_string_truncate (fname, bname - fname->str);
2390 dname_vpath = vfs_path_from_str (fname->str);
2393 panel_cd (panel, dname_vpath, cd_exact);
2394 panel_set_current_by_name (panel, bname);
2396 vfs_path_free (dname_vpath, TRUE);
2397 g_string_free (fname, TRUE);
2401 /* --------------------------------------------------------------------------------------------- */
2403 static void
2404 next_page (WPanel *panel)
2406 int items;
2408 if (panel->dir.len == 0 || panel->current < 0 || panel->current == panel->dir.len - 1)
2409 return;
2411 unselect_item (panel);
2412 items = panel_items (panel);
2413 if (panel->top > panel->dir.len - 2 * items)
2414 items = panel->dir.len - items - panel->top;
2415 if (panel->top + items < 0)
2416 items = -panel->top;
2417 if (items == 0)
2418 panel->current = panel->dir.len - 1;
2419 else
2420 panel->current += items;
2421 panel->top += items;
2423 select_item (panel);
2424 paint_dir (panel);
2427 /* --------------------------------------------------------------------------------------------- */
2429 static void
2430 goto_child_dir (WPanel *panel)
2432 const file_entry_t *fe;
2434 fe = panel_current_entry (panel);
2436 if (fe != NULL && (S_ISDIR (fe->st.st_mode) || link_isdir (fe)))
2438 vfs_path_t *vpath;
2440 vpath = vfs_path_from_str (fe->fname->str);
2441 panel_cd (panel, vpath, cd_exact);
2442 vfs_path_free (vpath, TRUE);
2446 /* --------------------------------------------------------------------------------------------- */
2448 static void
2449 goto_top_file (WPanel *panel)
2451 if (panel->dir.len == 0 || panel->current < 0)
2452 return;
2454 unselect_item (panel);
2455 panel->current = panel->top;
2456 select_item (panel);
2459 /* --------------------------------------------------------------------------------------------- */
2461 static void
2462 goto_middle_file (WPanel *panel)
2464 if (panel->dir.len == 0 || panel->current < 0)
2465 return;
2467 unselect_item (panel);
2468 panel->current = panel->top + panel_items (panel) / 2;
2469 select_item (panel);
2472 /* --------------------------------------------------------------------------------------------- */
2474 static void
2475 goto_bottom_file (WPanel *panel)
2477 if (panel->dir.len == 0 || panel->current < 0)
2478 return;
2480 unselect_item (panel);
2481 panel->current = panel->top + panel_items (panel) - 1;
2482 select_item (panel);
2485 /* --------------------------------------------------------------------------------------------- */
2487 static void
2488 move_home (WPanel *panel)
2490 if (panel->dir.len == 0 || panel->current <= 0)
2491 return;
2493 unselect_item (panel);
2495 if (panels_options.torben_fj_mode)
2497 int middle_pos;
2499 middle_pos = panel->top + panel_items (panel) / 2;
2501 if (panel->current > middle_pos)
2503 goto_middle_file (panel);
2504 return;
2506 if (panel->current != panel->top)
2508 goto_top_file (panel);
2509 return;
2513 panel->top = 0;
2514 panel->current = 0;
2516 paint_dir (panel);
2517 select_item (panel);
2520 /* --------------------------------------------------------------------------------------------- */
2522 static void
2523 move_end (WPanel *panel)
2525 if (panel->dir.len == 0 || panel->current < 0 || panel->current == panel->dir.len - 1)
2526 return;
2528 unselect_item (panel);
2530 if (panels_options.torben_fj_mode)
2532 int items, middle_pos;
2534 items = panel_items (panel);
2535 middle_pos = panel->top + items / 2;
2537 if (panel->current < middle_pos)
2539 goto_middle_file (panel);
2540 return;
2542 if (panel->current != panel->top + items - 1)
2544 goto_bottom_file (panel);
2545 return;
2549 panel->current = panel->dir.len - 1;
2550 paint_dir (panel);
2551 select_item (panel);
2554 /* --------------------------------------------------------------------------------------------- */
2556 static void
2557 do_mark_file (WPanel *panel, mark_act_t do_move)
2559 const file_entry_t *fe;
2561 fe = panel_current_entry (panel);
2562 if (fe == NULL)
2563 return;
2565 do_file_mark (panel, panel->current, fe->f.marked ? 0 : 1);
2567 if ((panels_options.mark_moves_down && do_move == MARK_DOWN) || do_move == MARK_FORCE_DOWN)
2568 move_down (panel);
2569 else if (do_move == MARK_FORCE_UP)
2570 move_up (panel);
2573 /* --------------------------------------------------------------------------------------------- */
2575 static inline void
2576 mark_file (WPanel *panel)
2578 do_mark_file (panel, MARK_DOWN);
2581 /* --------------------------------------------------------------------------------------------- */
2583 static inline void
2584 mark_file_up (WPanel *panel)
2586 do_mark_file (panel, MARK_FORCE_UP);
2589 /* --------------------------------------------------------------------------------------------- */
2591 static inline void
2592 mark_file_down (WPanel *panel)
2594 do_mark_file (panel, MARK_FORCE_DOWN);
2597 /* --------------------------------------------------------------------------------------------- */
2599 static void
2600 mark_file_right (WPanel *panel)
2602 int lines;
2604 if (state_mark < 0)
2606 const file_entry_t *fe;
2608 fe = panel_current_entry (panel);
2609 if (fe == NULL)
2610 return;
2612 state_mark = fe->f.marked ? 0 : 1;
2615 lines = panel_lines (panel);
2616 lines = MIN (lines, panel->dir.len - panel->current - 1);
2617 for (; lines != 0; lines--)
2619 do_file_mark (panel, panel->current, state_mark);
2620 move_down (panel);
2622 do_file_mark (panel, panel->current, state_mark);
2625 /* --------------------------------------------------------------------------------------------- */
2627 static void
2628 mark_file_left (WPanel *panel)
2630 int lines;
2632 if (state_mark < 0)
2634 const file_entry_t *fe;
2636 fe = panel_current_entry (panel);
2637 if (fe == NULL)
2638 return;
2640 state_mark = fe->f.marked ? 0 : 1;
2643 lines = panel_lines (panel);
2644 lines = MIN (lines, panel->current + 1);
2645 for (; lines != 0; lines--)
2647 do_file_mark (panel, panel->current, state_mark);
2648 move_up (panel);
2650 do_file_mark (panel, panel->current, state_mark);
2653 /* --------------------------------------------------------------------------------------------- */
2655 static mc_search_t *
2656 panel_select_unselect_files_dialog (select_flags_t *flags, const char *title,
2657 const char *history_name, const char *help_section, char **str)
2659 gboolean files_only = (*flags & SELECT_FILES_ONLY) != 0;
2660 gboolean case_sens = (*flags & SELECT_MATCH_CASE) != 0;
2661 gboolean shell_patterns = (*flags & SELECT_SHELL_PATTERNS) != 0;
2663 char *reg_exp;
2664 mc_search_t *search;
2666 quick_widget_t quick_widgets[] = {
2667 /* *INDENT-OFF* */
2668 QUICK_INPUT (INPUT_LAST_TEXT, history_name, &reg_exp, NULL,
2669 FALSE, FALSE, INPUT_COMPLETE_FILENAMES),
2670 QUICK_START_COLUMNS,
2671 QUICK_CHECKBOX (N_("&Files only"), &files_only, NULL),
2672 QUICK_CHECKBOX (N_("&Using shell patterns"), &shell_patterns, NULL),
2673 QUICK_NEXT_COLUMN,
2674 QUICK_CHECKBOX (N_("&Case sensitive"), &case_sens, NULL),
2675 QUICK_STOP_COLUMNS,
2676 QUICK_END
2677 /* *INDENT-ON* */
2680 WRect r = { -1, -1, 0, 50 };
2682 quick_dialog_t qdlg = {
2683 r, title, help_section,
2684 quick_widgets, NULL, NULL
2687 if (quick_dialog (&qdlg) == B_CANCEL)
2688 return NULL;
2690 if (*reg_exp == '\0')
2692 g_free (reg_exp);
2693 if (str != NULL)
2694 *str = NULL;
2695 return SELECT_RESET;
2698 search = mc_search_new (reg_exp, NULL);
2699 search->search_type = shell_patterns ? MC_SEARCH_T_GLOB : MC_SEARCH_T_REGEX;
2700 search->is_entire_line = TRUE;
2701 search->is_case_sensitive = case_sens;
2703 if (str != NULL)
2704 *str = reg_exp;
2705 else
2706 g_free (reg_exp);
2708 if (!mc_search_prepare (search))
2710 message (D_ERROR, MSG_ERROR, _("Malformed regular expression"));
2711 mc_search_free (search);
2712 return SELECT_ERROR;
2715 /* result flags */
2716 *flags = 0;
2717 if (case_sens)
2718 *flags |= SELECT_MATCH_CASE;
2719 if (files_only)
2720 *flags |= SELECT_FILES_ONLY;
2721 if (shell_patterns)
2722 *flags |= SELECT_SHELL_PATTERNS;
2724 return search;
2727 /* --------------------------------------------------------------------------------------------- */
2729 static void
2730 panel_select_unselect_files (WPanel *panel, const char *title, const char *history_name,
2731 const char *help_section, gboolean do_select)
2733 mc_search_t *search;
2734 gboolean files_only;
2735 int i;
2737 search = panel_select_unselect_files_dialog (&panels_options.select_flags, title, history_name,
2738 help_section, NULL);
2739 if (search == NULL || search == SELECT_RESET || search == SELECT_ERROR)
2740 return;
2742 files_only = (panels_options.select_flags & SELECT_FILES_ONLY) != 0;
2744 for (i = 0; i < panel->dir.len; i++)
2746 if (DIR_IS_DOTDOT (panel->dir.list[i].fname->str))
2747 continue;
2748 if (S_ISDIR (panel->dir.list[i].st.st_mode) && files_only)
2749 continue;
2751 if (mc_search_run
2752 (search, panel->dir.list[i].fname->str, 0, panel->dir.list[i].fname->len, NULL))
2753 do_file_mark (panel, i, do_select ? 1 : 0);
2756 mc_search_free (search);
2759 /* --------------------------------------------------------------------------------------------- */
2761 static void
2762 panel_select_files (WPanel *panel)
2764 panel_select_unselect_files (panel, _("Select"), MC_HISTORY_FM_PANEL_SELECT,
2765 "[Select/Unselect Files]", TRUE);
2768 /* --------------------------------------------------------------------------------------------- */
2770 static void
2771 panel_unselect_files (WPanel *panel)
2773 panel_select_unselect_files (panel, _("Unselect"), MC_HISTORY_FM_PANEL_UNSELECT,
2774 "[Select/Unselect Files]", FALSE);
2777 /* --------------------------------------------------------------------------------------------- */
2779 static void
2780 panel_select_invert_files (WPanel *panel)
2782 int i;
2784 for (i = 0; i < panel->dir.len; i++)
2786 file_entry_t *file = &panel->dir.list[i];
2788 if (!panels_options.reverse_files_only || !S_ISDIR (file->st.st_mode))
2789 do_file_mark (panel, i, file->f.marked ? 0 : 1);
2793 /* --------------------------------------------------------------------------------------------- */
2795 static void
2796 panel_do_set_filter (WPanel *panel)
2798 /* *INDENT-OFF* */
2799 file_filter_t ff = { .value = NULL, .handler = NULL, .flags = panel->filter.flags };
2800 /* *INDENT-ON* */
2802 ff.handler =
2803 panel_select_unselect_files_dialog (&ff.flags, _("Filter"), MC_HISTORY_FM_PANEL_FILTER,
2804 "[Filter...]", &ff.value);
2806 if (ff.handler == NULL || ff.handler == SELECT_ERROR)
2807 return;
2809 if (ff.handler == SELECT_RESET)
2810 ff.handler = NULL;
2812 panel_set_filter (panel, &ff);
2815 /* --------------------------------------------------------------------------------------------- */
2816 /** Incremental search of a file name in the panel.
2817 * @param panel instance of WPanel structure
2818 * @param c_code key code
2821 static void
2822 do_search (WPanel *panel, int c_code)
2824 int curr;
2825 int i;
2826 gboolean wrapped = FALSE;
2827 char *act;
2828 mc_search_t *search;
2829 char *reg_exp, *esc_str;
2830 gboolean is_found = FALSE;
2832 if (c_code == KEY_BACKSPACE)
2834 if (panel->quick_search.buffer->len != 0)
2836 act = panel->quick_search.buffer->str + panel->quick_search.buffer->len;
2837 str_prev_noncomb_char (&act, panel->quick_search.buffer->str);
2838 g_string_set_size (panel->quick_search.buffer, act - panel->quick_search.buffer->str);
2840 panel->quick_search.chpoint = 0;
2842 else
2844 if (c_code != 0 && (gsize) panel->quick_search.chpoint < sizeof (panel->quick_search.ch))
2846 panel->quick_search.ch[panel->quick_search.chpoint] = c_code;
2847 panel->quick_search.chpoint++;
2850 if (panel->quick_search.chpoint > 0)
2852 switch (str_is_valid_char (panel->quick_search.ch, panel->quick_search.chpoint))
2854 case -2:
2855 return;
2856 case -1:
2857 panel->quick_search.chpoint = 0;
2858 return;
2859 default:
2860 g_string_append_len (panel->quick_search.buffer, panel->quick_search.ch,
2861 panel->quick_search.chpoint);
2862 panel->quick_search.chpoint = 0;
2867 reg_exp = g_strdup_printf ("%s*", panel->quick_search.buffer->str);
2868 esc_str = str_escape (reg_exp, -1, ",|\\{}[]", TRUE);
2869 search = mc_search_new (esc_str, NULL);
2870 search->search_type = MC_SEARCH_T_GLOB;
2871 search->is_entire_line = TRUE;
2873 switch (panels_options.qsearch_mode)
2875 case QSEARCH_CASE_SENSITIVE:
2876 search->is_case_sensitive = TRUE;
2877 break;
2878 case QSEARCH_CASE_INSENSITIVE:
2879 search->is_case_sensitive = FALSE;
2880 break;
2881 default:
2882 search->is_case_sensitive = panel->sort_info.case_sensitive;
2883 break;
2886 curr = panel->current;
2888 for (i = panel->current; !wrapped || i != panel->current; i++)
2890 if (i >= panel->dir.len)
2892 i = 0;
2893 if (wrapped)
2894 break;
2895 wrapped = TRUE;
2897 if (mc_search_run
2898 (search, panel->dir.list[i].fname->str, 0, panel->dir.list[i].fname->len, NULL))
2900 curr = i;
2901 is_found = TRUE;
2902 break;
2905 if (is_found)
2907 unselect_item (panel);
2908 panel->current = curr;
2909 select_item (panel);
2910 widget_draw (WIDGET (panel));
2912 else if (c_code != KEY_BACKSPACE)
2914 act = panel->quick_search.buffer->str + panel->quick_search.buffer->len;
2915 str_prev_noncomb_char (&act, panel->quick_search.buffer->str);
2916 g_string_set_size (panel->quick_search.buffer, act - panel->quick_search.buffer->str);
2918 mc_search_free (search);
2919 g_free (reg_exp);
2920 g_free (esc_str);
2923 /* --------------------------------------------------------------------------------------------- */
2924 /** Start new search.
2925 * @param panel instance of WPanel structure
2928 static void
2929 start_search (WPanel *panel)
2931 if (panel->quick_search.active)
2933 if (panel->current == panel->dir.len - 1)
2934 panel->current = 0;
2935 else
2936 move_down (panel);
2938 /* in case if there was no search string we need to recall
2939 previous string, with which we ended previous searching */
2940 if (panel->quick_search.buffer->len == 0)
2941 mc_g_string_copy (panel->quick_search.buffer, panel->quick_search.prev_buffer);
2943 do_search (panel, 0);
2945 else if (panel->dir.len != 0)
2947 panel->quick_search.active = TRUE;
2948 g_string_set_size (panel->quick_search.buffer, 0);
2949 panel->quick_search.ch[0] = '\0';
2950 panel->quick_search.chpoint = 0;
2951 display_mini_info (panel);
2955 /* --------------------------------------------------------------------------------------------- */
2957 static void
2958 stop_search (WPanel *panel)
2960 if (!panel->quick_search.active)
2961 return;
2963 panel->quick_search.active = FALSE;
2965 /* if user overrdied search string, we need to store it
2966 to the quick_search.prev_buffer */
2967 if (panel->quick_search.buffer->len != 0)
2968 mc_g_string_copy (panel->quick_search.prev_buffer, panel->quick_search.buffer);
2970 display_mini_info (panel);
2973 /* --------------------------------------------------------------------------------------------- */
2974 /** Return TRUE if the Enter key has been processed, FALSE otherwise */
2976 static gboolean
2977 do_enter_on_file_entry (WPanel *panel, const file_entry_t *fe)
2979 const char *fname = fe->fname->str;
2980 char *fname_quoted;
2981 vfs_path_t *full_name_vpath;
2982 gboolean ok;
2985 * Directory or link to directory - change directory.
2986 * Try the same for the entries on which mc_lstat() has failed.
2988 if (S_ISDIR (fe->st.st_mode) || link_isdir (fe) || (fe->st.st_mode == 0))
2990 vfs_path_t *fname_vpath;
2992 fname_vpath = vfs_path_from_str (fname);
2993 if (!panel_cd (panel, fname_vpath, cd_exact))
2994 cd_error_message (fname);
2995 vfs_path_free (fname_vpath, TRUE);
2996 return TRUE;
2999 full_name_vpath = vfs_path_append_new (panel->cwd_vpath, fname, (char *) NULL);
3001 /* Try associated command */
3002 ok = regex_command (full_name_vpath, "Open") != 0;
3003 vfs_path_free (full_name_vpath, TRUE);
3004 if (ok)
3005 return TRUE;
3007 /* Check if the file is executable */
3008 full_name_vpath = vfs_path_append_new (panel->cwd_vpath, fname, (char *) NULL);
3009 ok = (is_exe (fe->st.st_mode) && if_link_is_exe (full_name_vpath, fe));
3010 vfs_path_free (full_name_vpath, TRUE);
3011 if (!ok)
3012 return FALSE;
3014 if (confirm_execute
3015 && query_dialog (_("The Midnight Commander"), _("Do you really want to execute?"), D_NORMAL,
3016 2, _("&Yes"), _("&No")) != 0)
3017 return TRUE;
3019 if (!vfs_current_is_local ())
3021 int ret;
3022 vfs_path_t *tmp_vpath;
3024 tmp_vpath = vfs_path_append_new (vfs_get_raw_current_dir (), fname, (char *) NULL);
3025 ret = mc_setctl (tmp_vpath, VFS_SETCTL_RUN, NULL);
3026 vfs_path_free (tmp_vpath, TRUE);
3027 /* We took action only if the dialog was shown or the execution was successful */
3028 return confirm_execute || (ret == 0);
3031 fname_quoted = name_quote (fname, FALSE);
3032 if (fname_quoted != NULL)
3034 char *cmd;
3036 cmd = g_strconcat ("." PATH_SEP_STR, fname_quoted, (char *) NULL);
3037 g_free (fname_quoted);
3039 shell_execute (cmd, 0);
3040 g_free (cmd);
3043 #ifdef HAVE_CHARSET
3044 mc_global.source_codepage = default_source_codepage;
3045 #endif
3047 return TRUE;
3050 /* --------------------------------------------------------------------------------------------- */
3052 static inline gboolean
3053 do_enter (WPanel *panel)
3055 const file_entry_t *fe;
3057 fe = panel_current_entry (panel);
3059 return (fe == NULL ? FALSE : do_enter_on_file_entry (panel, fe));
3062 /* --------------------------------------------------------------------------------------------- */
3064 static void
3065 panel_cycle_listing_format (WPanel *panel)
3067 panel->list_format = (panel->list_format + 1) % LIST_FORMATS;
3069 if (set_panel_formats (panel) == 0)
3070 do_refresh ();
3073 /* --------------------------------------------------------------------------------------------- */
3075 static void
3076 chdir_other_panel (WPanel *panel)
3078 const file_entry_t *entry;
3079 vfs_path_t *new_dir_vpath;
3080 char *curr_entry = NULL;
3081 WPanel *p;
3083 entry = panel_current_entry (panel);
3084 if (entry == NULL)
3085 return;
3087 if (get_other_type () != view_listing)
3088 create_panel (get_other_index (), view_listing);
3090 if (S_ISDIR (entry->st.st_mode) || link_isdir (entry))
3091 new_dir_vpath = vfs_path_append_new (panel->cwd_vpath, entry->fname->str, (char *) NULL);
3092 else
3094 new_dir_vpath = vfs_path_append_new (panel->cwd_vpath, "..", (char *) NULL);
3095 curr_entry = strrchr (vfs_path_get_last_path_str (panel->cwd_vpath), PATH_SEP);
3098 p = change_panel ();
3099 panel_cd (p, new_dir_vpath, cd_exact);
3100 vfs_path_free (new_dir_vpath, TRUE);
3102 if (curr_entry != NULL)
3103 panel_set_current_by_name (p, curr_entry);
3104 (void) change_panel ();
3106 move_down (panel);
3109 /* --------------------------------------------------------------------------------------------- */
3111 * Make the current directory of the current panel also the current
3112 * directory of the other panel. Put the other panel to the listing
3113 * mode if needed. If the current panel is panelized, the other panel
3114 * doesn't become panelized.
3117 static void
3118 panel_sync_other (const WPanel *panel)
3120 if (get_other_type () != view_listing)
3121 create_panel (get_other_index (), view_listing);
3123 panel_do_cd (other_panel, panel->cwd_vpath, cd_exact);
3125 /* try to set current filename on the other panel */
3126 if (!panel->is_panelized)
3128 const file_entry_t *fe;
3130 fe = panel_current_entry (panel);
3131 if (fe != NULL)
3132 panel_set_current_by_name (other_panel, fe->fname->str);
3136 /* --------------------------------------------------------------------------------------------- */
3138 static void
3139 chdir_to_readlink (WPanel *panel)
3141 const file_entry_t *fe;
3142 vfs_path_t *new_dir_vpath;
3143 char buffer[MC_MAXPATHLEN];
3144 int i;
3145 struct stat st;
3146 vfs_path_t *panel_fname_vpath;
3147 gboolean ok;
3148 WPanel *cpanel;
3150 if (get_other_type () != view_listing)
3151 return;
3153 fe = panel_current_entry (panel);
3155 if (fe == NULL || !S_ISLNK (fe->st.st_mode))
3156 return;
3158 i = readlink (fe->fname->str, buffer, MC_MAXPATHLEN - 1);
3159 if (i < 0)
3160 return;
3162 panel_fname_vpath = vfs_path_from_str (fe->fname->str);
3163 ok = (mc_stat (panel_fname_vpath, &st) >= 0);
3164 vfs_path_free (panel_fname_vpath, TRUE);
3165 if (!ok)
3166 return;
3168 buffer[i] = '\0';
3169 if (!S_ISDIR (st.st_mode))
3171 char *p;
3173 p = strrchr (buffer, PATH_SEP);
3174 if (p != NULL && p[1] == '\0')
3176 *p = '\0';
3177 p = strrchr (buffer, PATH_SEP);
3179 if (p == NULL)
3180 return;
3182 p[1] = '\0';
3184 if (IS_PATH_SEP (*buffer))
3185 new_dir_vpath = vfs_path_from_str (buffer);
3186 else
3187 new_dir_vpath = vfs_path_append_new (panel->cwd_vpath, buffer, (char *) NULL);
3189 cpanel = change_panel ();
3190 panel_cd (cpanel, new_dir_vpath, cd_exact);
3191 vfs_path_free (new_dir_vpath, TRUE);
3192 (void) change_panel ();
3194 move_down (panel);
3197 /* --------------------------------------------------------------------------------------------- */
3199 function return 0 if not found and REAL_INDEX+1 if found
3202 static gsize
3203 panel_get_format_field_index_by_name (const WPanel *panel, const char *name)
3205 GSList *format;
3206 gsize lc_index;
3208 for (lc_index = 1, format = panel->format;
3209 format != NULL && strcmp (((format_item_t *) format->data)->title, name) != 0;
3210 format = g_slist_next (format), lc_index++)
3213 if (format == NULL)
3214 lc_index = 0;
3216 return lc_index;
3219 /* --------------------------------------------------------------------------------------------- */
3221 static const panel_field_t *
3222 panel_get_sortable_field_by_format (const WPanel *panel, gsize lc_index)
3224 const panel_field_t *pfield;
3225 const format_item_t *format;
3227 format = (const format_item_t *) g_slist_nth_data (panel->format, lc_index);
3228 if (format == NULL)
3229 return NULL;
3231 pfield = panel_get_field_by_title (format->title);
3232 if (pfield == NULL)
3233 return NULL;
3234 if (pfield->sort_routine == NULL)
3235 return NULL;
3236 return pfield;
3239 /* --------------------------------------------------------------------------------------------- */
3241 static void
3242 panel_toggle_sort_order_prev (WPanel *panel)
3244 gsize lc_index, i;
3245 const char *title;
3246 const panel_field_t *pfield = NULL;
3248 title = panel_get_title_without_hotkey (panel->sort_field->title_hotkey);
3249 lc_index = panel_get_format_field_index_by_name (panel, title);
3251 if (lc_index > 1)
3253 /* search for prev sortable column in panel format */
3254 for (i = lc_index - 1;
3255 i != 0 && (pfield = panel_get_sortable_field_by_format (panel, i - 1)) == NULL; i--)
3259 if (pfield == NULL)
3261 /* Sortable field not found. Try to search in each array */
3262 for (i = g_slist_length (panel->format);
3263 i != 0 && (pfield = panel_get_sortable_field_by_format (panel, i - 1)) == NULL; i--)
3267 if (pfield != NULL)
3269 panel->sort_field = pfield;
3270 panel_set_sort_order (panel, pfield);
3274 /* --------------------------------------------------------------------------------------------- */
3276 static void
3277 panel_toggle_sort_order_next (WPanel *panel)
3279 gsize lc_index, i;
3280 const panel_field_t *pfield = NULL;
3281 gsize format_field_count;
3282 const char *title;
3284 format_field_count = g_slist_length (panel->format);
3285 title = panel_get_title_without_hotkey (panel->sort_field->title_hotkey);
3286 lc_index = panel_get_format_field_index_by_name (panel, title);
3288 if (lc_index != 0 && lc_index != format_field_count)
3290 /* search for prev sortable column in panel format */
3291 for (i = lc_index;
3292 i != format_field_count
3293 && (pfield = panel_get_sortable_field_by_format (panel, i)) == NULL; i++)
3297 if (pfield == NULL)
3299 /* Sortable field not found. Try to search in each array */
3300 for (i = 0;
3301 i != format_field_count
3302 && (pfield = panel_get_sortable_field_by_format (panel, i)) == NULL; i++)
3306 if (pfield != NULL)
3308 panel->sort_field = pfield;
3309 panel_set_sort_order (panel, pfield);
3313 /* --------------------------------------------------------------------------------------------- */
3315 static void
3316 panel_select_sort_order (WPanel *panel)
3318 const panel_field_t *sort_order;
3320 sort_order = sort_box (&panel->sort_info, panel->sort_field);
3321 if (sort_order != NULL)
3323 panel->sort_field = sort_order;
3324 panel_set_sort_order (panel, sort_order);
3328 /* --------------------------------------------------------------------------------------------- */
3331 * panel_content_scroll_left:
3332 * @param panel the pointer to the panel on which we operate
3334 * scroll long filename to the left (decrement scroll pointer)
3338 static void
3339 panel_content_scroll_left (WPanel *panel)
3341 if (panel->content_shift > -1)
3343 if (panel->content_shift > panel->max_shift)
3344 panel->content_shift = panel->max_shift;
3346 panel->content_shift--;
3347 show_dir (panel);
3348 paint_dir (panel);
3352 /* --------------------------------------------------------------------------------------------- */
3354 * panel_content_scroll_right:
3355 * @param panel the pointer to the panel on which we operate
3357 * scroll long filename to the right (increment scroll pointer)
3361 static void
3362 panel_content_scroll_right (WPanel *panel)
3364 if (panel->content_shift < 0 || panel->content_shift < panel->max_shift)
3366 panel->content_shift++;
3367 show_dir (panel);
3368 paint_dir (panel);
3372 /* --------------------------------------------------------------------------------------------- */
3374 static void
3375 panel_set_sort_type_by_id (WPanel *panel, const char *name)
3377 if (strcmp (panel->sort_field->id, name) == 0)
3378 panel->sort_info.reverse = !panel->sort_info.reverse;
3379 else
3381 const panel_field_t *sort_order;
3383 sort_order = panel_get_field_by_id (name);
3384 if (sort_order == NULL)
3385 return;
3387 panel->sort_field = sort_order;
3390 panel_set_sort_order (panel, panel->sort_field);
3393 /* --------------------------------------------------------------------------------------------- */
3395 * If we moved to the parent directory move the 'current' pointer to
3396 * the old directory name; If we leave VFS dir, remove FS specificator.
3398 * You do _NOT_ want to add any vfs aware code here. <pavel@ucw.cz>
3401 static const char *
3402 get_parent_dir_name (const vfs_path_t *cwd_vpath, const vfs_path_t *lwd_vpath)
3404 size_t llen, clen;
3405 const char *p, *lwd;
3407 llen = vfs_path_len (lwd_vpath);
3408 clen = vfs_path_len (cwd_vpath);
3410 if (llen <= clen)
3411 return NULL;
3413 lwd = vfs_path_as_str (lwd_vpath);
3415 p = g_strrstr (lwd, VFS_PATH_URL_DELIMITER);
3417 if (p == NULL)
3419 const char *cwd;
3421 cwd = vfs_path_as_str (cwd_vpath);
3423 p = strrchr (lwd, PATH_SEP);
3425 if (p != NULL && strncmp (cwd, lwd, (size_t) (p - lwd)) == 0
3426 && (clen == (size_t) (p - lwd) || (p == lwd && IS_PATH_SEP (cwd[0]) && cwd[1] == '\0')))
3427 return (p + 1);
3429 return NULL;
3432 /* skip VFS prefix */
3433 while (--p > lwd && !IS_PATH_SEP (*p))
3435 /* get last component */
3436 while (--p > lwd && !IS_PATH_SEP (*p))
3439 /* return last component */
3440 return (p != lwd || IS_PATH_SEP (*p)) ? p + 1 : p;
3443 /* --------------------------------------------------------------------------------------------- */
3444 /** Wrapper for do_subshell_chdir, check for availability of subshell */
3446 static void
3447 subshell_chdir (const vfs_path_t *vpath)
3449 #ifdef ENABLE_SUBSHELL
3450 if (mc_global.tty.use_subshell && vfs_current_is_local ())
3451 do_subshell_chdir (vpath, FALSE);
3452 #else /* ENABLE_SUBSHELL */
3453 (void) vpath;
3454 #endif /* ENABLE_SUBSHELL */
3457 /* --------------------------------------------------------------------------------------------- */
3459 * Changes the current directory of the panel.
3460 * Don't record change in the directory history.
3463 static gboolean
3464 panel_do_cd_int (WPanel *panel, const vfs_path_t *new_dir_vpath, enum cd_enum cd_type)
3466 vfs_path_t *olddir_vpath;
3468 /* Convert *new_path to a suitable pathname, handle ~user */
3469 if (cd_type == cd_parse_command)
3471 const vfs_path_element_t *element;
3473 element = vfs_path_get_by_index (new_dir_vpath, 0);
3474 if (strcmp (element->path, "-") == 0)
3475 new_dir_vpath = panel->lwd_vpath;
3478 if (mc_chdir (new_dir_vpath) == -1)
3479 return FALSE;
3481 /* Success: save previous directory, shutdown status of previous dir */
3482 olddir_vpath = vfs_path_clone (panel->cwd_vpath);
3483 panel_set_lwd (panel, panel->cwd_vpath);
3484 input_complete_free (cmdline);
3486 vfs_path_free (panel->cwd_vpath, TRUE);
3487 vfs_setup_cwd ();
3488 panel->cwd_vpath = vfs_path_clone (vfs_get_raw_current_dir ());
3490 vfs_release_path (olddir_vpath);
3492 subshell_chdir (panel->cwd_vpath);
3494 /* Reload current panel */
3495 panel_clean_dir (panel);
3497 if (!dir_list_load (&panel->dir, panel->cwd_vpath, panel->sort_field->sort_routine,
3498 &panel->sort_info, &panel->filter))
3499 message (D_ERROR, MSG_ERROR, _("Cannot read directory contents"));
3501 if (panel->dir.len == 0)
3502 panel_set_current (panel, -1);
3504 panel_set_current_by_name (panel, get_parent_dir_name (panel->cwd_vpath, olddir_vpath));
3506 load_hint (FALSE);
3507 panel->dirty = TRUE;
3508 update_xterm_title_path ();
3509 update_terminal_cwd ();
3511 vfs_path_free (olddir_vpath, TRUE);
3513 return TRUE;
3516 /* --------------------------------------------------------------------------------------------- */
3518 static void
3519 directory_history_next (WPanel *panel)
3521 gboolean ok;
3525 GList *next;
3527 ok = TRUE;
3528 next = g_list_next (panel->dir_history.current);
3529 if (next != NULL)
3531 vfs_path_t *data_vpath;
3533 data_vpath = vfs_path_from_str ((char *) next->data);
3534 ok = panel_do_cd_int (panel, data_vpath, cd_exact);
3535 vfs_path_free (data_vpath, TRUE);
3536 panel->dir_history.current = next;
3538 /* skip directories that present in history but absent in file system */
3540 while (!ok);
3543 /* --------------------------------------------------------------------------------------------- */
3545 static void
3546 directory_history_prev (WPanel *panel)
3548 gboolean ok;
3552 GList *prev;
3554 ok = TRUE;
3555 prev = g_list_previous (panel->dir_history.current);
3556 if (prev != NULL)
3558 vfs_path_t *data_vpath;
3560 data_vpath = vfs_path_from_str ((char *) prev->data);
3561 ok = panel_do_cd_int (panel, data_vpath, cd_exact);
3562 vfs_path_free (data_vpath, TRUE);
3563 panel->dir_history.current = prev;
3565 /* skip directories that present in history but absent in file system */
3567 while (!ok);
3570 /* --------------------------------------------------------------------------------------------- */
3572 static void
3573 directory_history_list (WPanel *panel)
3575 history_descriptor_t hd;
3576 gboolean ok = FALSE;
3577 size_t pos;
3579 pos = g_list_position (panel->dir_history.current, panel->dir_history.list);
3581 history_descriptor_init (&hd, WIDGET (panel)->rect.y, WIDGET (panel)->rect.x,
3582 panel->dir_history.list, (int) pos);
3583 history_show (&hd);
3585 panel->dir_history.list = hd.list;
3586 if (hd.text != NULL)
3588 vfs_path_t *s_vpath;
3590 s_vpath = vfs_path_from_str (hd.text);
3591 ok = panel_do_cd_int (panel, s_vpath, cd_exact);
3592 if (ok)
3593 directory_history_add (panel, panel->cwd_vpath);
3594 else
3595 cd_error_message (hd.text);
3596 vfs_path_free (s_vpath, TRUE);
3597 g_free (hd.text);
3600 if (!ok)
3602 /* Since history is fully modified in history_show(), panel->dir_history actually
3603 * points to the invalid place. Try restore current position here. */
3605 size_t i;
3607 panel->dir_history.current = panel->dir_history.list;
3609 for (i = 0; i <= pos; i++)
3611 GList *prev;
3613 prev = g_list_previous (panel->dir_history.current);
3614 if (prev == NULL)
3615 break;
3617 panel->dir_history.current = prev;
3622 /* --------------------------------------------------------------------------------------------- */
3624 static cb_ret_t
3625 panel_execute_cmd (WPanel *panel, long command)
3627 int res = MSG_HANDLED;
3629 if (command != CK_Search)
3630 stop_search (panel);
3632 switch (command)
3634 case CK_Up:
3635 case CK_Down:
3636 case CK_Left:
3637 case CK_Right:
3638 case CK_Bottom:
3639 case CK_Top:
3640 case CK_PageDown:
3641 case CK_PageUp:
3642 /* reset state of marks flag */
3643 state_mark = -1;
3644 break;
3645 default:
3646 break;
3649 switch (command)
3651 case CK_CycleListingFormat:
3652 panel_cycle_listing_format (panel);
3653 break;
3654 case CK_PanelOtherCd:
3655 chdir_other_panel (panel);
3656 break;
3657 case CK_PanelOtherCdLink:
3658 chdir_to_readlink (panel);
3659 break;
3660 case CK_CopySingle:
3661 copy_cmd_local (panel);
3662 break;
3663 case CK_DeleteSingle:
3664 delete_cmd_local (panel);
3665 break;
3666 case CK_Enter:
3667 do_enter (panel);
3668 break;
3669 case CK_ViewRaw:
3670 view_raw_cmd (panel);
3671 break;
3672 case CK_EditNew:
3673 edit_cmd_new ();
3674 break;
3675 case CK_MoveSingle:
3676 rename_cmd_local (panel);
3677 break;
3678 case CK_SelectInvert:
3679 panel_select_invert_files (panel);
3680 break;
3681 case CK_Select:
3682 panel_select_files (panel);
3683 break;
3684 case CK_SelectExt:
3685 panel_select_ext_cmd (panel);
3686 break;
3687 case CK_Unselect:
3688 panel_unselect_files (panel);
3689 break;
3690 case CK_Filter:
3691 panel_do_set_filter (panel);
3692 break;
3693 case CK_PageDown:
3694 next_page (panel);
3695 break;
3696 case CK_PageUp:
3697 prev_page (panel);
3698 break;
3699 case CK_CdChild:
3700 goto_child_dir (panel);
3701 break;
3702 case CK_CdParent:
3703 goto_parent_dir (panel);
3704 break;
3705 case CK_History:
3706 directory_history_list (panel);
3707 break;
3708 case CK_HistoryNext:
3709 directory_history_next (panel);
3710 break;
3711 case CK_HistoryPrev:
3712 directory_history_prev (panel);
3713 break;
3714 case CK_BottomOnScreen:
3715 goto_bottom_file (panel);
3716 break;
3717 case CK_MiddleOnScreen:
3718 goto_middle_file (panel);
3719 break;
3720 case CK_TopOnScreen:
3721 goto_top_file (panel);
3722 break;
3723 case CK_Mark:
3724 mark_file (panel);
3725 break;
3726 case CK_MarkUp:
3727 mark_file_up (panel);
3728 break;
3729 case CK_MarkDown:
3730 mark_file_down (panel);
3731 break;
3732 case CK_MarkLeft:
3733 mark_file_left (panel);
3734 break;
3735 case CK_MarkRight:
3736 mark_file_right (panel);
3737 break;
3738 case CK_CdParentSmart:
3739 res = force_maybe_cd (panel);
3740 break;
3741 case CK_Up:
3742 move_up (panel);
3743 break;
3744 case CK_Down:
3745 move_down (panel);
3746 break;
3747 case CK_Left:
3748 res = move_left (panel);
3749 break;
3750 case CK_Right:
3751 res = move_right (panel);
3752 break;
3753 case CK_Bottom:
3754 move_end (panel);
3755 break;
3756 case CK_Top:
3757 move_home (panel);
3758 break;
3759 #ifdef HAVE_CHARSET
3760 case CK_SelectCodepage:
3761 panel_change_encoding (panel);
3762 break;
3763 #endif
3764 case CK_ScrollLeft:
3765 panel_content_scroll_left (panel);
3766 break;
3767 case CK_ScrollRight:
3768 panel_content_scroll_right (panel);
3769 break;
3770 case CK_Search:
3771 start_search (panel);
3772 break;
3773 case CK_SearchStop:
3774 break;
3775 case CK_PanelOtherSync:
3776 panel_sync_other (panel);
3777 break;
3778 case CK_Sort:
3779 panel_select_sort_order (panel);
3780 break;
3781 case CK_SortPrev:
3782 panel_toggle_sort_order_prev (panel);
3783 break;
3784 case CK_SortNext:
3785 panel_toggle_sort_order_next (panel);
3786 break;
3787 case CK_SortReverse:
3788 panel->sort_info.reverse = !panel->sort_info.reverse;
3789 panel_set_sort_order (panel, panel->sort_field);
3790 break;
3791 case CK_SortByName:
3792 panel_set_sort_type_by_id (panel, "name");
3793 break;
3794 case CK_SortByExt:
3795 panel_set_sort_type_by_id (panel, "extension");
3796 break;
3797 case CK_SortBySize:
3798 panel_set_sort_type_by_id (panel, "size");
3799 break;
3800 case CK_SortByMTime:
3801 panel_set_sort_type_by_id (panel, "mtime");
3802 break;
3803 default:
3804 res = MSG_NOT_HANDLED;
3805 break;
3808 return res;
3811 /* --------------------------------------------------------------------------------------------- */
3813 static cb_ret_t
3814 panel_key (WPanel *panel, int key)
3816 long command;
3818 if (is_abort_char (key))
3820 stop_search (panel);
3821 return MSG_HANDLED;
3824 if (panel->quick_search.active && ((key >= ' ' && key <= 255) || key == KEY_BACKSPACE))
3826 do_search (panel, key);
3827 return MSG_HANDLED;
3830 command = widget_lookup_key (WIDGET (panel), key);
3831 if (command != CK_IgnoreKey)
3832 return panel_execute_cmd (panel, command);
3834 if (panels_options.torben_fj_mode && key == ALT ('h'))
3836 goto_middle_file (panel);
3837 return MSG_HANDLED;
3840 if (!command_prompt && ((key >= ' ' && key <= 255) || key == KEY_BACKSPACE))
3842 start_search (panel);
3843 do_search (panel, key);
3844 return MSG_HANDLED;
3847 return MSG_NOT_HANDLED;
3850 /* --------------------------------------------------------------------------------------------- */
3852 static cb_ret_t
3853 panel_callback (Widget *w, Widget *sender, widget_msg_t msg, int parm, void *data)
3855 WPanel *panel = PANEL (w);
3856 WDialog *h = DIALOG (w->owner);
3857 WButtonBar *bb;
3859 switch (msg)
3861 case MSG_INIT:
3862 /* subscribe to "history_load" event */
3863 mc_event_add (h->event_group, MCEVENT_HISTORY_LOAD, panel_load_history, w, NULL);
3864 /* subscribe to "history_save" event */
3865 mc_event_add (h->event_group, MCEVENT_HISTORY_SAVE, panel_save_history, w, NULL);
3866 return MSG_HANDLED;
3868 case MSG_DRAW:
3869 /* Repaint everything, including frame and separator */
3870 widget_erase (w);
3871 show_dir (panel);
3872 panel_print_header (panel);
3873 adjust_top_file (panel);
3874 paint_dir (panel);
3875 mini_info_separator (panel);
3876 display_mini_info (panel);
3877 panel->dirty = FALSE;
3878 return MSG_HANDLED;
3880 case MSG_FOCUS:
3881 state_mark = -1;
3882 current_panel = panel;
3883 panel->active = TRUE;
3885 if (mc_chdir (panel->cwd_vpath) != 0)
3887 char *cwd;
3889 cwd = vfs_path_to_str_flags (panel->cwd_vpath, 0, VPF_STRIP_PASSWORD);
3890 cd_error_message (cwd);
3891 g_free (cwd);
3893 else
3894 subshell_chdir (panel->cwd_vpath);
3896 update_xterm_title_path ();
3897 update_terminal_cwd ();
3898 select_item (panel);
3900 bb = buttonbar_find (h);
3901 midnight_set_buttonbar (bb);
3902 return MSG_HANDLED;
3904 case MSG_UNFOCUS:
3905 /* Janne: look at this for the multiple panel options */
3906 stop_search (panel);
3907 panel->active = FALSE;
3908 unselect_item (panel);
3909 return MSG_HANDLED;
3911 case MSG_KEY:
3912 return panel_key (panel, parm);
3914 case MSG_ACTION:
3915 return panel_execute_cmd (panel, parm);
3917 case MSG_DESTROY:
3918 vfs_stamp_path (panel->cwd_vpath);
3919 /* unsubscribe from "history_load" event */
3920 mc_event_del (h->event_group, MCEVENT_HISTORY_LOAD, panel_load_history, w);
3921 /* unsubscribe from "history_save" event */
3922 mc_event_del (h->event_group, MCEVENT_HISTORY_SAVE, panel_save_history, w);
3923 panel_destroy (panel);
3924 free_my_statfs ();
3925 return MSG_HANDLED;
3927 default:
3928 return widget_default_callback (w, sender, msg, parm, data);
3932 /* --------------------------------------------------------------------------------------------- */
3933 /* */
3934 /* Panel mouse events support routines */
3935 /* */
3937 static void
3938 mouse_toggle_mark (WPanel *panel)
3940 const file_entry_t *fe;
3942 fe = panel_current_entry (panel);
3943 if (fe != NULL)
3945 do_mark_file (panel, MARK_DONT_MOVE);
3946 mouse_marking = fe->f.marked != 0;
3947 mouse_mark_panel = current_panel;
3951 /* --------------------------------------------------------------------------------------------- */
3953 static void
3954 mouse_set_mark (WPanel *panel)
3956 if (mouse_mark_panel == panel)
3958 const file_entry_t *fe;
3960 fe = panel_current_entry (panel);
3961 if (fe != NULL)
3963 if (mouse_marking && fe->f.marked == 0)
3964 do_mark_file (panel, MARK_DONT_MOVE);
3965 else if (!mouse_marking && fe->f.marked != 0)
3966 do_mark_file (panel, MARK_DONT_MOVE);
3971 /* --------------------------------------------------------------------------------------------- */
3973 static void
3974 mark_if_marking (WPanel *panel, const mouse_event_t *event, int previous_current)
3976 if ((event->buttons & GPM_B_RIGHT) == 0)
3977 return;
3979 if (event->msg == MSG_MOUSE_DOWN)
3980 mouse_toggle_mark (panel);
3981 else
3983 int pcurr, curr1, curr2;
3985 pcurr = panel->current;
3986 curr1 = MIN (previous_current, panel->current);
3987 curr2 = MAX (previous_current, panel->current);
3989 for (; curr1 <= curr2; curr1++)
3991 panel->current = curr1;
3992 mouse_set_mark (panel);
3995 panel->current = pcurr;
3999 /* --------------------------------------------------------------------------------------------- */
4000 /** Determine which column was clicked, and sort the panel on
4001 * that column, or reverse sort on that column if already
4002 * sorted on that column.
4005 static void
4006 mouse_sort_col (WPanel *panel, int x)
4008 int i = 0;
4009 GSList *format;
4010 const char *lc_sort_name = NULL;
4011 panel_field_t *col_sort_format = NULL;
4013 for (format = panel->format; format != NULL; format = g_slist_next (format))
4015 format_item_t *fi = (format_item_t *) format->data;
4017 i += fi->field_len;
4018 if (x < i + 1)
4020 /* found column */
4021 lc_sort_name = fi->title;
4022 break;
4026 if (lc_sort_name == NULL)
4027 return;
4029 for (i = 0; panel_fields[i].id != NULL; i++)
4031 const char *title;
4033 title = panel_get_title_without_hotkey (panel_fields[i].title_hotkey);
4034 if (panel_fields[i].sort_routine != NULL && strcmp (title, lc_sort_name) == 0)
4036 col_sort_format = &panel_fields[i];
4037 break;
4041 if (col_sort_format != NULL)
4043 if (panel->sort_field == col_sort_format)
4044 /* reverse the sort if clicked column is already the sorted column */
4045 panel->sort_info.reverse = !panel->sort_info.reverse;
4046 else
4047 /* new sort is forced to be ascending */
4048 panel->sort_info.reverse = FALSE;
4050 panel_set_sort_order (panel, col_sort_format);
4054 /* --------------------------------------------------------------------------------------------- */
4056 static int
4057 panel_mouse_is_on_item (const WPanel *panel, int y, int x)
4059 int lines, col_width, col;
4061 if (y < 0)
4062 return MOUSE_UPPER_FILE_LIST;
4064 lines = panel_lines (panel);
4065 if (y >= lines)
4066 return MOUSE_BELOW_FILE_LIST;
4068 col_width = (CONST_WIDGET (panel)->rect.cols - 2) / panel->list_cols;
4069 /* column where mouse is */
4070 col = x / col_width;
4072 y += panel->top + lines * col;
4074 /* are we below or in the next column of last file? */
4075 if (y > panel->dir.len)
4076 return MOUSE_AFTER_LAST_FILE;
4078 /* we are on item of the file file; return an index to select a file */
4079 return y;
4082 /* --------------------------------------------------------------------------------------------- */
4084 static void
4085 panel_mouse_callback (Widget *w, mouse_msg_t msg, mouse_event_t *event)
4087 WPanel *panel = PANEL (w);
4088 gboolean is_active;
4090 is_active = widget_is_active (w);
4092 switch (msg)
4094 case MSG_MOUSE_DOWN:
4095 if (event->y == 0)
4097 /* top frame */
4098 if (event->x == 1)
4099 /* "<" button */
4100 directory_history_prev (panel);
4101 else if (event->x == w->rect.cols - 2)
4102 /* ">" button */
4103 directory_history_next (panel);
4104 else if (event->x >= w->rect.cols - 5 && event->x <= w->rect.cols - 3)
4105 /* "^" button */
4106 directory_history_list (panel);
4107 else if (event->x == w->rect.cols - 6)
4108 /* "." button show/hide hidden files */
4109 send_message (filemanager, NULL, MSG_ACTION, CK_ShowHidden, NULL);
4110 else
4112 /* no other events on 1st line, return MOU_UNHANDLED */
4113 event->result.abort = TRUE;
4114 /* avoid extra panel redraw */
4115 panel->dirty = FALSE;
4117 break;
4120 if (event->y == 1)
4122 /* sort on clicked column */
4123 mouse_sort_col (panel, event->x + 1);
4124 break;
4127 if (!is_active)
4128 (void) change_panel ();
4129 MC_FALLTHROUGH;
4131 case MSG_MOUSE_DRAG:
4133 int my_index;
4134 int previous_current;
4136 my_index = panel_mouse_is_on_item (panel, event->y - 2, event->x);
4137 previous_current = panel->current;
4139 switch (my_index)
4141 case MOUSE_UPPER_FILE_LIST:
4142 move_up (panel);
4143 mark_if_marking (panel, event, previous_current);
4144 break;
4146 case MOUSE_BELOW_FILE_LIST:
4147 move_down (panel);
4148 mark_if_marking (panel, event, previous_current);
4149 break;
4151 case MOUSE_AFTER_LAST_FILE:
4152 break; /* do nothing */
4154 default:
4155 if (my_index != panel->current)
4157 unselect_item (panel);
4158 panel->current = my_index;
4159 select_item (panel);
4162 mark_if_marking (panel, event, previous_current);
4163 break;
4166 break;
4168 case MSG_MOUSE_UP:
4169 break;
4171 case MSG_MOUSE_CLICK:
4172 if ((event->count & GPM_DOUBLE) != 0 && (event->buttons & GPM_B_LEFT) != 0 &&
4173 panel_mouse_is_on_item (panel, event->y - 2, event->x) >= 0)
4174 do_enter (panel);
4175 break;
4177 case MSG_MOUSE_MOVE:
4178 break;
4180 case MSG_MOUSE_SCROLL_UP:
4181 if (is_active)
4183 if (panels_options.mouse_move_pages && panel->top > 0)
4184 prev_page (panel);
4185 else /* We are in first page */
4186 move_up (panel);
4188 break;
4190 case MSG_MOUSE_SCROLL_DOWN:
4191 if (is_active)
4193 if (panels_options.mouse_move_pages
4194 && panel->top + panel_items (panel) < panel->dir.len)
4195 next_page (panel);
4196 else /* We are in last page */
4197 move_down (panel);
4199 break;
4201 default:
4202 break;
4205 if (panel->dirty)
4206 widget_draw (w);
4209 /* --------------------------------------------------------------------------------------------- */
4211 static void
4212 reload_panelized (WPanel *panel)
4214 int i, j;
4215 dir_list *list = &panel->dir;
4217 /* refresh current VFS directory required for vfs_path_from_str() */
4218 (void) mc_chdir (panel->cwd_vpath);
4220 for (i = 0, j = 0; i < list->len; i++)
4222 vfs_path_t *vpath;
4224 vpath = vfs_path_from_str (list->list[i].fname->str);
4225 if (mc_lstat (vpath, &list->list[i].st) != 0)
4226 g_string_free (list->list[i].fname, TRUE);
4227 else
4229 if (j != i)
4230 list->list[j] = list->list[i];
4231 j++;
4233 vfs_path_free (vpath, TRUE);
4235 if (j == 0)
4236 dir_list_init (list);
4237 else
4238 list->len = j;
4240 recalculate_panel_summary (panel);
4242 if (panel != current_panel)
4243 (void) mc_chdir (current_panel->cwd_vpath);
4246 /* --------------------------------------------------------------------------------------------- */
4248 static void
4249 update_one_panel_widget (WPanel *panel, panel_update_flags_t flags, const char *current_file)
4251 gboolean free_pointer;
4252 char *my_current_file = NULL;
4254 if ((flags & UP_RELOAD) != 0)
4256 panel->is_panelized = FALSE;
4257 mc_setctl (panel->cwd_vpath, VFS_SETCTL_FLUSH, NULL);
4258 memset (&(panel->dir_stat), 0, sizeof (panel->dir_stat));
4261 /* If current_file == -1 (an invalid pointer) then preserve current */
4262 free_pointer = current_file == UP_KEEPSEL;
4264 if (free_pointer)
4266 const file_entry_t *fe;
4268 fe = panel_current_entry (panel);
4269 if (fe != NULL)
4270 my_current_file = g_strndup (fe->fname->str, fe->fname->len);
4271 current_file = my_current_file;
4274 if (panel->is_panelized)
4275 reload_panelized (panel);
4276 else
4277 panel_reload (panel);
4279 panel_set_current_by_name (panel, current_file);
4280 panel->dirty = TRUE;
4282 if (free_pointer)
4283 g_free (my_current_file);
4286 /* --------------------------------------------------------------------------------------------- */
4288 static void
4289 update_one_panel (int which, panel_update_flags_t flags, const char *current_file)
4291 if (get_panel_type (which) == view_listing)
4293 WPanel *panel;
4295 panel = PANEL (get_panel_widget (which));
4296 if (panel->is_panelized)
4297 flags &= ~UP_RELOAD;
4298 update_one_panel_widget (panel, flags, current_file);
4302 /* --------------------------------------------------------------------------------------------- */
4304 /* event callback */
4305 static gboolean
4306 event_update_panels (const gchar *event_group_name, const gchar *event_name,
4307 gpointer init_data, gpointer data)
4309 (void) event_group_name;
4310 (void) event_name;
4311 (void) init_data;
4312 (void) data;
4314 update_panels (UP_RELOAD, UP_KEEPSEL);
4316 return TRUE;
4319 /* --------------------------------------------------------------------------------------------- */
4321 /* event callback */
4322 static gboolean
4323 panel_save_current_file_to_clip_file (const gchar *event_group_name, const gchar *event_name,
4324 gpointer init_data, gpointer data)
4326 (void) event_group_name;
4327 (void) event_name;
4328 (void) init_data;
4329 (void) data;
4331 if (current_panel->marked == 0)
4333 const file_entry_t *fe;
4335 fe = panel_current_entry (current_panel);
4336 if (fe != NULL)
4337 mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_text_to_file", (gpointer) fe->fname->str);
4339 else
4341 int i;
4342 gboolean first = TRUE;
4343 char *flist = NULL;
4345 for (i = 0; i < current_panel->dir.len; i++)
4347 const file_entry_t *fe = &current_panel->dir.list[i];
4349 if (fe->f.marked != 0)
4350 { /* Skip the unmarked ones */
4351 if (first)
4353 flist = g_strndup (fe->fname->str, fe->fname->len);
4354 first = FALSE;
4356 else
4358 /* Add empty lines after the file */
4359 char *tmp;
4361 tmp = g_strconcat (flist, "\n", fe->fname->str, (char *) NULL);
4362 g_free (flist);
4363 flist = tmp;
4368 mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_text_to_file", (gpointer) flist);
4369 g_free (flist);
4371 return TRUE;
4374 /* --------------------------------------------------------------------------------------------- */
4376 static vfs_path_t *
4377 panel_recursive_cd_to_parent (const vfs_path_t *vpath)
4379 vfs_path_t *cwd_vpath;
4381 cwd_vpath = vfs_path_clone (vpath);
4383 while (mc_chdir (cwd_vpath) < 0)
4385 const char *panel_cwd_path;
4386 vfs_path_t *tmp_vpath;
4388 /* check if path contains only '/' */
4389 panel_cwd_path = vfs_path_as_str (cwd_vpath);
4390 if (panel_cwd_path != NULL && IS_PATH_SEP (panel_cwd_path[0]) && panel_cwd_path[1] == '\0')
4392 vfs_path_free (cwd_vpath, TRUE);
4393 return NULL;
4396 tmp_vpath = vfs_path_vtokens_get (cwd_vpath, 0, -1);
4397 vfs_path_free (cwd_vpath, TRUE);
4398 cwd_vpath =
4399 vfs_path_build_filename (PATH_SEP_STR, vfs_path_as_str (tmp_vpath), (char *) NULL);
4400 vfs_path_free (tmp_vpath, TRUE);
4403 return cwd_vpath;
4406 /* --------------------------------------------------------------------------------------------- */
4408 static void
4409 panel_dir_list_callback (dir_list_cb_state_t state, void *data)
4411 static int count = 0;
4413 (void) data;
4415 switch (state)
4417 case DIR_OPEN:
4418 count = 0;
4419 break;
4421 case DIR_READ:
4422 count++;
4423 if ((count & 15) == 0)
4424 rotate_dash (TRUE);
4425 break;
4427 case DIR_CLOSE:
4428 rotate_dash (FALSE);
4429 break;
4431 default:
4432 g_assert_not_reached ();
4436 /* --------------------------------------------------------------------------------------------- */
4437 /*** public functions ****************************************************************************/
4438 /* --------------------------------------------------------------------------------------------- */
4440 file_entry_t *
4441 panel_current_entry (const WPanel *panel)
4443 file_entry_t *fe;
4445 if (panel->dir.len == 0 || panel->current < 0 || panel->current >= panel->dir.len)
4446 return NULL;
4448 fe = &(panel->dir.list[panel->current]);
4450 return fe->fname == NULL ? NULL : fe;
4453 /* --------------------------------------------------------------------------------------------- */
4455 void
4456 panel_set_current_by_name (WPanel *panel, const char *name)
4458 int i;
4459 char *subdir;
4461 if (panel->dir.len == 0)
4463 panel_set_current (panel, -1);
4464 return;
4467 if (name == NULL)
4469 panel_set_current (panel, 0);
4470 return;
4473 /* We only want the last component of the directory,
4474 * and from this only the name without suffix.
4475 * Cut prefix if the panel is not panelized */
4476 if (panel->is_panelized)
4477 subdir = vfs_strip_suffix_from_filename (name);
4478 else
4479 subdir = vfs_strip_suffix_from_filename (x_basename (name));
4481 /* Search that subdir or filename without prefix (if not panelized panel),
4482 make it current if found */
4483 for (i = 0; i < panel->dir.len; i++)
4484 if (strcmp (subdir, panel->dir.list[i].fname->str) == 0)
4486 panel_set_current (panel, i);
4487 g_free (subdir);
4488 return;
4491 /* Make current near the filee that is missing */
4492 if (panel->current >= panel->dir.len)
4493 panel_set_current (panel, panel->dir.len - 1);
4494 g_free (subdir);
4496 select_item (panel);
4499 /* --------------------------------------------------------------------------------------------- */
4501 void
4502 panel_clean_dir (WPanel *panel)
4504 panel->top = 0;
4505 panel->current = 0;
4506 panel->marked = 0;
4507 panel->dirs_marked = 0;
4508 panel->total = 0;
4509 panel->quick_search.active = FALSE;
4510 panel->is_panelized = FALSE;
4511 panel->dirty = TRUE;
4512 panel->content_shift = -1;
4513 panel->max_shift = -1;
4515 dir_list_free_list (&panel->dir);
4518 /* --------------------------------------------------------------------------------------------- */
4520 * Set Up panel's current dir object
4522 * @param panel panel object
4523 * @param path_str string contain path
4526 void
4527 panel_set_cwd (WPanel *panel, const vfs_path_t *vpath)
4529 if (vpath != panel->cwd_vpath) /* check if new vpath is not the panel->cwd_vpath object */
4531 vfs_path_free (panel->cwd_vpath, TRUE);
4532 panel->cwd_vpath = vfs_path_clone (vpath);
4536 /* --------------------------------------------------------------------------------------------- */
4538 * Set Up panel's last working dir object
4540 * @param panel panel object
4541 * @param path_str string contain path
4544 void
4545 panel_set_lwd (WPanel *panel, const vfs_path_t *vpath)
4547 if (vpath != panel->lwd_vpath) /* check if new vpath is not the panel->lwd_vpath object */
4549 vfs_path_free (panel->lwd_vpath, TRUE);
4550 panel->lwd_vpath = vfs_path_clone (vpath);
4554 /* --------------------------------------------------------------------------------------------- */
4556 * Creatie an empty panel with specified size.
4558 * @param panel_name name of panel for setup receiving
4559 * @param r panel areaa
4561 * @return new instance of WPanel
4564 WPanel *
4565 panel_sized_empty_new (const char *panel_name, const WRect *r)
4567 WPanel *panel;
4568 Widget *w;
4569 char *section;
4570 int i, err;
4572 panel = g_new0 (WPanel, 1);
4573 w = WIDGET (panel);
4574 widget_init (w, r, panel_callback, panel_mouse_callback);
4575 w->options |= WOP_SELECTABLE | WOP_TOP_SELECT;
4576 w->keymap = panel_map;
4578 panel->dir.size = DIR_LIST_MIN_SIZE;
4579 panel->dir.list = g_new (file_entry_t, panel->dir.size);
4580 panel->dir.len = 0;
4581 panel->dir.callback = panel_dir_list_callback;
4583 panel->list_cols = 1;
4584 panel->brief_cols = 2;
4585 panel->dirty = TRUE;
4586 panel->content_shift = -1;
4587 panel->max_shift = -1;
4589 panel->list_format = list_full;
4590 panel->user_format = g_strdup (DEFAULT_USER_FORMAT);
4592 panel->filter.flags = FILE_FILTER_DEFAULT_FLAGS;
4594 for (i = 0; i < LIST_FORMATS; i++)
4595 panel->user_status_format[i] = g_strdup (DEFAULT_USER_FORMAT);
4597 #ifdef HAVE_CHARSET
4598 panel->codepage = SELECT_CHARSET_NO_TRANSLATE;
4599 #endif
4601 panel->frame_size = frame_half;
4603 panel->quick_search.buffer = g_string_sized_new (MC_MAXFILENAMELEN);
4604 panel->quick_search.prev_buffer = g_string_sized_new (MC_MAXFILENAMELEN);
4606 panel->name = g_strdup (panel_name);
4607 panel->dir_history.name = g_strconcat ("Dir Hist ", panel->name, (char *) NULL);
4608 /* directories history will be get later */
4610 section = g_strconcat ("Temporal:", panel->name, (char *) NULL);
4611 if (!mc_config_has_group (mc_global.main_config, section))
4613 g_free (section);
4614 section = g_strdup (panel->name);
4616 panel_load_setup (panel, section);
4617 g_free (section);
4619 if (panel->filter.value != NULL)
4621 gboolean case_sens = (panel->filter.flags & SELECT_MATCH_CASE) != 0;
4622 gboolean shell_patterns = (panel->filter.flags & SELECT_SHELL_PATTERNS) != 0;
4624 panel->filter.handler = mc_search_new (panel->filter.value, NULL);
4625 panel->filter.handler->search_type = shell_patterns ? MC_SEARCH_T_GLOB : MC_SEARCH_T_REGEX;
4626 panel->filter.handler->is_entire_line = TRUE;
4627 panel->filter.handler->is_case_sensitive = case_sens;
4629 /* FIXME: silent check -- do not display an error message */
4630 if (!mc_search_prepare (panel->filter.handler))
4631 file_filter_clear (&panel->filter);
4634 /* Load format strings */
4635 err = set_panel_formats (panel);
4636 if (err != 0)
4637 set_panel_formats (panel);
4639 return panel;
4642 /* --------------------------------------------------------------------------------------------- */
4644 * Panel creation for specified size and directory.
4646 * @param panel_name name of panel for setup retrieving
4647 * @param r panel areaa
4648 * @param vpath working panel directory. If NULL then current directory is used
4650 * @return new instance of WPanel
4653 WPanel *
4654 panel_sized_with_dir_new (const char *panel_name, const WRect *r, const vfs_path_t *vpath)
4656 WPanel *panel;
4657 char *curdir = NULL;
4658 #ifdef HAVE_CHARSET
4659 const vfs_path_element_t *path_element;
4660 #endif
4662 panel = panel_sized_empty_new (panel_name, r);
4664 if (vpath != NULL)
4666 curdir = vfs_get_cwd ();
4667 panel_set_cwd (panel, vpath);
4669 else
4671 vfs_setup_cwd ();
4672 panel->cwd_vpath = vfs_path_clone (vfs_get_raw_current_dir ());
4675 panel_set_lwd (panel, vfs_get_raw_current_dir ());
4677 #ifdef HAVE_CHARSET
4678 path_element = vfs_path_get_by_index (panel->cwd_vpath, -1);
4679 if (path_element->encoding != NULL)
4680 panel->codepage = get_codepage_index (path_element->encoding);
4681 #endif
4683 if (mc_chdir (panel->cwd_vpath) != 0)
4685 #ifdef HAVE_CHARSET
4686 panel->codepage = SELECT_CHARSET_NO_TRANSLATE;
4687 #endif
4688 vfs_setup_cwd ();
4689 vfs_path_free (panel->cwd_vpath, TRUE);
4690 panel->cwd_vpath = vfs_path_clone (vfs_get_raw_current_dir ());
4693 /* Load the default format */
4694 if (!dir_list_load (&panel->dir, panel->cwd_vpath, panel->sort_field->sort_routine,
4695 &panel->sort_info, &panel->filter))
4696 message (D_ERROR, MSG_ERROR, _("Cannot read directory contents"));
4698 if (panel->dir.len == 0)
4699 panel_set_current (panel, -1);
4701 /* Restore old right path */
4702 if (curdir != NULL)
4704 vfs_path_t *tmp_vpath;
4705 int err;
4707 tmp_vpath = vfs_path_from_str (curdir);
4708 mc_chdir (tmp_vpath);
4709 vfs_path_free (tmp_vpath, TRUE);
4710 (void) err;
4712 g_free (curdir);
4714 return panel;
4717 /* --------------------------------------------------------------------------------------------- */
4719 void
4720 panel_reload (WPanel *panel)
4722 struct stat current_stat;
4723 vfs_path_t *cwd_vpath;
4725 if (panels_options.fast_reload && stat (vfs_path_as_str (panel->cwd_vpath), &current_stat) == 0
4726 && current_stat.st_ctime == panel->dir_stat.st_ctime
4727 && current_stat.st_mtime == panel->dir_stat.st_mtime)
4728 return;
4730 cwd_vpath = panel_recursive_cd_to_parent (panel->cwd_vpath);
4731 vfs_path_free (panel->cwd_vpath, TRUE);
4733 if (cwd_vpath == NULL)
4735 panel->cwd_vpath = vfs_path_from_str (PATH_SEP_STR);
4736 panel_clean_dir (panel);
4737 dir_list_init (&panel->dir);
4738 return;
4741 panel->cwd_vpath = cwd_vpath;
4742 memset (&(panel->dir_stat), 0, sizeof (panel->dir_stat));
4743 show_dir (panel);
4745 if (!dir_list_reload (&panel->dir, panel->cwd_vpath, panel->sort_field->sort_routine,
4746 &panel->sort_info, &panel->filter))
4747 message (D_ERROR, MSG_ERROR, _("Cannot read directory contents"));
4749 panel->dirty = TRUE;
4751 if (panel->dir.len == 0)
4752 panel_set_current (panel, -1);
4753 else if (panel->current >= panel->dir.len)
4754 panel_set_current (panel, panel->dir.len - 1);
4756 recalculate_panel_summary (panel);
4759 /* --------------------------------------------------------------------------------------------- */
4760 /* Switches the panel to the mode specified in the format */
4761 /* Setting up both format and status string. Return: 0 - on success; */
4762 /* 1 - format error; 2 - status error; 3 - errors in both formats. */
4765 set_panel_formats (WPanel *p)
4767 GSList *form;
4768 char *err = NULL;
4769 int retcode = 0;
4771 form = use_display_format (p, panel_format (p), &err, FALSE);
4773 if (err != NULL)
4775 g_free (err);
4776 retcode = 1;
4778 else
4780 g_slist_free_full (p->format, (GDestroyNotify) format_item_free);
4781 p->format = form;
4784 if (panels_options.show_mini_info)
4786 form = use_display_format (p, mini_status_format (p), &err, TRUE);
4788 if (err != NULL)
4790 g_free (err);
4791 retcode += 2;
4793 else
4795 g_slist_free_full (p->status_format, (GDestroyNotify) format_item_free);
4796 p->status_format = form;
4800 panel_update_cols (WIDGET (p), p->frame_size);
4802 if (retcode != 0)
4803 message (D_ERROR, _("Warning"),
4804 _("User supplied format looks invalid, reverting to default."));
4805 if ((retcode & 0x01) != 0)
4807 g_free (p->user_format);
4808 p->user_format = g_strdup (DEFAULT_USER_FORMAT);
4810 if ((retcode & 0x02) != 0)
4812 g_free (p->user_status_format[p->list_format]);
4813 p->user_status_format[p->list_format] = g_strdup (DEFAULT_USER_FORMAT);
4816 return retcode;
4819 /* --------------------------------------------------------------------------------------------- */
4821 void
4822 panel_set_filter (WPanel *panel, const file_filter_t *filter)
4824 MC_PTR_FREE (panel->filter.value);
4825 mc_search_free (panel->filter.handler);
4826 panel->filter.handler = NULL;
4828 /* NULL to clear filter */
4829 if (filter != NULL)
4830 panel->filter = *filter;
4832 reread_cmd ();
4835 /* --------------------------------------------------------------------------------------------- */
4837 /* Select current item and readjust the panel */
4838 void
4839 select_item (WPanel *panel)
4841 adjust_top_file (panel);
4843 panel->dirty = TRUE;
4845 execute_hooks (select_file_hook);
4848 /* --------------------------------------------------------------------------------------------- */
4849 /** Clears all files in the panel, used only when one file was marked */
4850 void
4851 unmark_files (WPanel *panel)
4853 if (panel->marked != 0)
4855 int i;
4857 for (i = 0; i < panel->dir.len; i++)
4858 file_mark (panel, i, 0);
4860 panel->dirs_marked = 0;
4861 panel->marked = 0;
4862 panel->total = 0;
4866 /* --------------------------------------------------------------------------------------------- */
4867 /** Recalculate the panels summary information, used e.g. when marked
4868 files might have been removed by an external command */
4870 void
4871 recalculate_panel_summary (WPanel *panel)
4873 int i;
4875 panel->marked = 0;
4876 panel->dirs_marked = 0;
4877 panel->total = 0;
4879 for (i = 0; i < panel->dir.len; i++)
4880 if (panel->dir.list[i].f.marked != 0)
4882 /* do_file_mark will return immediately if newmark == oldmark.
4883 So we have to first unmark it to get panel's summary information
4884 updated. (Norbert) */
4885 panel->dir.list[i].f.marked = 0;
4886 do_file_mark (panel, i, 1);
4890 /* --------------------------------------------------------------------------------------------- */
4891 /** This routine marks a file or a directory */
4893 void
4894 do_file_mark (WPanel *panel, int idx, int mark)
4896 if (panel->dir.list[idx].f.marked == mark)
4897 return;
4899 /* Only '..' can't be marked, '.' isn't visible */
4900 if (DIR_IS_DOTDOT (panel->dir.list[idx].fname->str))
4901 return;
4903 file_mark (panel, idx, mark);
4904 if (panel->dir.list[idx].f.marked != 0)
4906 panel->marked++;
4908 if (S_ISDIR (panel->dir.list[idx].st.st_mode))
4910 if (panel->dir.list[idx].f.dir_size_computed != 0)
4911 panel->total += (uintmax_t) panel->dir.list[idx].st.st_size;
4912 panel->dirs_marked++;
4914 else
4915 panel->total += (uintmax_t) panel->dir.list[idx].st.st_size;
4917 set_colors (panel);
4919 else
4921 if (S_ISDIR (panel->dir.list[idx].st.st_mode))
4923 if (panel->dir.list[idx].f.dir_size_computed != 0)
4924 panel->total -= (uintmax_t) panel->dir.list[idx].st.st_size;
4925 panel->dirs_marked--;
4927 else
4928 panel->total -= (uintmax_t) panel->dir.list[idx].st.st_size;
4930 panel->marked--;
4934 /* --------------------------------------------------------------------------------------------- */
4936 * Changes the current directory of the panel.
4937 * Record change in the directory history.
4939 gboolean
4940 panel_do_cd (WPanel *panel, const vfs_path_t *new_dir_vpath, enum cd_enum cd_type)
4942 gboolean r;
4944 r = panel_do_cd_int (panel, new_dir_vpath, cd_type);
4945 if (r)
4946 directory_history_add (panel, panel->cwd_vpath);
4947 return r;
4950 /* --------------------------------------------------------------------------------------------- */
4952 void
4953 file_mark (WPanel *panel, int lc_index, int val)
4955 if (panel->dir.list[lc_index].f.marked != val)
4957 panel->dir.list[lc_index].f.marked = val;
4958 panel->dirty = TRUE;
4962 /* --------------------------------------------------------------------------------------------- */
4964 * Find marked file starting from the given position.
4966 * @param panel WPanel object
4967 * @param curent_file a staring position to get current or search next marked file
4969 * @return pointer to the name of find file or NULL if no file found or @current_file is out of range
4972 const GString *
4973 panel_find_marked_file (const WPanel *panel, int *current_file)
4975 while (panel->dir.list[*current_file].f.marked == 0 && *current_file < panel->dir.len)
4976 (*current_file)++;
4978 return (*current_file >= panel->dir.len ? NULL : panel->dir.list[*current_file].fname);
4981 /* --------------------------------------------------------------------------------------------- */
4983 * Get marked file clsest to the given position.
4985 * @param panel WPanel object
4986 * @param curent_file a staring position to get current or closest next marked file. If there are
4987 * no marked files in @panel, @panel->current is used.
4989 * @return pointer to the name of find file or NULL if no file found or @current_file is out of range.
4992 const GString *
4993 panel_get_marked_file (const WPanel *panel, int *current_file)
4995 const file_entry_t *fe;
4997 if (panel->marked != 0)
4998 return panel_find_marked_file (panel, current_file);
5000 fe = panel_current_entry (panel);
5002 return (fe == NULL ? NULL : fe->fname);
5005 /* --------------------------------------------------------------------------------------------- */
5007 void
5008 panel_re_sort (WPanel *panel)
5010 char *filename;
5011 const file_entry_t *fe;
5012 int i;
5014 if (panel == NULL)
5015 return;
5017 fe = panel_current_entry (panel);
5018 if (fe == NULL)
5019 return;
5021 filename = g_strndup (fe->fname->str, fe->fname->len);
5022 unselect_item (panel);
5023 dir_list_sort (&panel->dir, panel->sort_field->sort_routine, &panel->sort_info);
5024 panel->current = -1;
5026 for (i = panel->dir.len; i != 0; i--)
5027 if (strcmp (panel->dir.list[i - 1].fname->str, filename) == 0)
5029 panel->current = i - 1;
5030 break;
5033 g_free (filename);
5034 panel->top = panel->current - panel_items (panel) / 2;
5035 select_item (panel);
5036 panel->dirty = TRUE;
5039 /* --------------------------------------------------------------------------------------------- */
5041 void
5042 panel_set_sort_order (WPanel *panel, const panel_field_t *sort_order)
5044 if (sort_order == NULL)
5045 return;
5047 panel->sort_field = sort_order;
5049 /* The directory is already sorted, we have to load the unsorted stuff */
5050 if (sort_order->sort_routine == (GCompareFunc) unsorted)
5052 const file_entry_t *fe;
5053 char *current_file = NULL;
5055 fe = panel_current_entry (panel);
5056 if (fe != NULL)
5057 current_file = g_strndup (fe->fname->str, fe->fname->len);
5058 panel_reload (panel);
5059 panel_set_current_by_name (panel, current_file);
5060 g_free (current_file);
5062 panel_re_sort (panel);
5065 /* --------------------------------------------------------------------------------------------- */
5067 #ifdef HAVE_CHARSET
5070 * Change panel encoding.
5071 * @param panel WPanel object
5074 void
5075 panel_change_encoding (WPanel *panel)
5077 const char *encoding = NULL;
5078 char *errmsg;
5079 int r;
5081 r = select_charset (-1, -1, panel->codepage, FALSE);
5083 if (r == SELECT_CHARSET_CANCEL)
5084 return; /* Cancel */
5086 panel->codepage = r;
5088 if (panel->codepage == SELECT_CHARSET_NO_TRANSLATE)
5090 /* No translation */
5091 vfs_path_t *cd_path_vpath;
5093 g_free (init_translation_table (mc_global.display_codepage, mc_global.display_codepage));
5094 cd_path_vpath = remove_encoding_from_path (panel->cwd_vpath);
5095 panel_do_cd (panel, cd_path_vpath, cd_parse_command);
5096 show_dir (panel);
5097 vfs_path_free (cd_path_vpath, TRUE);
5098 return;
5101 errmsg = init_translation_table (panel->codepage, mc_global.display_codepage);
5102 if (errmsg != NULL)
5104 message (D_ERROR, MSG_ERROR, "%s", errmsg);
5105 g_free (errmsg);
5106 return;
5109 encoding = get_codepage_id (panel->codepage);
5110 if (encoding != NULL)
5112 vfs_path_change_encoding (panel->cwd_vpath, encoding);
5114 if (!panel_do_cd (panel, panel->cwd_vpath, cd_parse_command))
5115 cd_error_message (vfs_path_as_str (panel->cwd_vpath));
5119 /* --------------------------------------------------------------------------------------------- */
5122 * Remove encode info from last path element.
5125 vfs_path_t *
5126 remove_encoding_from_path (const vfs_path_t *vpath)
5128 vfs_path_t *ret_vpath;
5129 GString *tmp_conv;
5130 int indx;
5132 ret_vpath = vfs_path_new (FALSE);
5134 tmp_conv = g_string_new ("");
5136 for (indx = 0; indx < vfs_path_elements_count (vpath); indx++)
5138 GIConv converter;
5139 vfs_path_element_t *path_element;
5141 path_element = vfs_path_element_clone (vfs_path_get_by_index (vpath, indx));
5143 if (path_element->encoding == NULL)
5145 vfs_path_add_element (ret_vpath, path_element);
5146 continue;
5149 converter = str_crt_conv_to (path_element->encoding);
5150 if (converter == INVALID_CONV)
5152 vfs_path_add_element (ret_vpath, path_element);
5153 continue;
5156 MC_PTR_FREE (path_element->encoding);
5158 str_vfs_convert_from (converter, path_element->path, tmp_conv);
5160 g_free (path_element->path);
5161 path_element->path = g_strndup (tmp_conv->str, tmp_conv->len);
5163 g_string_set_size (tmp_conv, 0);
5165 str_close_conv (converter);
5166 str_close_conv (path_element->dir.converter);
5167 path_element->dir.converter = INVALID_CONV;
5168 vfs_path_add_element (ret_vpath, path_element);
5170 g_string_free (tmp_conv, TRUE);
5171 return ret_vpath;
5173 #endif /* HAVE_CHARSET */
5175 /* --------------------------------------------------------------------------------------------- */
5178 * This routine reloads the directory in both panels. It tries to
5179 * select current_file in current_panel and other_file in other_panel.
5180 * If current_file == -1 then it automatically sets current_file and
5181 * other_file to the current files in the panels.
5183 * If flags has the UP_ONLY_CURRENT bit toggled on, then it
5184 * will not reload the other panel.
5186 * @param flags for reload panel
5187 * @param current_file name of the current file
5190 void
5191 update_panels (panel_update_flags_t flags, const char *current_file)
5193 WPanel *panel;
5195 /* first, update other panel... */
5196 if ((flags & UP_ONLY_CURRENT) == 0)
5197 update_one_panel (get_other_index (), flags, UP_KEEPSEL);
5198 /* ...then current one */
5199 update_one_panel (get_current_index (), flags, current_file);
5201 if (get_current_type () == view_listing)
5202 panel = PANEL (get_panel_widget (get_current_index ()));
5203 else
5204 panel = PANEL (get_panel_widget (get_other_index ()));
5206 if (!panel->is_panelized)
5207 (void) mc_chdir (panel->cwd_vpath);
5210 /* --------------------------------------------------------------------------------------------- */
5212 gsize
5213 panel_get_num_of_sortable_fields (void)
5215 gsize ret = 0, lc_index;
5217 for (lc_index = 0; panel_fields[lc_index].id != NULL; lc_index++)
5218 if (panel_fields[lc_index].is_user_choice)
5219 ret++;
5220 return ret;
5223 /* --------------------------------------------------------------------------------------------- */
5225 char **
5226 panel_get_sortable_fields (gsize *array_size)
5228 char **ret;
5229 gsize lc_index, i;
5231 lc_index = panel_get_num_of_sortable_fields ();
5233 ret = g_try_new0 (char *, lc_index + 1);
5234 if (ret == NULL)
5235 return NULL;
5237 if (array_size != NULL)
5238 *array_size = lc_index;
5240 lc_index = 0;
5242 for (i = 0; panel_fields[i].id != NULL; i++)
5243 if (panel_fields[i].is_user_choice)
5244 ret[lc_index++] = g_strdup (_(panel_fields[i].title_hotkey));
5246 return ret;
5249 /* --------------------------------------------------------------------------------------------- */
5251 const panel_field_t *
5252 panel_get_field_by_id (const char *name)
5254 gsize lc_index;
5256 for (lc_index = 0; panel_fields[lc_index].id != NULL; lc_index++)
5257 if (panel_fields[lc_index].id != NULL && strcmp (name, panel_fields[lc_index].id) == 0)
5258 return &panel_fields[lc_index];
5260 return NULL;
5263 /* --------------------------------------------------------------------------------------------- */
5265 const panel_field_t *
5266 panel_get_field_by_title_hotkey (const char *name)
5268 gsize lc_index;
5270 for (lc_index = 0; panel_fields[lc_index].id != NULL; lc_index++)
5271 if (panel_fields[lc_index].title_hotkey != NULL &&
5272 strcmp (name, _(panel_fields[lc_index].title_hotkey)) == 0)
5273 return &panel_fields[lc_index];
5275 return NULL;
5278 /* --------------------------------------------------------------------------------------------- */
5280 const panel_field_t *
5281 panel_get_field_by_title (const char *name)
5283 gsize lc_index;
5285 for (lc_index = 0; panel_fields[lc_index].id != NULL; lc_index++)
5287 const char *title;
5289 title = panel_get_title_without_hotkey (panel_fields[lc_index].title_hotkey);
5290 if (strcmp (title, name) == 0)
5291 return &panel_fields[lc_index];
5294 return NULL;
5297 /* --------------------------------------------------------------------------------------------- */
5299 gsize
5300 panel_get_num_of_user_possible_fields (void)
5302 gsize ret = 0, lc_index;
5304 for (lc_index = 0; panel_fields[lc_index].id != NULL; lc_index++)
5305 if (panel_fields[lc_index].use_in_user_format)
5306 ret++;
5308 return ret;
5311 /* --------------------------------------------------------------------------------------------- */
5313 char **
5314 panel_get_user_possible_fields (gsize *array_size)
5316 char **ret;
5317 gsize lc_index, i;
5319 lc_index = panel_get_num_of_user_possible_fields ();
5321 ret = g_try_new0 (char *, lc_index + 1);
5322 if (ret == NULL)
5323 return NULL;
5325 if (array_size != NULL)
5326 *array_size = lc_index;
5328 lc_index = 0;
5330 for (i = 0; panel_fields[i].id != NULL; i++)
5331 if (panel_fields[i].use_in_user_format)
5332 ret[lc_index++] = g_strdup (_(panel_fields[i].title_hotkey));
5334 return ret;
5337 /* --------------------------------------------------------------------------------------------- */
5339 void
5340 panel_panelize_cd (void)
5342 WPanel *panel;
5343 int i;
5344 dir_list *list;
5345 panelized_descr_t *pdescr;
5346 dir_list *plist;
5347 gboolean panelized_same;
5349 if (!SELECTED_IS_PANEL)
5350 create_panel (MENU_PANEL_IDX, view_listing);
5352 panel = PANEL (get_panel_widget (MENU_PANEL_IDX));
5354 dir_list_clean (&panel->dir);
5356 if (panel->panelized_descr == NULL)
5357 panel->panelized_descr = panelized_descr_new ();
5359 pdescr = panel->panelized_descr;
5360 plist = &pdescr->list;
5362 if (pdescr->root_vpath == NULL)
5363 panel_panelize_change_root (panel, panel->cwd_vpath);
5365 if (plist->len < 1)
5366 dir_list_init (plist);
5367 else if (plist->len > panel->dir.size)
5368 dir_list_grow (&panel->dir, plist->len - panel->dir.size);
5370 list = &panel->dir;
5371 list->len = plist->len;
5373 panelized_same = vfs_path_equal (pdescr->root_vpath, panel->cwd_vpath);
5375 for (i = 0; i < plist->len; i++)
5377 if (panelized_same || DIR_IS_DOTDOT (plist->list[i].fname->str))
5378 list->list[i].fname = mc_g_string_dup (plist->list[i].fname);
5379 else
5381 vfs_path_t *tmp_vpath;
5383 tmp_vpath =
5384 vfs_path_append_new (pdescr->root_vpath, plist->list[i].fname->str, (char *) NULL);
5385 list->list[i].fname = g_string_new_take (vfs_path_free (tmp_vpath, FALSE));
5387 list->list[i].f.link_to_dir = plist->list[i].f.link_to_dir;
5388 list->list[i].f.stale_link = plist->list[i].f.stale_link;
5389 list->list[i].f.dir_size_computed = plist->list[i].f.dir_size_computed;
5390 list->list[i].f.marked = plist->list[i].f.marked;
5391 list->list[i].st = plist->list[i].st;
5392 list->list[i].name_sort_key = plist->list[i].name_sort_key;
5393 list->list[i].extension_sort_key = plist->list[i].extension_sort_key;
5396 panel->is_panelized = TRUE;
5397 panel_panelize_absolutize_if_needed (panel);
5399 panel_set_current_by_name (panel, NULL);
5402 /* --------------------------------------------------------------------------------------------- */
5405 * Change root directory of panelized content.
5406 * @param panel file panel
5407 * @param new_root new path
5409 void
5410 panel_panelize_change_root (WPanel *panel, const vfs_path_t *new_root)
5412 if (panel->panelized_descr == NULL)
5413 panel->panelized_descr = panelized_descr_new ();
5414 else
5415 vfs_path_free (panel->panelized_descr->root_vpath, TRUE);
5417 panel->panelized_descr->root_vpath = vfs_path_clone (new_root);
5420 /* --------------------------------------------------------------------------------------------- */
5423 * Conditionally switches a panel's directory to "/" (root).
5425 * If a panelized panel's listing contain absolute paths, this function
5426 * sets the panel's directory to "/". Otherwise it does nothing.
5428 * Rationale:
5430 * This makes tokenized strings like "%d/%p" work. This also makes other
5431 * places work where such naive concatenation is done in code (e.g., when
5432 * pressing ctrl+shift+enter, for CK_PutCurrentFullSelected).
5434 * When to call:
5436 * You should always call this function after you populate the listing
5437 * of a panelized panel.
5439 void
5440 panel_panelize_absolutize_if_needed (WPanel *panel)
5442 const dir_list *const list = &panel->dir;
5444 /* Note: We don't support mixing of absolute and relative paths, which is
5445 * why it's ok for us to check only the 1st entry. */
5446 if (list->len > 1 && g_path_is_absolute (list->list[1].fname->str))
5448 vfs_path_t *root;
5450 root = vfs_path_from_str (PATH_SEP_STR);
5451 panel_set_cwd (panel, root);
5452 if (panel == current_panel)
5453 mc_chdir (root);
5454 vfs_path_free (root, TRUE);
5458 /* --------------------------------------------------------------------------------------------- */
5460 void
5461 panel_panelize_save (WPanel *panel)
5463 int i;
5464 dir_list *list = &panel->dir;
5465 dir_list *plist;
5467 panel_panelize_change_root (panel, panel->cwd_vpath);
5469 plist = &panel->panelized_descr->list;
5471 if (plist->len > 0)
5472 dir_list_clean (plist);
5473 if (panel->dir.len == 0)
5474 return;
5476 if (panel->dir.len > plist->size)
5477 dir_list_grow (plist, panel->dir.len - plist->size);
5478 plist->len = panel->dir.len;
5480 for (i = 0; i < panel->dir.len; i++)
5482 plist->list[i].fname = mc_g_string_dup (list->list[i].fname);
5483 plist->list[i].f.link_to_dir = list->list[i].f.link_to_dir;
5484 plist->list[i].f.stale_link = list->list[i].f.stale_link;
5485 plist->list[i].f.dir_size_computed = list->list[i].f.dir_size_computed;
5486 plist->list[i].f.marked = list->list[i].f.marked;
5487 plist->list[i].st = list->list[i].st;
5488 plist->list[i].name_sort_key = list->list[i].name_sort_key;
5489 plist->list[i].extension_sort_key = list->list[i].extension_sort_key;
5493 /* --------------------------------------------------------------------------------------------- */
5495 void
5496 panel_init (void)
5498 panel_sort_up_char = mc_skin_get ("widget-panel", "sort-up-char", "'");
5499 panel_sort_down_char = mc_skin_get ("widget-panel", "sort-down-char", ".");
5500 panel_hiddenfiles_show_char = mc_skin_get ("widget-panel", "hiddenfiles-show-char", ".");
5501 panel_hiddenfiles_hide_char = mc_skin_get ("widget-panel", "hiddenfiles-hide-char", ".");
5502 panel_history_prev_item_char = mc_skin_get ("widget-panel", "history-prev-item-char", "<");
5503 panel_history_next_item_char = mc_skin_get ("widget-panel", "history-next-item-char", ">");
5504 panel_history_show_list_char = mc_skin_get ("widget-panel", "history-show-list-char", "^");
5505 panel_filename_scroll_left_char =
5506 mc_skin_get ("widget-panel", "filename-scroll-left-char", "{");
5507 panel_filename_scroll_right_char =
5508 mc_skin_get ("widget-panel", "filename-scroll-right-char", "}");
5510 string_file_name_buffer = g_string_sized_new (MC_MAXFILENAMELEN);
5512 mc_event_add (MCEVENT_GROUP_FILEMANAGER, "update_panels", event_update_panels, NULL, NULL);
5513 mc_event_add (MCEVENT_GROUP_FILEMANAGER, "panel_save_current_file_to_clip_file",
5514 panel_save_current_file_to_clip_file, NULL, NULL);
5517 /* --------------------------------------------------------------------------------------------- */
5519 void
5520 panel_deinit (void)
5522 g_free (panel_sort_up_char);
5523 g_free (panel_sort_down_char);
5524 g_free (panel_hiddenfiles_show_char);
5525 g_free (panel_hiddenfiles_hide_char);
5526 g_free (panel_history_prev_item_char);
5527 g_free (panel_history_next_item_char);
5528 g_free (panel_history_show_list_char);
5529 g_free (panel_filename_scroll_left_char);
5530 g_free (panel_filename_scroll_right_char);
5531 g_string_free (string_file_name_buffer, TRUE);
5534 /* --------------------------------------------------------------------------------------------- */
5536 gboolean
5537 panel_cd (WPanel *panel, const vfs_path_t *new_dir_vpath, enum cd_enum exact)
5539 gboolean res;
5540 const vfs_path_t *_new_dir_vpath = new_dir_vpath;
5542 if (panel->is_panelized)
5544 size_t new_vpath_len;
5546 new_vpath_len = vfs_path_len (new_dir_vpath);
5547 if (vfs_path_equal_len (new_dir_vpath, panel->panelized_descr->root_vpath, new_vpath_len))
5548 _new_dir_vpath = panel->panelized_descr->root_vpath;
5551 res = panel_do_cd (panel, _new_dir_vpath, exact);
5553 #ifdef HAVE_CHARSET
5554 if (res)
5556 const vfs_path_element_t *path_element;
5558 path_element = vfs_path_get_by_index (panel->cwd_vpath, -1);
5559 if (path_element->encoding != NULL)
5560 panel->codepage = get_codepage_index (path_element->encoding);
5561 else
5562 panel->codepage = SELECT_CHARSET_NO_TRANSLATE;
5564 #endif /* HAVE_CHARSET */
5566 return res;
5569 /* --------------------------------------------------------------------------------------------- */