Optimization of history save.
[midnight-commander.git] / src / filemanager / panel.c
blob24ee4be99bacf316fca316a5358c4d62a765d1f7
1 /* Panel managing.
2 Copyright (C) 1994, 1995, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
3 2005, 2006, 2007, 2009 Free Software Foundation, Inc.
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 Written by: 1995 Miguel de Icaza
16 1997, 1999 Timur Bakeyev
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
22 /** \file panel.c
23 * \brief Source: panel managin module
26 #include <config.h>
28 #include <errno.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <unistd.h>
34 #include "lib/global.h"
36 #include "lib/tty/tty.h"
37 #include "lib/tty/mouse.h" /* For Gpm_Event */
38 #include "lib/tty/key.h" /* XCTRL and ALT macros */
39 #include "lib/skin.h"
40 #include "lib/strescape.h"
41 #include "lib/filehighlight.h"
42 #include "lib/mcconfig.h"
43 #include "lib/vfs/vfs.h"
44 #include "lib/unixcompat.h"
45 #include "lib/timefmt.h"
46 #include "lib/util.h"
47 #include "lib/widget.h"
48 #ifdef HAVE_CHARSET
49 #include "lib/charsets.h" /* get_codepage_id () */
50 #endif
51 #include "lib/event.h"
53 #include "src/setup.h" /* For loading/saving panel options */
54 #include "src/execute.h"
55 #include "src/selcodepage.h" /* select_charset (), SELECT_CHARSET_NO_TRANSLATE */
56 #include "src/keybind-defaults.h" /* global_keymap_t */
57 #include "src/subshell.h" /* do_subshell_chdir() */
59 #include "dir.h"
60 #include "boxes.h"
61 #include "tree.h"
62 #include "ext.h" /* regexp_command */
63 #include "layout.h" /* Most layout variables are here */
64 #include "cmd.h"
65 #include "command.h" /* cmdline */
66 #include "usermenu.h"
67 #include "midnight.h"
68 #include "mountlist.h" /* my_statfs */
70 #include "panel.h"
72 /*** global variables ****************************************************************************/
74 /* The hook list for the select file function */
75 hook_t *select_file_hook = NULL;
77 static const char *string_file_name (file_entry *, int);
78 static const char *string_file_size (file_entry *, int);
79 static const char *string_file_size_brief (file_entry *, int);
80 static const char *string_file_type (file_entry *, int);
81 static const char *string_file_mtime (file_entry *, int);
82 static const char *string_file_atime (file_entry *, int);
83 static const char *string_file_ctime (file_entry *, int);
84 static const char *string_file_permission (file_entry *, int);
85 static const char *string_file_perm_octal (file_entry *, int);
86 static const char *string_file_nlinks (file_entry *, int);
87 static const char *string_inode (file_entry *, int);
88 static const char *string_file_nuid (file_entry *, int);
89 static const char *string_file_ngid (file_entry *, int);
90 static const char *string_file_owner (file_entry *, int);
91 static const char *string_file_group (file_entry *, int);
92 static const char *string_marked (file_entry *, int);
93 static const char *string_space (file_entry *, int);
94 static const char *string_dot (file_entry *, int);
96 /* *INDENT-OFF* */
97 panel_field_t panel_fields[] = {
99 "unsorted", 12, 1, J_LEFT_FIT,
100 /* TRANSLATORS: one single character to represent 'unsorted' sort mode */
101 /* TRANSLATORS: no need to translate 'sort', it's just a context prefix */
102 N_("sort|u"),
103 N_("&Unsorted"), TRUE, FALSE,
104 string_file_name,
105 (sortfn *) unsorted
109 "name", 12, 1, J_LEFT_FIT,
110 /* TRANSLATORS: one single character to represent 'name' sort mode */
111 /* TRANSLATORS: no need to translate 'sort', it's just a context prefix */
112 N_("sort|n"),
113 N_("&Name"), TRUE, TRUE,
114 string_file_name,
115 (sortfn *) sort_name
119 "version", 12, 1, J_LEFT_FIT,
120 /* TRANSLATORS: one single character to represent 'version' sort mode */
121 /* TRANSLATORS: no need to translate 'sort', it's just a context prefix */
122 N_("sort|v"),
123 N_("&Version"), TRUE, FALSE,
124 string_file_name,
125 (sortfn *) sort_vers
129 "extension", 12, 1, J_LEFT_FIT,
130 /* TRANSLATORS: one single character to represent 'extension' sort mode */
131 /* TRANSLATORS: no need to translate 'sort', it's just a context prefix */
132 N_("sort|e"),
133 N_("&Extension"), TRUE, FALSE,
134 string_file_name, /* TODO: string_file_ext */
135 (sortfn *) sort_ext
139 "size", 7, 0, J_RIGHT,
140 /* TRANSLATORS: one single character to represent 'size' sort mode */
141 /* TRANSLATORS: no need to translate 'sort', it's just a context prefix */
142 N_("sort|s"),
143 N_("&Size"), TRUE, TRUE,
144 string_file_size,
145 (sortfn *) sort_size
149 "bsize", 7, 0, J_RIGHT,
151 N_("Block Size"), FALSE, FALSE,
152 string_file_size_brief,
153 (sortfn *) sort_size
157 "type", 1, 0, J_LEFT,
159 "", FALSE, TRUE,
160 string_file_type,
161 NULL
165 "mtime", 12, 0, J_RIGHT,
166 /* TRANSLATORS: one single character to represent 'Modify time' sort mode */
167 /* TRANSLATORS: no need to translate 'sort', it's just a context prefix */
168 N_("sort|m"),
169 N_("&Modify time"), TRUE, TRUE,
170 string_file_mtime,
171 (sortfn *) sort_time
175 "atime", 12, 0, J_RIGHT,
176 /* TRANSLATORS: one single character to represent 'Access time' sort mode */
177 /* TRANSLATORS: no need to translate 'sort', it's just a context prefix */
178 N_("sort|a"),
179 N_("&Access time"), TRUE, TRUE,
180 string_file_atime,
181 (sortfn *) sort_atime
185 "ctime", 12, 0, J_RIGHT,
186 /* TRANSLATORS: one single character to represent 'Change time' sort mode */
187 /* TRANSLATORS: no need to translate 'sort', it's just a context prefix */
188 N_("sort|h"),
189 N_("C&hange time"), TRUE, TRUE,
190 string_file_ctime,
191 (sortfn *) sort_ctime
195 "perm", 10, 0, J_LEFT,
197 N_("Permission"), FALSE, TRUE,
198 string_file_permission,
199 NULL
203 "mode", 6, 0, J_RIGHT,
205 N_("Perm"), FALSE, TRUE,
206 string_file_perm_octal,
207 NULL
211 "nlink", 2, 0, J_RIGHT,
213 N_("Nl"), FALSE, TRUE,
214 string_file_nlinks, NULL
218 "inode", 5, 0, J_RIGHT,
219 /* TRANSLATORS: one single character to represent 'inode' sort mode */
220 /* TRANSLATORS: no need to translate 'sort', it's just a context prefix */
221 N_("sort|i"),
222 N_("&Inode"), TRUE, TRUE,
223 string_inode,
224 (sortfn *) sort_inode
228 "nuid", 5, 0, J_RIGHT,
230 N_("UID"), FALSE, FALSE,
231 string_file_nuid,
232 NULL
236 "ngid", 5, 0, J_RIGHT,
238 N_("GID"), FALSE, FALSE,
239 string_file_ngid,
240 NULL
244 "owner", 8, 0, J_LEFT_FIT,
246 N_("Owner"), FALSE, TRUE,
247 string_file_owner,
248 NULL
252 "group", 8, 0, J_LEFT_FIT,
254 N_("Group"), FALSE, TRUE,
255 string_file_group,
256 NULL
260 "mark", 1, 0, J_RIGHT,
262 " ", FALSE, TRUE,
263 string_marked,
264 NULL
268 "|", 1, 0, J_RIGHT,
270 " ", FALSE, TRUE,
271 NULL,
272 NULL
276 "space", 1, 0, J_RIGHT,
278 " ", FALSE, TRUE,
279 string_space,
280 NULL
284 "dot", 1, 0, J_RIGHT,
286 " ", FALSE, FALSE,
287 string_dot,
288 NULL
292 NULL, 0, 0, J_RIGHT, NULL, NULL, FALSE, FALSE, NULL, NULL
295 /* *INDENT-ON* */
297 extern int saving_setup;
299 /*** file scope macro definitions ****************************************************************/
301 #define ELEMENTS(arr) ( sizeof(arr) / sizeof((arr)[0]) )
303 #define NORMAL 0
304 #define SELECTED 1
305 #define MARKED 2
306 #define MARKED_SELECTED 3
307 #define STATUS 5
309 /* This macro extracts the number of available lines in a panel */
310 #define llines(p) (p->widget.lines - 3 - (panels_options.show_mini_info ? 2 : 0))
312 /*** file scope type declarations ****************************************************************/
314 typedef enum
316 MARK_DONT_MOVE = 0,
317 MARK_DOWN = 1,
318 MARK_FORCE_DOWN = 2,
319 MARK_FORCE_UP = 3
320 } mark_act_t;
323 * This describes a format item. The parse_display_format routine parses
324 * the user specified format and creates a linked list of format_e structures.
326 typedef struct format_e
328 struct format_e *next;
329 int requested_field_len;
330 int field_len;
331 align_crt_t just_mode;
332 int expand;
333 const char *(*string_fn) (file_entry *, int len);
334 char *title;
335 const char *id;
336 } format_e;
338 /*** file scope variables ************************************************************************/
340 static char *panel_sort_up_sign = NULL;
341 static char *panel_sort_down_sign = NULL;
343 static char *panel_hiddenfiles_sign_show = NULL;
344 static char *panel_hiddenfiles_sign_hide = NULL;
345 static char *panel_history_prev_item_sign = NULL;
346 static char *panel_history_next_item_sign = NULL;
347 static char *panel_history_show_list_sign = NULL;
349 /* Panel that selection started */
350 static WPanel *mouse_mark_panel = NULL;
352 static int mouse_marking = 0;
354 /*** file scope functions ************************************************************************/
355 /* --------------------------------------------------------------------------------------------- */
357 static void
358 set_colors (WPanel * panel)
360 (void) panel;
361 tty_set_normal_attrs ();
362 tty_setcolor (NORMAL_COLOR);
365 /* --------------------------------------------------------------------------------------------- */
366 /** Delete format string, it is a linked list */
368 static void
369 delete_format (format_e * format)
371 while (format != NULL)
373 format_e *next = format->next;
374 g_free (format->title);
375 g_free (format);
376 format = next;
380 /* --------------------------------------------------------------------------------------------- */
381 /** This code relies on the default justification!!! */
383 static void
384 add_permission_string (char *dest, int width, file_entry * fe, int attr, int color, int is_octal)
386 int i, r, l;
388 l = get_user_permissions (&fe->st);
390 if (is_octal)
392 /* Place of the access bit in octal mode */
393 l = width + l - 3;
394 r = l + 1;
396 else
398 /* The same to the triplet in string mode */
399 l = l * 3 + 1;
400 r = l + 3;
403 for (i = 0; i < width; i++)
405 if (i >= l && i < r)
407 if (attr == SELECTED || attr == MARKED_SELECTED)
408 tty_setcolor (MARKED_SELECTED_COLOR);
409 else
410 tty_setcolor (MARKED_COLOR);
412 else if (color >= 0)
413 tty_setcolor (color);
414 else
415 tty_lowlevel_setcolor (-color);
417 tty_print_char (dest[i]);
421 /* --------------------------------------------------------------------------------------------- */
422 /** String representations of various file attributes name */
424 static const char *
425 string_file_name (file_entry * fe, int len)
427 static char buffer[MC_MAXPATHLEN * MB_LEN_MAX + 1];
429 (void) len;
430 g_strlcpy (buffer, fe->fname, sizeof (buffer));
431 return buffer;
434 /* --------------------------------------------------------------------------------------------- */
436 static unsigned int
437 ilog10 (dev_t n)
439 unsigned int digits = 0;
442 digits++, n /= 10;
444 while (n != 0);
445 return digits;
448 /* --------------------------------------------------------------------------------------------- */
450 static void
451 format_device_number (char *buf, size_t bufsize, dev_t dev)
453 dev_t major_dev = major (dev);
454 dev_t minor_dev = minor (dev);
455 unsigned int major_digits = ilog10 (major_dev);
456 unsigned int minor_digits = ilog10 (minor_dev);
458 g_assert (bufsize >= 1);
459 if (major_digits + 1 + minor_digits + 1 <= bufsize)
461 g_snprintf (buf, bufsize, "%lu,%lu", (unsigned long) major_dev, (unsigned long) minor_dev);
463 else
465 g_strlcpy (buf, _("[dev]"), bufsize);
469 /* --------------------------------------------------------------------------------------------- */
470 /** size */
472 static const char *
473 string_file_size (file_entry * fe, int len)
475 static char buffer[BUF_TINY];
477 /* Don't ever show size of ".." since we don't calculate it */
478 if (!strcmp (fe->fname, ".."))
480 return _("UP--DIR");
483 #ifdef HAVE_STRUCT_STAT_ST_RDEV
484 if (S_ISBLK (fe->st.st_mode) || S_ISCHR (fe->st.st_mode))
485 format_device_number (buffer, len + 1, fe->st.st_rdev);
486 else
487 #endif
489 size_trunc_len (buffer, (unsigned int) len, fe->st.st_size, 0, panels_options.kilobyte_si);
491 return buffer;
494 /* --------------------------------------------------------------------------------------------- */
495 /** bsize */
497 static const char *
498 string_file_size_brief (file_entry * fe, int len)
500 if (S_ISLNK (fe->st.st_mode) && !fe->f.link_to_dir)
502 return _("SYMLINK");
505 if ((S_ISDIR (fe->st.st_mode) || fe->f.link_to_dir) && strcmp (fe->fname, ".."))
507 return _("SUB-DIR");
510 return string_file_size (fe, len);
513 /* --------------------------------------------------------------------------------------------- */
514 /** This functions return a string representation of a file entry type */
516 static const char *
517 string_file_type (file_entry * fe, int len)
519 static char buffer[2];
521 (void) len;
522 if (S_ISDIR (fe->st.st_mode))
523 buffer[0] = PATH_SEP;
524 else if (S_ISLNK (fe->st.st_mode))
526 if (fe->f.link_to_dir)
527 buffer[0] = '~';
528 else if (fe->f.stale_link)
529 buffer[0] = '!';
530 else
531 buffer[0] = '@';
533 else if (S_ISCHR (fe->st.st_mode))
534 buffer[0] = '-';
535 else if (S_ISSOCK (fe->st.st_mode))
536 buffer[0] = '=';
537 else if (S_ISDOOR (fe->st.st_mode))
538 buffer[0] = '>';
539 else if (S_ISBLK (fe->st.st_mode))
540 buffer[0] = '+';
541 else if (S_ISFIFO (fe->st.st_mode))
542 buffer[0] = '|';
543 else if (S_ISNAM (fe->st.st_mode))
544 buffer[0] = '#';
545 else if (!S_ISREG (fe->st.st_mode))
546 buffer[0] = '?'; /* non-regular of unknown kind */
547 else if (is_exe (fe->st.st_mode))
548 buffer[0] = '*';
549 else
550 buffer[0] = ' ';
551 buffer[1] = '\0';
552 return buffer;
555 /* --------------------------------------------------------------------------------------------- */
556 /** mtime */
558 static const char *
559 string_file_mtime (file_entry * fe, int len)
561 (void) len;
562 return file_date (fe->st.st_mtime);
565 /* --------------------------------------------------------------------------------------------- */
566 /** atime */
568 static const char *
569 string_file_atime (file_entry * fe, int len)
571 (void) len;
572 return file_date (fe->st.st_atime);
575 /* --------------------------------------------------------------------------------------------- */
576 /** ctime */
578 static const char *
579 string_file_ctime (file_entry * fe, int len)
581 (void) len;
582 return file_date (fe->st.st_ctime);
585 /* --------------------------------------------------------------------------------------------- */
586 /** perm */
588 static const char *
589 string_file_permission (file_entry * fe, int len)
591 (void) len;
592 return string_perm (fe->st.st_mode);
595 /* --------------------------------------------------------------------------------------------- */
596 /** mode */
598 static const char *
599 string_file_perm_octal (file_entry * fe, int len)
601 static char buffer[10];
603 (void) len;
604 g_snprintf (buffer, sizeof (buffer), "0%06lo", (unsigned long) fe->st.st_mode);
605 return buffer;
608 /* --------------------------------------------------------------------------------------------- */
609 /** nlink */
611 static const char *
612 string_file_nlinks (file_entry * fe, int len)
614 static char buffer[BUF_TINY];
616 (void) len;
617 g_snprintf (buffer, sizeof (buffer), "%16d", (int) fe->st.st_nlink);
618 return buffer;
621 /* --------------------------------------------------------------------------------------------- */
622 /** inode */
624 static const char *
625 string_inode (file_entry * fe, int len)
627 static char buffer[10];
629 (void) len;
630 g_snprintf (buffer, sizeof (buffer), "%lu", (unsigned long) fe->st.st_ino);
631 return buffer;
634 /* --------------------------------------------------------------------------------------------- */
635 /** nuid */
637 static const char *
638 string_file_nuid (file_entry * fe, int len)
640 static char buffer[10];
642 (void) len;
643 g_snprintf (buffer, sizeof (buffer), "%lu", (unsigned long) fe->st.st_uid);
644 return buffer;
647 /* --------------------------------------------------------------------------------------------- */
648 /** ngid */
650 static const char *
651 string_file_ngid (file_entry * fe, int len)
653 static char buffer[10];
655 (void) len;
656 g_snprintf (buffer, sizeof (buffer), "%lu", (unsigned long) fe->st.st_gid);
657 return buffer;
660 /* --------------------------------------------------------------------------------------------- */
661 /** owner */
663 static const char *
664 string_file_owner (file_entry * fe, int len)
666 (void) len;
667 return get_owner (fe->st.st_uid);
670 /* --------------------------------------------------------------------------------------------- */
671 /** group */
673 static const char *
674 string_file_group (file_entry * fe, int len)
676 (void) len;
677 return get_group (fe->st.st_gid);
680 /* --------------------------------------------------------------------------------------------- */
681 /** mark */
683 static const char *
684 string_marked (file_entry * fe, int len)
686 (void) len;
687 return fe->f.marked ? "*" : " ";
690 /* --------------------------------------------------------------------------------------------- */
691 /** space */
693 static const char *
694 string_space (file_entry * fe, int len)
696 (void) fe;
697 (void) len;
698 return " ";
701 /* --------------------------------------------------------------------------------------------- */
702 /** dot */
704 static const char *
705 string_dot (file_entry * fe, int len)
707 (void) fe;
708 (void) len;
709 return ".";
712 /* --------------------------------------------------------------------------------------------- */
714 static int
715 file_compute_color (int attr, file_entry * fe)
717 switch (attr)
719 case SELECTED:
720 return (SELECTED_COLOR);
721 case MARKED:
722 return (MARKED_COLOR);
723 case MARKED_SELECTED:
724 return (MARKED_SELECTED_COLOR);
725 case STATUS:
726 return (NORMAL_COLOR);
727 case NORMAL:
728 default:
729 if (!panels_options.filetype_mode)
730 return (NORMAL_COLOR);
733 return mc_fhl_get_color (mc_filehighlight, fe);
736 /* --------------------------------------------------------------------------------------------- */
737 /** Formats the file number file_index of panel in the buffer dest */
739 static void
740 format_file (char *dest, int limit, WPanel * panel, int file_index, int width, int attr,
741 int isstatus)
743 int color, length, empty_line;
744 const char *txt;
745 format_e *format, *home;
746 file_entry *fe;
748 (void) dest;
749 (void) limit;
750 length = 0;
751 empty_line = (file_index >= panel->count);
752 home = (isstatus) ? panel->status_format : panel->format;
753 fe = &panel->dir.list[file_index];
755 if (!empty_line)
756 color = file_compute_color (attr, fe);
757 else
758 color = NORMAL_COLOR;
760 for (format = home; format; format = format->next)
762 if (length == width)
763 break;
765 if (format->string_fn)
767 int len, perm;
768 char *preperad_text;
770 if (empty_line)
771 txt = " ";
772 else
773 txt = (*format->string_fn) (fe, format->field_len);
775 len = format->field_len;
776 if (len + length > width)
777 len = width - length;
778 if (len <= 0)
779 break;
781 perm = 0;
782 if (panels_options.permission_mode)
784 if (!strcmp (format->id, "perm"))
785 perm = 1;
786 else if (!strcmp (format->id, "mode"))
787 perm = 2;
790 if (color >= 0)
791 tty_setcolor (color);
792 else
793 tty_lowlevel_setcolor (-color);
795 preperad_text = (char *) str_fit_to_term (txt, len, format->just_mode);
796 if (perm)
797 add_permission_string (preperad_text, format->field_len, fe, attr, color, perm - 1);
798 else
799 tty_print_string (preperad_text);
801 length += len;
803 else
805 if (attr == SELECTED || attr == MARKED_SELECTED)
806 tty_setcolor (SELECTED_COLOR);
807 else
808 tty_setcolor (NORMAL_COLOR);
809 tty_print_one_vline (TRUE);
810 length++;
814 if (length < width)
815 tty_draw_hline (-1, -1, ' ', width - length);
818 /* --------------------------------------------------------------------------------------------- */
820 static void
821 repaint_file (WPanel * panel, int file_index, int mv, int attr, int isstatus)
823 int second_column = 0;
824 int width;
825 int offset = 0;
826 char buffer[BUF_MEDIUM];
828 gboolean panel_is_split = !isstatus && panel->split;
830 width = panel->widget.cols - 2;
832 if (panel_is_split)
834 second_column = (file_index - panel->top_file) / llines (panel);
835 width = width / 2 - 1;
837 if (second_column != 0)
839 offset = 1 + width;
840 /*width = (panel->widget.cols-2) - (panel->widget.cols-2)/2 - 1; */
841 width = panel->widget.cols - offset - 2;
845 /* Nothing to paint */
846 if (width <= 0)
847 return;
849 if (mv)
851 if (panel_is_split)
852 widget_move (&panel->widget,
853 (file_index - panel->top_file) % llines (panel) + 2, offset + 1);
854 else
855 widget_move (&panel->widget, file_index - panel->top_file + 2, 1);
858 format_file (buffer, sizeof (buffer), panel, file_index, width, attr, isstatus);
860 if (panel_is_split)
862 if (second_column)
863 tty_print_char (' ');
864 else
866 tty_setcolor (NORMAL_COLOR);
867 tty_print_one_vline (TRUE);
872 /* --------------------------------------------------------------------------------------------- */
874 static void
875 display_mini_info (WPanel * panel)
877 if (!panels_options.show_mini_info)
878 return;
880 widget_move (&panel->widget, llines (panel) + 3, 1);
882 if (panel->searching)
884 tty_setcolor (INPUT_COLOR);
885 tty_print_char ('/');
886 tty_print_string (str_fit_to_term (panel->search_buffer, panel->widget.cols - 3, J_LEFT));
887 return;
890 /* Status resolves links and show them */
891 set_colors (panel);
893 if (S_ISLNK (panel->dir.list[panel->selected].st.st_mode))
895 char *lc_link, link_target[MC_MAXPATHLEN];
896 int len;
898 lc_link = concat_dir_and_file (panel->cwd, panel->dir.list[panel->selected].fname);
899 len = mc_readlink (lc_link, link_target, MC_MAXPATHLEN - 1);
900 g_free (lc_link);
901 if (len > 0)
903 link_target[len] = 0;
904 tty_print_string ("-> ");
905 tty_print_string (str_fit_to_term (link_target, panel->widget.cols - 5, J_LEFT_FIT));
907 else
908 tty_print_string (str_fit_to_term (_("<readlink failed>"),
909 panel->widget.cols - 2, J_LEFT));
911 else if (strcmp (panel->dir.list[panel->selected].fname, "..") == 0)
913 /* FIXME:
914 * while loading directory (do_load_dir() and do_reload_dir()),
915 * the actual stat info about ".." directory isn't got;
916 * so just don't display incorrect info about ".." directory */
917 tty_print_string (str_fit_to_term (_("UP--DIR"), panel->widget.cols - 2, J_LEFT));
919 else
920 /* Default behavior */
921 repaint_file (panel, panel->selected, 0, STATUS, 1);
924 /* --------------------------------------------------------------------------------------------- */
926 static void
927 paint_dir (WPanel * panel)
929 int i;
930 int color; /* Color value of the line */
931 int items; /* Number of items */
933 items = llines (panel) * (panel->split ? 2 : 1);
935 for (i = 0; i < items; i++)
937 if (i + panel->top_file >= panel->count)
938 color = 0;
939 else
941 color = 2 * (panel->dir.list[i + panel->top_file].f.marked);
942 color += (panel->selected == i + panel->top_file && panel->active);
944 repaint_file (panel, i + panel->top_file, 1, color, 0);
946 tty_set_normal_attrs ();
949 /* --------------------------------------------------------------------------------------------- */
951 static void
952 display_total_marked_size (WPanel * panel, int y, int x, gboolean size_only)
954 char buffer[BUF_SMALL], b_bytes[BUF_SMALL], *buf;
955 int cols;
957 if (panel->marked <= 0)
958 return;
960 buf = size_only ? b_bytes : buffer;
961 cols = panel->widget.cols - 2;
964 * This is a trick to use two ngettext() calls in one sentence.
965 * First make "N bytes", then insert it into "X in M files".
967 g_snprintf (b_bytes, sizeof (b_bytes),
968 ngettext ("%s byte", "%s bytes", panel->total),
969 size_trunc_sep (panel->total, panels_options.kilobyte_si));
970 if (!size_only)
971 g_snprintf (buffer, sizeof (buffer),
972 ngettext ("%s in %d file", "%s in %d files", panel->marked),
973 b_bytes, panel->marked);
975 /* don't forget spaces around buffer content */
976 buf = (char *) str_trunc (buf, cols - 4);
978 if (x < 0)
979 /* center in panel */
980 x = (panel->widget.cols - str_term_width1 (buf)) / 2 - 1;
983 * y == llines (panel) + 2 for mini_info_separator
984 * y == panel->widget.lines - 1 for panel bottom frame
986 widget_move (&panel->widget, y, x);
987 tty_setcolor (MARKED_COLOR);
988 tty_printf (" %s ", buf);
991 /* --------------------------------------------------------------------------------------------- */
993 static void
994 mini_info_separator (WPanel * panel)
996 if (panels_options.show_mini_info)
998 const int y = llines (panel) + 2;
1000 tty_setcolor (NORMAL_COLOR);
1001 tty_draw_hline (panel->widget.y + y, panel->widget.x + 1,
1002 ACS_HLINE, panel->widget.cols - 2);
1003 /* Status displays total marked size.
1004 * Centered in panel, full format. */
1005 display_total_marked_size (panel, y, -1, FALSE);
1009 /* --------------------------------------------------------------------------------------------- */
1011 static void
1012 show_free_space (WPanel * panel)
1014 /* Used to figure out how many free space we have */
1015 static struct my_statfs myfs_stats;
1016 /* Old current working directory for displaying free space */
1017 static char *old_cwd = NULL;
1019 /* Don't try to stat non-local fs */
1020 if (!vfs_file_is_local (panel->cwd) || !free_space)
1021 return;
1023 if (old_cwd == NULL || strcmp (old_cwd, panel->cwd) != 0)
1025 char rpath[PATH_MAX];
1027 init_my_statfs ();
1028 g_free (old_cwd);
1029 old_cwd = g_strdup (panel->cwd);
1031 if (mc_realpath (panel->cwd, rpath) == NULL)
1032 return;
1034 my_statfs (&myfs_stats, rpath);
1037 if (myfs_stats.avail > 0 || myfs_stats.total > 0)
1039 char buffer1[6], buffer2[6], tmp[BUF_SMALL];
1040 size_trunc_len (buffer1, sizeof (buffer1) - 1, myfs_stats.avail, 1,
1041 panels_options.kilobyte_si);
1042 size_trunc_len (buffer2, sizeof (buffer2) - 1, myfs_stats.total, 1,
1043 panels_options.kilobyte_si);
1044 g_snprintf (tmp, sizeof (tmp), " %s/%s (%d%%) ", buffer1, buffer2,
1045 myfs_stats.total >
1046 0 ? (int) (100 * (double) myfs_stats.avail / myfs_stats.total) : 0);
1047 widget_move (&panel->widget, panel->widget.lines - 1,
1048 panel->widget.cols - 2 - (int) strlen (tmp));
1049 tty_setcolor (NORMAL_COLOR);
1050 tty_print_string (tmp);
1054 /* --------------------------------------------------------------------------------------------- */
1056 static void
1057 show_dir (WPanel * panel)
1059 gchar *tmp;
1060 set_colors (panel);
1061 draw_box (panel->widget.owner,
1062 panel->widget.y, panel->widget.x, panel->widget.lines, panel->widget.cols, FALSE);
1064 if (panels_options.show_mini_info)
1066 widget_move (&panel->widget, llines (panel) + 2, 0);
1067 tty_print_alt_char (ACS_LTEE, FALSE);
1068 widget_move (&panel->widget, llines (panel) + 2, panel->widget.cols - 1);
1069 tty_print_alt_char (ACS_RTEE, FALSE);
1072 widget_move (&panel->widget, 0, 1);
1073 tty_print_string (panel_history_prev_item_sign);
1075 tmp = panels_options.show_dot_files ? panel_hiddenfiles_sign_show : panel_hiddenfiles_sign_hide;
1076 tmp = g_strdup_printf ("%s[%s]%s", tmp, panel_history_show_list_sign,
1077 panel_history_next_item_sign);
1079 widget_move (&panel->widget, 0, panel->widget.cols - 6);
1080 tty_print_string (tmp);
1082 g_free (tmp);
1084 if (panel->active)
1085 tty_setcolor (REVERSE_COLOR);
1087 widget_move (&panel->widget, 0, 3);
1089 tty_printf (" %s ",
1090 str_term_trim (strip_home_and_password (panel->cwd),
1091 min (max (panel->widget.cols - 12, 0), panel->widget.cols)));
1093 if (!panels_options.show_mini_info)
1095 if (panel->marked == 0)
1097 /* Show size of curret file in the bottom of panel */
1098 if (S_ISREG (panel->dir.list[panel->selected].st.st_mode))
1100 char buffer[BUF_SMALL];
1102 g_snprintf (buffer, sizeof (buffer), " %s ",
1103 size_trunc_sep (panel->dir.list[panel->selected].st.st_size,
1104 panels_options.kilobyte_si));
1105 tty_setcolor (NORMAL_COLOR);
1106 widget_move (&panel->widget, panel->widget.lines - 1, 4);
1107 tty_print_string (buffer);
1110 else
1112 /* Show total size of marked files
1113 * In the bottom of panel, display size only. */
1114 display_total_marked_size (panel, panel->widget.lines - 1, 2, TRUE);
1118 show_free_space (panel);
1120 if (panel->active)
1121 tty_set_normal_attrs ();
1124 /* --------------------------------------------------------------------------------------------- */
1125 /** To be used only by long_frame and full_frame to adjust top_file */
1127 static void
1128 adjust_top_file (WPanel * panel)
1130 int old_top = panel->top_file;
1132 if (panel->selected - old_top > llines (panel))
1133 panel->top_file = panel->selected;
1134 if (old_top - panel->count > llines (panel))
1135 panel->top_file = panel->count - llines (panel);
1138 /* --------------------------------------------------------------------------------------------- */
1139 /** add "#enc:encodning" to end of path */
1140 /* if path end width a previous #enc:, only encoding is changed no additional
1141 * #enc: is appended
1142 * retun new string
1145 static char *
1146 add_encoding_to_path (const char *path, const char *encoding)
1148 char *result;
1149 char *semi;
1150 char *slash;
1152 semi = g_strrstr (path, VFS_ENCODING_PREFIX);
1154 if (semi != NULL)
1156 slash = strchr (semi, PATH_SEP);
1157 if (slash != NULL)
1159 result = g_strconcat (path, PATH_SEP_STR VFS_ENCODING_PREFIX, encoding, (char *) NULL);
1161 else
1163 *semi = '\0';
1164 result = g_strconcat (path, PATH_SEP_STR VFS_ENCODING_PREFIX, encoding, (char *) NULL);
1165 *semi = '#';
1168 else
1170 result = g_strconcat (path, PATH_SEP_STR VFS_ENCODING_PREFIX, encoding, (char *) NULL);
1173 return result;
1176 /* --------------------------------------------------------------------------------------------- */
1178 static char *
1179 panel_save_name (WPanel * panel)
1181 /* If the program is shuting down */
1182 if ((mc_global.widget.midnight_shutdown && auto_save_setup) || saving_setup)
1183 return g_strdup (panel->panel_name);
1184 else
1185 return g_strconcat ("Temporal:", panel->panel_name, (char *) NULL);
1188 /* --------------------------------------------------------------------------------------------- */
1190 /* "history_save" event handler */
1191 static gboolean
1192 panel_save_history (const gchar * event_group_name, const gchar * event_name,
1193 gpointer init_data, gpointer data)
1195 WPanel *p = (WPanel *) init_data;
1197 (void) event_group_name;
1198 (void) event_name;
1200 if (p->dir_history != NULL)
1202 ev_history_load_save_t *ev = (ev_history_load_save_t *) data;
1204 history_save (ev->cfg, p->hist_name, p->dir_history);
1207 return TRUE;
1210 /* --------------------------------------------------------------------------------------------- */
1212 static void
1213 panel_destroy (WPanel * p)
1215 size_t i;
1217 if (panels_options.auto_save_setup)
1219 char *name;
1221 name = panel_save_name (p);
1222 panel_save_setup (p, name);
1223 g_free (name);
1226 panel_clean_dir (p);
1228 /* clean history */
1229 if (p->dir_history != NULL)
1231 /* directory history is already saved before this moment */
1232 p->dir_history = g_list_first (p->dir_history);
1233 g_list_foreach (p->dir_history, (GFunc) g_free, NULL);
1234 g_list_free (p->dir_history);
1236 g_free (p->hist_name);
1238 delete_format (p->format);
1239 delete_format (p->status_format);
1241 g_free (p->user_format);
1242 for (i = 0; i < LIST_TYPES; i++)
1243 g_free (p->user_status_format[i]);
1244 g_free (p->dir.list);
1245 g_free (p->panel_name);
1248 /* --------------------------------------------------------------------------------------------- */
1250 static void
1251 panel_format_modified (WPanel * panel)
1253 panel->format_modified = 1;
1256 /* --------------------------------------------------------------------------------------------- */
1258 static void
1259 panel_paint_sort_info (WPanel * panel)
1261 if (*panel->sort_info.sort_field->hotkey != '\0')
1263 const char *sort_sign =
1264 panel->sort_info.reverse ? panel_sort_down_sign : panel_sort_up_sign;
1265 char *str;
1267 str = g_strdup_printf ("%s%s", sort_sign, Q_ (panel->sort_info.sort_field->hotkey));
1268 widget_move (&panel->widget, 1, 1);
1269 tty_print_string (str);
1270 g_free (str);
1274 /* --------------------------------------------------------------------------------------------- */
1276 static gchar *
1277 panel_get_title_without_hotkey (const char *title)
1279 char *translated_title;
1280 char *hkey;
1282 if (title == NULL)
1283 return NULL;
1284 if (title[0] == '\0')
1285 return g_strdup ("");
1287 translated_title = g_strdup (_(title));
1289 hkey = strchr (translated_title, '&');
1290 if ((hkey != NULL) && (hkey[1] != '\0'))
1291 memmove ((void *) hkey, (void *) hkey + 1, strlen (hkey));
1293 return translated_title;
1296 /* --------------------------------------------------------------------------------------------- */
1298 static void
1299 paint_frame (WPanel * panel)
1301 int side, width;
1302 GString *format_txt;
1304 if (!panel->split)
1305 adjust_top_file (panel);
1307 widget_erase (&panel->widget);
1308 show_dir (panel);
1310 widget_move (&panel->widget, 1, 1);
1312 for (side = 0; side <= panel->split; side++)
1314 format_e *format;
1316 if (side)
1318 tty_setcolor (NORMAL_COLOR);
1319 tty_print_one_vline (TRUE);
1320 width = panel->widget.cols - panel->widget.cols / 2 - 1;
1322 else if (panel->split)
1323 width = panel->widget.cols / 2 - 3;
1324 else
1325 width = panel->widget.cols - 2;
1327 format_txt = g_string_new ("");
1328 for (format = panel->format; format; format = format->next)
1330 if (format->string_fn)
1332 g_string_set_size (format_txt, 0);
1334 if (panel->list_type == list_long
1335 && strcmp (format->id, panel->sort_info.sort_field->id) == 0)
1336 g_string_append (format_txt,
1337 panel->sort_info.reverse
1338 ? panel_sort_down_sign : panel_sort_up_sign);
1340 g_string_append (format_txt, format->title);
1341 if (strcmp (format->id, "name") == 0 && panel->filter && *panel->filter)
1343 g_string_append (format_txt, " [");
1344 g_string_append (format_txt, panel->filter);
1345 g_string_append (format_txt, "]");
1348 tty_setcolor (HEADER_COLOR);
1349 tty_print_string (str_fit_to_term (format_txt->str, format->field_len,
1350 J_CENTER_LEFT));
1351 width -= format->field_len;
1353 else
1355 tty_setcolor (NORMAL_COLOR);
1356 tty_print_one_vline (TRUE);
1357 width--;
1360 g_string_free (format_txt, TRUE);
1362 if (width > 0)
1363 tty_draw_hline (-1, -1, ' ', width);
1366 if (panel->list_type != list_long)
1367 panel_paint_sort_info (panel);
1370 /* --------------------------------------------------------------------------------------------- */
1372 static const char *
1373 parse_panel_size (WPanel * panel, const char *format, int isstatus)
1375 panel_display_t frame = frame_half;
1376 format = skip_separators (format);
1378 if (!strncmp (format, "full", 4))
1380 frame = frame_full;
1381 format += 4;
1383 else if (!strncmp (format, "half", 4))
1385 frame = frame_half;
1386 format += 4;
1389 if (!isstatus)
1391 panel->frame_size = frame;
1392 panel->split = 0;
1395 /* Now, the optional column specifier */
1396 format = skip_separators (format);
1398 if (*format == '1' || *format == '2')
1400 if (!isstatus)
1401 panel->split = *format == '2';
1402 format++;
1405 if (!isstatus)
1406 panel_update_cols (&(panel->widget), panel->frame_size);
1408 return skip_separators (format);
1411 /* Format is:
1413 all := panel_format? format
1414 panel_format := [full|half] [1|2]
1415 format := one_format_e
1416 | format , one_format_e
1418 one_format_e := just format.id [opt_size]
1419 just := [<=>]
1420 opt_size := : size [opt_expand]
1421 size := [0-9]+
1422 opt_expand := +
1426 /* --------------------------------------------------------------------------------------------- */
1428 static format_e *
1429 parse_display_format (WPanel * panel, const char *format, char **error, int isstatus,
1430 int *res_total_cols)
1432 format_e *darr, *old = 0, *home = 0; /* The formats we return */
1433 int total_cols = 0; /* Used columns by the format */
1434 int set_justify; /* flag: set justification mode? */
1435 align_crt_t justify = J_LEFT; /* Which mode. */
1436 size_t i;
1438 static size_t i18n_timelength = 0; /* flag: check ?Time length at startup */
1440 *error = 0;
1442 if (i18n_timelength == 0)
1444 i18n_timelength = i18n_checktimelength (); /* Musn't be 0 */
1446 for (i = 0; panel_fields[i].id != NULL; i++)
1447 if (strcmp ("time", panel_fields[i].id + 1) == 0)
1448 panel_fields[i].min_size = i18n_timelength;
1452 * This makes sure that the panel and mini status full/half mode
1453 * setting is equal
1455 format = parse_panel_size (panel, format, isstatus);
1457 while (*format)
1458 { /* format can be an empty string */
1459 int found = 0;
1461 darr = g_new0 (format_e, 1);
1463 /* I'm so ugly, don't look at me :-) */
1464 if (!home)
1465 home = old = darr;
1467 old->next = darr;
1468 darr->next = 0;
1469 old = darr;
1471 format = skip_separators (format);
1473 if (strchr ("<=>", *format))
1475 set_justify = 1;
1476 switch (*format)
1478 case '<':
1479 justify = J_LEFT;
1480 break;
1481 case '=':
1482 justify = J_CENTER;
1483 break;
1484 case '>':
1485 default:
1486 justify = J_RIGHT;
1487 break;
1489 format = skip_separators (format + 1);
1491 else
1492 set_justify = 0;
1494 for (i = 0; panel_fields[i].id != NULL; i++)
1496 size_t klen = strlen (panel_fields[i].id);
1498 if (strncmp (format, panel_fields[i].id, klen) != 0)
1499 continue;
1501 format += klen;
1503 darr->requested_field_len = panel_fields[i].min_size;
1504 darr->string_fn = panel_fields[i].string_fn;
1505 darr->title = panel_get_title_without_hotkey (panel_fields[i].title_hotkey);
1507 darr->id = panel_fields[i].id;
1508 darr->expand = panel_fields[i].expands;
1509 darr->just_mode = panel_fields[i].default_just;
1511 if (set_justify)
1513 if (IS_FIT (darr->just_mode))
1514 darr->just_mode = MAKE_FIT (justify);
1515 else
1516 darr->just_mode = justify;
1518 found = 1;
1520 format = skip_separators (format);
1522 /* If we have a size specifier */
1523 if (*format == ':')
1525 int req_length;
1527 /* If the size was specified, we don't want
1528 * auto-expansion by default
1530 darr->expand = 0;
1531 format++;
1532 req_length = atoi (format);
1533 darr->requested_field_len = req_length;
1535 format = skip_numbers (format);
1537 /* Now, if they insist on expansion */
1538 if (*format == '+')
1540 darr->expand = 1;
1541 format++;
1546 break;
1548 if (!found)
1550 char *tmp_format = g_strdup (format);
1552 int pos = min (8, strlen (format));
1553 delete_format (home);
1554 tmp_format[pos] = 0;
1555 *error =
1556 g_strconcat (_("Unknown tag on display format:"), " ", tmp_format, (char *) NULL);
1557 g_free (tmp_format);
1558 return 0;
1560 total_cols += darr->requested_field_len;
1563 *res_total_cols = total_cols;
1564 return home;
1567 /* --------------------------------------------------------------------------------------------- */
1569 static format_e *
1570 use_display_format (WPanel * panel, const char *format, char **error, int isstatus)
1572 #define MAX_EXPAND 4
1573 int expand_top = 0; /* Max used element in expand */
1574 int usable_columns; /* Usable columns in the panel */
1575 int total_cols = 0;
1576 int i;
1577 format_e *darr, *home;
1579 if (!format)
1580 format = DEFAULT_USER_FORMAT;
1582 home = parse_display_format (panel, format, error, isstatus, &total_cols);
1584 if (*error)
1585 return 0;
1587 panel->dirty = 1;
1589 /* Status needn't to be split */
1590 usable_columns = ((panel->widget.cols - 2) / ((isstatus)
1592 : (panel->split + 1))) - (!isstatus
1593 && panel->split);
1595 /* Look for the expandable fields and set field_len based on the requested field len */
1596 for (darr = home; darr && expand_top < MAX_EXPAND; darr = darr->next)
1598 darr->field_len = darr->requested_field_len;
1599 if (darr->expand)
1600 expand_top++;
1603 /* If we used more columns than the available columns, adjust that */
1604 if (total_cols > usable_columns)
1606 int pdif, dif = total_cols - usable_columns;
1608 while (dif)
1610 pdif = dif;
1611 for (darr = home; darr; darr = darr->next)
1613 if (dif && darr->field_len - 1)
1615 darr->field_len--;
1616 dif--;
1620 /* avoid endless loop if num fields > 40 */
1621 if (pdif == dif)
1622 break;
1624 total_cols = usable_columns; /* give up, the rest should be truncated */
1627 /* Expand the available space */
1628 if ((usable_columns > total_cols) && expand_top)
1630 int spaces = (usable_columns - total_cols) / expand_top;
1631 int extra = (usable_columns - total_cols) % expand_top;
1633 for (i = 0, darr = home; darr && (i < expand_top); darr = darr->next)
1634 if (darr->expand)
1636 darr->field_len += (spaces + ((i == 0) ? extra : 0));
1637 i++;
1640 return home;
1643 /* --------------------------------------------------------------------------------------------- */
1644 /** Given the panel->view_type returns the format string to be parsed */
1646 static const char *
1647 panel_format (WPanel * panel)
1649 switch (panel->list_type)
1651 case list_long:
1652 return "full perm space nlink space owner space group space size space mtime space name";
1654 case list_brief:
1655 return "half 2 type name";
1657 case list_user:
1658 return panel->user_format;
1660 default:
1661 case list_full:
1662 return "half type name | size | mtime";
1666 /* --------------------------------------------------------------------------------------------- */
1668 static const char *
1669 mini_status_format (WPanel * panel)
1671 if (panel->user_mini_status)
1672 return panel->user_status_format[panel->list_type];
1674 switch (panel->list_type)
1677 case list_long:
1678 return "full perm space nlink space owner space group space size space mtime space name";
1680 case list_brief:
1681 return "half type name space bsize space perm space";
1683 case list_full:
1684 return "half type name";
1686 default:
1687 case list_user:
1688 return panel->user_format;
1692 /* */
1693 /* Panel operation commands */
1694 /* */
1696 /* --------------------------------------------------------------------------------------------- */
1697 /** Used to emulate Lynx's entering leaving a directory with the arrow keys */
1699 static cb_ret_t
1700 maybe_cd (int move_up_dir)
1702 if (panels_options.navigate_with_arrows && (cmdline->buffer[0] == '\0'))
1704 if (move_up_dir)
1706 do_cd ("..", cd_exact);
1707 return MSG_HANDLED;
1710 if (S_ISDIR (selection (current_panel)->st.st_mode)
1711 || link_isdir (selection (current_panel)))
1713 do_cd (selection (current_panel)->fname, cd_exact);
1714 return MSG_HANDLED;
1717 return MSG_NOT_HANDLED;
1720 /* --------------------------------------------------------------------------------------------- */
1722 /* if command line is empty then do 'cd ..' */
1723 static cb_ret_t
1724 force_maybe_cd (void)
1726 if (cmdline->buffer[0] == '\0')
1728 do_cd ("..", cd_exact);
1729 return MSG_HANDLED;
1731 return MSG_NOT_HANDLED;
1734 /* --------------------------------------------------------------------------------------------- */
1736 /* Returns the number of items in the given panel */
1737 static int
1738 ITEMS (WPanel * p)
1740 if (p->split)
1741 return llines (p) * 2;
1742 else
1743 return llines (p);
1746 /* --------------------------------------------------------------------------------------------- */
1748 static void
1749 unselect_item (WPanel * panel)
1751 repaint_file (panel, panel->selected, 1, 2 * selection (panel)->f.marked, 0);
1754 /* --------------------------------------------------------------------------------------------- */
1756 static void
1757 move_down (WPanel * panel)
1759 if (panel->selected + 1 == panel->count)
1760 return;
1762 unselect_item (panel);
1763 panel->selected++;
1764 if (panels_options.scroll_pages && panel->selected - panel->top_file == ITEMS (panel))
1766 /* Scroll window half screen */
1767 panel->top_file += ITEMS (panel) / 2;
1768 if (panel->top_file > panel->count - ITEMS (panel))
1769 panel->top_file = panel->count - ITEMS (panel);
1770 paint_dir (panel);
1772 select_item (panel);
1775 /* --------------------------------------------------------------------------------------------- */
1777 static void
1778 move_up (WPanel * panel)
1780 if (panel->selected == 0)
1781 return;
1783 unselect_item (panel);
1784 panel->selected--;
1785 if (panels_options.scroll_pages && panel->selected < panel->top_file)
1787 /* Scroll window half screen */
1788 panel->top_file -= ITEMS (panel) / 2;
1789 if (panel->top_file < 0)
1790 panel->top_file = 0;
1791 paint_dir (panel);
1793 select_item (panel);
1796 /* --------------------------------------------------------------------------------------------- */
1797 /** Changes the selection by lines (may be negative) */
1799 static void
1800 move_selection (WPanel * panel, int lines)
1802 int new_pos;
1803 int adjust = 0;
1805 new_pos = panel->selected + lines;
1806 if (new_pos >= panel->count)
1807 new_pos = panel->count - 1;
1809 if (new_pos < 0)
1810 new_pos = 0;
1812 unselect_item (panel);
1813 panel->selected = new_pos;
1815 if (panel->selected - panel->top_file >= ITEMS (panel))
1817 panel->top_file += lines;
1818 adjust = 1;
1821 if (panel->selected - panel->top_file < 0)
1823 panel->top_file += lines;
1824 adjust = 1;
1827 if (adjust)
1829 if (panel->top_file > panel->selected)
1830 panel->top_file = panel->selected;
1831 if (panel->top_file < 0)
1832 panel->top_file = 0;
1833 paint_dir (panel);
1835 select_item (panel);
1838 /* --------------------------------------------------------------------------------------------- */
1840 static cb_ret_t
1841 move_left (WPanel * panel)
1843 if (panel->split)
1845 move_selection (panel, -llines (panel));
1846 return MSG_HANDLED;
1848 else
1849 return maybe_cd (1); /* cd .. */
1852 /* --------------------------------------------------------------------------------------------- */
1854 static int
1855 move_right (WPanel * panel)
1857 if (panel->split)
1859 move_selection (panel, llines (panel));
1860 return MSG_HANDLED;
1862 else
1863 return maybe_cd (0); /* cd (selection) */
1866 /* --------------------------------------------------------------------------------------------- */
1868 static void
1869 prev_page (WPanel * panel)
1871 int items;
1873 if (!panel->selected && !panel->top_file)
1874 return;
1875 unselect_item (panel);
1876 items = ITEMS (panel);
1877 if (panel->top_file < items)
1878 items = panel->top_file;
1879 if (!items)
1880 panel->selected = 0;
1881 else
1882 panel->selected -= items;
1883 panel->top_file -= items;
1885 select_item (panel);
1886 paint_dir (panel);
1889 /* --------------------------------------------------------------------------------------------- */
1891 static void
1892 goto_parent_dir (WPanel * panel)
1894 (void) panel;
1895 do_cd ("..", cd_exact);
1898 /* --------------------------------------------------------------------------------------------- */
1900 static void
1901 next_page (WPanel * panel)
1903 int items;
1905 if (panel->selected == panel->count - 1)
1906 return;
1907 unselect_item (panel);
1908 items = ITEMS (panel);
1909 if (panel->top_file > panel->count - 2 * items)
1910 items = panel->count - items - panel->top_file;
1911 if (panel->top_file + items < 0)
1912 items = -panel->top_file;
1913 if (!items)
1914 panel->selected = panel->count - 1;
1915 else
1916 panel->selected += items;
1917 panel->top_file += items;
1919 select_item (panel);
1920 paint_dir (panel);
1923 /* --------------------------------------------------------------------------------------------- */
1925 static void
1926 goto_child_dir (WPanel * panel)
1928 if ((S_ISDIR (selection (panel)->st.st_mode) || link_isdir (selection (panel))))
1930 do_cd (selection (panel)->fname, cd_exact);
1934 /* --------------------------------------------------------------------------------------------- */
1936 static void
1937 goto_top_file (WPanel * panel)
1939 unselect_item (panel);
1940 panel->selected = panel->top_file;
1941 select_item (panel);
1944 /* --------------------------------------------------------------------------------------------- */
1946 static void
1947 goto_middle_file (WPanel * panel)
1949 unselect_item (panel);
1950 panel->selected = panel->top_file + (ITEMS (panel) / 2);
1951 select_item (panel);
1954 /* --------------------------------------------------------------------------------------------- */
1956 static void
1957 goto_bottom_file (WPanel * panel)
1959 unselect_item (panel);
1960 panel->selected = panel->top_file + ITEMS (panel) - 1;
1961 select_item (panel);
1964 /* --------------------------------------------------------------------------------------------- */
1966 static void
1967 move_home (WPanel * panel)
1969 if (panel->selected == 0)
1970 return;
1972 unselect_item (panel);
1974 if (panels_options.torben_fj_mode)
1976 int middle_pos = panel->top_file + (ITEMS (panel) / 2);
1978 if (panel->selected > middle_pos)
1980 goto_middle_file (panel);
1981 return;
1983 if (panel->selected != panel->top_file)
1985 goto_top_file (panel);
1986 return;
1990 panel->top_file = 0;
1991 panel->selected = 0;
1993 paint_dir (panel);
1994 select_item (panel);
1997 /* --------------------------------------------------------------------------------------------- */
1999 static void
2000 move_end (WPanel * panel)
2002 if (panel->selected == panel->count - 1)
2003 return;
2005 unselect_item (panel);
2007 if (panels_options.torben_fj_mode)
2009 int middle_pos = panel->top_file + (ITEMS (panel) / 2);
2011 if (panel->selected < middle_pos)
2013 goto_middle_file (panel);
2014 return;
2016 if (panel->selected != (panel->top_file + ITEMS (panel) - 1))
2018 goto_bottom_file (panel);
2019 return;
2023 panel->selected = panel->count - 1;
2024 paint_dir (panel);
2025 select_item (panel);
2028 /* --------------------------------------------------------------------------------------------- */
2030 static void
2031 do_mark_file (WPanel * panel, mark_act_t do_move)
2033 do_file_mark (panel, panel->selected, selection (panel)->f.marked ? 0 : 1);
2034 if ((panels_options.mark_moves_down && do_move == MARK_DOWN) || do_move == MARK_FORCE_DOWN)
2035 move_down (panel);
2036 else if (do_move == MARK_FORCE_UP)
2037 move_up (panel);
2040 /* --------------------------------------------------------------------------------------------- */
2042 static void
2043 mark_file (WPanel * panel)
2045 do_mark_file (panel, MARK_DOWN);
2048 /* --------------------------------------------------------------------------------------------- */
2050 static void
2051 mark_file_up (WPanel * panel)
2053 do_mark_file (panel, MARK_FORCE_UP);
2056 /* --------------------------------------------------------------------------------------------- */
2058 static void
2059 mark_file_down (WPanel * panel)
2061 do_mark_file (panel, MARK_FORCE_DOWN);
2064 /* --------------------------------------------------------------------------------------------- */
2065 /** Incremental search of a file name in the panel.
2066 * @param panel instance of WPanel structure
2067 * @param c_code key code
2070 static void
2071 do_search (WPanel * panel, int c_code)
2073 size_t l;
2074 int i, sel;
2075 gboolean wrapped = FALSE;
2076 char *act;
2077 mc_search_t *search;
2078 char *reg_exp, *esc_str;
2079 gboolean is_found = FALSE;
2081 l = strlen (panel->search_buffer);
2082 if (c_code == KEY_BACKSPACE)
2084 if (l != 0)
2086 act = panel->search_buffer + l;
2087 str_prev_noncomb_char (&act, panel->search_buffer);
2088 act[0] = '\0';
2090 panel->search_chpoint = 0;
2092 else
2094 if (c_code != 0 && (gsize) panel->search_chpoint < sizeof (panel->search_char))
2096 panel->search_char[panel->search_chpoint] = c_code;
2097 panel->search_chpoint++;
2100 if (panel->search_chpoint > 0)
2102 switch (str_is_valid_char (panel->search_char, panel->search_chpoint))
2104 case -2:
2105 return;
2106 case -1:
2107 panel->search_chpoint = 0;
2108 return;
2109 default:
2110 if (l + panel->search_chpoint < sizeof (panel->search_buffer))
2112 memcpy (panel->search_buffer + l, panel->search_char, panel->search_chpoint);
2113 l += panel->search_chpoint;
2114 *(panel->search_buffer + l) = '\0';
2115 panel->search_chpoint = 0;
2121 reg_exp = g_strdup_printf ("%s*", panel->search_buffer);
2122 esc_str = strutils_escape (reg_exp, -1, ",|\\{}[]", TRUE);
2123 search = mc_search_new (esc_str, -1);
2124 search->search_type = MC_SEARCH_T_GLOB;
2125 search->is_entire_line = TRUE;
2126 switch (panels_options.qsearch_mode)
2128 case QSEARCH_CASE_SENSITIVE:
2129 search->is_case_sensitive = TRUE;
2130 break;
2131 case QSEARCH_CASE_INSENSITIVE:
2132 search->is_case_sensitive = FALSE;
2133 break;
2134 default:
2135 search->is_case_sensitive = panel->sort_info.case_sensitive;
2136 break;
2138 sel = panel->selected;
2139 for (i = panel->selected; !wrapped || i != panel->selected; i++)
2141 if (i >= panel->count)
2143 i = 0;
2144 if (wrapped)
2145 break;
2146 wrapped = TRUE;
2148 if (mc_search_run (search, panel->dir.list[i].fname, 0, panel->dir.list[i].fnamelen, NULL))
2150 sel = i;
2151 is_found = TRUE;
2152 break;
2155 if (is_found)
2157 unselect_item (panel);
2158 panel->selected = sel;
2159 select_item (panel);
2160 send_message ((Widget *) panel, WIDGET_DRAW, 0);
2162 else if (c_code != KEY_BACKSPACE)
2164 act = panel->search_buffer + l;
2165 str_prev_noncomb_char (&act, panel->search_buffer);
2166 act[0] = '\0';
2168 mc_search_free (search);
2169 g_free (reg_exp);
2170 g_free (esc_str);
2173 /* --------------------------------------------------------------------------------------------- */
2174 /** Start new search.
2175 * @param panel instance of WPanel structure
2178 static void
2179 start_search (WPanel * panel)
2181 if (panel->searching)
2183 if (panel->selected + 1 == panel->count)
2184 panel->selected = 0;
2185 else
2186 move_down (panel);
2188 /* in case if there was no search string we need to recall
2189 previous string, with which we ended previous searching */
2190 if (panel->search_buffer[0] == '\0')
2191 g_strlcpy (panel->search_buffer, panel->prev_search_buffer,
2192 sizeof (panel->search_buffer));
2194 do_search (panel, 0);
2196 else
2198 panel->searching = TRUE;
2199 panel->search_buffer[0] = '\0';
2200 panel->search_char[0] = '\0';
2201 panel->search_chpoint = 0;
2202 display_mini_info (panel);
2203 mc_refresh ();
2207 /* --------------------------------------------------------------------------------------------- */
2209 static void
2210 stop_search (WPanel * panel)
2212 panel->searching = FALSE;
2214 /* if user had overrdied search string, we need to store it
2215 to the previous_search_buffer */
2216 if (panel->search_buffer[0] != '\0')
2217 g_strlcpy (panel->prev_search_buffer, panel->search_buffer,
2218 sizeof (panel->prev_search_buffer));
2220 display_mini_info (panel);
2223 /* --------------------------------------------------------------------------------------------- */
2224 /** Return 1 if the Enter key has been processed, 0 otherwise */
2226 static int
2227 do_enter_on_file_entry (file_entry * fe)
2229 char *full_name;
2232 * Directory or link to directory - change directory.
2233 * Try the same for the entries on which mc_lstat() has failed.
2235 if (S_ISDIR (fe->st.st_mode) || link_isdir (fe) || (fe->st.st_mode == 0))
2237 if (!do_cd (fe->fname, cd_exact))
2238 message (D_ERROR, MSG_ERROR, _("Cannot change directory"));
2239 return 1;
2242 /* Try associated command */
2243 if (regex_command (fe->fname, "Open", NULL) != 0)
2244 return 1;
2246 /* Check if the file is executable */
2247 full_name = concat_dir_and_file (current_panel->cwd, fe->fname);
2248 if (!is_exe (fe->st.st_mode) || !if_link_is_exe (full_name, fe))
2250 g_free (full_name);
2251 return 0;
2253 g_free (full_name);
2255 if (confirm_execute)
2257 if (query_dialog
2258 (_("The Midnight Commander"),
2259 _("Do you really want to execute?"), D_NORMAL, 2, _("&Yes"), _("&No")) != 0)
2260 return 1;
2263 if (!vfs_current_is_local ())
2265 char *tmp;
2266 int ret;
2268 tmp = concat_dir_and_file (vfs_get_current_dir (), fe->fname);
2269 ret = mc_setctl (tmp, VFS_SETCTL_RUN, NULL);
2270 g_free (tmp);
2271 /* We took action only if the dialog was shown or the execution
2272 * was successful */
2273 return confirm_execute || (ret == 0);
2277 char *tmp = name_quote (fe->fname, 0);
2278 char *cmd = g_strconcat (".", PATH_SEP_STR, tmp, (char *) NULL);
2279 g_free (tmp);
2280 shell_execute (cmd, 0);
2281 g_free (cmd);
2284 #if HAVE_CHARSET
2285 mc_global.source_codepage = default_source_codepage;
2286 #endif
2288 return 1;
2291 /* --------------------------------------------------------------------------------------------- */
2293 static int
2294 do_enter (WPanel * panel)
2296 return do_enter_on_file_entry (selection (panel));
2299 /* --------------------------------------------------------------------------------------------- */
2301 static void
2302 chdir_other_panel (WPanel * panel)
2304 char *new_dir;
2305 char *sel_entry = NULL;
2307 if (get_other_type () != view_listing)
2309 set_display_type (get_other_index (), view_listing);
2312 if (!S_ISDIR (panel->dir.list[panel->selected].st.st_mode))
2314 new_dir = concat_dir_and_file (panel->cwd, "..");
2315 sel_entry = strrchr (panel->cwd, PATH_SEP);
2317 else
2318 new_dir = concat_dir_and_file (panel->cwd, panel->dir.list[panel->selected].fname);
2320 change_panel ();
2321 do_cd (new_dir, cd_exact);
2322 if (sel_entry)
2323 try_to_select (current_panel, sel_entry);
2324 change_panel ();
2326 move_down (panel);
2328 g_free (new_dir);
2331 /* --------------------------------------------------------------------------------------------- */
2333 * Make the current directory of the current panel also the current
2334 * directory of the other panel. Put the other panel to the listing
2335 * mode if needed. If the current panel is panelized, the other panel
2336 * doesn't become panelized.
2339 static void
2340 panel_sync_other (const WPanel * panel)
2342 if (get_other_type () != view_listing)
2344 set_display_type (get_other_index (), view_listing);
2347 do_panel_cd (other_panel, current_panel->cwd, cd_exact);
2349 /* try to select current filename on the other panel */
2350 if (!panel->is_panelized)
2352 try_to_select (other_panel, selection (panel)->fname);
2356 /* --------------------------------------------------------------------------------------------- */
2358 static void
2359 chdir_to_readlink (WPanel * panel)
2361 char *new_dir;
2363 if (get_other_type () != view_listing)
2364 return;
2366 if (S_ISLNK (panel->dir.list[panel->selected].st.st_mode))
2368 char buffer[MC_MAXPATHLEN], *p;
2369 int i;
2370 struct stat st;
2372 i = readlink (selection (panel)->fname, buffer, MC_MAXPATHLEN - 1);
2373 if (i < 0)
2374 return;
2375 if (mc_stat (selection (panel)->fname, &st) < 0)
2376 return;
2377 buffer[i] = 0;
2378 if (!S_ISDIR (st.st_mode))
2380 p = strrchr (buffer, PATH_SEP);
2381 if (p && !p[1])
2383 *p = 0;
2384 p = strrchr (buffer, PATH_SEP);
2386 if (!p)
2387 return;
2388 p[1] = 0;
2390 if (*buffer == PATH_SEP)
2391 new_dir = g_strdup (buffer);
2392 else
2393 new_dir = concat_dir_and_file (panel->cwd, buffer);
2395 change_panel ();
2396 do_cd (new_dir, cd_exact);
2397 change_panel ();
2399 move_down (panel);
2401 g_free (new_dir);
2405 /* --------------------------------------------------------------------------------------------- */
2407 static gsize
2408 panel_get_format_field_count (WPanel * panel)
2410 format_e *format;
2411 gsize lc_index;
2412 for (lc_index = 0, format = panel->format; format != NULL; format = format->next, lc_index++);
2413 return lc_index;
2416 /* --------------------------------------------------------------------------------------------- */
2418 function return 0 if not found and REAL_INDEX+1 if found
2421 static gsize
2422 panel_get_format_field_index_by_name (WPanel * panel, const char *name)
2424 format_e *format;
2425 gsize lc_index;
2427 for (lc_index = 1, format = panel->format;
2428 !(format == NULL || strcmp (format->title, name) == 0); format = format->next, lc_index++);
2429 if (format == NULL)
2430 lc_index = 0;
2432 return lc_index;
2435 /* --------------------------------------------------------------------------------------------- */
2437 static format_e *
2438 panel_get_format_field_by_index (WPanel * panel, gsize lc_index)
2440 format_e *format;
2441 for (format = panel->format;
2442 !(format == NULL || lc_index == 0); format = format->next, lc_index--);
2443 return format;
2446 /* --------------------------------------------------------------------------------------------- */
2448 static const panel_field_t *
2449 panel_get_sortable_field_by_format (WPanel * panel, gsize lc_index)
2451 const panel_field_t *pfield;
2452 format_e *format;
2454 format = panel_get_format_field_by_index (panel, lc_index);
2455 if (format == NULL)
2456 return NULL;
2457 pfield = panel_get_field_by_title (format->title);
2458 if (pfield == NULL)
2459 return NULL;
2460 if (pfield->sort_routine == NULL)
2461 return NULL;
2462 return pfield;
2465 /* --------------------------------------------------------------------------------------------- */
2467 static void
2468 panel_toggle_sort_order_prev (WPanel * panel)
2470 gsize lc_index, i;
2471 gchar *title;
2473 const panel_field_t *pfield = NULL;
2475 title = panel_get_title_without_hotkey (panel->sort_info.sort_field->title_hotkey);
2476 lc_index = panel_get_format_field_index_by_name (panel, title);
2477 g_free (title);
2479 if (lc_index > 1)
2481 /* search for prev sortable column in panel format */
2482 for (i = lc_index - 1;
2483 i != 0 && (pfield = panel_get_sortable_field_by_format (panel, i - 1)) == NULL; i--);
2486 if (pfield == NULL)
2488 /* Sortable field not found. Try to search in each array */
2489 for (i = panel_get_format_field_count (panel);
2490 i != 0 && (pfield = panel_get_sortable_field_by_format (panel, i - 1)) == NULL; i--);
2493 if (pfield != NULL)
2495 panel->sort_info.sort_field = pfield;
2496 panel_set_sort_order (panel, pfield);
2500 /* --------------------------------------------------------------------------------------------- */
2502 static void
2503 panel_toggle_sort_order_next (WPanel * panel)
2505 gsize lc_index, i;
2506 const panel_field_t *pfield = NULL;
2507 gsize format_field_count;
2508 gchar *title;
2510 format_field_count = panel_get_format_field_count (panel);
2511 title = panel_get_title_without_hotkey (panel->sort_info.sort_field->title_hotkey);
2512 lc_index = panel_get_format_field_index_by_name (panel, title);
2513 g_free (title);
2515 if (lc_index != 0 && lc_index != format_field_count)
2517 /* search for prev sortable column in panel format */
2518 for (i = lc_index;
2519 i != format_field_count
2520 && (pfield = panel_get_sortable_field_by_format (panel, i)) == NULL; i++);
2523 if (pfield == NULL)
2525 /* Sortable field not found. Try to search in each array */
2526 for (i = 0;
2527 i != format_field_count
2528 && (pfield = panel_get_sortable_field_by_format (panel, i)) == NULL; i++);
2531 if (pfield != NULL)
2533 panel->sort_info.sort_field = pfield;
2534 panel_set_sort_order (panel, pfield);
2538 /* --------------------------------------------------------------------------------------------- */
2540 static void
2541 panel_select_sort_order (WPanel * panel)
2543 const panel_field_t *sort_order;
2545 sort_order = sort_box (&panel->sort_info);
2546 if (sort_order != NULL)
2548 panel->sort_info.sort_field = sort_order;
2549 panel_set_sort_order (panel, sort_order);
2553 /* --------------------------------------------------------------------------------------------- */
2555 static void
2556 panel_set_sort_type_by_id (WPanel * panel, const char *name)
2558 if (strcmp (panel->sort_info.sort_field->id, name) != 0)
2560 const panel_field_t *sort_order;
2562 sort_order = panel_get_field_by_id (name);
2563 if (sort_order == NULL)
2564 return;
2565 panel->sort_info.sort_field = sort_order;
2567 else
2568 panel->sort_info.reverse = !panel->sort_info.reverse;
2570 panel_set_sort_order (panel, panel->sort_info.sort_field);
2573 /* --------------------------------------------------------------------------------------------- */
2575 * If we moved to the parent directory move the selection pointer to
2576 * the old directory name; If we leave VFS dir, remove FS specificator.
2578 * You do _NOT_ want to add any vfs aware code here. <pavel@ucw.cz>
2581 static const char *
2582 get_parent_dir_name (const char *cwd, const char *lwd)
2584 size_t llen, clen;
2586 llen = strlen (lwd);
2587 clen = strlen (cwd);
2589 if (llen > clen)
2591 const char *p;
2593 p = strrchr (lwd, PATH_SEP);
2595 if ((p != NULL)
2596 && (strncmp (cwd, lwd, (size_t) (p - lwd)) == 0)
2597 && (clen == (size_t) (p - lwd)
2598 || ((p == lwd) && (cwd[0] == PATH_SEP) && (cwd[1] == '\0'))))
2599 return (p + 1);
2602 return NULL;
2605 /* --------------------------------------------------------------------------------------------- */
2606 /** Wrapper for do_subshell_chdir, check for availability of subshell */
2608 static void
2609 subshell_chdir (const char *directory)
2611 #ifdef HAVE_SUBSHELL_SUPPORT
2612 if (mc_global.tty.use_subshell && vfs_current_is_local ())
2613 do_subshell_chdir (directory, FALSE, TRUE);
2614 #endif /* HAVE_SUBSHELL_SUPPORT */
2617 /* --------------------------------------------------------------------------------------------- */
2619 * Changes the current directory of the panel.
2620 * Don't record change in the directory history.
2623 static gboolean
2624 _do_panel_cd (WPanel * panel, const char *new_dir, enum cd_enum cd_type)
2626 const char *directory;
2627 char *olddir;
2628 char temp[MC_MAXPATHLEN];
2629 char *translated_url;
2631 if (cd_type == cd_parse_command)
2633 while (*new_dir == ' ')
2634 new_dir++;
2637 olddir = g_strdup (panel->cwd);
2638 new_dir = translated_url = vfs_translate_url (new_dir);
2640 /* Convert *new_path to a suitable pathname, handle ~user */
2642 if (cd_type == cd_parse_command)
2644 if (!strcmp (new_dir, "-"))
2646 strcpy (temp, panel->lwd);
2647 new_dir = temp;
2650 directory = *new_dir ? new_dir : mc_config_get_home_dir ();
2652 if (mc_chdir (directory) == -1)
2654 strcpy (panel->cwd, olddir);
2655 g_free (olddir);
2656 g_free (translated_url);
2657 return FALSE;
2659 g_free (translated_url);
2661 /* Success: save previous directory, shutdown status of previous dir */
2662 strcpy (panel->lwd, olddir);
2663 input_free_completions (cmdline);
2665 mc_get_current_wd (panel->cwd, sizeof (panel->cwd) - 2);
2667 vfs_release_path (olddir);
2669 subshell_chdir (panel->cwd);
2671 /* Reload current panel */
2672 panel_clean_dir (panel);
2673 panel->count =
2674 do_load_dir (panel->cwd, &panel->dir, panel->sort_info.sort_field->sort_routine,
2675 panel->sort_info.reverse, panel->sort_info.case_sensitive,
2676 panel->sort_info.exec_first, panel->filter);
2677 try_to_select (panel, get_parent_dir_name (panel->cwd, olddir));
2678 load_hint (0);
2679 panel->dirty = 1;
2680 update_xterm_title_path ();
2682 g_free (olddir);
2684 return TRUE;
2687 /* --------------------------------------------------------------------------------------------- */
2689 static void
2690 directory_history_next (WPanel * panel)
2692 GList *nextdir;
2694 nextdir = g_list_next (panel->dir_history);
2696 if ((nextdir != NULL) && (_do_panel_cd (panel, (char *) nextdir->data, cd_exact)))
2697 panel->dir_history = nextdir;
2700 /* --------------------------------------------------------------------------------------------- */
2702 static void
2703 directory_history_prev (WPanel * panel)
2705 GList *prevdir;
2707 prevdir = g_list_previous (panel->dir_history);
2709 if ((prevdir != NULL) && (_do_panel_cd (panel, (char *) prevdir->data, cd_exact)))
2710 panel->dir_history = prevdir;
2713 /* --------------------------------------------------------------------------------------------- */
2715 static void
2716 directory_history_list (WPanel * panel)
2718 char *s;
2720 s = history_show (&panel->dir_history, &panel->widget);
2722 if (s != NULL)
2724 if (_do_panel_cd (panel, s, cd_exact))
2725 directory_history_add (panel, panel->cwd);
2726 else
2727 message (D_ERROR, MSG_ERROR, _("Cannot change directory"));
2728 g_free (s);
2732 /* --------------------------------------------------------------------------------------------- */
2734 static cb_ret_t
2735 panel_execute_cmd (WPanel * panel, unsigned long command)
2737 int res = MSG_HANDLED;
2739 if (command != CK_Search)
2740 stop_search (panel);
2742 switch (command)
2744 case CK_PanelOtherCd:
2745 chdir_other_panel (panel);
2746 break;
2747 case CK_PanelOtherCdLink:
2748 chdir_to_readlink (panel);
2749 break;
2750 case CK_CopySingle:
2751 copy_cmd_local ();
2752 break;
2753 case CK_DeleteSingle:
2754 delete_cmd_local ();
2755 break;
2756 case CK_Enter:
2757 do_enter (panel);
2758 break;
2759 case CK_ViewRaw:
2760 view_raw_cmd ();
2761 break;
2762 case CK_EditNew:
2763 edit_cmd_new ();
2764 break;
2765 case CK_MoveSingle:
2766 rename_cmd_local ();
2767 break;
2768 case CK_SelectInvert:
2769 select_invert_cmd ();
2770 break;
2771 case CK_Select:
2772 select_cmd ();
2773 break;
2774 case CK_Unselect:
2775 unselect_cmd ();
2776 break;
2777 case CK_PageDown:
2778 next_page (panel);
2779 break;
2780 case CK_PageUp:
2781 prev_page (panel);
2782 break;
2783 case CK_CdChild:
2784 goto_child_dir (panel);
2785 break;
2786 case CK_CdParent:
2787 goto_parent_dir (panel);
2788 break;
2789 case CK_History:
2790 directory_history_list (panel);
2791 break;
2792 case CK_HistoryNext:
2793 directory_history_next (panel);
2794 break;
2795 case CK_HistoryPrev:
2796 directory_history_prev (panel);
2797 break;
2798 case CK_BottomOnScreen:
2799 goto_bottom_file (panel);
2800 break;
2801 case CK_MiddleOnScreen:
2802 goto_middle_file (panel);
2803 break;
2804 case CK_TopOnScreen:
2805 goto_top_file (panel);
2806 break;
2807 case CK_Mark:
2808 mark_file (panel);
2809 break;
2810 case CK_MarkUp:
2811 mark_file_up (panel);
2812 break;
2813 case CK_MarkDown:
2814 mark_file_down (panel);
2815 break;
2816 case CK_CdParentSmart:
2817 res = force_maybe_cd ();
2818 break;
2819 case CK_Up:
2820 move_up (panel);
2821 break;
2822 case CK_Down:
2823 move_down (panel);
2824 break;
2825 case CK_Left:
2826 res = move_left (panel);
2827 break;
2828 case CK_Right:
2829 res = move_right (panel);
2830 break;
2831 case CK_Bottom:
2832 move_end (panel);
2833 break;
2834 case CK_Top:
2835 move_home (panel);
2836 break;
2837 #ifdef HAVE_CHARSET
2838 case CK_SelectCodepage:
2839 panel_change_encoding (panel);
2840 break;
2841 #endif
2842 case CK_Search:
2843 start_search (panel);
2844 break;
2845 case CK_SearchStop:
2846 break;
2847 case CK_PanelOtherSync:
2848 panel_sync_other (panel);
2849 break;
2850 case CK_Sort:
2851 panel_select_sort_order (panel);
2852 break;
2853 case CK_SortPrev:
2854 panel_toggle_sort_order_prev (panel);
2855 break;
2856 case CK_SortNext:
2857 panel_toggle_sort_order_next (panel);
2858 break;
2859 case CK_SortReverse:
2860 panel->sort_info.reverse = !panel->sort_info.reverse;
2861 panel_set_sort_order (panel, panel->sort_info.sort_field);
2862 break;
2863 case CK_SortByName:
2864 panel_set_sort_type_by_id (panel, "name");
2865 break;
2866 case CK_SortByExt:
2867 panel_set_sort_type_by_id (panel, "extension");
2868 break;
2869 case CK_SortBySize:
2870 panel_set_sort_type_by_id (panel, "size");
2871 break;
2872 case CK_SortByMTime:
2873 panel_set_sort_type_by_id (panel, "mtime");
2874 break;
2876 return res;
2879 /* --------------------------------------------------------------------------------------------- */
2881 static cb_ret_t
2882 panel_key (WPanel * panel, int key)
2884 size_t i;
2886 for (i = 0; panel_map[i].key != 0; i++)
2887 if (key == panel_map[i].key)
2888 return panel_execute_cmd (panel, panel_map[i].command);
2890 if (panels_options.torben_fj_mode && key == ALT ('h'))
2892 goto_middle_file (panel);
2893 return MSG_HANDLED;
2896 if (is_abort_char (key))
2898 stop_search (panel);
2899 return MSG_HANDLED;
2902 /* Do not eat characters not meant for the panel below ' ' (e.g. C-l). */
2903 if ((key >= ' ' && key <= 255) || key == KEY_BACKSPACE)
2905 if (panel->searching)
2907 do_search (panel, key);
2908 return MSG_HANDLED;
2911 if (!command_prompt)
2913 start_search (panel);
2914 do_search (panel, key);
2915 return MSG_HANDLED;
2919 return MSG_NOT_HANDLED;
2922 /* --------------------------------------------------------------------------------------------- */
2924 static cb_ret_t
2925 panel_callback (Widget * w, widget_msg_t msg, int parm)
2927 WPanel *panel = (WPanel *) w;
2928 WButtonBar *bb;
2930 switch (msg)
2932 case WIDGET_INIT:
2933 /* subscribe to "history_save" event */
2934 mc_event_add (w->owner->event_group, MCEVENT_HISTORY_SAVE, panel_save_history, w, NULL);
2935 return MSG_HANDLED;
2937 case WIDGET_DRAW:
2938 /* Repaint everything, including frame and separator */
2939 paint_frame (panel); /* including show_dir */
2940 paint_dir (panel);
2941 mini_info_separator (panel);
2942 display_mini_info (panel);
2943 panel->dirty = 0;
2944 return MSG_HANDLED;
2946 case WIDGET_FOCUS:
2947 current_panel = panel;
2948 panel->active = 1;
2949 if (mc_chdir (panel->cwd) != 0)
2951 char *cwd = strip_password (g_strdup (panel->cwd), 1);
2952 message (D_ERROR, MSG_ERROR, _("Cannot chdir to \"%s\"\n%s"),
2953 cwd, unix_error_string (errno));
2954 g_free (cwd);
2956 else
2957 subshell_chdir (panel->cwd);
2959 update_xterm_title_path ();
2960 select_item (panel);
2961 show_dir (panel);
2962 paint_dir (panel);
2963 panel->dirty = 0;
2965 bb = find_buttonbar (panel->widget.owner);
2966 midnight_set_buttonbar (bb);
2967 buttonbar_redraw (bb);
2968 return MSG_HANDLED;
2970 case WIDGET_UNFOCUS:
2971 /* Janne: look at this for the multiple panel options */
2972 stop_search (panel);
2973 panel->active = 0;
2974 show_dir (panel);
2975 unselect_item (panel);
2976 return MSG_HANDLED;
2978 case WIDGET_KEY:
2979 return panel_key (panel, parm);
2981 case WIDGET_COMMAND:
2982 return panel_execute_cmd (panel, parm);
2984 case WIDGET_DESTROY:
2985 /* unsubscribe from "history_save" event */
2986 mc_event_del (w->owner->event_group, MCEVENT_HISTORY_SAVE, panel_save_history, w);
2987 panel_destroy (panel);
2988 free_my_statfs ();
2989 return MSG_HANDLED;
2991 default:
2992 return default_proc (msg, parm);
2996 /* --------------------------------------------------------------------------------------------- */
2997 /* */
2998 /* Panel mouse events support routines */
2999 /* */
3001 static void
3002 mouse_toggle_mark (WPanel * panel)
3004 do_mark_file (panel, MARK_DONT_MOVE);
3005 mouse_marking = selection (panel)->f.marked;
3006 mouse_mark_panel = current_panel;
3009 /* --------------------------------------------------------------------------------------------- */
3011 static void
3012 mouse_set_mark (WPanel * panel)
3015 if (mouse_mark_panel == panel)
3017 if (mouse_marking && !(selection (panel)->f.marked))
3018 do_mark_file (panel, MARK_DONT_MOVE);
3019 else if (!mouse_marking && (selection (panel)->f.marked))
3020 do_mark_file (panel, MARK_DONT_MOVE);
3024 /* --------------------------------------------------------------------------------------------- */
3026 static int
3027 mark_if_marking (WPanel * panel, Gpm_Event * event)
3029 if (event->buttons & GPM_B_RIGHT)
3031 if (event->type & GPM_DOWN)
3032 mouse_toggle_mark (panel);
3033 else
3034 mouse_set_mark (panel);
3035 return 1;
3037 return 0;
3040 /* --------------------------------------------------------------------------------------------- */
3041 /** Determine which column was clicked, and sort the panel on
3042 * that column, or reverse sort on that column if already
3043 * sorted on that column.
3046 static void
3047 mouse_sort_col (Gpm_Event * event, WPanel * panel)
3049 int i;
3050 const char *lc_sort_name = NULL;
3051 panel_field_t *col_sort_format = NULL;
3052 format_e *format;
3053 gchar *title;
3055 for (i = 0, format = panel->format; format != NULL; format = format->next)
3057 i += format->field_len;
3058 if (event->x < i + 1)
3060 /* found column */
3061 lc_sort_name = format->title;
3062 break;
3066 if (lc_sort_name == NULL)
3067 return;
3069 for (i = 0; panel_fields[i].id != NULL; i++)
3071 title = panel_get_title_without_hotkey (panel_fields[i].title_hotkey);
3072 if (!strcmp (lc_sort_name, title) && panel_fields[i].sort_routine)
3074 col_sort_format = &panel_fields[i];
3075 g_free (title);
3076 break;
3078 g_free (title);
3081 if (col_sort_format == NULL)
3082 return;
3084 if (panel->sort_info.sort_field == col_sort_format)
3086 /* reverse the sort if clicked column is already the sorted column */
3087 panel->sort_info.reverse = !panel->sort_info.reverse;
3089 else
3091 /* new sort is forced to be ascending */
3092 panel->sort_info.reverse = FALSE;
3094 panel_set_sort_order (panel, col_sort_format);
3098 /* --------------------------------------------------------------------------------------------- */
3100 * Mouse callback of the panel minus repainting.
3101 * If the event is redirected to the menu, *redir is set to TRUE.
3103 static int
3104 do_panel_event (Gpm_Event * event, WPanel * panel, gboolean * redir)
3106 const int lines = llines (panel);
3107 const gboolean is_active = dlg_widget_active (panel);
3108 const gboolean mouse_down = (event->type & GPM_DOWN) != 0;
3110 *redir = FALSE;
3112 /* 1st line */
3113 if (mouse_down && event->y == 1)
3115 /* "<" button */
3116 if (event->x == 2)
3118 directory_history_prev (panel);
3119 return MOU_NORMAL;
3122 /* "." button show/hide hidden files */
3123 if (event->x == panel->widget.cols - 5)
3125 panel->widget.owner->callback (panel->widget.owner, NULL,
3126 DLG_ACTION, CK_ShowHidden, NULL);
3127 repaint_screen ();
3128 return MOU_NORMAL;
3131 /* ">" button */
3132 if (event->x == panel->widget.cols - 1)
3134 directory_history_next (panel);
3135 return MOU_NORMAL;
3138 /* "^" button */
3139 if (event->x >= panel->widget.cols - 4 && event->x <= panel->widget.cols - 2)
3141 directory_history_list (panel);
3142 return MOU_NORMAL;
3145 /* rest of the upper frame, the menu is invisible - call menu */
3146 if (!menubar_visible)
3148 *redir = TRUE;
3149 event->x += panel->widget.x;
3150 return the_menubar->widget.mouse (event, the_menubar);
3153 /* no other events on 1st line */
3154 return MOU_NORMAL;
3157 /* sort on clicked column; don't handle wheel events */
3158 if (mouse_down && (event->buttons & (GPM_B_UP | GPM_B_DOWN)) == 0 && event->y == 2)
3160 mouse_sort_col (event, panel);
3161 return MOU_NORMAL;
3164 /* Mouse wheel events */
3165 if (mouse_down && (event->buttons & GPM_B_UP))
3167 if (is_active)
3169 if (panels_options.mouse_move_pages && (panel->top_file > 0))
3170 prev_page (panel);
3171 else /* We are in first page */
3172 move_up (panel);
3174 return MOU_NORMAL;
3177 if (mouse_down && (event->buttons & GPM_B_DOWN))
3179 if (is_active)
3181 if (panels_options.mouse_move_pages && (panel->top_file + ITEMS (panel) < panel->count))
3182 next_page (panel);
3183 else /* We are in last page */
3184 move_down (panel);
3186 return MOU_NORMAL;
3189 event->y -= 2;
3190 if ((event->type & (GPM_DOWN | GPM_DRAG)))
3192 int my_index;
3194 if (!is_active)
3195 change_panel ();
3197 if (panel->top_file + event->y > panel->count)
3198 my_index = panel->count - 1;
3199 else
3201 my_index = panel->top_file + event->y - 1;
3202 if (panel->split && (event->x > ((panel->widget.cols - 2) / 2)))
3203 my_index += llines (panel);
3205 if (my_index >= panel->count)
3206 my_index = panel->count - 1;
3209 if (my_index != panel->selected)
3211 unselect_item (panel);
3212 panel->selected = my_index;
3213 select_item (panel);
3216 /* This one is new */
3217 mark_if_marking (panel, event);
3219 else if ((event->type & (GPM_UP | GPM_DOUBLE)) == (GPM_UP | GPM_DOUBLE))
3221 if (event->y > 0 && event->y <= lines)
3222 do_enter (panel);
3224 return MOU_NORMAL;
3227 /* --------------------------------------------------------------------------------------------- */
3228 /** Mouse callback of the panel */
3230 static int
3231 panel_event (Gpm_Event * event, void *data)
3233 WPanel *panel = data;
3234 int ret;
3235 gboolean redir;
3237 ret = do_panel_event (event, panel, &redir);
3238 if (!redir)
3239 send_message ((Widget *) panel, WIDGET_DRAW, 0);
3241 return ret;
3244 /* --------------------------------------------------------------------------------------------- */
3246 static void
3247 reload_panelized (WPanel * panel)
3249 int i, j;
3250 dir_list *list = &panel->dir;
3252 if (panel != current_panel)
3254 int ret;
3255 ret = mc_chdir (panel->cwd);
3258 for (i = 0, j = 0; i < panel->count; i++)
3260 if (list->list[i].f.marked)
3262 /* Unmark the file in advance. In case the following mc_lstat
3263 * fails we are done, else we have to mark the file again
3264 * (Note: do_file_mark depends on a valid "list->list [i].buf").
3265 * IMO that's the best way to update the panel's summary status
3266 * -- Norbert
3268 do_file_mark (panel, i, 0);
3270 if (mc_lstat (list->list[i].fname, &list->list[i].st))
3272 g_free (list->list[i].fname);
3273 continue;
3275 if (list->list[i].f.marked)
3276 do_file_mark (panel, i, 1);
3277 if (j != i)
3278 list->list[j] = list->list[i];
3279 j++;
3281 if (j == 0)
3282 panel->count = set_zero_dir (list) ? 1 : 0;
3283 else
3284 panel->count = j;
3286 if (panel != current_panel)
3288 int ret;
3289 ret = mc_chdir (current_panel->cwd);
3293 /* --------------------------------------------------------------------------------------------- */
3295 static void
3296 update_one_panel_widget (WPanel * panel, panel_update_flags_t flags, const char *current_file)
3298 gboolean free_pointer;
3299 char *my_current_file = NULL;
3301 if ((flags & UP_RELOAD) != 0)
3303 panel->is_panelized = 0;
3304 mc_setctl (panel->cwd, VFS_SETCTL_FLUSH, 0);
3305 memset (&(panel->dir_stat), 0, sizeof (panel->dir_stat));
3308 /* If current_file == -1 (an invalid pointer) then preserve selection */
3309 free_pointer = current_file == UP_KEEPSEL;
3311 if (free_pointer)
3313 my_current_file = g_strdup (panel->dir.list[panel->selected].fname);
3314 current_file = my_current_file;
3317 if (panel->is_panelized)
3318 reload_panelized (panel);
3319 else
3320 panel_reload (panel);
3322 try_to_select (panel, current_file);
3323 panel->dirty = 1;
3325 if (free_pointer)
3326 g_free (my_current_file);
3329 /* --------------------------------------------------------------------------------------------- */
3331 static void
3332 update_one_panel (int which, panel_update_flags_t flags, const char *current_file)
3334 if (get_display_type (which) == view_listing)
3336 WPanel *panel;
3337 panel = (WPanel *) get_panel_widget (which);
3338 update_one_panel_widget (panel, flags, current_file);
3342 /* --------------------------------------------------------------------------------------------- */
3343 /*** public functions ****************************************************************************/
3344 /* --------------------------------------------------------------------------------------------- */
3346 char *
3347 remove_encoding_from_path (const char *path)
3349 GString *ret;
3350 GString *tmp_path, *tmp_conv;
3351 char *tmp;
3353 ret = g_string_new ("");
3354 tmp_conv = g_string_new ("");
3355 tmp_path = g_string_new (path);
3357 while ((tmp = g_strrstr (tmp_path->str, PATH_SEP_STR VFS_ENCODING_PREFIX)) != NULL)
3359 const char *enc;
3360 GIConv converter;
3361 char *tmp2;
3363 enc = vfs_get_encoding ((const char *) tmp);
3364 converter = enc != NULL ? str_crt_conv_to (enc) : str_cnv_to_term;
3365 if (converter == INVALID_CONV)
3366 converter = str_cnv_to_term;
3368 tmp2 = tmp + 1;
3369 while (*tmp2 != '\0' && *tmp2 != PATH_SEP)
3370 tmp2++;
3372 if (*tmp2 != '\0')
3374 str_vfs_convert_from (converter, tmp2, tmp_conv);
3375 g_string_prepend (ret, tmp_conv->str);
3376 g_string_set_size (tmp_conv, 0);
3379 g_string_set_size (tmp_path, tmp - tmp_path->str);
3380 str_close_conv (converter);
3383 g_string_prepend (ret, tmp_path->str);
3384 g_string_free (tmp_path, TRUE);
3385 g_string_free (tmp_conv, TRUE);
3387 return g_string_free (ret, FALSE);
3390 /* --------------------------------------------------------------------------------------------- */
3392 static void
3393 do_select (WPanel * panel, int i)
3395 if (i != panel->selected)
3397 panel->dirty = 1;
3398 panel->selected = i;
3399 panel->top_file = panel->selected - (panel->widget.lines - 2) / 2;
3400 if (panel->top_file < 0)
3401 panel->top_file = 0;
3405 /* --------------------------------------------------------------------------------------------- */
3407 static void
3408 do_try_to_select (WPanel * panel, const char *name)
3410 int i;
3411 char *subdir;
3413 if (!name)
3415 do_select (panel, 0);
3416 return;
3419 /* We only want the last component of the directory,
3420 * and from this only the name without suffix. */
3421 subdir = vfs_strip_suffix_from_filename (x_basename (name));
3423 /* Search that subdirectory, if found select it */
3424 for (i = 0; i < panel->count; i++)
3426 if (strcmp (subdir, panel->dir.list[i].fname) == 0)
3428 do_select (panel, i);
3429 g_free (subdir);
3430 return;
3434 /* Try to select a file near the file that is missing */
3435 if (panel->selected >= panel->count)
3436 do_select (panel, panel->count - 1);
3437 g_free (subdir);
3440 /* --------------------------------------------------------------------------------------------- */
3442 /* event callback */
3443 static gboolean
3444 event_update_panels (const gchar * event_group_name, const gchar * event_name,
3445 gpointer init_data, gpointer data)
3447 (void) event_group_name;
3448 (void) event_name;
3449 (void) init_data;
3450 (void) data;
3452 update_panels (UP_RELOAD, UP_KEEPSEL);
3454 return TRUE;
3457 /* --------------------------------------------------------------------------------------------- */
3459 /* event callback */
3460 static gboolean
3461 panel_save_curent_file_to_clip_file (const gchar * event_group_name, const gchar * event_name,
3462 gpointer init_data, gpointer data)
3464 (void) event_group_name;
3465 (void) event_name;
3466 (void) init_data;
3467 (void) data;
3469 if (current_panel->marked == 0)
3470 mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_text_to_file",
3471 (gpointer) selection (current_panel)->fname);
3472 else
3474 int i;
3475 gboolean first = TRUE;
3476 char *flist = NULL;
3478 for (i = 0; i < current_panel->count; i++)
3479 if (current_panel->dir.list[i].f.marked != 0)
3480 { /* Skip the unmarked ones */
3481 if (first)
3483 flist = g_strdup (current_panel->dir.list[i].fname);
3484 first = FALSE;
3486 else
3488 /* Add empty lines after the file */
3489 char *tmp;
3491 tmp =
3492 g_strconcat (flist, "\n", current_panel->dir.list[i].fname, (char *) NULL);
3493 g_free (flist);
3494 flist = tmp;
3498 mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_text_to_file", (gpointer) flist);
3499 g_free (flist);
3501 return TRUE;
3504 /* --------------------------------------------------------------------------------------------- */
3505 /*** public functions ****************************************************************************/
3506 /* --------------------------------------------------------------------------------------------- */
3508 void
3509 try_to_select (WPanel * panel, const char *name)
3511 do_try_to_select (panel, name);
3512 select_item (panel);
3515 /* --------------------------------------------------------------------------------------------- */
3517 void
3518 panel_clean_dir (WPanel * panel)
3520 int count = panel->count;
3522 panel->count = 0;
3523 panel->top_file = 0;
3524 panel->selected = 0;
3525 panel->marked = 0;
3526 panel->dirs_marked = 0;
3527 panel->total = 0;
3528 panel->searching = FALSE;
3529 panel->is_panelized = 0;
3530 panel->dirty = 1;
3532 clean_dir (&panel->dir, count);
3535 /* --------------------------------------------------------------------------------------------- */
3536 /** Panel creation.
3537 * @param panel_name the name of the panel for setup retieving
3538 * @returns new instance of WPanel
3541 WPanel *
3542 panel_new (const char *panel_name)
3544 return panel_new_with_dir (panel_name, NULL);
3547 /* --------------------------------------------------------------------------------------------- */
3548 /** Panel creation for specified directory.
3549 * @param panel_name specifies the name of the panel for setup retieving
3550 * @param the path of working panel directory. If path is NULL then panel will be created for current directory
3551 * @returns new instance of WPanel
3554 WPanel *
3555 panel_new_with_dir (const char *panel_name, const char *wpath)
3557 WPanel *panel;
3558 char *section;
3559 int i, err;
3560 char curdir[MC_MAXPATHLEN] = "\0";
3562 panel = g_new0 (WPanel, 1);
3564 /* No know sizes of the panel at startup */
3565 init_widget (&panel->widget, 0, 0, 0, 0, panel_callback, panel_event);
3567 /* We do not want the cursor */
3568 widget_want_cursor (panel->widget, 0);
3570 if (wpath != NULL)
3572 g_strlcpy (panel->cwd, wpath, sizeof (panel->cwd));
3573 mc_get_current_wd (curdir, sizeof (curdir) - 2);
3575 else
3576 mc_get_current_wd (panel->cwd, sizeof (panel->cwd) - 2);
3578 strcpy (panel->lwd, ".");
3580 panel->hist_name = g_strconcat ("Dir Hist ", panel_name, (char *) NULL);
3581 panel->dir_history = history_get (panel->hist_name);
3582 directory_history_add (panel, panel->cwd);
3584 panel->dir.list = g_new (file_entry, MIN_FILES);
3585 panel->dir.size = MIN_FILES;
3586 panel->active = 0;
3587 panel->filter = 0;
3588 panel->split = 0;
3589 panel->top_file = 0;
3590 panel->selected = 0;
3591 panel->marked = 0;
3592 panel->total = 0;
3593 panel->dirty = 1;
3594 panel->searching = FALSE;
3595 panel->dirs_marked = 0;
3596 panel->is_panelized = 0;
3597 panel->format = 0;
3598 panel->status_format = 0;
3599 panel->format_modified = 1;
3601 panel->panel_name = g_strdup (panel_name);
3602 panel->user_format = g_strdup (DEFAULT_USER_FORMAT);
3604 panel->codepage = SELECT_CHARSET_NO_TRANSLATE;
3606 for (i = 0; i < LIST_TYPES; i++)
3607 panel->user_status_format[i] = g_strdup (DEFAULT_USER_FORMAT);
3609 panel->search_buffer[0] = '\0';
3610 panel->prev_search_buffer[0] = '\0';
3611 panel->frame_size = frame_half;
3613 section = g_strconcat ("Temporal:", panel->panel_name, (char *) NULL);
3614 if (!mc_config_has_group (mc_main_config, section))
3616 g_free (section);
3617 section = g_strdup (panel->panel_name);
3619 panel_load_setup (panel, section);
3620 g_free (section);
3622 /* Load format strings */
3623 err = set_panel_formats (panel);
3624 if (err != 0)
3625 set_panel_formats (panel);
3627 #ifdef HAVE_CHARSET
3629 const char *enc = vfs_get_encoding (panel->cwd);
3630 if (enc != NULL)
3631 panel->codepage = get_codepage_index (enc);
3633 #endif
3635 if (mc_chdir (panel->cwd) != 0)
3637 panel->codepage = SELECT_CHARSET_NO_TRANSLATE;
3638 mc_get_current_wd (panel->cwd, sizeof (panel->cwd) - 2);
3641 /* Load the default format */
3642 panel->count =
3643 do_load_dir (panel->cwd, &panel->dir, panel->sort_info.sort_field->sort_routine,
3644 panel->sort_info.reverse, panel->sort_info.case_sensitive,
3645 panel->sort_info.exec_first, panel->filter);
3647 /* Restore old right path */
3648 if (curdir[0] != '\0')
3649 err = mc_chdir (curdir);
3651 return panel;
3654 /* --------------------------------------------------------------------------------------------- */
3656 void
3657 panel_reload (WPanel * panel)
3659 struct stat current_stat;
3661 if (panels_options.fast_reload && !stat (panel->cwd, &current_stat)
3662 && current_stat.st_ctime == panel->dir_stat.st_ctime
3663 && current_stat.st_mtime == panel->dir_stat.st_mtime)
3664 return;
3666 while (mc_chdir (panel->cwd) == -1)
3668 char *last_slash;
3670 if (panel->cwd[0] == PATH_SEP && panel->cwd[1] == 0)
3672 panel_clean_dir (panel);
3673 panel->count = set_zero_dir (&panel->dir) ? 1 : 0;
3674 return;
3676 last_slash = strrchr (panel->cwd, PATH_SEP);
3677 if (!last_slash || last_slash == panel->cwd)
3678 strcpy (panel->cwd, PATH_SEP_STR);
3679 else
3680 *last_slash = 0;
3681 memset (&(panel->dir_stat), 0, sizeof (panel->dir_stat));
3682 show_dir (panel);
3685 panel->count =
3686 do_reload_dir (panel->cwd, &panel->dir, panel->sort_info.sort_field->sort_routine,
3687 panel->count, panel->sort_info.reverse, panel->sort_info.case_sensitive,
3688 panel->sort_info.exec_first, panel->filter);
3690 panel->dirty = 1;
3691 if (panel->selected >= panel->count)
3692 do_select (panel, panel->count - 1);
3694 recalculate_panel_summary (panel);
3697 /* --------------------------------------------------------------------------------------------- */
3698 /* Switches the panel to the mode specified in the format */
3699 /* Seting up both format and status string. Return: 0 - on success; */
3700 /* 1 - format error; 2 - status error; 3 - errors in both formats. */
3703 set_panel_formats (WPanel * p)
3705 format_e *form;
3706 char *err = NULL;
3707 int retcode = 0;
3709 form = use_display_format (p, panel_format (p), &err, 0);
3711 if (err != NULL)
3713 g_free (err);
3714 retcode = 1;
3716 else
3718 delete_format (p->format);
3719 p->format = form;
3722 if (panels_options.show_mini_info)
3724 form = use_display_format (p, mini_status_format (p), &err, 1);
3726 if (err != NULL)
3728 g_free (err);
3729 retcode += 2;
3731 else
3733 delete_format (p->status_format);
3734 p->status_format = form;
3738 panel_format_modified (p);
3739 panel_update_cols (&(p->widget), p->frame_size);
3741 if (retcode)
3742 message (D_ERROR, _("Warning"),
3743 _("User supplied format looks invalid, reverting to default."));
3744 if (retcode & 0x01)
3746 g_free (p->user_format);
3747 p->user_format = g_strdup (DEFAULT_USER_FORMAT);
3749 if (retcode & 0x02)
3751 g_free (p->user_status_format[p->list_type]);
3752 p->user_status_format[p->list_type] = g_strdup (DEFAULT_USER_FORMAT);
3755 return retcode;
3758 /* --------------------------------------------------------------------------------------------- */
3760 /* Select current item and readjust the panel */
3761 void
3762 select_item (WPanel * panel)
3764 int items = ITEMS (panel);
3766 /* Although currently all over the code we set the selection and
3767 top file to decent values before calling select_item, I could
3768 forget it someday, so it's better to do the actual fitting here */
3770 if (panel->top_file < 0)
3771 panel->top_file = 0;
3773 if (panel->selected < 0)
3774 panel->selected = 0;
3776 if (panel->selected > panel->count - 1)
3777 panel->selected = panel->count - 1;
3779 if (panel->top_file > panel->count - 1)
3780 panel->top_file = panel->count - 1;
3782 if ((panel->count - panel->top_file) < items)
3784 panel->top_file = panel->count - items;
3785 if (panel->top_file < 0)
3786 panel->top_file = 0;
3789 if (panel->selected < panel->top_file)
3790 panel->top_file = panel->selected;
3792 if ((panel->selected - panel->top_file) >= items)
3793 panel->top_file = panel->selected - items + 1;
3795 panel->dirty = 1;
3797 execute_hooks (select_file_hook);
3800 /* --------------------------------------------------------------------------------------------- */
3801 /** Clears all files in the panel, used only when one file was marked */
3802 void
3803 unmark_files (WPanel * panel)
3805 int i;
3807 if (!panel->marked)
3808 return;
3809 for (i = 0; i < panel->count; i++)
3810 file_mark (panel, i, 0);
3812 panel->dirs_marked = 0;
3813 panel->marked = 0;
3814 panel->total = 0;
3817 /* --------------------------------------------------------------------------------------------- */
3818 /** Recalculate the panels summary information, used e.g. when marked
3819 files might have been removed by an external command */
3821 void
3822 recalculate_panel_summary (WPanel * panel)
3824 int i;
3826 panel->marked = 0;
3827 panel->dirs_marked = 0;
3828 panel->total = 0;
3830 for (i = 0; i < panel->count; i++)
3831 if (panel->dir.list[i].f.marked)
3833 /* do_file_mark will return immediately if newmark == oldmark.
3834 So we have to first unmark it to get panel's summary information
3835 updated. (Norbert) */
3836 panel->dir.list[i].f.marked = 0;
3837 do_file_mark (panel, i, 1);
3841 /* --------------------------------------------------------------------------------------------- */
3842 /** This routine marks a file or a directory */
3844 void
3845 do_file_mark (WPanel * panel, int idx, int mark)
3847 if (panel->dir.list[idx].f.marked == mark)
3848 return;
3850 /* Only '..' can't be marked, '.' isn't visible */
3851 if (strcmp (panel->dir.list[idx].fname, "..") == 0)
3852 return;
3854 file_mark (panel, idx, mark);
3855 if (panel->dir.list[idx].f.marked)
3857 panel->marked++;
3858 if (S_ISDIR (panel->dir.list[idx].st.st_mode))
3860 if (panel->dir.list[idx].f.dir_size_computed)
3861 panel->total += (uintmax_t) panel->dir.list[idx].st.st_size;
3862 panel->dirs_marked++;
3864 else
3865 panel->total += (uintmax_t) panel->dir.list[idx].st.st_size;
3866 set_colors (panel);
3868 else
3870 if (S_ISDIR (panel->dir.list[idx].st.st_mode))
3872 if (panel->dir.list[idx].f.dir_size_computed)
3873 panel->total -= (uintmax_t) panel->dir.list[idx].st.st_size;
3874 panel->dirs_marked--;
3876 else
3877 panel->total -= (uintmax_t) panel->dir.list[idx].st.st_size;
3878 panel->marked--;
3882 /* --------------------------------------------------------------------------------------------- */
3884 * Changes the current directory of the panel.
3885 * Record change in the directory history.
3887 gboolean
3888 do_panel_cd (struct WPanel *panel, const char *new_dir, enum cd_enum cd_type)
3890 gboolean r;
3892 r = _do_panel_cd (panel, new_dir, cd_type);
3893 if (r)
3894 directory_history_add (panel, panel->cwd);
3895 return r;
3898 /* --------------------------------------------------------------------------------------------- */
3900 void
3901 file_mark (WPanel * panel, int lc_index, int val)
3903 if (panel->dir.list[lc_index].f.marked != val)
3905 panel->dir.list[lc_index].f.marked = val;
3906 panel->dirty = 1;
3910 /* --------------------------------------------------------------------------------------------- */
3912 void
3913 panel_re_sort (WPanel * panel)
3915 char *filename;
3916 int i;
3918 if (panel == NULL)
3919 return;
3921 filename = g_strdup (selection (panel)->fname);
3922 unselect_item (panel);
3923 do_sort (&panel->dir, panel->sort_info.sort_field->sort_routine, panel->count - 1,
3924 panel->sort_info.reverse, panel->sort_info.case_sensitive,
3925 panel->sort_info.exec_first);
3926 panel->selected = -1;
3927 for (i = panel->count; i; i--)
3929 if (!strcmp (panel->dir.list[i - 1].fname, filename))
3931 panel->selected = i - 1;
3932 break;
3935 g_free (filename);
3936 panel->top_file = panel->selected - ITEMS (panel) / 2;
3937 select_item (panel);
3938 panel->dirty = 1;
3941 /* --------------------------------------------------------------------------------------------- */
3943 void
3944 panel_set_sort_order (WPanel * panel, const panel_field_t * sort_order)
3946 if (sort_order == NULL)
3947 return;
3949 panel->sort_info.sort_field = sort_order;
3951 /* The directory is already sorted, we have to load the unsorted stuff */
3952 if (sort_order->sort_routine == (sortfn *) unsorted)
3954 char *current_file;
3956 current_file = g_strdup (panel->dir.list[panel->selected].fname);
3957 panel_reload (panel);
3958 try_to_select (panel, current_file);
3959 g_free (current_file);
3961 panel_re_sort (panel);
3964 /* --------------------------------------------------------------------------------------------- */
3966 * Change panel encoding.
3967 * @param panel WPanel object
3970 void
3971 panel_change_encoding (WPanel * panel)
3973 const char *encoding = NULL;
3974 char *cd_path;
3975 #ifdef HAVE_CHARSET
3976 char *errmsg;
3977 int r;
3979 r = select_charset (-1, -1, panel->codepage, FALSE);
3981 if (r == SELECT_CHARSET_CANCEL)
3982 return; /* Cancel */
3984 panel->codepage = r;
3986 if (panel->codepage == SELECT_CHARSET_NO_TRANSLATE)
3988 /* No translation */
3989 g_free (init_translation_table (mc_global.display_codepage, mc_global.display_codepage));
3990 cd_path = remove_encoding_from_path (panel->cwd);
3991 do_panel_cd (panel, cd_path, cd_parse_command);
3992 g_free (cd_path);
3993 return;
3996 errmsg = init_translation_table (panel->codepage, mc_global.display_codepage);
3997 if (errmsg != NULL)
3999 message (D_ERROR, MSG_ERROR, "%s", errmsg);
4000 g_free (errmsg);
4001 return;
4004 encoding = get_codepage_id (panel->codepage);
4005 #endif
4006 if (encoding != NULL)
4008 const char *enc;
4010 enc = vfs_get_encoding (panel->cwd);
4012 /* don't add current encoding */
4013 if ((enc == NULL) || (strcmp (encoding, enc) != 0))
4015 cd_path = add_encoding_to_path (panel->cwd, encoding);
4016 if (!do_panel_cd (panel, cd_path, cd_parse_command))
4017 message (D_ERROR, MSG_ERROR, _("Cannot chdir to \"%s\""), cd_path);
4018 g_free (cd_path);
4023 /* --------------------------------------------------------------------------------------------- */
4025 * This routine reloads the directory in both panels. It tries to
4026 * select current_file in current_panel and other_file in other_panel.
4027 * If current_file == -1 then it automatically sets current_file and
4028 * other_file to the currently selected files in the panels.
4030 * if force_update has the UP_ONLY_CURRENT bit toggled on, then it
4031 * will not reload the other panel.
4034 void
4035 update_panels (panel_update_flags_t flags, const char *current_file)
4037 gboolean reload_other = (flags & UP_ONLY_CURRENT) == 0;
4038 WPanel *panel;
4039 int ret;
4041 update_one_panel (get_current_index (), flags, current_file);
4042 if (reload_other)
4043 update_one_panel (get_other_index (), flags, UP_KEEPSEL);
4045 if (get_current_type () == view_listing)
4046 panel = (WPanel *) get_panel_widget (get_current_index ());
4047 else
4048 panel = (WPanel *) get_panel_widget (get_other_index ());
4050 ret = mc_chdir (panel->cwd);
4053 /* --------------------------------------------------------------------------------------------- */
4055 void
4056 directory_history_add (struct WPanel *panel, const char *dir)
4058 char *tmp;
4060 tmp = g_strdup (dir);
4061 strip_password (tmp, 1);
4063 panel->dir_history = list_append_unique (panel->dir_history, tmp);
4066 /* --------------------------------------------------------------------------------------------- */
4068 gsize
4069 panel_get_num_of_sortable_fields (void)
4071 gsize ret = 0, lc_index;
4073 for (lc_index = 0; panel_fields[lc_index].id != NULL; lc_index++)
4074 if (panel_fields[lc_index].is_user_choice)
4075 ret++;
4076 return ret;
4079 /* --------------------------------------------------------------------------------------------- */
4081 const char **
4082 panel_get_sortable_fields (gsize * array_size)
4084 char **ret;
4085 gsize lc_index, i;
4087 lc_index = panel_get_num_of_sortable_fields ();
4089 ret = g_try_new0 (char *, lc_index + 1);
4090 if (ret == NULL)
4091 return NULL;
4093 if (array_size != NULL)
4094 *array_size = lc_index;
4096 lc_index = 0;
4098 for (i = 0; panel_fields[i].id != NULL; i++)
4099 if (panel_fields[i].is_user_choice)
4100 ret[lc_index++] = g_strdup (_(panel_fields[i].title_hotkey));
4101 return (const char **) ret;
4104 /* --------------------------------------------------------------------------------------------- */
4106 const panel_field_t *
4107 panel_get_field_by_id (const char *name)
4109 gsize lc_index;
4110 for (lc_index = 0; panel_fields[lc_index].id != NULL; lc_index++)
4111 if (panel_fields[lc_index].id != NULL && strcmp (name, panel_fields[lc_index].id) == 0)
4112 return &panel_fields[lc_index];
4113 return NULL;
4116 /* --------------------------------------------------------------------------------------------- */
4118 const panel_field_t *
4119 panel_get_field_by_title_hotkey (const char *name)
4121 gsize lc_index;
4122 for (lc_index = 0; panel_fields[lc_index].id != NULL; lc_index++)
4123 if (panel_fields[lc_index].title_hotkey != NULL &&
4124 strcmp (name, _(panel_fields[lc_index].title_hotkey)) == 0)
4125 return &panel_fields[lc_index];
4126 return NULL;
4129 /* --------------------------------------------------------------------------------------------- */
4131 const panel_field_t *
4132 panel_get_field_by_title (const char *name)
4134 gsize lc_index;
4135 gchar *title = NULL;
4137 for (lc_index = 0; panel_fields[lc_index].id != NULL; lc_index++)
4139 title = panel_get_title_without_hotkey (panel_fields[lc_index].title_hotkey);
4140 if (panel_fields[lc_index].title_hotkey != NULL && strcmp (name, title) == 0)
4142 g_free (title);
4143 return &panel_fields[lc_index];
4146 g_free (title);
4147 return NULL;
4150 /* --------------------------------------------------------------------------------------------- */
4152 gsize
4153 panel_get_num_of_user_possible_fields (void)
4155 gsize ret = 0, lc_index;
4157 for (lc_index = 0; panel_fields[lc_index].id != NULL; lc_index++)
4158 if (panel_fields[lc_index].use_in_user_format)
4159 ret++;
4160 return ret;
4163 /* --------------------------------------------------------------------------------------------- */
4165 const char **
4166 panel_get_user_possible_fields (gsize * array_size)
4168 char **ret;
4169 gsize lc_index, i;
4171 lc_index = panel_get_num_of_user_possible_fields ();
4173 ret = g_try_new0 (char *, lc_index + 1);
4174 if (ret == NULL)
4175 return NULL;
4177 if (array_size != NULL)
4178 *array_size = lc_index;
4180 lc_index = 0;
4182 for (i = 0; panel_fields[i].id != NULL; i++)
4183 if (panel_fields[i].use_in_user_format)
4184 ret[lc_index++] = g_strdup (_(panel_fields[i].title_hotkey));
4185 return (const char **) ret;
4188 /* --------------------------------------------------------------------------------------------- */
4190 void
4191 panel_init (void)
4193 panel_sort_up_sign = mc_skin_get ("widget-common", "sort-sign-up", "'");
4194 panel_sort_down_sign = mc_skin_get ("widget-common", "sort-sign-down", ",");
4196 panel_hiddenfiles_sign_show = mc_skin_get ("widget-panel", "hiddenfiles-sign-show", ".");
4197 panel_hiddenfiles_sign_hide = mc_skin_get ("widget-panel", "hiddenfiles-sign-hide", ".");
4198 panel_history_prev_item_sign = mc_skin_get ("widget-panel", "history-prev-item-sign", "<");
4199 panel_history_next_item_sign = mc_skin_get ("widget-panel", "history-next-item-sign", ">");
4200 panel_history_show_list_sign = mc_skin_get ("widget-panel", "history-show-list-sign", "^");
4202 mc_event_add (MCEVENT_GROUP_FILEMANAGER, "update_panels", event_update_panels, NULL, NULL);
4203 mc_event_add (MCEVENT_GROUP_FILEMANAGER, "panel_save_curent_file_to_clip_file",
4204 panel_save_curent_file_to_clip_file, NULL, NULL);
4208 /* --------------------------------------------------------------------------------------------- */
4210 void
4211 panel_deinit (void)
4213 g_free (panel_sort_up_sign);
4214 g_free (panel_sort_down_sign);
4216 g_free (panel_hiddenfiles_sign_show);
4217 g_free (panel_hiddenfiles_sign_hide);
4218 g_free (panel_history_prev_item_sign);
4219 g_free (panel_history_next_item_sign);
4220 g_free (panel_history_show_list_sign);
4224 /* --------------------------------------------------------------------------------------------- */