Ticket #2534 (mark files by shift-left/shift-right)
[midnight-commander.git] / src / filemanager / panel.c
blobc85d9d37a4205c3a8c58a50dfc2df80cdd317060
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;
353 static int state_mark = 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;
1018 vfs_path_t *vpath = vfs_path_from_str (panel->cwd);
1020 /* Don't try to stat non-local fs */
1021 if (!vfs_file_is_local (vpath) || !free_space)
1023 vfs_path_free (vpath);
1024 return;
1026 vfs_path_free (vpath);
1028 if (old_cwd == NULL || strcmp (old_cwd, panel->cwd) != 0)
1030 char rpath[PATH_MAX];
1032 init_my_statfs ();
1033 g_free (old_cwd);
1034 old_cwd = g_strdup (panel->cwd);
1036 if (mc_realpath (panel->cwd, rpath) == NULL)
1037 return;
1039 my_statfs (&myfs_stats, rpath);
1042 if (myfs_stats.avail > 0 || myfs_stats.total > 0)
1044 char buffer1[6], buffer2[6], tmp[BUF_SMALL];
1045 size_trunc_len (buffer1, sizeof (buffer1) - 1, myfs_stats.avail, 1,
1046 panels_options.kilobyte_si);
1047 size_trunc_len (buffer2, sizeof (buffer2) - 1, myfs_stats.total, 1,
1048 panels_options.kilobyte_si);
1049 g_snprintf (tmp, sizeof (tmp), " %s/%s (%d%%) ", buffer1, buffer2,
1050 myfs_stats.total >
1051 0 ? (int) (100 * (double) myfs_stats.avail / myfs_stats.total) : 0);
1052 widget_move (&panel->widget, panel->widget.lines - 1,
1053 panel->widget.cols - 2 - (int) strlen (tmp));
1054 tty_setcolor (NORMAL_COLOR);
1055 tty_print_string (tmp);
1059 /* --------------------------------------------------------------------------------------------- */
1061 static void
1062 show_dir (WPanel * panel)
1064 gchar *tmp;
1065 set_colors (panel);
1066 draw_box (panel->widget.owner,
1067 panel->widget.y, panel->widget.x, panel->widget.lines, panel->widget.cols, FALSE);
1069 if (panels_options.show_mini_info)
1071 widget_move (&panel->widget, llines (panel) + 2, 0);
1072 tty_print_alt_char (ACS_LTEE, FALSE);
1073 widget_move (&panel->widget, llines (panel) + 2, panel->widget.cols - 1);
1074 tty_print_alt_char (ACS_RTEE, FALSE);
1077 widget_move (&panel->widget, 0, 1);
1078 tty_print_string (panel_history_prev_item_sign);
1080 tmp = panels_options.show_dot_files ? panel_hiddenfiles_sign_show : panel_hiddenfiles_sign_hide;
1081 tmp = g_strdup_printf ("%s[%s]%s", tmp, panel_history_show_list_sign,
1082 panel_history_next_item_sign);
1084 widget_move (&panel->widget, 0, panel->widget.cols - 6);
1085 tty_print_string (tmp);
1087 g_free (tmp);
1089 if (panel->active)
1090 tty_setcolor (REVERSE_COLOR);
1092 widget_move (&panel->widget, 0, 3);
1094 tty_printf (" %s ",
1095 str_term_trim (strip_home_and_password (panel->cwd),
1096 min (max (panel->widget.cols - 12, 0), panel->widget.cols)));
1098 if (!panels_options.show_mini_info)
1100 if (panel->marked == 0)
1102 /* Show size of curret file in the bottom of panel */
1103 if (S_ISREG (panel->dir.list[panel->selected].st.st_mode))
1105 char buffer[BUF_SMALL];
1107 g_snprintf (buffer, sizeof (buffer), " %s ",
1108 size_trunc_sep (panel->dir.list[panel->selected].st.st_size,
1109 panels_options.kilobyte_si));
1110 tty_setcolor (NORMAL_COLOR);
1111 widget_move (&panel->widget, panel->widget.lines - 1, 4);
1112 tty_print_string (buffer);
1115 else
1117 /* Show total size of marked files
1118 * In the bottom of panel, display size only. */
1119 display_total_marked_size (panel, panel->widget.lines - 1, 2, TRUE);
1123 show_free_space (panel);
1125 if (panel->active)
1126 tty_set_normal_attrs ();
1129 /* --------------------------------------------------------------------------------------------- */
1130 /** To be used only by long_frame and full_frame to adjust top_file */
1132 static void
1133 adjust_top_file (WPanel * panel)
1135 int old_top = panel->top_file;
1137 if (panel->selected - old_top > llines (panel))
1138 panel->top_file = panel->selected;
1139 if (old_top - panel->count > llines (panel))
1140 panel->top_file = panel->count - llines (panel);
1143 /* --------------------------------------------------------------------------------------------- */
1144 /** add "#enc:encodning" to end of path */
1145 /* if path end width a previous #enc:, only encoding is changed no additional
1146 * #enc: is appended
1147 * retun new string
1150 static char *
1151 panel_save_name (WPanel * panel)
1153 /* If the program is shuting down */
1154 if ((mc_global.widget.midnight_shutdown && auto_save_setup) || saving_setup)
1155 return g_strdup (panel->panel_name);
1156 else
1157 return g_strconcat ("Temporal:", panel->panel_name, (char *) NULL);
1160 /* --------------------------------------------------------------------------------------------- */
1162 /* "history_load" event handler */
1163 static gboolean
1164 panel_load_history (const gchar * event_group_name, const gchar * event_name,
1165 gpointer init_data, gpointer data)
1167 WPanel *p = (WPanel *) init_data;
1168 ev_history_load_save_t *ev = (ev_history_load_save_t *) data;
1170 (void) event_group_name;
1171 (void) event_name;
1173 if (ev->receiver == NULL || ev->receiver == (Widget *) p)
1175 if (ev->cfg != NULL)
1176 p->dir_history = history_load (ev->cfg, p->hist_name);
1177 else
1178 p->dir_history = history_get (p->hist_name);
1180 directory_history_add (p, p->cwd);
1183 return TRUE;
1186 /* --------------------------------------------------------------------------------------------- */
1188 /* "history_save" event handler */
1189 static gboolean
1190 panel_save_history (const gchar * event_group_name, const gchar * event_name,
1191 gpointer init_data, gpointer data)
1193 WPanel *p = (WPanel *) init_data;
1195 (void) event_group_name;
1196 (void) event_name;
1198 if (p->dir_history != NULL)
1200 ev_history_load_save_t *ev = (ev_history_load_save_t *) data;
1202 history_save (ev->cfg, p->hist_name, p->dir_history);
1205 return TRUE;
1208 /* --------------------------------------------------------------------------------------------- */
1210 static void
1211 panel_destroy (WPanel * p)
1213 size_t i;
1215 if (panels_options.auto_save_setup)
1217 char *name;
1219 name = panel_save_name (p);
1220 panel_save_setup (p, name);
1221 g_free (name);
1224 panel_clean_dir (p);
1226 /* clean history */
1227 if (p->dir_history != NULL)
1229 /* directory history is already saved before this moment */
1230 p->dir_history = g_list_first (p->dir_history);
1231 g_list_foreach (p->dir_history, (GFunc) g_free, NULL);
1232 g_list_free (p->dir_history);
1234 g_free (p->hist_name);
1236 delete_format (p->format);
1237 delete_format (p->status_format);
1239 g_free (p->user_format);
1240 for (i = 0; i < LIST_TYPES; i++)
1241 g_free (p->user_status_format[i]);
1242 g_free (p->dir.list);
1243 g_free (p->panel_name);
1246 /* --------------------------------------------------------------------------------------------- */
1248 static void
1249 panel_format_modified (WPanel * panel)
1251 panel->format_modified = 1;
1254 /* --------------------------------------------------------------------------------------------- */
1256 static void
1257 panel_paint_sort_info (WPanel * panel)
1259 if (*panel->sort_info.sort_field->hotkey != '\0')
1261 const char *sort_sign =
1262 panel->sort_info.reverse ? panel_sort_down_sign : panel_sort_up_sign;
1263 char *str;
1265 str = g_strdup_printf ("%s%s", sort_sign, Q_ (panel->sort_info.sort_field->hotkey));
1266 widget_move (&panel->widget, 1, 1);
1267 tty_print_string (str);
1268 g_free (str);
1272 /* --------------------------------------------------------------------------------------------- */
1274 static gchar *
1275 panel_get_title_without_hotkey (const char *title)
1277 char *translated_title;
1278 char *hkey;
1280 if (title == NULL)
1281 return NULL;
1282 if (title[0] == '\0')
1283 return g_strdup ("");
1285 translated_title = g_strdup (_(title));
1287 hkey = strchr (translated_title, '&');
1288 if ((hkey != NULL) && (hkey[1] != '\0'))
1289 memmove ((void *) hkey, (void *) hkey + 1, strlen (hkey));
1291 return translated_title;
1294 /* --------------------------------------------------------------------------------------------- */
1296 static void
1297 paint_frame (WPanel * panel)
1299 int side, width;
1300 GString *format_txt;
1302 if (!panel->split)
1303 adjust_top_file (panel);
1305 widget_erase (&panel->widget);
1306 show_dir (panel);
1308 widget_move (&panel->widget, 1, 1);
1310 for (side = 0; side <= panel->split; side++)
1312 format_e *format;
1314 if (side)
1316 tty_setcolor (NORMAL_COLOR);
1317 tty_print_one_vline (TRUE);
1318 width = panel->widget.cols - panel->widget.cols / 2 - 1;
1320 else if (panel->split)
1321 width = panel->widget.cols / 2 - 3;
1322 else
1323 width = panel->widget.cols - 2;
1325 format_txt = g_string_new ("");
1326 for (format = panel->format; format; format = format->next)
1328 if (format->string_fn)
1330 g_string_set_size (format_txt, 0);
1332 if (panel->list_type == list_long
1333 && strcmp (format->id, panel->sort_info.sort_field->id) == 0)
1334 g_string_append (format_txt,
1335 panel->sort_info.reverse
1336 ? panel_sort_down_sign : panel_sort_up_sign);
1338 g_string_append (format_txt, format->title);
1339 if (strcmp (format->id, "name") == 0 && panel->filter && *panel->filter)
1341 g_string_append (format_txt, " [");
1342 g_string_append (format_txt, panel->filter);
1343 g_string_append (format_txt, "]");
1346 tty_setcolor (HEADER_COLOR);
1347 tty_print_string (str_fit_to_term (format_txt->str, format->field_len,
1348 J_CENTER_LEFT));
1349 width -= format->field_len;
1351 else
1353 tty_setcolor (NORMAL_COLOR);
1354 tty_print_one_vline (TRUE);
1355 width--;
1358 g_string_free (format_txt, TRUE);
1360 if (width > 0)
1361 tty_draw_hline (-1, -1, ' ', width);
1364 if (panel->list_type != list_long)
1365 panel_paint_sort_info (panel);
1368 /* --------------------------------------------------------------------------------------------- */
1370 static const char *
1371 parse_panel_size (WPanel * panel, const char *format, int isstatus)
1373 panel_display_t frame = frame_half;
1374 format = skip_separators (format);
1376 if (!strncmp (format, "full", 4))
1378 frame = frame_full;
1379 format += 4;
1381 else if (!strncmp (format, "half", 4))
1383 frame = frame_half;
1384 format += 4;
1387 if (!isstatus)
1389 panel->frame_size = frame;
1390 panel->split = 0;
1393 /* Now, the optional column specifier */
1394 format = skip_separators (format);
1396 if (*format == '1' || *format == '2')
1398 if (!isstatus)
1399 panel->split = *format == '2';
1400 format++;
1403 if (!isstatus)
1404 panel_update_cols (&(panel->widget), panel->frame_size);
1406 return skip_separators (format);
1409 /* Format is:
1411 all := panel_format? format
1412 panel_format := [full|half] [1|2]
1413 format := one_format_e
1414 | format , one_format_e
1416 one_format_e := just format.id [opt_size]
1417 just := [<=>]
1418 opt_size := : size [opt_expand]
1419 size := [0-9]+
1420 opt_expand := +
1424 /* --------------------------------------------------------------------------------------------- */
1426 static format_e *
1427 parse_display_format (WPanel * panel, const char *format, char **error, int isstatus,
1428 int *res_total_cols)
1430 format_e *darr, *old = 0, *home = 0; /* The formats we return */
1431 int total_cols = 0; /* Used columns by the format */
1432 int set_justify; /* flag: set justification mode? */
1433 align_crt_t justify = J_LEFT; /* Which mode. */
1434 size_t i;
1436 static size_t i18n_timelength = 0; /* flag: check ?Time length at startup */
1438 *error = 0;
1440 if (i18n_timelength == 0)
1442 i18n_timelength = i18n_checktimelength (); /* Musn't be 0 */
1444 for (i = 0; panel_fields[i].id != NULL; i++)
1445 if (strcmp ("time", panel_fields[i].id + 1) == 0)
1446 panel_fields[i].min_size = i18n_timelength;
1450 * This makes sure that the panel and mini status full/half mode
1451 * setting is equal
1453 format = parse_panel_size (panel, format, isstatus);
1455 while (*format)
1456 { /* format can be an empty string */
1457 int found = 0;
1459 darr = g_new0 (format_e, 1);
1461 /* I'm so ugly, don't look at me :-) */
1462 if (!home)
1463 home = old = darr;
1465 old->next = darr;
1466 darr->next = 0;
1467 old = darr;
1469 format = skip_separators (format);
1471 if (strchr ("<=>", *format))
1473 set_justify = 1;
1474 switch (*format)
1476 case '<':
1477 justify = J_LEFT;
1478 break;
1479 case '=':
1480 justify = J_CENTER;
1481 break;
1482 case '>':
1483 default:
1484 justify = J_RIGHT;
1485 break;
1487 format = skip_separators (format + 1);
1489 else
1490 set_justify = 0;
1492 for (i = 0; panel_fields[i].id != NULL; i++)
1494 size_t klen = strlen (panel_fields[i].id);
1496 if (strncmp (format, panel_fields[i].id, klen) != 0)
1497 continue;
1499 format += klen;
1501 darr->requested_field_len = panel_fields[i].min_size;
1502 darr->string_fn = panel_fields[i].string_fn;
1503 darr->title = panel_get_title_without_hotkey (panel_fields[i].title_hotkey);
1505 darr->id = panel_fields[i].id;
1506 darr->expand = panel_fields[i].expands;
1507 darr->just_mode = panel_fields[i].default_just;
1509 if (set_justify)
1511 if (IS_FIT (darr->just_mode))
1512 darr->just_mode = MAKE_FIT (justify);
1513 else
1514 darr->just_mode = justify;
1516 found = 1;
1518 format = skip_separators (format);
1520 /* If we have a size specifier */
1521 if (*format == ':')
1523 int req_length;
1525 /* If the size was specified, we don't want
1526 * auto-expansion by default
1528 darr->expand = 0;
1529 format++;
1530 req_length = atoi (format);
1531 darr->requested_field_len = req_length;
1533 format = skip_numbers (format);
1535 /* Now, if they insist on expansion */
1536 if (*format == '+')
1538 darr->expand = 1;
1539 format++;
1544 break;
1546 if (!found)
1548 char *tmp_format = g_strdup (format);
1550 int pos = min (8, strlen (format));
1551 delete_format (home);
1552 tmp_format[pos] = 0;
1553 *error =
1554 g_strconcat (_("Unknown tag on display format:"), " ", tmp_format, (char *) NULL);
1555 g_free (tmp_format);
1556 return 0;
1558 total_cols += darr->requested_field_len;
1561 *res_total_cols = total_cols;
1562 return home;
1565 /* --------------------------------------------------------------------------------------------- */
1567 static format_e *
1568 use_display_format (WPanel * panel, const char *format, char **error, int isstatus)
1570 #define MAX_EXPAND 4
1571 int expand_top = 0; /* Max used element in expand */
1572 int usable_columns; /* Usable columns in the panel */
1573 int total_cols = 0;
1574 int i;
1575 format_e *darr, *home;
1577 if (!format)
1578 format = DEFAULT_USER_FORMAT;
1580 home = parse_display_format (panel, format, error, isstatus, &total_cols);
1582 if (*error)
1583 return 0;
1585 panel->dirty = 1;
1587 /* Status needn't to be split */
1588 usable_columns = ((panel->widget.cols - 2) / ((isstatus)
1590 : (panel->split + 1))) - (!isstatus
1591 && panel->split);
1593 /* Look for the expandable fields and set field_len based on the requested field len */
1594 for (darr = home; darr && expand_top < MAX_EXPAND; darr = darr->next)
1596 darr->field_len = darr->requested_field_len;
1597 if (darr->expand)
1598 expand_top++;
1601 /* If we used more columns than the available columns, adjust that */
1602 if (total_cols > usable_columns)
1604 int pdif, dif = total_cols - usable_columns;
1606 while (dif)
1608 pdif = dif;
1609 for (darr = home; darr; darr = darr->next)
1611 if (dif && darr->field_len - 1)
1613 darr->field_len--;
1614 dif--;
1618 /* avoid endless loop if num fields > 40 */
1619 if (pdif == dif)
1620 break;
1622 total_cols = usable_columns; /* give up, the rest should be truncated */
1625 /* Expand the available space */
1626 if ((usable_columns > total_cols) && expand_top)
1628 int spaces = (usable_columns - total_cols) / expand_top;
1629 int extra = (usable_columns - total_cols) % expand_top;
1631 for (i = 0, darr = home; darr && (i < expand_top); darr = darr->next)
1632 if (darr->expand)
1634 darr->field_len += (spaces + ((i == 0) ? extra : 0));
1635 i++;
1638 return home;
1641 /* --------------------------------------------------------------------------------------------- */
1642 /** Given the panel->view_type returns the format string to be parsed */
1644 static const char *
1645 panel_format (WPanel * panel)
1647 switch (panel->list_type)
1649 case list_long:
1650 return "full perm space nlink space owner space group space size space mtime space name";
1652 case list_brief:
1653 return "half 2 type name";
1655 case list_user:
1656 return panel->user_format;
1658 default:
1659 case list_full:
1660 return "half type name | size | mtime";
1664 /* --------------------------------------------------------------------------------------------- */
1666 static const char *
1667 mini_status_format (WPanel * panel)
1669 if (panel->user_mini_status)
1670 return panel->user_status_format[panel->list_type];
1672 switch (panel->list_type)
1675 case list_long:
1676 return "full perm space nlink space owner space group space size space mtime space name";
1678 case list_brief:
1679 return "half type name space bsize space perm space";
1681 case list_full:
1682 return "half type name";
1684 default:
1685 case list_user:
1686 return panel->user_format;
1690 /* */
1691 /* Panel operation commands */
1692 /* */
1694 /* --------------------------------------------------------------------------------------------- */
1695 /** Used to emulate Lynx's entering leaving a directory with the arrow keys */
1697 static cb_ret_t
1698 maybe_cd (int move_up_dir)
1700 if (panels_options.navigate_with_arrows && (cmdline->buffer[0] == '\0'))
1702 if (move_up_dir)
1704 do_cd ("..", cd_exact);
1705 return MSG_HANDLED;
1708 if (S_ISDIR (selection (current_panel)->st.st_mode)
1709 || link_isdir (selection (current_panel)))
1711 do_cd (selection (current_panel)->fname, cd_exact);
1712 return MSG_HANDLED;
1715 return MSG_NOT_HANDLED;
1718 /* --------------------------------------------------------------------------------------------- */
1720 /* if command line is empty then do 'cd ..' */
1721 static cb_ret_t
1722 force_maybe_cd (void)
1724 if (cmdline->buffer[0] == '\0')
1726 do_cd ("..", cd_exact);
1727 return MSG_HANDLED;
1729 return MSG_NOT_HANDLED;
1732 /* --------------------------------------------------------------------------------------------- */
1734 /* Returns the number of items in the given panel */
1735 static int
1736 ITEMS (WPanel * p)
1738 if (p->split)
1739 return llines (p) * 2;
1740 else
1741 return llines (p);
1744 /* --------------------------------------------------------------------------------------------- */
1746 static void
1747 unselect_item (WPanel * panel)
1749 repaint_file (panel, panel->selected, 1, 2 * selection (panel)->f.marked, 0);
1752 /* --------------------------------------------------------------------------------------------- */
1754 static void
1755 move_down (WPanel * panel)
1757 if (panel->selected + 1 == panel->count)
1758 return;
1760 unselect_item (panel);
1761 panel->selected++;
1762 if (panels_options.scroll_pages && panel->selected - panel->top_file == ITEMS (panel))
1764 /* Scroll window half screen */
1765 panel->top_file += ITEMS (panel) / 2;
1766 if (panel->top_file > panel->count - ITEMS (panel))
1767 panel->top_file = panel->count - ITEMS (panel);
1768 paint_dir (panel);
1770 select_item (panel);
1773 /* --------------------------------------------------------------------------------------------- */
1775 static void
1776 move_up (WPanel * panel)
1778 if (panel->selected == 0)
1779 return;
1781 unselect_item (panel);
1782 panel->selected--;
1783 if (panels_options.scroll_pages && panel->selected < panel->top_file)
1785 /* Scroll window half screen */
1786 panel->top_file -= ITEMS (panel) / 2;
1787 if (panel->top_file < 0)
1788 panel->top_file = 0;
1789 paint_dir (panel);
1791 select_item (panel);
1794 /* --------------------------------------------------------------------------------------------- */
1795 /** Changes the selection by lines (may be negative) */
1797 static void
1798 move_selection (WPanel * panel, int lines)
1800 int new_pos;
1801 int adjust = 0;
1803 new_pos = panel->selected + lines;
1804 if (new_pos >= panel->count)
1805 new_pos = panel->count - 1;
1807 if (new_pos < 0)
1808 new_pos = 0;
1810 unselect_item (panel);
1811 panel->selected = new_pos;
1813 if (panel->selected - panel->top_file >= ITEMS (panel))
1815 panel->top_file += lines;
1816 adjust = 1;
1819 if (panel->selected - panel->top_file < 0)
1821 panel->top_file += lines;
1822 adjust = 1;
1825 if (adjust)
1827 if (panel->top_file > panel->selected)
1828 panel->top_file = panel->selected;
1829 if (panel->top_file < 0)
1830 panel->top_file = 0;
1831 paint_dir (panel);
1833 select_item (panel);
1836 /* --------------------------------------------------------------------------------------------- */
1838 static cb_ret_t
1839 move_left (WPanel * panel)
1841 if (panel->split)
1843 move_selection (panel, -llines (panel));
1844 return MSG_HANDLED;
1846 else
1847 return maybe_cd (1); /* cd .. */
1850 /* --------------------------------------------------------------------------------------------- */
1852 static int
1853 move_right (WPanel * panel)
1855 if (panel->split)
1857 move_selection (panel, llines (panel));
1858 return MSG_HANDLED;
1860 else
1861 return maybe_cd (0); /* cd (selection) */
1864 /* --------------------------------------------------------------------------------------------- */
1866 static void
1867 prev_page (WPanel * panel)
1869 int items;
1871 if (!panel->selected && !panel->top_file)
1872 return;
1873 unselect_item (panel);
1874 items = ITEMS (panel);
1875 if (panel->top_file < items)
1876 items = panel->top_file;
1877 if (!items)
1878 panel->selected = 0;
1879 else
1880 panel->selected -= items;
1881 panel->top_file -= items;
1883 select_item (panel);
1884 paint_dir (panel);
1887 /* --------------------------------------------------------------------------------------------- */
1889 static void
1890 goto_parent_dir (WPanel * panel)
1892 (void) panel;
1893 do_cd ("..", cd_exact);
1896 /* --------------------------------------------------------------------------------------------- */
1898 static void
1899 next_page (WPanel * panel)
1901 int items;
1903 if (panel->selected == panel->count - 1)
1904 return;
1905 unselect_item (panel);
1906 items = ITEMS (panel);
1907 if (panel->top_file > panel->count - 2 * items)
1908 items = panel->count - items - panel->top_file;
1909 if (panel->top_file + items < 0)
1910 items = -panel->top_file;
1911 if (!items)
1912 panel->selected = panel->count - 1;
1913 else
1914 panel->selected += items;
1915 panel->top_file += items;
1917 select_item (panel);
1918 paint_dir (panel);
1921 /* --------------------------------------------------------------------------------------------- */
1923 static void
1924 goto_child_dir (WPanel * panel)
1926 if ((S_ISDIR (selection (panel)->st.st_mode) || link_isdir (selection (panel))))
1928 do_cd (selection (panel)->fname, cd_exact);
1932 /* --------------------------------------------------------------------------------------------- */
1934 static void
1935 goto_top_file (WPanel * panel)
1937 unselect_item (panel);
1938 panel->selected = panel->top_file;
1939 select_item (panel);
1942 /* --------------------------------------------------------------------------------------------- */
1944 static void
1945 goto_middle_file (WPanel * panel)
1947 unselect_item (panel);
1948 panel->selected = panel->top_file + (ITEMS (panel) / 2);
1949 select_item (panel);
1952 /* --------------------------------------------------------------------------------------------- */
1954 static void
1955 goto_bottom_file (WPanel * panel)
1957 unselect_item (panel);
1958 panel->selected = panel->top_file + ITEMS (panel) - 1;
1959 select_item (panel);
1962 /* --------------------------------------------------------------------------------------------- */
1964 static void
1965 move_home (WPanel * panel)
1967 if (panel->selected == 0)
1968 return;
1970 unselect_item (panel);
1972 if (panels_options.torben_fj_mode)
1974 int middle_pos = panel->top_file + (ITEMS (panel) / 2);
1976 if (panel->selected > middle_pos)
1978 goto_middle_file (panel);
1979 return;
1981 if (panel->selected != panel->top_file)
1983 goto_top_file (panel);
1984 return;
1988 panel->top_file = 0;
1989 panel->selected = 0;
1991 paint_dir (panel);
1992 select_item (panel);
1995 /* --------------------------------------------------------------------------------------------- */
1997 static void
1998 move_end (WPanel * panel)
2000 if (panel->selected == panel->count - 1)
2001 return;
2003 unselect_item (panel);
2005 if (panels_options.torben_fj_mode)
2007 int middle_pos = panel->top_file + (ITEMS (panel) / 2);
2009 if (panel->selected < middle_pos)
2011 goto_middle_file (panel);
2012 return;
2014 if (panel->selected != (panel->top_file + ITEMS (panel) - 1))
2016 goto_bottom_file (panel);
2017 return;
2021 panel->selected = panel->count - 1;
2022 paint_dir (panel);
2023 select_item (panel);
2026 /* --------------------------------------------------------------------------------------------- */
2028 static void
2029 do_mark_file (WPanel * panel, mark_act_t do_move)
2031 do_file_mark (panel, panel->selected, selection (panel)->f.marked ? 0 : 1);
2032 if ((panels_options.mark_moves_down && do_move == MARK_DOWN) || do_move == MARK_FORCE_DOWN)
2033 move_down (panel);
2034 else if (do_move == MARK_FORCE_UP)
2035 move_up (panel);
2038 /* --------------------------------------------------------------------------------------------- */
2040 static void
2041 mark_file (WPanel * panel)
2043 do_mark_file (panel, MARK_DOWN);
2046 /* --------------------------------------------------------------------------------------------- */
2048 static void
2049 mark_file_up (WPanel * panel)
2051 do_mark_file (panel, MARK_FORCE_UP);
2054 /* --------------------------------------------------------------------------------------------- */
2056 static void
2057 mark_file_down (WPanel * panel)
2059 do_mark_file (panel, MARK_FORCE_DOWN);
2062 /* --------------------------------------------------------------------------------------------- */
2064 static void
2065 mark_file_right (WPanel * panel)
2067 int lines = llines (panel);
2069 if (state_mark < 0)
2070 state_mark = selection (panel)->f.marked ? 0 : 1;
2072 lines = min (lines, panel->count - panel->selected - 1);
2073 for (; lines != 0; lines--)
2075 do_file_mark (panel, panel->selected, state_mark);
2076 move_down (panel);
2078 do_file_mark (panel, panel->selected, state_mark);
2081 /* --------------------------------------------------------------------------------------------- */
2083 static void
2084 mark_file_left (WPanel * panel)
2086 int lines = llines (panel);
2088 if (state_mark < 0)
2089 state_mark = selection (panel)->f.marked ? 0 : 1;
2091 lines = min (lines, panel->selected + 1);
2092 for (; lines != 0; lines--)
2094 do_file_mark (panel, panel->selected, state_mark);
2095 move_up (panel);
2097 do_file_mark (panel, panel->selected, state_mark);
2100 /* --------------------------------------------------------------------------------------------- */
2101 /** Incremental search of a file name in the panel.
2102 * @param panel instance of WPanel structure
2103 * @param c_code key code
2106 static void
2107 do_search (WPanel * panel, int c_code)
2109 size_t l;
2110 int i, sel;
2111 gboolean wrapped = FALSE;
2112 char *act;
2113 mc_search_t *search;
2114 char *reg_exp, *esc_str;
2115 gboolean is_found = FALSE;
2117 l = strlen (panel->search_buffer);
2118 if (c_code == KEY_BACKSPACE)
2120 if (l != 0)
2122 act = panel->search_buffer + l;
2123 str_prev_noncomb_char (&act, panel->search_buffer);
2124 act[0] = '\0';
2126 panel->search_chpoint = 0;
2128 else
2130 if (c_code != 0 && (gsize) panel->search_chpoint < sizeof (panel->search_char))
2132 panel->search_char[panel->search_chpoint] = c_code;
2133 panel->search_chpoint++;
2136 if (panel->search_chpoint > 0)
2138 switch (str_is_valid_char (panel->search_char, panel->search_chpoint))
2140 case -2:
2141 return;
2142 case -1:
2143 panel->search_chpoint = 0;
2144 return;
2145 default:
2146 if (l + panel->search_chpoint < sizeof (panel->search_buffer))
2148 memcpy (panel->search_buffer + l, panel->search_char, panel->search_chpoint);
2149 l += panel->search_chpoint;
2150 *(panel->search_buffer + l) = '\0';
2151 panel->search_chpoint = 0;
2157 reg_exp = g_strdup_printf ("%s*", panel->search_buffer);
2158 esc_str = strutils_escape (reg_exp, -1, ",|\\{}[]", TRUE);
2159 search = mc_search_new (esc_str, -1);
2160 search->search_type = MC_SEARCH_T_GLOB;
2161 search->is_entire_line = TRUE;
2162 switch (panels_options.qsearch_mode)
2164 case QSEARCH_CASE_SENSITIVE:
2165 search->is_case_sensitive = TRUE;
2166 break;
2167 case QSEARCH_CASE_INSENSITIVE:
2168 search->is_case_sensitive = FALSE;
2169 break;
2170 default:
2171 search->is_case_sensitive = panel->sort_info.case_sensitive;
2172 break;
2174 sel = panel->selected;
2175 for (i = panel->selected; !wrapped || i != panel->selected; i++)
2177 if (i >= panel->count)
2179 i = 0;
2180 if (wrapped)
2181 break;
2182 wrapped = TRUE;
2184 if (mc_search_run (search, panel->dir.list[i].fname, 0, panel->dir.list[i].fnamelen, NULL))
2186 sel = i;
2187 is_found = TRUE;
2188 break;
2191 if (is_found)
2193 unselect_item (panel);
2194 panel->selected = sel;
2195 select_item (panel);
2196 send_message ((Widget *) panel, WIDGET_DRAW, 0);
2198 else if (c_code != KEY_BACKSPACE)
2200 act = panel->search_buffer + l;
2201 str_prev_noncomb_char (&act, panel->search_buffer);
2202 act[0] = '\0';
2204 mc_search_free (search);
2205 g_free (reg_exp);
2206 g_free (esc_str);
2209 /* --------------------------------------------------------------------------------------------- */
2210 /** Start new search.
2211 * @param panel instance of WPanel structure
2214 static void
2215 start_search (WPanel * panel)
2217 if (panel->searching)
2219 if (panel->selected + 1 == panel->count)
2220 panel->selected = 0;
2221 else
2222 move_down (panel);
2224 /* in case if there was no search string we need to recall
2225 previous string, with which we ended previous searching */
2226 if (panel->search_buffer[0] == '\0')
2227 g_strlcpy (panel->search_buffer, panel->prev_search_buffer,
2228 sizeof (panel->search_buffer));
2230 do_search (panel, 0);
2232 else
2234 panel->searching = TRUE;
2235 panel->search_buffer[0] = '\0';
2236 panel->search_char[0] = '\0';
2237 panel->search_chpoint = 0;
2238 display_mini_info (panel);
2239 mc_refresh ();
2243 /* --------------------------------------------------------------------------------------------- */
2245 static void
2246 stop_search (WPanel * panel)
2248 panel->searching = FALSE;
2250 /* if user had overrdied search string, we need to store it
2251 to the previous_search_buffer */
2252 if (panel->search_buffer[0] != '\0')
2253 g_strlcpy (panel->prev_search_buffer, panel->search_buffer,
2254 sizeof (panel->prev_search_buffer));
2256 display_mini_info (panel);
2259 /* --------------------------------------------------------------------------------------------- */
2260 /** Return 1 if the Enter key has been processed, 0 otherwise */
2262 static int
2263 do_enter_on_file_entry (file_entry * fe)
2265 char *full_name;
2268 * Directory or link to directory - change directory.
2269 * Try the same for the entries on which mc_lstat() has failed.
2271 if (S_ISDIR (fe->st.st_mode) || link_isdir (fe) || (fe->st.st_mode == 0))
2273 if (!do_cd (fe->fname, cd_exact))
2274 message (D_ERROR, MSG_ERROR, _("Cannot change directory"));
2275 return 1;
2278 /* Try associated command */
2279 if (regex_command (fe->fname, "Open", NULL) != 0)
2280 return 1;
2282 /* Check if the file is executable */
2283 full_name = concat_dir_and_file (current_panel->cwd, fe->fname);
2284 if (!is_exe (fe->st.st_mode) || !if_link_is_exe (full_name, fe))
2286 g_free (full_name);
2287 return 0;
2289 g_free (full_name);
2291 if (confirm_execute)
2293 if (query_dialog
2294 (_("The Midnight Commander"),
2295 _("Do you really want to execute?"), D_NORMAL, 2, _("&Yes"), _("&No")) != 0)
2296 return 1;
2299 if (!vfs_current_is_local ())
2301 char *tmp, *tmp_curr_dir;
2302 int ret;
2304 tmp_curr_dir = vfs_get_current_dir ();
2305 tmp = concat_dir_and_file (tmp_curr_dir, fe->fname);
2306 g_free (tmp_curr_dir);
2307 ret = mc_setctl (tmp, VFS_SETCTL_RUN, NULL);
2308 g_free (tmp);
2309 /* We took action only if the dialog was shown or the execution
2310 * was successful */
2311 return confirm_execute || (ret == 0);
2315 char *tmp = name_quote (fe->fname, 0);
2316 char *cmd = g_strconcat (".", PATH_SEP_STR, tmp, (char *) NULL);
2317 g_free (tmp);
2318 shell_execute (cmd, 0);
2319 g_free (cmd);
2322 #if HAVE_CHARSET
2323 mc_global.source_codepage = default_source_codepage;
2324 #endif
2326 return 1;
2329 /* --------------------------------------------------------------------------------------------- */
2331 static int
2332 do_enter (WPanel * panel)
2334 return do_enter_on_file_entry (selection (panel));
2337 /* --------------------------------------------------------------------------------------------- */
2339 static void
2340 chdir_other_panel (WPanel * panel)
2342 char *new_dir;
2343 char *sel_entry = NULL;
2345 if (get_other_type () != view_listing)
2347 set_display_type (get_other_index (), view_listing);
2350 if (!S_ISDIR (panel->dir.list[panel->selected].st.st_mode))
2352 new_dir = concat_dir_and_file (panel->cwd, "..");
2353 sel_entry = strrchr (panel->cwd, PATH_SEP);
2355 else
2356 new_dir = concat_dir_and_file (panel->cwd, panel->dir.list[panel->selected].fname);
2358 change_panel ();
2359 do_cd (new_dir, cd_exact);
2360 if (sel_entry)
2361 try_to_select (current_panel, sel_entry);
2362 change_panel ();
2364 move_down (panel);
2366 g_free (new_dir);
2369 /* --------------------------------------------------------------------------------------------- */
2371 * Make the current directory of the current panel also the current
2372 * directory of the other panel. Put the other panel to the listing
2373 * mode if needed. If the current panel is panelized, the other panel
2374 * doesn't become panelized.
2377 static void
2378 panel_sync_other (const WPanel * panel)
2380 if (get_other_type () != view_listing)
2382 set_display_type (get_other_index (), view_listing);
2385 do_panel_cd (other_panel, current_panel->cwd, cd_exact);
2387 /* try to select current filename on the other panel */
2388 if (!panel->is_panelized)
2390 try_to_select (other_panel, selection (panel)->fname);
2394 /* --------------------------------------------------------------------------------------------- */
2396 static void
2397 chdir_to_readlink (WPanel * panel)
2399 char *new_dir;
2401 if (get_other_type () != view_listing)
2402 return;
2404 if (S_ISLNK (panel->dir.list[panel->selected].st.st_mode))
2406 char buffer[MC_MAXPATHLEN], *p;
2407 int i;
2408 struct stat st;
2410 i = readlink (selection (panel)->fname, buffer, MC_MAXPATHLEN - 1);
2411 if (i < 0)
2412 return;
2413 if (mc_stat (selection (panel)->fname, &st) < 0)
2414 return;
2415 buffer[i] = 0;
2416 if (!S_ISDIR (st.st_mode))
2418 p = strrchr (buffer, PATH_SEP);
2419 if (p && !p[1])
2421 *p = 0;
2422 p = strrchr (buffer, PATH_SEP);
2424 if (!p)
2425 return;
2426 p[1] = 0;
2428 if (*buffer == PATH_SEP)
2429 new_dir = g_strdup (buffer);
2430 else
2431 new_dir = concat_dir_and_file (panel->cwd, buffer);
2433 change_panel ();
2434 do_cd (new_dir, cd_exact);
2435 change_panel ();
2437 move_down (panel);
2439 g_free (new_dir);
2443 /* --------------------------------------------------------------------------------------------- */
2445 static gsize
2446 panel_get_format_field_count (WPanel * panel)
2448 format_e *format;
2449 gsize lc_index;
2450 for (lc_index = 0, format = panel->format; format != NULL; format = format->next, lc_index++);
2451 return lc_index;
2454 /* --------------------------------------------------------------------------------------------- */
2456 function return 0 if not found and REAL_INDEX+1 if found
2459 static gsize
2460 panel_get_format_field_index_by_name (WPanel * panel, const char *name)
2462 format_e *format;
2463 gsize lc_index;
2465 for (lc_index = 1, format = panel->format;
2466 !(format == NULL || strcmp (format->title, name) == 0); format = format->next, lc_index++);
2467 if (format == NULL)
2468 lc_index = 0;
2470 return lc_index;
2473 /* --------------------------------------------------------------------------------------------- */
2475 static format_e *
2476 panel_get_format_field_by_index (WPanel * panel, gsize lc_index)
2478 format_e *format;
2479 for (format = panel->format;
2480 !(format == NULL || lc_index == 0); format = format->next, lc_index--);
2481 return format;
2484 /* --------------------------------------------------------------------------------------------- */
2486 static const panel_field_t *
2487 panel_get_sortable_field_by_format (WPanel * panel, gsize lc_index)
2489 const panel_field_t *pfield;
2490 format_e *format;
2492 format = panel_get_format_field_by_index (panel, lc_index);
2493 if (format == NULL)
2494 return NULL;
2495 pfield = panel_get_field_by_title (format->title);
2496 if (pfield == NULL)
2497 return NULL;
2498 if (pfield->sort_routine == NULL)
2499 return NULL;
2500 return pfield;
2503 /* --------------------------------------------------------------------------------------------- */
2505 static void
2506 panel_toggle_sort_order_prev (WPanel * panel)
2508 gsize lc_index, i;
2509 gchar *title;
2511 const panel_field_t *pfield = NULL;
2513 title = panel_get_title_without_hotkey (panel->sort_info.sort_field->title_hotkey);
2514 lc_index = panel_get_format_field_index_by_name (panel, title);
2515 g_free (title);
2517 if (lc_index > 1)
2519 /* search for prev sortable column in panel format */
2520 for (i = lc_index - 1;
2521 i != 0 && (pfield = panel_get_sortable_field_by_format (panel, i - 1)) == NULL; i--);
2524 if (pfield == NULL)
2526 /* Sortable field not found. Try to search in each array */
2527 for (i = panel_get_format_field_count (panel);
2528 i != 0 && (pfield = panel_get_sortable_field_by_format (panel, i - 1)) == 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_toggle_sort_order_next (WPanel * panel)
2543 gsize lc_index, i;
2544 const panel_field_t *pfield = NULL;
2545 gsize format_field_count;
2546 gchar *title;
2548 format_field_count = panel_get_format_field_count (panel);
2549 title = panel_get_title_without_hotkey (panel->sort_info.sort_field->title_hotkey);
2550 lc_index = panel_get_format_field_index_by_name (panel, title);
2551 g_free (title);
2553 if (lc_index != 0 && lc_index != format_field_count)
2555 /* search for prev sortable column in panel format */
2556 for (i = lc_index;
2557 i != format_field_count
2558 && (pfield = panel_get_sortable_field_by_format (panel, i)) == NULL; i++);
2561 if (pfield == NULL)
2563 /* Sortable field not found. Try to search in each array */
2564 for (i = 0;
2565 i != format_field_count
2566 && (pfield = panel_get_sortable_field_by_format (panel, i)) == NULL; i++);
2569 if (pfield != NULL)
2571 panel->sort_info.sort_field = pfield;
2572 panel_set_sort_order (panel, pfield);
2576 /* --------------------------------------------------------------------------------------------- */
2578 static void
2579 panel_select_sort_order (WPanel * panel)
2581 const panel_field_t *sort_order;
2583 sort_order = sort_box (&panel->sort_info);
2584 if (sort_order != NULL)
2586 panel->sort_info.sort_field = sort_order;
2587 panel_set_sort_order (panel, sort_order);
2591 /* --------------------------------------------------------------------------------------------- */
2593 static void
2594 panel_set_sort_type_by_id (WPanel * panel, const char *name)
2596 if (strcmp (panel->sort_info.sort_field->id, name) != 0)
2598 const panel_field_t *sort_order;
2600 sort_order = panel_get_field_by_id (name);
2601 if (sort_order == NULL)
2602 return;
2603 panel->sort_info.sort_field = sort_order;
2605 else
2606 panel->sort_info.reverse = !panel->sort_info.reverse;
2608 panel_set_sort_order (panel, panel->sort_info.sort_field);
2611 /* --------------------------------------------------------------------------------------------- */
2613 * If we moved to the parent directory move the selection pointer to
2614 * the old directory name; If we leave VFS dir, remove FS specificator.
2616 * You do _NOT_ want to add any vfs aware code here. <pavel@ucw.cz>
2619 static const char *
2620 get_parent_dir_name (const char *cwd, const char *lwd)
2622 size_t llen, clen;
2623 const char *p;
2625 llen = strlen (lwd);
2626 clen = strlen (cwd);
2628 if (llen <= clen)
2629 return NULL;
2631 p = g_strrstr (lwd, VFS_PATH_URL_DELIMITER);
2633 if (p == NULL)
2635 p = strrchr (lwd, PATH_SEP);
2637 if ((p != NULL)
2638 && (strncmp (cwd, lwd, (size_t) (p - lwd)) == 0)
2639 && (clen == (size_t) (p - lwd)
2640 || ((p == lwd) && (cwd[0] == PATH_SEP) && (cwd[1] == '\0'))))
2641 return (p + 1);
2643 return NULL;
2646 while (--p > lwd && *p != PATH_SEP);
2647 while (--p > lwd && *p != PATH_SEP);
2649 return (p != lwd) ? p + 1 : NULL;
2652 /* --------------------------------------------------------------------------------------------- */
2653 /** Wrapper for do_subshell_chdir, check for availability of subshell */
2655 static void
2656 subshell_chdir (const char *directory)
2658 #ifdef HAVE_SUBSHELL_SUPPORT
2659 if (mc_global.tty.use_subshell && vfs_current_is_local ())
2660 do_subshell_chdir (directory, FALSE, TRUE);
2661 #endif /* HAVE_SUBSHELL_SUPPORT */
2664 /* --------------------------------------------------------------------------------------------- */
2666 * Changes the current directory of the panel.
2667 * Don't record change in the directory history.
2670 static gboolean
2671 _do_panel_cd (WPanel * panel, const char *new_dir, enum cd_enum cd_type)
2673 const char *directory;
2674 char *olddir;
2675 char temp[MC_MAXPATHLEN];
2677 if (cd_type == cd_parse_command)
2679 while (*new_dir == ' ')
2680 new_dir++;
2683 olddir = g_strdup (panel->cwd);
2685 /* Convert *new_path to a suitable pathname, handle ~user */
2687 if (cd_type == cd_parse_command)
2689 if (!strcmp (new_dir, "-"))
2691 strcpy (temp, panel->lwd);
2692 new_dir = temp;
2695 directory = *new_dir ? new_dir : mc_config_get_home_dir ();
2697 if (mc_chdir (directory) == -1)
2699 strcpy (panel->cwd, olddir);
2700 g_free (olddir);
2701 return FALSE;
2704 /* Success: save previous directory, shutdown status of previous dir */
2705 strcpy (panel->lwd, olddir);
2706 input_free_completions (cmdline);
2708 mc_get_current_wd (panel->cwd, sizeof (panel->cwd) - 2);
2710 vfs_release_path (olddir);
2712 subshell_chdir (panel->cwd);
2714 /* Reload current panel */
2715 panel_clean_dir (panel);
2716 panel->count =
2717 do_load_dir (panel->cwd, &panel->dir, panel->sort_info.sort_field->sort_routine,
2718 panel->sort_info.reverse, panel->sort_info.case_sensitive,
2719 panel->sort_info.exec_first, panel->filter);
2720 try_to_select (panel, get_parent_dir_name (panel->cwd, olddir));
2721 load_hint (0);
2722 panel->dirty = 1;
2723 update_xterm_title_path ();
2725 g_free (olddir);
2727 return TRUE;
2730 /* --------------------------------------------------------------------------------------------- */
2732 static void
2733 directory_history_next (WPanel * panel)
2735 GList *nextdir;
2737 nextdir = g_list_next (panel->dir_history);
2739 if ((nextdir != NULL) && (_do_panel_cd (panel, (char *) nextdir->data, cd_exact)))
2740 panel->dir_history = nextdir;
2743 /* --------------------------------------------------------------------------------------------- */
2745 static void
2746 directory_history_prev (WPanel * panel)
2748 GList *prevdir;
2750 prevdir = g_list_previous (panel->dir_history);
2752 if ((prevdir != NULL) && (_do_panel_cd (panel, (char *) prevdir->data, cd_exact)))
2753 panel->dir_history = prevdir;
2756 /* --------------------------------------------------------------------------------------------- */
2758 static void
2759 directory_history_list (WPanel * panel)
2761 char *s;
2763 s = history_show (&panel->dir_history, &panel->widget);
2765 if (s != NULL)
2767 if (_do_panel_cd (panel, s, cd_exact))
2768 directory_history_add (panel, panel->cwd);
2769 else
2770 message (D_ERROR, MSG_ERROR, _("Cannot change directory"));
2771 g_free (s);
2775 /* --------------------------------------------------------------------------------------------- */
2777 static cb_ret_t
2778 panel_execute_cmd (WPanel * panel, unsigned long command)
2780 int res = MSG_HANDLED;
2782 if (command != CK_Search)
2783 stop_search (panel);
2786 switch (command)
2788 case CK_Up:
2789 case CK_Down:
2790 case CK_Left:
2791 case CK_Right:
2792 case CK_Bottom:
2793 case CK_Top:
2794 case CK_PageDown:
2795 case CK_PageUp:
2796 /* reset state of marks flag*/
2797 state_mark = -1;
2798 break;
2800 switch (command)
2802 case CK_PanelOtherCd:
2803 chdir_other_panel (panel);
2804 break;
2805 case CK_PanelOtherCdLink:
2806 chdir_to_readlink (panel);
2807 break;
2808 case CK_CopySingle:
2809 copy_cmd_local ();
2810 break;
2811 case CK_DeleteSingle:
2812 delete_cmd_local ();
2813 break;
2814 case CK_Enter:
2815 do_enter (panel);
2816 break;
2817 case CK_ViewRaw:
2818 view_raw_cmd ();
2819 break;
2820 case CK_EditNew:
2821 edit_cmd_new ();
2822 break;
2823 case CK_MoveSingle:
2824 rename_cmd_local ();
2825 break;
2826 case CK_SelectInvert:
2827 select_invert_cmd ();
2828 break;
2829 case CK_Select:
2830 select_cmd ();
2831 break;
2832 case CK_Unselect:
2833 unselect_cmd ();
2834 break;
2835 case CK_PageDown:
2836 next_page (panel);
2837 break;
2838 case CK_PageUp:
2839 prev_page (panel);
2840 break;
2841 case CK_CdChild:
2842 goto_child_dir (panel);
2843 break;
2844 case CK_CdParent:
2845 goto_parent_dir (panel);
2846 break;
2847 case CK_History:
2848 directory_history_list (panel);
2849 break;
2850 case CK_HistoryNext:
2851 directory_history_next (panel);
2852 break;
2853 case CK_HistoryPrev:
2854 directory_history_prev (panel);
2855 break;
2856 case CK_BottomOnScreen:
2857 goto_bottom_file (panel);
2858 break;
2859 case CK_MiddleOnScreen:
2860 goto_middle_file (panel);
2861 break;
2862 case CK_TopOnScreen:
2863 goto_top_file (panel);
2864 break;
2865 case CK_Mark:
2866 mark_file (panel);
2867 break;
2868 case CK_MarkUp:
2869 mark_file_up (panel);
2870 break;
2871 case CK_MarkDown:
2872 mark_file_down (panel);
2873 break;
2874 case CK_MarkLeft:
2875 mark_file_left (panel);
2876 break;
2877 case CK_MarkRight:
2878 mark_file_right (panel);
2879 break;
2880 case CK_CdParentSmart:
2881 res = force_maybe_cd ();
2882 break;
2883 case CK_Up:
2884 move_up (panel);
2885 break;
2886 case CK_Down:
2887 move_down (panel);
2888 break;
2889 case CK_Left:
2890 res = move_left (panel);
2891 break;
2892 case CK_Right:
2893 res = move_right (panel);
2894 break;
2895 case CK_Bottom:
2896 move_end (panel);
2897 break;
2898 case CK_Top:
2899 move_home (panel);
2900 break;
2901 #ifdef HAVE_CHARSET
2902 case CK_SelectCodepage:
2903 panel_change_encoding (panel);
2904 break;
2905 #endif
2906 case CK_Search:
2907 start_search (panel);
2908 break;
2909 case CK_SearchStop:
2910 break;
2911 case CK_PanelOtherSync:
2912 panel_sync_other (panel);
2913 break;
2914 case CK_Sort:
2915 panel_select_sort_order (panel);
2916 break;
2917 case CK_SortPrev:
2918 panel_toggle_sort_order_prev (panel);
2919 break;
2920 case CK_SortNext:
2921 panel_toggle_sort_order_next (panel);
2922 break;
2923 case CK_SortReverse:
2924 panel->sort_info.reverse = !panel->sort_info.reverse;
2925 panel_set_sort_order (panel, panel->sort_info.sort_field);
2926 break;
2927 case CK_SortByName:
2928 panel_set_sort_type_by_id (panel, "name");
2929 break;
2930 case CK_SortByExt:
2931 panel_set_sort_type_by_id (panel, "extension");
2932 break;
2933 case CK_SortBySize:
2934 panel_set_sort_type_by_id (panel, "size");
2935 break;
2936 case CK_SortByMTime:
2937 panel_set_sort_type_by_id (panel, "mtime");
2938 break;
2940 return res;
2943 /* --------------------------------------------------------------------------------------------- */
2945 static cb_ret_t
2946 panel_key (WPanel * panel, int key)
2948 size_t i;
2950 for (i = 0; panel_map[i].key != 0; i++)
2951 if (key == panel_map[i].key)
2952 return panel_execute_cmd (panel, panel_map[i].command);
2954 if (panels_options.torben_fj_mode && key == ALT ('h'))
2956 goto_middle_file (panel);
2957 return MSG_HANDLED;
2960 if (is_abort_char (key))
2962 stop_search (panel);
2963 return MSG_HANDLED;
2966 /* Do not eat characters not meant for the panel below ' ' (e.g. C-l). */
2967 if ((key >= ' ' && key <= 255) || key == KEY_BACKSPACE)
2969 if (panel->searching)
2971 do_search (panel, key);
2972 return MSG_HANDLED;
2975 if (!command_prompt)
2977 start_search (panel);
2978 do_search (panel, key);
2979 return MSG_HANDLED;
2983 return MSG_NOT_HANDLED;
2986 /* --------------------------------------------------------------------------------------------- */
2988 static cb_ret_t
2989 panel_callback (Widget * w, widget_msg_t msg, int parm)
2991 WPanel *panel = (WPanel *) w;
2992 WButtonBar *bb;
2994 switch (msg)
2996 case WIDGET_INIT:
2997 /* subscribe to "history_load" event */
2998 mc_event_add (w->owner->event_group, MCEVENT_HISTORY_LOAD, panel_load_history, w, NULL);
2999 /* subscribe to "history_save" event */
3000 mc_event_add (w->owner->event_group, MCEVENT_HISTORY_SAVE, panel_save_history, w, NULL);
3001 return MSG_HANDLED;
3003 case WIDGET_DRAW:
3004 /* Repaint everything, including frame and separator */
3005 paint_frame (panel); /* including show_dir */
3006 paint_dir (panel);
3007 mini_info_separator (panel);
3008 display_mini_info (panel);
3009 panel->dirty = 0;
3010 return MSG_HANDLED;
3012 case WIDGET_FOCUS:
3013 state_mark = -1;
3014 current_panel = panel;
3015 panel->active = 1;
3016 if (mc_chdir (panel->cwd) != 0)
3018 char *cwd = strip_password (g_strdup (panel->cwd), 1);
3019 message (D_ERROR, MSG_ERROR, _("Cannot chdir to \"%s\"\n%s"),
3020 cwd, unix_error_string (errno));
3021 g_free (cwd);
3023 else
3024 subshell_chdir (panel->cwd);
3026 update_xterm_title_path ();
3027 select_item (panel);
3028 show_dir (panel);
3029 paint_dir (panel);
3030 panel->dirty = 0;
3032 bb = find_buttonbar (panel->widget.owner);
3033 midnight_set_buttonbar (bb);
3034 buttonbar_redraw (bb);
3035 return MSG_HANDLED;
3037 case WIDGET_UNFOCUS:
3038 /* Janne: look at this for the multiple panel options */
3039 stop_search (panel);
3040 panel->active = 0;
3041 show_dir (panel);
3042 unselect_item (panel);
3043 return MSG_HANDLED;
3045 case WIDGET_KEY:
3046 return panel_key (panel, parm);
3048 case WIDGET_COMMAND:
3049 return panel_execute_cmd (panel, parm);
3051 case WIDGET_DESTROY:
3052 /* unsubscribe from "history_load" event */
3053 mc_event_del (w->owner->event_group, MCEVENT_HISTORY_LOAD, panel_load_history, w);
3054 /* unsubscribe from "history_save" event */
3055 mc_event_del (w->owner->event_group, MCEVENT_HISTORY_SAVE, panel_save_history, w);
3056 panel_destroy (panel);
3057 free_my_statfs ();
3058 return MSG_HANDLED;
3060 default:
3061 return default_proc (msg, parm);
3065 /* --------------------------------------------------------------------------------------------- */
3066 /* */
3067 /* Panel mouse events support routines */
3068 /* */
3070 static void
3071 mouse_toggle_mark (WPanel * panel)
3073 do_mark_file (panel, MARK_DONT_MOVE);
3074 mouse_marking = selection (panel)->f.marked;
3075 mouse_mark_panel = current_panel;
3078 /* --------------------------------------------------------------------------------------------- */
3080 static void
3081 mouse_set_mark (WPanel * panel)
3084 if (mouse_mark_panel == panel)
3086 if (mouse_marking && !(selection (panel)->f.marked))
3087 do_mark_file (panel, MARK_DONT_MOVE);
3088 else if (!mouse_marking && (selection (panel)->f.marked))
3089 do_mark_file (panel, MARK_DONT_MOVE);
3093 /* --------------------------------------------------------------------------------------------- */
3095 static int
3096 mark_if_marking (WPanel * panel, Gpm_Event * event)
3098 if (event->buttons & GPM_B_RIGHT)
3100 if (event->type & GPM_DOWN)
3101 mouse_toggle_mark (panel);
3102 else
3103 mouse_set_mark (panel);
3104 return 1;
3106 return 0;
3109 /* --------------------------------------------------------------------------------------------- */
3110 /** Determine which column was clicked, and sort the panel on
3111 * that column, or reverse sort on that column if already
3112 * sorted on that column.
3115 static void
3116 mouse_sort_col (Gpm_Event * event, WPanel * panel)
3118 int i;
3119 const char *lc_sort_name = NULL;
3120 panel_field_t *col_sort_format = NULL;
3121 format_e *format;
3122 gchar *title;
3124 for (i = 0, format = panel->format; format != NULL; format = format->next)
3126 i += format->field_len;
3127 if (event->x < i + 1)
3129 /* found column */
3130 lc_sort_name = format->title;
3131 break;
3135 if (lc_sort_name == NULL)
3136 return;
3138 for (i = 0; panel_fields[i].id != NULL; i++)
3140 title = panel_get_title_without_hotkey (panel_fields[i].title_hotkey);
3141 if (!strcmp (lc_sort_name, title) && panel_fields[i].sort_routine)
3143 col_sort_format = &panel_fields[i];
3144 g_free (title);
3145 break;
3147 g_free (title);
3150 if (col_sort_format == NULL)
3151 return;
3153 if (panel->sort_info.sort_field == col_sort_format)
3155 /* reverse the sort if clicked column is already the sorted column */
3156 panel->sort_info.reverse = !panel->sort_info.reverse;
3158 else
3160 /* new sort is forced to be ascending */
3161 panel->sort_info.reverse = FALSE;
3163 panel_set_sort_order (panel, col_sort_format);
3167 /* --------------------------------------------------------------------------------------------- */
3169 * Mouse callback of the panel minus repainting.
3170 * If the event is redirected to the menu, *redir is set to TRUE.
3172 static int
3173 do_panel_event (Gpm_Event * event, WPanel * panel, gboolean * redir)
3175 const int lines = llines (panel);
3176 const gboolean is_active = dlg_widget_active (panel);
3177 const gboolean mouse_down = (event->type & GPM_DOWN) != 0;
3179 *redir = FALSE;
3181 /* 1st line */
3182 if (mouse_down && event->y == 1)
3184 /* "<" button */
3185 if (event->x == 2)
3187 directory_history_prev (panel);
3188 return MOU_NORMAL;
3191 /* "." button show/hide hidden files */
3192 if (event->x == panel->widget.cols - 5)
3194 panel->widget.owner->callback (panel->widget.owner, NULL,
3195 DLG_ACTION, CK_ShowHidden, NULL);
3196 repaint_screen ();
3197 return MOU_NORMAL;
3200 /* ">" button */
3201 if (event->x == panel->widget.cols - 1)
3203 directory_history_next (panel);
3204 return MOU_NORMAL;
3207 /* "^" button */
3208 if (event->x >= panel->widget.cols - 4 && event->x <= panel->widget.cols - 2)
3210 directory_history_list (panel);
3211 return MOU_NORMAL;
3214 /* rest of the upper frame, the menu is invisible - call menu */
3215 if (!menubar_visible)
3217 *redir = TRUE;
3218 event->x += panel->widget.x;
3219 return the_menubar->widget.mouse (event, the_menubar);
3222 /* no other events on 1st line */
3223 return MOU_NORMAL;
3226 /* sort on clicked column; don't handle wheel events */
3227 if (mouse_down && (event->buttons & (GPM_B_UP | GPM_B_DOWN)) == 0 && event->y == 2)
3229 mouse_sort_col (event, panel);
3230 return MOU_NORMAL;
3233 /* Mouse wheel events */
3234 if (mouse_down && (event->buttons & GPM_B_UP))
3236 if (is_active)
3238 if (panels_options.mouse_move_pages && (panel->top_file > 0))
3239 prev_page (panel);
3240 else /* We are in first page */
3241 move_up (panel);
3243 return MOU_NORMAL;
3246 if (mouse_down && (event->buttons & GPM_B_DOWN))
3248 if (is_active)
3250 if (panels_options.mouse_move_pages && (panel->top_file + ITEMS (panel) < panel->count))
3251 next_page (panel);
3252 else /* We are in last page */
3253 move_down (panel);
3255 return MOU_NORMAL;
3258 event->y -= 2;
3259 if ((event->type & (GPM_DOWN | GPM_DRAG)))
3261 int my_index;
3263 if (!is_active)
3264 change_panel ();
3266 if (panel->top_file + event->y > panel->count)
3267 my_index = panel->count - 1;
3268 else
3270 my_index = panel->top_file + event->y - 1;
3271 if (panel->split && (event->x > ((panel->widget.cols - 2) / 2)))
3272 my_index += llines (panel);
3274 if (my_index >= panel->count)
3275 my_index = panel->count - 1;
3278 if (my_index != panel->selected)
3280 unselect_item (panel);
3281 panel->selected = my_index;
3282 select_item (panel);
3285 /* This one is new */
3286 mark_if_marking (panel, event);
3288 else if ((event->type & (GPM_UP | GPM_DOUBLE)) == (GPM_UP | GPM_DOUBLE))
3290 if (event->y > 0 && event->y <= lines)
3291 do_enter (panel);
3293 return MOU_NORMAL;
3296 /* --------------------------------------------------------------------------------------------- */
3297 /** Mouse callback of the panel */
3299 static int
3300 panel_event (Gpm_Event * event, void *data)
3302 WPanel *panel = data;
3303 int ret;
3304 gboolean redir;
3306 ret = do_panel_event (event, panel, &redir);
3307 if (!redir)
3308 send_message ((Widget *) panel, WIDGET_DRAW, 0);
3310 return ret;
3313 /* --------------------------------------------------------------------------------------------- */
3315 static void
3316 reload_panelized (WPanel * panel)
3318 int i, j;
3319 dir_list *list = &panel->dir;
3321 if (panel != current_panel)
3323 int ret;
3324 ret = mc_chdir (panel->cwd);
3327 for (i = 0, j = 0; i < panel->count; i++)
3329 if (list->list[i].f.marked)
3331 /* Unmark the file in advance. In case the following mc_lstat
3332 * fails we are done, else we have to mark the file again
3333 * (Note: do_file_mark depends on a valid "list->list [i].buf").
3334 * IMO that's the best way to update the panel's summary status
3335 * -- Norbert
3337 do_file_mark (panel, i, 0);
3339 if (mc_lstat (list->list[i].fname, &list->list[i].st))
3341 g_free (list->list[i].fname);
3342 continue;
3344 if (list->list[i].f.marked)
3345 do_file_mark (panel, i, 1);
3346 if (j != i)
3347 list->list[j] = list->list[i];
3348 j++;
3350 if (j == 0)
3351 panel->count = set_zero_dir (list) ? 1 : 0;
3352 else
3353 panel->count = j;
3355 if (panel != current_panel)
3357 int ret;
3358 ret = mc_chdir (current_panel->cwd);
3362 /* --------------------------------------------------------------------------------------------- */
3364 static void
3365 update_one_panel_widget (WPanel * panel, panel_update_flags_t flags, const char *current_file)
3367 gboolean free_pointer;
3368 char *my_current_file = NULL;
3370 if ((flags & UP_RELOAD) != 0)
3372 panel->is_panelized = 0;
3373 mc_setctl (panel->cwd, VFS_SETCTL_FLUSH, 0);
3374 memset (&(panel->dir_stat), 0, sizeof (panel->dir_stat));
3377 /* If current_file == -1 (an invalid pointer) then preserve selection */
3378 free_pointer = current_file == UP_KEEPSEL;
3380 if (free_pointer)
3382 my_current_file = g_strdup (panel->dir.list[panel->selected].fname);
3383 current_file = my_current_file;
3386 if (panel->is_panelized)
3387 reload_panelized (panel);
3388 else
3389 panel_reload (panel);
3391 try_to_select (panel, current_file);
3392 panel->dirty = 1;
3394 if (free_pointer)
3395 g_free (my_current_file);
3398 /* --------------------------------------------------------------------------------------------- */
3400 static void
3401 update_one_panel (int which, panel_update_flags_t flags, const char *current_file)
3403 if (get_display_type (which) == view_listing)
3405 WPanel *panel;
3406 panel = (WPanel *) get_panel_widget (which);
3407 update_one_panel_widget (panel, flags, current_file);
3411 /* --------------------------------------------------------------------------------------------- */
3412 /*** public functions ****************************************************************************/
3413 /* --------------------------------------------------------------------------------------------- */
3415 char *
3416 remove_encoding_from_path (const char *path)
3418 GString *ret;
3419 GString *tmp_path, *tmp_conv;
3420 char *tmp;
3422 ret = g_string_new ("");
3423 tmp_conv = g_string_new ("");
3424 tmp_path = g_string_new (path);
3426 while ((tmp = g_strrstr (tmp_path->str, PATH_SEP_STR VFS_ENCODING_PREFIX)) != NULL)
3428 GIConv converter;
3429 char *tmp2;
3431 vfs_path_t *vpath = vfs_path_from_str (tmp);
3432 vfs_path_element_t *path_element = vfs_path_get_by_index (vpath, -1);
3434 converter =
3435 path_element->encoding !=
3436 NULL ? str_crt_conv_to (path_element->encoding) : str_cnv_to_term;
3437 vfs_path_free (vpath);
3439 if (converter == INVALID_CONV)
3440 converter = str_cnv_to_term;
3442 tmp2 = tmp + 1;
3443 while (*tmp2 != '\0' && *tmp2 != PATH_SEP)
3444 tmp2++;
3446 if (*tmp2 != '\0')
3448 str_vfs_convert_from (converter, tmp2, tmp_conv);
3449 g_string_prepend (ret, tmp_conv->str);
3450 g_string_set_size (tmp_conv, 0);
3453 g_string_set_size (tmp_path, tmp - tmp_path->str);
3454 str_close_conv (converter);
3457 g_string_prepend (ret, tmp_path->str);
3458 g_string_free (tmp_path, TRUE);
3459 g_string_free (tmp_conv, TRUE);
3461 return g_string_free (ret, FALSE);
3464 /* --------------------------------------------------------------------------------------------- */
3466 static void
3467 do_select (WPanel * panel, int i)
3469 if (i != panel->selected)
3471 panel->dirty = 1;
3472 panel->selected = i;
3473 panel->top_file = panel->selected - (panel->widget.lines - 2) / 2;
3474 if (panel->top_file < 0)
3475 panel->top_file = 0;
3479 /* --------------------------------------------------------------------------------------------- */
3481 static void
3482 do_try_to_select (WPanel * panel, const char *name)
3484 int i;
3485 char *subdir;
3487 if (!name)
3489 do_select (panel, 0);
3490 return;
3493 /* We only want the last component of the directory,
3494 * and from this only the name without suffix. */
3495 subdir = vfs_strip_suffix_from_filename (x_basename (name));
3497 /* Search that subdirectory, if found select it */
3498 for (i = 0; i < panel->count; i++)
3500 if (strcmp (subdir, panel->dir.list[i].fname) == 0)
3502 do_select (panel, i);
3503 g_free (subdir);
3504 return;
3508 /* Try to select a file near the file that is missing */
3509 if (panel->selected >= panel->count)
3510 do_select (panel, panel->count - 1);
3511 g_free (subdir);
3514 /* --------------------------------------------------------------------------------------------- */
3516 /* event callback */
3517 static gboolean
3518 event_update_panels (const gchar * event_group_name, const gchar * event_name,
3519 gpointer init_data, gpointer data)
3521 (void) event_group_name;
3522 (void) event_name;
3523 (void) init_data;
3524 (void) data;
3526 update_panels (UP_RELOAD, UP_KEEPSEL);
3528 return TRUE;
3531 /* --------------------------------------------------------------------------------------------- */
3533 /* event callback */
3534 static gboolean
3535 panel_save_curent_file_to_clip_file (const gchar * event_group_name, const gchar * event_name,
3536 gpointer init_data, gpointer data)
3538 (void) event_group_name;
3539 (void) event_name;
3540 (void) init_data;
3541 (void) data;
3543 if (current_panel->marked == 0)
3544 mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_text_to_file",
3545 (gpointer) selection (current_panel)->fname);
3546 else
3548 int i;
3549 gboolean first = TRUE;
3550 char *flist = NULL;
3552 for (i = 0; i < current_panel->count; i++)
3553 if (current_panel->dir.list[i].f.marked != 0)
3554 { /* Skip the unmarked ones */
3555 if (first)
3557 flist = g_strdup (current_panel->dir.list[i].fname);
3558 first = FALSE;
3560 else
3562 /* Add empty lines after the file */
3563 char *tmp;
3565 tmp =
3566 g_strconcat (flist, "\n", current_panel->dir.list[i].fname, (char *) NULL);
3567 g_free (flist);
3568 flist = tmp;
3572 mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_text_to_file", (gpointer) flist);
3573 g_free (flist);
3575 return TRUE;
3578 /* --------------------------------------------------------------------------------------------- */
3579 /*** public functions ****************************************************************************/
3580 /* --------------------------------------------------------------------------------------------- */
3582 void
3583 try_to_select (WPanel * panel, const char *name)
3585 do_try_to_select (panel, name);
3586 select_item (panel);
3589 /* --------------------------------------------------------------------------------------------- */
3591 void
3592 panel_clean_dir (WPanel * panel)
3594 int count = panel->count;
3596 panel->count = 0;
3597 panel->top_file = 0;
3598 panel->selected = 0;
3599 panel->marked = 0;
3600 panel->dirs_marked = 0;
3601 panel->total = 0;
3602 panel->searching = FALSE;
3603 panel->is_panelized = 0;
3604 panel->dirty = 1;
3606 clean_dir (&panel->dir, count);
3609 /* --------------------------------------------------------------------------------------------- */
3610 /** Panel creation.
3611 * @param panel_name the name of the panel for setup retieving
3612 * @returns new instance of WPanel
3615 WPanel *
3616 panel_new (const char *panel_name)
3618 return panel_new_with_dir (panel_name, NULL);
3621 /* --------------------------------------------------------------------------------------------- */
3622 /** Panel creation for specified directory.
3623 * @param panel_name specifies the name of the panel for setup retieving
3624 * @param the path of working panel directory. If path is NULL then panel will be created for current directory
3625 * @returns new instance of WPanel
3628 WPanel *
3629 panel_new_with_dir (const char *panel_name, const char *wpath)
3631 WPanel *panel;
3632 char *section;
3633 int i, err;
3634 char curdir[MC_MAXPATHLEN] = "\0";
3636 panel = g_new0 (WPanel, 1);
3638 /* No know sizes of the panel at startup */
3639 init_widget (&panel->widget, 0, 0, 0, 0, panel_callback, panel_event);
3641 /* We do not want the cursor */
3642 widget_want_cursor (panel->widget, 0);
3644 if (wpath != NULL)
3646 g_strlcpy (panel->cwd, wpath, sizeof (panel->cwd));
3647 mc_get_current_wd (curdir, sizeof (curdir) - 2);
3649 else
3650 mc_get_current_wd (panel->cwd, sizeof (panel->cwd) - 2);
3652 strcpy (panel->lwd, ".");
3654 panel->hist_name = g_strconcat ("Dir Hist ", panel_name, (char *) NULL);
3655 /* directories history will be get later */
3657 panel->dir.list = g_new (file_entry, MIN_FILES);
3658 panel->dir.size = MIN_FILES;
3659 panel->active = 0;
3660 panel->filter = 0;
3661 panel->split = 0;
3662 panel->top_file = 0;
3663 panel->selected = 0;
3664 panel->marked = 0;
3665 panel->total = 0;
3666 panel->dirty = 1;
3667 panel->searching = FALSE;
3668 panel->dirs_marked = 0;
3669 panel->is_panelized = 0;
3670 panel->format = 0;
3671 panel->status_format = 0;
3672 panel->format_modified = 1;
3674 panel->panel_name = g_strdup (panel_name);
3675 panel->user_format = g_strdup (DEFAULT_USER_FORMAT);
3677 panel->codepage = SELECT_CHARSET_NO_TRANSLATE;
3679 for (i = 0; i < LIST_TYPES; i++)
3680 panel->user_status_format[i] = g_strdup (DEFAULT_USER_FORMAT);
3682 panel->search_buffer[0] = '\0';
3683 panel->prev_search_buffer[0] = '\0';
3684 panel->frame_size = frame_half;
3686 section = g_strconcat ("Temporal:", panel->panel_name, (char *) NULL);
3687 if (!mc_config_has_group (mc_main_config, section))
3689 g_free (section);
3690 section = g_strdup (panel->panel_name);
3692 panel_load_setup (panel, section);
3693 g_free (section);
3695 /* Load format strings */
3696 err = set_panel_formats (panel);
3697 if (err != 0)
3698 set_panel_formats (panel);
3700 #ifdef HAVE_CHARSET
3702 vfs_path_t *vpath = vfs_path_from_str (panel->cwd);
3703 vfs_path_element_t *path_element = vfs_path_get_by_index (vpath, -1);
3705 if (path_element->encoding != NULL)
3706 panel->codepage = get_codepage_index (path_element->encoding);
3708 vfs_path_free (vpath);
3710 #endif
3712 if (mc_chdir (panel->cwd) != 0)
3714 panel->codepage = SELECT_CHARSET_NO_TRANSLATE;
3715 mc_get_current_wd (panel->cwd, sizeof (panel->cwd) - 2);
3718 /* Load the default format */
3719 panel->count =
3720 do_load_dir (panel->cwd, &panel->dir, panel->sort_info.sort_field->sort_routine,
3721 panel->sort_info.reverse, panel->sort_info.case_sensitive,
3722 panel->sort_info.exec_first, panel->filter);
3724 /* Restore old right path */
3725 if (curdir[0] != '\0')
3726 err = mc_chdir (curdir);
3728 return panel;
3731 /* --------------------------------------------------------------------------------------------- */
3733 void
3734 panel_reload (WPanel * panel)
3736 struct stat current_stat;
3738 if (panels_options.fast_reload && !stat (panel->cwd, &current_stat)
3739 && current_stat.st_ctime == panel->dir_stat.st_ctime
3740 && current_stat.st_mtime == panel->dir_stat.st_mtime)
3741 return;
3743 while (mc_chdir (panel->cwd) == -1)
3745 char *last_slash;
3747 if (panel->cwd[0] == PATH_SEP && panel->cwd[1] == 0)
3749 panel_clean_dir (panel);
3750 panel->count = set_zero_dir (&panel->dir) ? 1 : 0;
3751 return;
3753 last_slash = strrchr (panel->cwd, PATH_SEP);
3754 if (!last_slash || last_slash == panel->cwd)
3755 strcpy (panel->cwd, PATH_SEP_STR);
3756 else
3757 *last_slash = 0;
3758 memset (&(panel->dir_stat), 0, sizeof (panel->dir_stat));
3759 show_dir (panel);
3762 panel->count =
3763 do_reload_dir (panel->cwd, &panel->dir, panel->sort_info.sort_field->sort_routine,
3764 panel->count, panel->sort_info.reverse, panel->sort_info.case_sensitive,
3765 panel->sort_info.exec_first, panel->filter);
3767 panel->dirty = 1;
3768 if (panel->selected >= panel->count)
3769 do_select (panel, panel->count - 1);
3771 recalculate_panel_summary (panel);
3774 /* --------------------------------------------------------------------------------------------- */
3775 /* Switches the panel to the mode specified in the format */
3776 /* Seting up both format and status string. Return: 0 - on success; */
3777 /* 1 - format error; 2 - status error; 3 - errors in both formats. */
3780 set_panel_formats (WPanel * p)
3782 format_e *form;
3783 char *err = NULL;
3784 int retcode = 0;
3786 form = use_display_format (p, panel_format (p), &err, 0);
3788 if (err != NULL)
3790 g_free (err);
3791 retcode = 1;
3793 else
3795 delete_format (p->format);
3796 p->format = form;
3799 if (panels_options.show_mini_info)
3801 form = use_display_format (p, mini_status_format (p), &err, 1);
3803 if (err != NULL)
3805 g_free (err);
3806 retcode += 2;
3808 else
3810 delete_format (p->status_format);
3811 p->status_format = form;
3815 panel_format_modified (p);
3816 panel_update_cols (&(p->widget), p->frame_size);
3818 if (retcode)
3819 message (D_ERROR, _("Warning"),
3820 _("User supplied format looks invalid, reverting to default."));
3821 if (retcode & 0x01)
3823 g_free (p->user_format);
3824 p->user_format = g_strdup (DEFAULT_USER_FORMAT);
3826 if (retcode & 0x02)
3828 g_free (p->user_status_format[p->list_type]);
3829 p->user_status_format[p->list_type] = g_strdup (DEFAULT_USER_FORMAT);
3832 return retcode;
3835 /* --------------------------------------------------------------------------------------------- */
3837 /* Select current item and readjust the panel */
3838 void
3839 select_item (WPanel * panel)
3841 int items = ITEMS (panel);
3843 /* Although currently all over the code we set the selection and
3844 top file to decent values before calling select_item, I could
3845 forget it someday, so it's better to do the actual fitting here */
3847 if (panel->top_file < 0)
3848 panel->top_file = 0;
3850 if (panel->selected < 0)
3851 panel->selected = 0;
3853 if (panel->selected > panel->count - 1)
3854 panel->selected = panel->count - 1;
3856 if (panel->top_file > panel->count - 1)
3857 panel->top_file = panel->count - 1;
3859 if ((panel->count - panel->top_file) < items)
3861 panel->top_file = panel->count - items;
3862 if (panel->top_file < 0)
3863 panel->top_file = 0;
3866 if (panel->selected < panel->top_file)
3867 panel->top_file = panel->selected;
3869 if ((panel->selected - panel->top_file) >= items)
3870 panel->top_file = panel->selected - items + 1;
3872 panel->dirty = 1;
3874 execute_hooks (select_file_hook);
3877 /* --------------------------------------------------------------------------------------------- */
3878 /** Clears all files in the panel, used only when one file was marked */
3879 void
3880 unmark_files (WPanel * panel)
3882 int i;
3884 if (!panel->marked)
3885 return;
3886 for (i = 0; i < panel->count; i++)
3887 file_mark (panel, i, 0);
3889 panel->dirs_marked = 0;
3890 panel->marked = 0;
3891 panel->total = 0;
3894 /* --------------------------------------------------------------------------------------------- */
3895 /** Recalculate the panels summary information, used e.g. when marked
3896 files might have been removed by an external command */
3898 void
3899 recalculate_panel_summary (WPanel * panel)
3901 int i;
3903 panel->marked = 0;
3904 panel->dirs_marked = 0;
3905 panel->total = 0;
3907 for (i = 0; i < panel->count; i++)
3908 if (panel->dir.list[i].f.marked)
3910 /* do_file_mark will return immediately if newmark == oldmark.
3911 So we have to first unmark it to get panel's summary information
3912 updated. (Norbert) */
3913 panel->dir.list[i].f.marked = 0;
3914 do_file_mark (panel, i, 1);
3918 /* --------------------------------------------------------------------------------------------- */
3919 /** This routine marks a file or a directory */
3921 void
3922 do_file_mark (WPanel * panel, int idx, int mark)
3924 if (panel->dir.list[idx].f.marked == mark)
3925 return;
3927 /* Only '..' can't be marked, '.' isn't visible */
3928 if (strcmp (panel->dir.list[idx].fname, "..") == 0)
3929 return;
3931 file_mark (panel, idx, mark);
3932 if (panel->dir.list[idx].f.marked)
3934 panel->marked++;
3935 if (S_ISDIR (panel->dir.list[idx].st.st_mode))
3937 if (panel->dir.list[idx].f.dir_size_computed)
3938 panel->total += (uintmax_t) panel->dir.list[idx].st.st_size;
3939 panel->dirs_marked++;
3941 else
3942 panel->total += (uintmax_t) panel->dir.list[idx].st.st_size;
3943 set_colors (panel);
3945 else
3947 if (S_ISDIR (panel->dir.list[idx].st.st_mode))
3949 if (panel->dir.list[idx].f.dir_size_computed)
3950 panel->total -= (uintmax_t) panel->dir.list[idx].st.st_size;
3951 panel->dirs_marked--;
3953 else
3954 panel->total -= (uintmax_t) panel->dir.list[idx].st.st_size;
3955 panel->marked--;
3959 /* --------------------------------------------------------------------------------------------- */
3961 * Changes the current directory of the panel.
3962 * Record change in the directory history.
3964 gboolean
3965 do_panel_cd (struct WPanel *panel, const char *new_dir, enum cd_enum cd_type)
3967 gboolean r;
3969 r = _do_panel_cd (panel, new_dir, cd_type);
3970 if (r)
3971 directory_history_add (panel, panel->cwd);
3972 return r;
3975 /* --------------------------------------------------------------------------------------------- */
3977 void
3978 file_mark (WPanel * panel, int lc_index, int val)
3980 if (panel->dir.list[lc_index].f.marked != val)
3982 panel->dir.list[lc_index].f.marked = val;
3983 panel->dirty = 1;
3987 /* --------------------------------------------------------------------------------------------- */
3989 void
3990 panel_re_sort (WPanel * panel)
3992 char *filename;
3993 int i;
3995 if (panel == NULL)
3996 return;
3998 filename = g_strdup (selection (panel)->fname);
3999 unselect_item (panel);
4000 do_sort (&panel->dir, panel->sort_info.sort_field->sort_routine, panel->count - 1,
4001 panel->sort_info.reverse, panel->sort_info.case_sensitive,
4002 panel->sort_info.exec_first);
4003 panel->selected = -1;
4004 for (i = panel->count; i; i--)
4006 if (!strcmp (panel->dir.list[i - 1].fname, filename))
4008 panel->selected = i - 1;
4009 break;
4012 g_free (filename);
4013 panel->top_file = panel->selected - ITEMS (panel) / 2;
4014 select_item (panel);
4015 panel->dirty = 1;
4018 /* --------------------------------------------------------------------------------------------- */
4020 void
4021 panel_set_sort_order (WPanel * panel, const panel_field_t * sort_order)
4023 if (sort_order == NULL)
4024 return;
4026 panel->sort_info.sort_field = sort_order;
4028 /* The directory is already sorted, we have to load the unsorted stuff */
4029 if (sort_order->sort_routine == (sortfn *) unsorted)
4031 char *current_file;
4033 current_file = g_strdup (panel->dir.list[panel->selected].fname);
4034 panel_reload (panel);
4035 try_to_select (panel, current_file);
4036 g_free (current_file);
4038 panel_re_sort (panel);
4041 /* --------------------------------------------------------------------------------------------- */
4043 * Change panel encoding.
4044 * @param panel WPanel object
4047 void
4048 panel_change_encoding (WPanel * panel)
4050 const char *encoding = NULL;
4051 char *cd_path;
4052 #ifdef HAVE_CHARSET
4053 char *errmsg;
4054 int r;
4056 r = select_charset (-1, -1, panel->codepage, FALSE);
4058 if (r == SELECT_CHARSET_CANCEL)
4059 return; /* Cancel */
4061 panel->codepage = r;
4063 if (panel->codepage == SELECT_CHARSET_NO_TRANSLATE)
4065 /* No translation */
4066 g_free (init_translation_table (mc_global.display_codepage, mc_global.display_codepage));
4067 cd_path = remove_encoding_from_path (panel->cwd);
4068 do_panel_cd (panel, cd_path, cd_parse_command);
4069 g_free (cd_path);
4070 return;
4073 errmsg = init_translation_table (panel->codepage, mc_global.display_codepage);
4074 if (errmsg != NULL)
4076 message (D_ERROR, MSG_ERROR, "%s", errmsg);
4077 g_free (errmsg);
4078 return;
4081 encoding = get_codepage_id (panel->codepage);
4082 #endif
4083 if (encoding != NULL)
4085 vfs_path_t *vpath = vfs_path_from_str (panel->cwd);
4087 vfs_change_encoding (vpath, encoding);
4089 cd_path = vfs_path_to_str (vpath);
4090 if (!do_panel_cd (panel, cd_path, cd_parse_command))
4091 message (D_ERROR, MSG_ERROR, _("Cannot chdir to \"%s\""), cd_path);
4092 g_free (cd_path);
4094 vfs_path_free (vpath);
4098 /* --------------------------------------------------------------------------------------------- */
4100 * This routine reloads the directory in both panels. It tries to
4101 * select current_file in current_panel and other_file in other_panel.
4102 * If current_file == -1 then it automatically sets current_file and
4103 * other_file to the currently selected files in the panels.
4105 * if force_update has the UP_ONLY_CURRENT bit toggled on, then it
4106 * will not reload the other panel.
4109 void
4110 update_panels (panel_update_flags_t flags, const char *current_file)
4112 gboolean reload_other = (flags & UP_ONLY_CURRENT) == 0;
4113 WPanel *panel;
4114 int ret;
4116 update_one_panel (get_current_index (), flags, current_file);
4117 if (reload_other)
4118 update_one_panel (get_other_index (), flags, UP_KEEPSEL);
4120 if (get_current_type () == view_listing)
4121 panel = (WPanel *) get_panel_widget (get_current_index ());
4122 else
4123 panel = (WPanel *) get_panel_widget (get_other_index ());
4125 ret = mc_chdir (panel->cwd);
4128 /* --------------------------------------------------------------------------------------------- */
4130 void
4131 directory_history_add (struct WPanel *panel, const char *dir)
4133 char *tmp;
4135 tmp = g_strdup (dir);
4136 strip_password (tmp, 1);
4138 panel->dir_history = list_append_unique (panel->dir_history, tmp);
4141 /* --------------------------------------------------------------------------------------------- */
4143 gsize
4144 panel_get_num_of_sortable_fields (void)
4146 gsize ret = 0, lc_index;
4148 for (lc_index = 0; panel_fields[lc_index].id != NULL; lc_index++)
4149 if (panel_fields[lc_index].is_user_choice)
4150 ret++;
4151 return ret;
4154 /* --------------------------------------------------------------------------------------------- */
4156 const char **
4157 panel_get_sortable_fields (gsize * array_size)
4159 char **ret;
4160 gsize lc_index, i;
4162 lc_index = panel_get_num_of_sortable_fields ();
4164 ret = g_try_new0 (char *, lc_index + 1);
4165 if (ret == NULL)
4166 return NULL;
4168 if (array_size != NULL)
4169 *array_size = lc_index;
4171 lc_index = 0;
4173 for (i = 0; panel_fields[i].id != NULL; i++)
4174 if (panel_fields[i].is_user_choice)
4175 ret[lc_index++] = g_strdup (_(panel_fields[i].title_hotkey));
4176 return (const char **) ret;
4179 /* --------------------------------------------------------------------------------------------- */
4181 const panel_field_t *
4182 panel_get_field_by_id (const char *name)
4184 gsize lc_index;
4185 for (lc_index = 0; panel_fields[lc_index].id != NULL; lc_index++)
4186 if (panel_fields[lc_index].id != NULL && strcmp (name, panel_fields[lc_index].id) == 0)
4187 return &panel_fields[lc_index];
4188 return NULL;
4191 /* --------------------------------------------------------------------------------------------- */
4193 const panel_field_t *
4194 panel_get_field_by_title_hotkey (const char *name)
4196 gsize lc_index;
4197 for (lc_index = 0; panel_fields[lc_index].id != NULL; lc_index++)
4198 if (panel_fields[lc_index].title_hotkey != NULL &&
4199 strcmp (name, _(panel_fields[lc_index].title_hotkey)) == 0)
4200 return &panel_fields[lc_index];
4201 return NULL;
4204 /* --------------------------------------------------------------------------------------------- */
4206 const panel_field_t *
4207 panel_get_field_by_title (const char *name)
4209 gsize lc_index;
4210 gchar *title = NULL;
4212 for (lc_index = 0; panel_fields[lc_index].id != NULL; lc_index++)
4214 title = panel_get_title_without_hotkey (panel_fields[lc_index].title_hotkey);
4215 if (panel_fields[lc_index].title_hotkey != NULL && strcmp (name, title) == 0)
4217 g_free (title);
4218 return &panel_fields[lc_index];
4221 g_free (title);
4222 return NULL;
4225 /* --------------------------------------------------------------------------------------------- */
4227 gsize
4228 panel_get_num_of_user_possible_fields (void)
4230 gsize ret = 0, lc_index;
4232 for (lc_index = 0; panel_fields[lc_index].id != NULL; lc_index++)
4233 if (panel_fields[lc_index].use_in_user_format)
4234 ret++;
4235 return ret;
4238 /* --------------------------------------------------------------------------------------------- */
4240 const char **
4241 panel_get_user_possible_fields (gsize * array_size)
4243 char **ret;
4244 gsize lc_index, i;
4246 lc_index = panel_get_num_of_user_possible_fields ();
4248 ret = g_try_new0 (char *, lc_index + 1);
4249 if (ret == NULL)
4250 return NULL;
4252 if (array_size != NULL)
4253 *array_size = lc_index;
4255 lc_index = 0;
4257 for (i = 0; panel_fields[i].id != NULL; i++)
4258 if (panel_fields[i].use_in_user_format)
4259 ret[lc_index++] = g_strdup (_(panel_fields[i].title_hotkey));
4260 return (const char **) ret;
4263 /* --------------------------------------------------------------------------------------------- */
4265 void
4266 panel_init (void)
4268 panel_sort_up_sign = mc_skin_get ("widget-common", "sort-sign-up", "'");
4269 panel_sort_down_sign = mc_skin_get ("widget-common", "sort-sign-down", ",");
4271 panel_hiddenfiles_sign_show = mc_skin_get ("widget-panel", "hiddenfiles-sign-show", ".");
4272 panel_hiddenfiles_sign_hide = mc_skin_get ("widget-panel", "hiddenfiles-sign-hide", ".");
4273 panel_history_prev_item_sign = mc_skin_get ("widget-panel", "history-prev-item-sign", "<");
4274 panel_history_next_item_sign = mc_skin_get ("widget-panel", "history-next-item-sign", ">");
4275 panel_history_show_list_sign = mc_skin_get ("widget-panel", "history-show-list-sign", "^");
4277 mc_event_add (MCEVENT_GROUP_FILEMANAGER, "update_panels", event_update_panels, NULL, NULL);
4278 mc_event_add (MCEVENT_GROUP_FILEMANAGER, "panel_save_curent_file_to_clip_file",
4279 panel_save_curent_file_to_clip_file, NULL, NULL);
4283 /* --------------------------------------------------------------------------------------------- */
4285 void
4286 panel_deinit (void)
4288 g_free (panel_sort_up_sign);
4289 g_free (panel_sort_down_sign);
4291 g_free (panel_hiddenfiles_sign_show);
4292 g_free (panel_hiddenfiles_sign_hide);
4293 g_free (panel_history_prev_item_sign);
4294 g_free (panel_history_next_item_sign);
4295 g_free (panel_history_show_list_sign);
4299 /* --------------------------------------------------------------------------------------------- */