Split file src/keybind.[ch] to lib/keybind.[ch] and src/keybind-defaults.[ch].
[midnight-commander.git] / src / screen.c
blob1be59b314d618e854b62589b476efada78da48e1
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 screen.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/mc-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
52 #include "dir.h"
53 #include "panel.h"
54 #include "boxes.h"
55 #include "tree.h"
56 #include "ext.h" /* regexp_command */
57 #include "layout.h" /* Most layout variables are here */
58 #include "cmd.h"
59 #include "command.h" /* cmdline */
60 #include "setup.h" /* For loading/saving panel options */
61 #include "user.h"
62 #include "execute.h"
63 #include "main-widgets.h"
64 #include "main.h"
65 #include "mountlist.h" /* my_statfs */
66 #include "selcodepage.h" /* select_charset (), SELECT_CHARSET_NO_TRANSLATE */
67 #include "keybind-defaults.h" /* global_keymap_t */
69 /*** global variables ****************************************************************************/
71 /* If true, show the mini-info on the panel */
72 int show_mini_info = 1;
74 /* If true, use some usability hacks by Torben */
75 int torben_fj_mode = 0;
77 /* The hook list for the select file function */
78 hook_t *select_file_hook = NULL;
82 static const char *string_file_name (file_entry *, int);
83 static const char *string_file_size (file_entry *, int);
84 static const char *string_file_size_brief (file_entry *, int);
85 static const char *string_file_type (file_entry *, int);
86 static const char *string_file_mtime (file_entry *, int);
87 static const char *string_file_atime (file_entry *, int);
88 static const char *string_file_ctime (file_entry *, int);
89 static const char *string_file_permission (file_entry *, int);
90 static const char *string_file_perm_octal (file_entry *, int);
91 static const char *string_file_nlinks (file_entry *, int);
92 static const char *string_inode (file_entry *, int);
93 static const char *string_file_nuid (file_entry *, int);
94 static const char *string_file_ngid (file_entry *, int);
95 static const char *string_file_owner (file_entry *, int);
96 static const char *string_file_group (file_entry *, int);
97 static const char *string_marked (file_entry *, int);
98 static const char *string_space (file_entry *, int);
99 static const char *string_dot (file_entry *, int);
101 /* *INDENT-OFF* */
102 panel_field_t panel_fields[] = {
104 "unsorted", 12, 1, J_LEFT_FIT,
105 /* TRANSLATORS: one single character to represent 'unsorted' sort mode */
106 /* TRANSLATORS: no need to translate 'sort', it's just a context prefix */
107 N_("sort|u"),
108 N_("&Unsorted"), TRUE, FALSE,
109 string_file_name,
110 (sortfn *) unsorted
114 "name", 12, 1, J_LEFT_FIT,
115 /* TRANSLATORS: one single character to represent 'name' sort mode */
116 /* TRANSLATORS: no need to translate 'sort', it's just a context prefix */
117 N_("sort|n"),
118 N_("&Name"), TRUE, TRUE,
119 string_file_name,
120 (sortfn *) sort_name
124 "version", 12, 1, J_LEFT_FIT,
125 /* TRANSLATORS: one single character to represent 'version' sort mode */
126 /* TRANSLATORS: no need to translate 'sort', it's just a context prefix */
127 N_("sort|v"),
128 N_("&Version"), TRUE, FALSE,
129 string_file_name,
130 (sortfn *) sort_vers
134 "extension", 12, 1, J_LEFT_FIT,
135 /* TRANSLATORS: one single character to represent 'extension' sort mode */
136 /* TRANSLATORS: no need to translate 'sort', it's just a context prefix */
137 N_("sort|e"),
138 N_("&Extension"), TRUE, FALSE,
139 string_file_name, /* TODO: string_file_ext */
140 (sortfn *) sort_ext
144 "size", 7, 0, J_RIGHT,
145 /* TRANSLATORS: one single character to represent 'size' sort mode */
146 /* TRANSLATORS: no need to translate 'sort', it's just a context prefix */
147 N_("sort|s"),
148 N_("&Size"), TRUE, TRUE,
149 string_file_size,
150 (sortfn *) sort_size
154 "bsize", 7, 0, J_RIGHT,
156 N_("Block Size"), FALSE, FALSE,
157 string_file_size_brief,
158 (sortfn *) sort_size
162 "type", 1, 0, J_LEFT,
164 "", FALSE, TRUE,
165 string_file_type,
166 NULL
170 "mtime", 12, 0, J_RIGHT,
171 /* TRANSLATORS: one single character to represent 'Modify time' sort mode */
172 /* TRANSLATORS: no need to translate 'sort', it's just a context prefix */
173 N_("sort|m"),
174 N_("&Modify time"), TRUE, TRUE,
175 string_file_mtime,
176 (sortfn *) sort_time
180 "atime", 12, 0, J_RIGHT,
181 /* TRANSLATORS: one single character to represent 'Access time' sort mode */
182 /* TRANSLATORS: no need to translate 'sort', it's just a context prefix */
183 N_("sort|a"),
184 N_("&Access time"), TRUE, TRUE,
185 string_file_atime,
186 (sortfn *) sort_atime
190 "ctime", 12, 0, J_RIGHT,
191 /* TRANSLATORS: one single character to represent 'Change time' sort mode */
192 /* TRANSLATORS: no need to translate 'sort', it's just a context prefix */
193 N_("sort|h"),
194 N_("C&hange time"), TRUE, TRUE,
195 string_file_ctime,
196 (sortfn *) sort_ctime
200 "perm", 10, 0, J_LEFT,
202 N_("Permission"), FALSE, TRUE,
203 string_file_permission,
204 NULL
208 "mode", 6, 0, J_RIGHT,
210 N_("Perm"), FALSE, TRUE,
211 string_file_perm_octal,
212 NULL
216 "nlink", 2, 0, J_RIGHT,
218 N_("Nl"), FALSE, TRUE,
219 string_file_nlinks, NULL
223 "inode", 5, 0, J_RIGHT,
224 /* TRANSLATORS: one single character to represent 'inode' sort mode */
225 /* TRANSLATORS: no need to translate 'sort', it's just a context prefix */
226 N_("sort|i"),
227 N_("&Inode"), TRUE, TRUE,
228 string_inode,
229 (sortfn *) sort_inode
233 "nuid", 5, 0, J_RIGHT,
235 N_("UID"), FALSE, FALSE,
236 string_file_nuid,
237 NULL
241 "ngid", 5, 0, J_RIGHT,
243 N_("GID"), FALSE, FALSE,
244 string_file_ngid,
245 NULL
249 "owner", 8, 0, J_LEFT_FIT,
251 N_("Owner"), FALSE, TRUE,
252 string_file_owner,
253 NULL
257 "group", 8, 0, J_LEFT_FIT,
259 N_("Group"), FALSE, TRUE,
260 string_file_group,
261 NULL
265 "mark", 1, 0, J_RIGHT,
267 " ", FALSE, TRUE,
268 string_marked,
269 NULL
273 "|", 1, 0, J_RIGHT,
275 " ", FALSE, TRUE,
276 NULL,
277 NULL
281 "space", 1, 0, J_RIGHT,
283 " ", FALSE, TRUE,
284 string_space,
285 NULL
289 "dot", 1, 0, J_RIGHT,
291 " ", FALSE, FALSE,
292 string_dot,
293 NULL
297 NULL, 0, 0, J_RIGHT, NULL, NULL, FALSE, FALSE, NULL, NULL
300 /* *INDENT-ON* */
302 extern int saving_setup;
304 /*** file scope macro definitions ****************************************************************/
306 #define ELEMENTS(arr) ( sizeof(arr) / sizeof((arr)[0]) )
308 #define NORMAL 0
309 #define SELECTED 1
310 #define MARKED 2
311 #define MARKED_SELECTED 3
312 #define STATUS 5
314 /* This macro extracts the number of available lines in a panel */
315 #define llines(p) (p->widget.lines-3 - (show_mini_info ? 2 : 0))
317 /*** file scope type declarations ****************************************************************/
319 typedef enum
321 MARK_DONT_MOVE = 0,
322 MARK_DOWN = 1,
323 MARK_FORCE_DOWN = 2,
324 MARK_FORCE_UP = 3
325 } mark_act_t;
328 * This describes a format item. The parse_display_format routine parses
329 * the user specified format and creates a linked list of format_e structures.
331 typedef struct format_e
333 struct format_e *next;
334 int requested_field_len;
335 int field_len;
336 align_crt_t just_mode;
337 int expand;
338 const char *(*string_fn) (file_entry *, int len);
339 char *title;
340 const char *id;
341 } format_e;
343 /*** file scope variables ************************************************************************/
345 static cb_ret_t panel_callback (Widget *, widget_msg_t msg, int parm);
346 static int panel_event (Gpm_Event * event, void *);
347 static void paint_frame (WPanel * panel);
348 static const char *panel_format (WPanel * panel);
349 static const char *mini_status_format (WPanel * panel);
351 static char *panel_sort_up_sign = NULL;
352 static char *panel_sort_down_sign = NULL;
354 static char *panel_hiddenfiles_sign_show = NULL;
355 static char *panel_hiddenfiles_sign_hide = NULL;
356 static char *panel_history_prev_item_sign = NULL;
357 static char *panel_history_next_item_sign = NULL;
358 static char *panel_history_show_list_sign = NULL;
360 static int mouse_marking = 0;
362 /*** file scope functions ************************************************************************/
363 /* --------------------------------------------------------------------------------------------- */
365 static void
366 set_colors (WPanel * panel)
368 (void) panel;
369 tty_set_normal_attrs ();
370 tty_setcolor (NORMAL_COLOR);
373 /* --------------------------------------------------------------------------------------------- */
374 /** Delete format string, it is a linked list */
376 static void
377 delete_format (format_e * format)
379 while (format != NULL)
381 format_e *next = format->next;
382 g_free (format->title);
383 g_free (format);
384 format = next;
388 /* --------------------------------------------------------------------------------------------- */
389 /** This code relies on the default justification!!! */
391 static void
392 add_permission_string (char *dest, int width, file_entry * fe, int attr, int color, int is_octal)
394 int i, r, l;
396 l = get_user_permissions (&fe->st);
398 if (is_octal)
400 /* Place of the access bit in octal mode */
401 l = width + l - 3;
402 r = l + 1;
404 else
406 /* The same to the triplet in string mode */
407 l = l * 3 + 1;
408 r = l + 3;
411 for (i = 0; i < width; i++)
413 if (i >= l && i < r)
415 if (attr == SELECTED || attr == MARKED_SELECTED)
416 tty_setcolor (MARKED_SELECTED_COLOR);
417 else
418 tty_setcolor (MARKED_COLOR);
420 else if (color >= 0)
421 tty_setcolor (color);
422 else
423 tty_lowlevel_setcolor (-color);
425 tty_print_char (dest[i]);
429 /* --------------------------------------------------------------------------------------------- */
430 /** String representations of various file attributes name */
432 static const char *
433 string_file_name (file_entry * fe, int len)
435 static char buffer[MC_MAXPATHLEN * MB_LEN_MAX + 1];
437 (void) len;
438 g_strlcpy (buffer, fe->fname, sizeof (buffer));
439 return buffer;
442 /* --------------------------------------------------------------------------------------------- */
444 static unsigned int
445 ilog10 (dev_t n)
447 unsigned int digits = 0;
450 digits++, n /= 10;
452 while (n != 0);
453 return digits;
456 /* --------------------------------------------------------------------------------------------- */
458 static void
459 format_device_number (char *buf, size_t bufsize, dev_t dev)
461 dev_t major_dev = major (dev);
462 dev_t minor_dev = minor (dev);
463 unsigned int major_digits = ilog10 (major_dev);
464 unsigned int minor_digits = ilog10 (minor_dev);
466 g_assert (bufsize >= 1);
467 if (major_digits + 1 + minor_digits + 1 <= bufsize)
469 g_snprintf (buf, bufsize, "%lu,%lu", (unsigned long) major_dev, (unsigned long) minor_dev);
471 else
473 g_strlcpy (buf, _("[dev]"), bufsize);
477 /* --------------------------------------------------------------------------------------------- */
478 /** size */
480 static const char *
481 string_file_size (file_entry * fe, int len)
483 static char buffer[BUF_TINY];
485 /* Don't ever show size of ".." since we don't calculate it */
486 if (!strcmp (fe->fname, ".."))
488 return _("UP--DIR");
491 #ifdef HAVE_STRUCT_STAT_ST_RDEV
492 if (S_ISBLK (fe->st.st_mode) || S_ISCHR (fe->st.st_mode))
493 format_device_number (buffer, len + 1, fe->st.st_rdev);
494 else
495 #endif
497 size_trunc_len (buffer, (unsigned int) len, fe->st.st_size, 0, panels_options.kilobyte_si);
499 return buffer;
502 /* --------------------------------------------------------------------------------------------- */
503 /** bsize */
505 static const char *
506 string_file_size_brief (file_entry * fe, int len)
508 if (S_ISLNK (fe->st.st_mode) && !fe->f.link_to_dir)
510 return _("SYMLINK");
513 if ((S_ISDIR (fe->st.st_mode) || fe->f.link_to_dir) && strcmp (fe->fname, ".."))
515 return _("SUB-DIR");
518 return string_file_size (fe, len);
521 /* --------------------------------------------------------------------------------------------- */
522 /** This functions return a string representation of a file entry type */
524 static const char *
525 string_file_type (file_entry * fe, int len)
527 static char buffer[2];
529 (void) len;
530 if (S_ISDIR (fe->st.st_mode))
531 buffer[0] = PATH_SEP;
532 else if (S_ISLNK (fe->st.st_mode))
534 if (fe->f.link_to_dir)
535 buffer[0] = '~';
536 else if (fe->f.stale_link)
537 buffer[0] = '!';
538 else
539 buffer[0] = '@';
541 else if (S_ISCHR (fe->st.st_mode))
542 buffer[0] = '-';
543 else if (S_ISSOCK (fe->st.st_mode))
544 buffer[0] = '=';
545 else if (S_ISDOOR (fe->st.st_mode))
546 buffer[0] = '>';
547 else if (S_ISBLK (fe->st.st_mode))
548 buffer[0] = '+';
549 else if (S_ISFIFO (fe->st.st_mode))
550 buffer[0] = '|';
551 else if (S_ISNAM (fe->st.st_mode))
552 buffer[0] = '#';
553 else if (!S_ISREG (fe->st.st_mode))
554 buffer[0] = '?'; /* non-regular of unknown kind */
555 else if (is_exe (fe->st.st_mode))
556 buffer[0] = '*';
557 else
558 buffer[0] = ' ';
559 buffer[1] = '\0';
560 return buffer;
563 /* --------------------------------------------------------------------------------------------- */
564 /** mtime */
566 static const char *
567 string_file_mtime (file_entry * fe, int len)
569 (void) len;
570 return file_date (fe->st.st_mtime);
573 /* --------------------------------------------------------------------------------------------- */
574 /** atime */
576 static const char *
577 string_file_atime (file_entry * fe, int len)
579 (void) len;
580 return file_date (fe->st.st_atime);
583 /* --------------------------------------------------------------------------------------------- */
584 /** ctime */
586 static const char *
587 string_file_ctime (file_entry * fe, int len)
589 (void) len;
590 return file_date (fe->st.st_ctime);
593 /* --------------------------------------------------------------------------------------------- */
594 /** perm */
596 static const char *
597 string_file_permission (file_entry * fe, int len)
599 (void) len;
600 return string_perm (fe->st.st_mode);
603 /* --------------------------------------------------------------------------------------------- */
604 /** mode */
606 static const char *
607 string_file_perm_octal (file_entry * fe, int len)
609 static char buffer[10];
611 (void) len;
612 g_snprintf (buffer, sizeof (buffer), "0%06lo", (unsigned long) fe->st.st_mode);
613 return buffer;
616 /* --------------------------------------------------------------------------------------------- */
617 /** nlink */
619 static const char *
620 string_file_nlinks (file_entry * fe, int len)
622 static char buffer[BUF_TINY];
624 (void) len;
625 g_snprintf (buffer, sizeof (buffer), "%16d", (int) fe->st.st_nlink);
626 return buffer;
629 /* --------------------------------------------------------------------------------------------- */
630 /** inode */
632 static const char *
633 string_inode (file_entry * fe, int len)
635 static char buffer[10];
637 (void) len;
638 g_snprintf (buffer, sizeof (buffer), "%lu", (unsigned long) fe->st.st_ino);
639 return buffer;
642 /* --------------------------------------------------------------------------------------------- */
643 /** nuid */
645 static const char *
646 string_file_nuid (file_entry * fe, int len)
648 static char buffer[10];
650 (void) len;
651 g_snprintf (buffer, sizeof (buffer), "%lu", (unsigned long) fe->st.st_uid);
652 return buffer;
655 /* --------------------------------------------------------------------------------------------- */
656 /** ngid */
658 static const char *
659 string_file_ngid (file_entry * fe, int len)
661 static char buffer[10];
663 (void) len;
664 g_snprintf (buffer, sizeof (buffer), "%lu", (unsigned long) fe->st.st_gid);
665 return buffer;
668 /* --------------------------------------------------------------------------------------------- */
669 /** owner */
671 static const char *
672 string_file_owner (file_entry * fe, int len)
674 (void) len;
675 return get_owner (fe->st.st_uid);
678 /* --------------------------------------------------------------------------------------------- */
679 /** group */
681 static const char *
682 string_file_group (file_entry * fe, int len)
684 (void) len;
685 return get_group (fe->st.st_gid);
688 /* --------------------------------------------------------------------------------------------- */
689 /** mark */
691 static const char *
692 string_marked (file_entry * fe, int len)
694 (void) len;
695 return fe->f.marked ? "*" : " ";
698 /* --------------------------------------------------------------------------------------------- */
699 /** space */
701 static const char *
702 string_space (file_entry * fe, int len)
704 (void) fe;
705 (void) len;
706 return " ";
709 /* --------------------------------------------------------------------------------------------- */
710 /** dot */
712 static const char *
713 string_dot (file_entry * fe, int len)
715 (void) fe;
716 (void) len;
717 return ".";
720 /* --------------------------------------------------------------------------------------------- */
722 static int
723 file_compute_color (int attr, file_entry * fe)
725 switch (attr)
727 case SELECTED:
728 return (SELECTED_COLOR);
729 case MARKED:
730 return (MARKED_COLOR);
731 case MARKED_SELECTED:
732 return (MARKED_SELECTED_COLOR);
733 case STATUS:
734 return (NORMAL_COLOR);
735 case NORMAL:
736 default:
737 if (!panels_options.filetype_mode)
738 return (NORMAL_COLOR);
741 return mc_fhl_get_color (mc_filehighlight, fe);
744 /* --------------------------------------------------------------------------------------------- */
745 /** Formats the file number file_index of panel in the buffer dest */
747 static void
748 format_file (char *dest, int limit, WPanel * panel, int file_index, int width, int attr,
749 int isstatus)
751 int color, length, empty_line;
752 const char *txt;
753 format_e *format, *home;
754 file_entry *fe;
756 (void) dest;
757 (void) limit;
758 length = 0;
759 empty_line = (file_index >= panel->count);
760 home = (isstatus) ? panel->status_format : panel->format;
761 fe = &panel->dir.list[file_index];
763 if (!empty_line)
764 color = file_compute_color (attr, fe);
765 else
766 color = NORMAL_COLOR;
768 for (format = home; format; format = format->next)
770 if (length == width)
771 break;
773 if (format->string_fn)
775 int len, perm;
776 char *preperad_text;
778 if (empty_line)
779 txt = " ";
780 else
781 txt = (*format->string_fn) (fe, format->field_len);
783 len = format->field_len;
784 if (len + length > width)
785 len = width - length;
786 if (len <= 0)
787 break;
789 perm = 0;
790 if (panels_options.permission_mode)
792 if (!strcmp (format->id, "perm"))
793 perm = 1;
794 else if (!strcmp (format->id, "mode"))
795 perm = 2;
798 if (color >= 0)
799 tty_setcolor (color);
800 else
801 tty_lowlevel_setcolor (-color);
803 preperad_text = (char *) str_fit_to_term (txt, len, format->just_mode);
804 if (perm)
805 add_permission_string (preperad_text, format->field_len, fe, attr, color, perm - 1);
806 else
807 tty_print_string (preperad_text);
809 length += len;
811 else
813 if (attr == SELECTED || attr == MARKED_SELECTED)
814 tty_setcolor (SELECTED_COLOR);
815 else
816 tty_setcolor (NORMAL_COLOR);
817 tty_print_one_vline (TRUE);
818 length++;
822 if (length < width)
823 tty_draw_hline (-1, -1, ' ', width - length);
826 /* --------------------------------------------------------------------------------------------- */
828 static void
829 repaint_file (WPanel * panel, int file_index, int mv, int attr, int isstatus)
831 int second_column = 0;
832 int width;
833 int offset = 0;
834 char buffer[BUF_MEDIUM];
836 gboolean panel_is_split = !isstatus && panel->split;
838 width = panel->widget.cols - 2;
840 if (panel_is_split)
842 second_column = (file_index - panel->top_file) / llines (panel);
843 width = width / 2 - 1;
845 if (second_column != 0)
847 offset = 1 + width;
848 /*width = (panel->widget.cols-2) - (panel->widget.cols-2)/2 - 1; */
849 width = panel->widget.cols - offset - 2;
853 /* Nothing to paint */
854 if (width <= 0)
855 return;
857 if (mv)
859 if (panel_is_split)
860 widget_move (&panel->widget,
861 (file_index - panel->top_file) % llines (panel) + 2, offset + 1);
862 else
863 widget_move (&panel->widget, file_index - panel->top_file + 2, 1);
866 format_file (buffer, sizeof (buffer), panel, file_index, width, attr, isstatus);
868 if (panel_is_split)
870 if (second_column)
871 tty_print_char (' ');
872 else
874 tty_setcolor (NORMAL_COLOR);
875 tty_print_one_vline (TRUE);
880 /* --------------------------------------------------------------------------------------------- */
882 static void
883 display_mini_info (WPanel * panel)
885 if (!show_mini_info)
886 return;
888 widget_move (&panel->widget, llines (panel) + 3, 1);
890 if (panel->searching)
892 tty_setcolor (INPUT_COLOR);
893 tty_print_char ('/');
894 tty_print_string (str_fit_to_term (panel->search_buffer, panel->widget.cols - 3, J_LEFT));
895 return;
898 /* Status resolves links and show them */
899 set_colors (panel);
901 if (S_ISLNK (panel->dir.list[panel->selected].st.st_mode))
903 char *lc_link, link_target[MC_MAXPATHLEN];
904 int len;
906 lc_link = concat_dir_and_file (panel->cwd, panel->dir.list[panel->selected].fname);
907 len = mc_readlink (lc_link, link_target, MC_MAXPATHLEN - 1);
908 g_free (lc_link);
909 if (len > 0)
911 link_target[len] = 0;
912 tty_print_string ("-> ");
913 tty_print_string (str_fit_to_term (link_target, panel->widget.cols - 5, J_LEFT_FIT));
915 else
916 tty_print_string (str_fit_to_term (_("<readlink failed>"),
917 panel->widget.cols - 2, J_LEFT));
919 else if (strcmp (panel->dir.list[panel->selected].fname, "..") == 0)
921 /* FIXME:
922 * while loading directory (do_load_dir() and do_reload_dir()),
923 * the actual stat info about ".." directory isn't got;
924 * so just don't display incorrect info about ".." directory */
925 tty_print_string (str_fit_to_term (_("UP--DIR"), panel->widget.cols - 2, J_LEFT));
927 else
928 /* Default behavior */
929 repaint_file (panel, panel->selected, 0, STATUS, 1);
932 /* --------------------------------------------------------------------------------------------- */
934 static void
935 paint_dir (WPanel * panel)
937 int i;
938 int color; /* Color value of the line */
939 int items; /* Number of items */
941 items = llines (panel) * (panel->split ? 2 : 1);
943 for (i = 0; i < items; i++)
945 if (i + panel->top_file >= panel->count)
946 color = 0;
947 else
949 color = 2 * (panel->dir.list[i + panel->top_file].f.marked);
950 color += (panel->selected == i + panel->top_file && panel->active);
952 repaint_file (panel, i + panel->top_file, 1, color, 0);
954 tty_set_normal_attrs ();
957 /* --------------------------------------------------------------------------------------------- */
959 static void
960 display_total_marked_size (WPanel * panel, int y, int x, gboolean size_only)
962 char buffer[BUF_SMALL], b_bytes[BUF_SMALL], *buf;
963 int cols;
965 if (panel->marked <= 0)
966 return;
968 buf = size_only ? b_bytes : buffer;
969 cols = panel->widget.cols - 2;
972 * This is a trick to use two ngettext() calls in one sentence.
973 * First make "N bytes", then insert it into "X in M files".
975 g_snprintf (b_bytes, sizeof (b_bytes),
976 ngettext ("%s byte", "%s bytes", (unsigned long) panel->total),
977 size_trunc_sep (panel->total, panels_options.kilobyte_si));
978 if (!size_only)
979 g_snprintf (buffer, sizeof (buffer),
980 ngettext ("%s in %d file", "%s in %d files", panel->marked),
981 b_bytes, panel->marked);
983 /* don't forget spaces around buffer content */
984 buf = (char *) str_trunc (buf, cols - 4);
986 if (x < 0)
987 /* center in panel */
988 x = (panel->widget.cols - str_term_width1 (buf)) / 2 - 1;
991 * y == llines (panel) + 2 for mini_info_separator
992 * y == panel->widget.lines - 1 for panel bottom frame
994 widget_move (&panel->widget, y, x);
995 tty_setcolor (MARKED_COLOR);
996 tty_printf (" %s ", buf);
999 /* --------------------------------------------------------------------------------------------- */
1001 static void
1002 mini_info_separator (WPanel * panel)
1004 if (show_mini_info)
1006 const int y = llines (panel) + 2;
1008 tty_setcolor (NORMAL_COLOR);
1009 tty_draw_hline (panel->widget.y + y, panel->widget.x + 1,
1010 ACS_HLINE, panel->widget.cols - 2);
1011 /* Status displays total marked size.
1012 * Centered in panel, full format. */
1013 display_total_marked_size (panel, y, -1, FALSE);
1017 /* --------------------------------------------------------------------------------------------- */
1019 static void
1020 show_free_space (WPanel * panel)
1022 /* Used to figure out how many free space we have */
1023 static struct my_statfs myfs_stats;
1024 /* Old current working directory for displaying free space */
1025 static char *old_cwd = NULL;
1027 /* Don't try to stat non-local fs */
1028 if (!vfs_file_is_local (panel->cwd) || !free_space)
1029 return;
1031 if (old_cwd == NULL || strcmp (old_cwd, panel->cwd) != 0)
1033 char rpath[PATH_MAX];
1035 init_my_statfs ();
1036 g_free (old_cwd);
1037 old_cwd = g_strdup (panel->cwd);
1039 if (mc_realpath (panel->cwd, rpath) == NULL)
1040 return;
1042 my_statfs (&myfs_stats, rpath);
1045 if (myfs_stats.avail > 0 || myfs_stats.total > 0)
1047 char buffer1[6], buffer2[6], tmp[BUF_SMALL];
1048 size_trunc_len (buffer1, sizeof (buffer1) - 1, myfs_stats.avail, 1,
1049 panels_options.kilobyte_si);
1050 size_trunc_len (buffer2, sizeof (buffer2) - 1, myfs_stats.total, 1,
1051 panels_options.kilobyte_si);
1052 g_snprintf (tmp, sizeof (tmp), " %s/%s (%d%%) ", buffer1, buffer2,
1053 myfs_stats.total >
1054 0 ? (int) (100 * (double) myfs_stats.avail / myfs_stats.total) : 0);
1055 widget_move (&panel->widget, panel->widget.lines - 1,
1056 panel->widget.cols - 2 - (int) strlen (tmp));
1057 tty_setcolor (NORMAL_COLOR);
1058 tty_print_string (tmp);
1062 /* --------------------------------------------------------------------------------------------- */
1064 static void
1065 show_dir (WPanel * panel)
1067 gchar *tmp;
1068 set_colors (panel);
1069 draw_box (panel->widget.owner,
1070 panel->widget.y, panel->widget.x, panel->widget.lines, panel->widget.cols, FALSE);
1072 if (show_mini_info)
1074 widget_move (&panel->widget, llines (panel) + 2, 0);
1075 tty_print_alt_char (ACS_LTEE, FALSE);
1076 widget_move (&panel->widget, llines (panel) + 2, panel->widget.cols - 1);
1077 tty_print_alt_char (ACS_RTEE, FALSE);
1080 widget_move (&panel->widget, 0, 1);
1081 tty_print_string (panel_history_prev_item_sign);
1083 tmp = panels_options.show_dot_files ? panel_hiddenfiles_sign_show : panel_hiddenfiles_sign_hide;
1084 tmp = g_strdup_printf ("%s[%s]%s", tmp, panel_history_show_list_sign,
1085 panel_history_next_item_sign);
1087 widget_move (&panel->widget, 0, panel->widget.cols - 6);
1088 tty_print_string (tmp);
1090 g_free (tmp);
1092 if (panel->active)
1093 tty_setcolor (REVERSE_COLOR);
1095 widget_move (&panel->widget, 0, 3);
1097 tty_printf (" %s ",
1098 str_term_trim (strip_home_and_password (panel->cwd),
1099 min (max (panel->widget.cols - 12, 0), panel->widget.cols)));
1101 if (!show_mini_info)
1103 if (panel->marked == 0)
1105 /* Show size of curret file in the bottom of panel */
1106 if (S_ISREG (panel->dir.list[panel->selected].st.st_mode))
1108 char buffer[BUF_SMALL];
1110 g_snprintf (buffer, sizeof (buffer), " %s ",
1111 size_trunc_sep (panel->dir.list[panel->selected].st.st_size,
1112 panels_options.kilobyte_si));
1113 tty_setcolor (NORMAL_COLOR);
1114 widget_move (&panel->widget, panel->widget.lines - 1, 4);
1115 tty_print_string (buffer);
1118 else
1120 /* Show total size of marked files
1121 * In the bottom of panel, display size only. */
1122 display_total_marked_size (panel, panel->widget.lines - 1, 2, TRUE);
1126 show_free_space (panel);
1128 if (panel->active)
1129 tty_set_normal_attrs ();
1132 /* --------------------------------------------------------------------------------------------- */
1133 /** To be used only by long_frame and full_frame to adjust top_file */
1135 static void
1136 adjust_top_file (WPanel * panel)
1138 int old_top = panel->top_file;
1140 if (panel->selected - old_top > llines (panel))
1141 panel->top_file = panel->selected;
1142 if (old_top - panel->count > llines (panel))
1143 panel->top_file = panel->count - llines (panel);
1146 /* --------------------------------------------------------------------------------------------- */
1147 /** Repaint everything, including frame and separator */
1149 static void
1150 paint_panel (WPanel * panel)
1152 paint_frame (panel); /* including show_dir */
1153 paint_dir (panel);
1154 mini_info_separator (panel);
1155 display_mini_info (panel);
1156 panel->dirty = 0;
1159 /* --------------------------------------------------------------------------------------------- */
1160 /** add "#enc:encodning" to end of path */
1161 /* if path end width a previous #enc:, only encoding is changed no additional
1162 * #enc: is appended
1163 * retun new string
1166 static char *
1167 add_encoding_to_path (const char *path, const char *encoding)
1169 char *result;
1170 char *semi;
1171 char *slash;
1173 semi = g_strrstr (path, VFS_ENCODING_PREFIX);
1175 if (semi != NULL)
1177 slash = strchr (semi, PATH_SEP);
1178 if (slash != NULL)
1180 result = g_strconcat (path, PATH_SEP_STR VFS_ENCODING_PREFIX, encoding, (char *) NULL);
1182 else
1184 *semi = '\0';
1185 result = g_strconcat (path, PATH_SEP_STR VFS_ENCODING_PREFIX, encoding, (char *) NULL);
1186 *semi = '#';
1189 else
1191 result = g_strconcat (path, PATH_SEP_STR VFS_ENCODING_PREFIX, encoding, (char *) NULL);
1194 return result;
1197 /* --------------------------------------------------------------------------------------------- */
1199 static char *
1200 panel_save_name (WPanel * panel)
1202 /* If the program is shuting down */
1203 if ((midnight_shutdown && auto_save_setup) || saving_setup)
1204 return g_strdup (panel->panel_name);
1205 else
1206 return g_strconcat ("Temporal:", panel->panel_name, (char *) NULL);
1209 /* --------------------------------------------------------------------------------------------- */
1211 static void
1212 panel_destroy (WPanel * p)
1214 size_t i;
1216 char *name = panel_save_name (p);
1218 panel_save_setup (p, name);
1219 panel_clean_dir (p);
1221 /* save and clean history */
1222 if (p->dir_history)
1224 history_put (p->hist_name, p->dir_history);
1226 p->dir_history = g_list_first (p->dir_history);
1227 g_list_foreach (p->dir_history, (GFunc) g_free, NULL);
1228 g_list_free (p->dir_history);
1231 g_free (p->hist_name);
1233 delete_format (p->format);
1234 delete_format (p->status_format);
1236 g_free (p->user_format);
1237 for (i = 0; i < LIST_TYPES; i++)
1238 g_free (p->user_status_format[i]);
1239 g_free (p->dir.list);
1240 g_free (p->panel_name);
1241 g_free (name);
1244 /* --------------------------------------------------------------------------------------------- */
1246 static void
1247 panel_format_modified (WPanel * panel)
1249 panel->format_modified = 1;
1252 /* --------------------------------------------------------------------------------------------- */
1254 static void
1255 panel_paint_sort_info (WPanel * panel)
1257 const char *sort_sign = (panel->reverse) ? panel_sort_down_sign : panel_sort_up_sign;
1258 char *str;
1260 if (*panel->current_sort_field->hotkey == '\0')
1261 return;
1263 str = g_strdup_printf ("%s%s", sort_sign, Q_ (panel->current_sort_field->hotkey));
1265 widget_move (&panel->widget, 1, 1);
1266 tty_print_string (str);
1267 g_free (str);
1270 /* --------------------------------------------------------------------------------------------- */
1272 static gchar *
1273 panel_get_title_without_hotkey (const char *title)
1275 char *translated_title;
1276 char *hkey;
1278 if (title == NULL)
1279 return NULL;
1280 if (title[0] == '\0')
1281 return g_strdup ("");
1283 translated_title = g_strdup (_(title));
1285 hkey = strchr (translated_title, '&');
1286 if ((hkey != NULL) && (hkey[1] != '\0'))
1287 memmove ((void *) hkey, (void *) hkey + 1, strlen (hkey));
1289 return translated_title;
1292 /* --------------------------------------------------------------------------------------------- */
1294 static void
1295 paint_frame (WPanel * panel)
1297 int side, width;
1298 GString *format_txt;
1300 if (!panel->split)
1301 adjust_top_file (panel);
1303 widget_erase (&panel->widget);
1304 show_dir (panel);
1306 widget_move (&panel->widget, 1, 1);
1308 for (side = 0; side <= panel->split; side++)
1310 format_e *format;
1312 if (side)
1314 tty_setcolor (NORMAL_COLOR);
1315 tty_print_one_vline (TRUE);
1316 width = panel->widget.cols - panel->widget.cols / 2 - 1;
1318 else if (panel->split)
1319 width = panel->widget.cols / 2 - 3;
1320 else
1321 width = panel->widget.cols - 2;
1323 format_txt = g_string_new ("");
1324 for (format = panel->format; format; format = format->next)
1326 if (format->string_fn)
1328 g_string_set_size (format_txt, 0);
1330 if (panel->list_type == list_long
1331 && strcmp (format->id, panel->current_sort_field->id) == 0)
1332 g_string_append (format_txt,
1333 (panel->reverse) ? panel_sort_down_sign : panel_sort_up_sign);
1335 g_string_append (format_txt, format->title);
1336 if (strcmp (format->id, "name") == 0 && panel->filter && *panel->filter)
1338 g_string_append (format_txt, " [");
1339 g_string_append (format_txt, panel->filter);
1340 g_string_append (format_txt, "]");
1343 tty_setcolor (HEADER_COLOR);
1344 tty_print_string (str_fit_to_term (format_txt->str, format->field_len,
1345 J_CENTER_LEFT));
1346 width -= format->field_len;
1348 else
1350 tty_setcolor (NORMAL_COLOR);
1351 tty_print_one_vline (TRUE);
1352 width--;
1355 g_string_free (format_txt, TRUE);
1357 if (width > 0)
1358 tty_draw_hline (-1, -1, ' ', width);
1361 if (panel->list_type != list_long)
1362 panel_paint_sort_info (panel);
1365 /* --------------------------------------------------------------------------------------------- */
1367 static const char *
1368 parse_panel_size (WPanel * panel, const char *format, int isstatus)
1370 int frame = frame_half;
1371 format = skip_separators (format);
1373 if (!strncmp (format, "full", 4))
1375 frame = frame_full;
1376 format += 4;
1378 else if (!strncmp (format, "half", 4))
1380 frame = frame_half;
1381 format += 4;
1384 if (!isstatus)
1386 panel->frame_size = frame;
1387 panel->split = 0;
1390 /* Now, the optional column specifier */
1391 format = skip_separators (format);
1393 if (*format == '1' || *format == '2')
1395 if (!isstatus)
1396 panel->split = *format == '2';
1397 format++;
1400 if (!isstatus)
1401 panel_update_cols (&(panel->widget), panel->frame_size);
1403 return skip_separators (format);
1406 /* Format is:
1408 all := panel_format? format
1409 panel_format := [full|half] [1|2]
1410 format := one_format_e
1411 | format , one_format_e
1413 one_format_e := just format.id [opt_size]
1414 just := [<=>]
1415 opt_size := : size [opt_expand]
1416 size := [0-9]+
1417 opt_expand := +
1421 /* --------------------------------------------------------------------------------------------- */
1423 static format_e *
1424 parse_display_format (WPanel * panel, const char *format, char **error, int isstatus,
1425 int *res_total_cols)
1427 format_e *darr, *old = 0, *home = 0; /* The formats we return */
1428 int total_cols = 0; /* Used columns by the format */
1429 int set_justify; /* flag: set justification mode? */
1430 align_crt_t justify = J_LEFT; /* Which mode. */
1431 size_t i;
1433 static size_t i18n_timelength = 0; /* flag: check ?Time length at startup */
1435 *error = 0;
1437 if (i18n_timelength == 0)
1439 i18n_timelength = i18n_checktimelength (); /* Musn't be 0 */
1441 for (i = 0; panel_fields[i].id != NULL; i++)
1442 if (strcmp ("time", panel_fields[i].id + 1) == 0)
1443 panel_fields[i].min_size = i18n_timelength;
1447 * This makes sure that the panel and mini status full/half mode
1448 * setting is equal
1450 format = parse_panel_size (panel, format, isstatus);
1452 while (*format)
1453 { /* format can be an empty string */
1454 int found = 0;
1456 darr = g_new0 (format_e, 1);
1458 /* I'm so ugly, don't look at me :-) */
1459 if (!home)
1460 home = old = darr;
1462 old->next = darr;
1463 darr->next = 0;
1464 old = darr;
1466 format = skip_separators (format);
1468 if (strchr ("<=>", *format))
1470 set_justify = 1;
1471 switch (*format)
1473 case '<':
1474 justify = J_LEFT;
1475 break;
1476 case '=':
1477 justify = J_CENTER;
1478 break;
1479 case '>':
1480 default:
1481 justify = J_RIGHT;
1482 break;
1484 format = skip_separators (format + 1);
1486 else
1487 set_justify = 0;
1489 for (i = 0; panel_fields[i].id != NULL; i++)
1491 size_t klen = strlen (panel_fields[i].id);
1493 if (strncmp (format, panel_fields[i].id, klen) != 0)
1494 continue;
1496 format += klen;
1498 darr->requested_field_len = panel_fields[i].min_size;
1499 darr->string_fn = panel_fields[i].string_fn;
1500 darr->title = panel_get_title_without_hotkey (panel_fields[i].title_hotkey);
1502 darr->id = panel_fields[i].id;
1503 darr->expand = panel_fields[i].expands;
1504 darr->just_mode = panel_fields[i].default_just;
1506 if (set_justify)
1508 if (IS_FIT (darr->just_mode))
1509 darr->just_mode = MAKE_FIT (justify);
1510 else
1511 darr->just_mode = justify;
1513 found = 1;
1515 format = skip_separators (format);
1517 /* If we have a size specifier */
1518 if (*format == ':')
1520 int req_length;
1522 /* If the size was specified, we don't want
1523 * auto-expansion by default
1525 darr->expand = 0;
1526 format++;
1527 req_length = atoi (format);
1528 darr->requested_field_len = req_length;
1530 format = skip_numbers (format);
1532 /* Now, if they insist on expansion */
1533 if (*format == '+')
1535 darr->expand = 1;
1536 format++;
1541 break;
1543 if (!found)
1545 char *tmp_format = g_strdup (format);
1547 int pos = min (8, strlen (format));
1548 delete_format (home);
1549 tmp_format[pos] = 0;
1550 *error =
1551 g_strconcat (_("Unknown tag on display format:"), " ", tmp_format, (char *) NULL);
1552 g_free (tmp_format);
1553 return 0;
1555 total_cols += darr->requested_field_len;
1558 *res_total_cols = total_cols;
1559 return home;
1562 /* --------------------------------------------------------------------------------------------- */
1564 static format_e *
1565 use_display_format (WPanel * panel, const char *format, char **error, int isstatus)
1567 #define MAX_EXPAND 4
1568 int expand_top = 0; /* Max used element in expand */
1569 int usable_columns; /* Usable columns in the panel */
1570 int total_cols = 0;
1571 int i;
1572 format_e *darr, *home;
1574 if (!format)
1575 format = DEFAULT_USER_FORMAT;
1577 home = parse_display_format (panel, format, error, isstatus, &total_cols);
1579 if (*error)
1580 return 0;
1582 panel->dirty = 1;
1584 /* Status needn't to be split */
1585 usable_columns = ((panel->widget.cols - 2) / ((isstatus)
1587 : (panel->split + 1))) - (!isstatus
1588 && panel->split);
1590 /* Look for the expandable fields and set field_len based on the requested field len */
1591 for (darr = home; darr && expand_top < MAX_EXPAND; darr = darr->next)
1593 darr->field_len = darr->requested_field_len;
1594 if (darr->expand)
1595 expand_top++;
1598 /* If we used more columns than the available columns, adjust that */
1599 if (total_cols > usable_columns)
1601 int pdif, dif = total_cols - usable_columns;
1603 while (dif)
1605 pdif = dif;
1606 for (darr = home; darr; darr = darr->next)
1608 if (dif && darr->field_len - 1)
1610 darr->field_len--;
1611 dif--;
1615 /* avoid endless loop if num fields > 40 */
1616 if (pdif == dif)
1617 break;
1619 total_cols = usable_columns; /* give up, the rest should be truncated */
1622 /* Expand the available space */
1623 if ((usable_columns > total_cols) && expand_top)
1625 int spaces = (usable_columns - total_cols) / expand_top;
1626 int extra = (usable_columns - total_cols) % expand_top;
1628 for (i = 0, darr = home; darr && (i < expand_top); darr = darr->next)
1629 if (darr->expand)
1631 darr->field_len += (spaces + ((i == 0) ? extra : 0));
1632 i++;
1635 return home;
1638 /* --------------------------------------------------------------------------------------------- */
1639 /** Given the panel->view_type returns the format string to be parsed */
1641 static const char *
1642 panel_format (WPanel * panel)
1644 switch (panel->list_type)
1647 case list_long:
1648 return "full perm space nlink space owner space group space size space mtime space name";
1650 case list_brief:
1651 return "half 2 type name";
1653 case list_user:
1654 return panel->user_format;
1656 default:
1657 case list_full:
1658 return "half type name | size | mtime";
1662 /* --------------------------------------------------------------------------------------------- */
1664 static const char *
1665 mini_status_format (WPanel * panel)
1667 if (panel->user_mini_status)
1668 return panel->user_status_format[panel->list_type];
1670 switch (panel->list_type)
1673 case list_long:
1674 return "full perm space nlink space owner space group space size space mtime space name";
1676 case list_brief:
1677 return "half type name space bsize space perm space";
1679 case list_full:
1680 return "half type name";
1682 default:
1683 case list_user:
1684 return panel->user_format;
1688 /* */
1689 /* Panel operation commands */
1690 /* */
1692 /* --------------------------------------------------------------------------------------------- */
1693 /** Used to emulate Lynx's entering leaving a directory with the arrow keys */
1695 static cb_ret_t
1696 maybe_cd (int move_up_dir)
1698 if (panels_options.navigate_with_arrows && (cmdline->buffer[0] == '\0'))
1700 if (move_up_dir)
1702 do_cd ("..", cd_exact);
1703 return MSG_HANDLED;
1706 if (S_ISDIR (selection (current_panel)->st.st_mode)
1707 || link_isdir (selection (current_panel)))
1709 do_cd (selection (current_panel)->fname, cd_exact);
1710 return MSG_HANDLED;
1713 return MSG_NOT_HANDLED;
1716 /* --------------------------------------------------------------------------------------------- */
1717 /** Returns the number of items in the given panel */
1719 static int
1720 ITEMS (WPanel * p)
1722 if (p->split)
1723 return llines (p) * 2;
1724 else
1725 return llines (p);
1728 /* --------------------------------------------------------------------------------------------- */
1730 static void
1731 unselect_item (WPanel * panel)
1733 repaint_file (panel, panel->selected, 1, 2 * selection (panel)->f.marked, 0);
1736 /* --------------------------------------------------------------------------------------------- */
1738 static void
1739 move_down (WPanel * panel)
1741 if (panel->selected + 1 == panel->count)
1742 return;
1744 unselect_item (panel);
1745 panel->selected++;
1746 if (panels_options.scroll_pages && panel->selected - panel->top_file == ITEMS (panel))
1748 /* Scroll window half screen */
1749 panel->top_file += ITEMS (panel) / 2;
1750 if (panel->top_file > panel->count - ITEMS (panel))
1751 panel->top_file = panel->count - ITEMS (panel);
1752 paint_dir (panel);
1754 select_item (panel);
1757 /* --------------------------------------------------------------------------------------------- */
1759 static void
1760 move_up (WPanel * panel)
1762 if (panel->selected == 0)
1763 return;
1765 unselect_item (panel);
1766 panel->selected--;
1767 if (panels_options.scroll_pages && panel->selected < panel->top_file)
1769 /* Scroll window half screen */
1770 panel->top_file -= ITEMS (panel) / 2;
1771 if (panel->top_file < 0)
1772 panel->top_file = 0;
1773 paint_dir (panel);
1775 select_item (panel);
1778 /* --------------------------------------------------------------------------------------------- */
1779 /** Changes the selection by lines (may be negative) */
1781 static void
1782 move_selection (WPanel * panel, int lines)
1784 int new_pos;
1785 int adjust = 0;
1787 new_pos = panel->selected + lines;
1788 if (new_pos >= panel->count)
1789 new_pos = panel->count - 1;
1791 if (new_pos < 0)
1792 new_pos = 0;
1794 unselect_item (panel);
1795 panel->selected = new_pos;
1797 if (panel->selected - panel->top_file >= ITEMS (panel))
1799 panel->top_file += lines;
1800 adjust = 1;
1803 if (panel->selected - panel->top_file < 0)
1805 panel->top_file += lines;
1806 adjust = 1;
1809 if (adjust)
1811 if (panel->top_file > panel->selected)
1812 panel->top_file = panel->selected;
1813 if (panel->top_file < 0)
1814 panel->top_file = 0;
1815 paint_dir (panel);
1817 select_item (panel);
1820 /* --------------------------------------------------------------------------------------------- */
1822 static cb_ret_t
1823 move_left (WPanel * panel)
1825 if (panel->split)
1827 move_selection (panel, -llines (panel));
1828 return MSG_HANDLED;
1830 else
1831 return maybe_cd (1); /* cd .. */
1834 /* --------------------------------------------------------------------------------------------- */
1836 static int
1837 move_right (WPanel * panel)
1839 if (panel->split)
1841 move_selection (panel, llines (panel));
1842 return MSG_HANDLED;
1844 else
1845 return maybe_cd (0); /* cd (selection) */
1848 /* --------------------------------------------------------------------------------------------- */
1850 static void
1851 prev_page (WPanel * panel)
1853 int items;
1855 if (!panel->selected && !panel->top_file)
1856 return;
1857 unselect_item (panel);
1858 items = ITEMS (panel);
1859 if (panel->top_file < items)
1860 items = panel->top_file;
1861 if (!items)
1862 panel->selected = 0;
1863 else
1864 panel->selected -= items;
1865 panel->top_file -= items;
1867 select_item (panel);
1868 paint_dir (panel);
1871 /* --------------------------------------------------------------------------------------------- */
1873 static void
1874 ctrl_prev_page (WPanel * panel)
1876 (void) panel;
1877 do_cd ("..", cd_exact);
1880 /* --------------------------------------------------------------------------------------------- */
1882 static void
1883 next_page (WPanel * panel)
1885 int items;
1887 if (panel->selected == panel->count - 1)
1888 return;
1889 unselect_item (panel);
1890 items = ITEMS (panel);
1891 if (panel->top_file > panel->count - 2 * items)
1892 items = panel->count - items - panel->top_file;
1893 if (panel->top_file + items < 0)
1894 items = -panel->top_file;
1895 if (!items)
1896 panel->selected = panel->count - 1;
1897 else
1898 panel->selected += items;
1899 panel->top_file += items;
1901 select_item (panel);
1902 paint_dir (panel);
1905 /* --------------------------------------------------------------------------------------------- */
1907 static void
1908 ctrl_next_page (WPanel * panel)
1910 if ((S_ISDIR (selection (panel)->st.st_mode) || link_isdir (selection (panel))))
1912 do_cd (selection (panel)->fname, cd_exact);
1916 /* --------------------------------------------------------------------------------------------- */
1918 static void
1919 goto_top_file (WPanel * panel)
1921 unselect_item (panel);
1922 panel->selected = panel->top_file;
1923 select_item (panel);
1926 /* --------------------------------------------------------------------------------------------- */
1928 static void
1929 goto_middle_file (WPanel * panel)
1931 unselect_item (panel);
1932 panel->selected = panel->top_file + (ITEMS (panel) / 2);
1933 select_item (panel);
1936 /* --------------------------------------------------------------------------------------------- */
1938 static void
1939 goto_bottom_file (WPanel * panel)
1941 unselect_item (panel);
1942 panel->selected = panel->top_file + ITEMS (panel) - 1;
1943 select_item (panel);
1946 /* --------------------------------------------------------------------------------------------- */
1948 static void
1949 move_home (WPanel * panel)
1951 if (panel->selected == 0)
1952 return;
1953 unselect_item (panel);
1955 if (torben_fj_mode)
1957 int middle_pos = panel->top_file + (ITEMS (panel) / 2);
1959 if (panel->selected > middle_pos)
1961 goto_middle_file (panel);
1962 return;
1964 if (panel->selected != panel->top_file)
1966 goto_top_file (panel);
1967 return;
1971 panel->top_file = 0;
1972 panel->selected = 0;
1974 paint_dir (panel);
1975 select_item (panel);
1978 /* --------------------------------------------------------------------------------------------- */
1980 static void
1981 move_end (WPanel * panel)
1983 if (panel->selected == panel->count - 1)
1984 return;
1985 unselect_item (panel);
1986 if (torben_fj_mode)
1988 int middle_pos = panel->top_file + (ITEMS (panel) / 2);
1990 if (panel->selected < middle_pos)
1992 goto_middle_file (panel);
1993 return;
1995 if (panel->selected != (panel->top_file + ITEMS (panel) - 1))
1997 goto_bottom_file (panel);
1998 return;
2002 panel->selected = panel->count - 1;
2003 paint_dir (panel);
2004 select_item (panel);
2007 /* --------------------------------------------------------------------------------------------- */
2009 static void
2010 do_mark_file (WPanel * panel, mark_act_t do_move)
2012 do_file_mark (panel, panel->selected, selection (panel)->f.marked ? 0 : 1);
2013 if ((panels_options.mark_moves_down && do_move == MARK_DOWN) || do_move == MARK_FORCE_DOWN)
2014 move_down (panel);
2015 else if (do_move == MARK_FORCE_UP)
2016 move_up (panel);
2019 /* --------------------------------------------------------------------------------------------- */
2021 static void
2022 mark_file (WPanel * panel)
2024 do_mark_file (panel, MARK_DOWN);
2027 /* --------------------------------------------------------------------------------------------- */
2029 static void
2030 mark_file_up (WPanel * panel)
2032 do_mark_file (panel, MARK_FORCE_UP);
2035 /* --------------------------------------------------------------------------------------------- */
2037 static void
2038 mark_file_down (WPanel * panel)
2040 do_mark_file (panel, MARK_FORCE_DOWN);
2043 /* --------------------------------------------------------------------------------------------- */
2044 /** Incremental search of a file name in the panel.
2045 * @param panel instance of WPanel structure
2046 * @param c_code key code
2049 static void
2050 do_search (WPanel * panel, int c_code)
2052 size_t l;
2053 int i, sel;
2054 gboolean wrapped = FALSE;
2055 char *act;
2056 mc_search_t *search;
2057 char *reg_exp, *esc_str;
2058 gboolean is_found = FALSE;
2060 l = strlen (panel->search_buffer);
2061 if (c_code == KEY_BACKSPACE)
2063 if (l != 0)
2065 act = panel->search_buffer + l;
2066 str_prev_noncomb_char (&act, panel->search_buffer);
2067 act[0] = '\0';
2069 panel->search_chpoint = 0;
2071 else
2073 if (c_code != 0 && (gsize) panel->search_chpoint < sizeof (panel->search_char))
2075 panel->search_char[panel->search_chpoint] = c_code;
2076 panel->search_chpoint++;
2079 if (panel->search_chpoint > 0)
2081 switch (str_is_valid_char (panel->search_char, panel->search_chpoint))
2083 case -2:
2084 return;
2085 case -1:
2086 panel->search_chpoint = 0;
2087 return;
2088 default:
2089 if (l + panel->search_chpoint < sizeof (panel->search_buffer))
2091 memcpy (panel->search_buffer + l, panel->search_char, panel->search_chpoint);
2092 l += panel->search_chpoint;
2093 *(panel->search_buffer + l) = '\0';
2094 panel->search_chpoint = 0;
2100 reg_exp = g_strdup_printf ("%s*", panel->search_buffer);
2101 esc_str = strutils_escape (reg_exp, -1, ",|\\{}[]", TRUE);
2102 search = mc_search_new (esc_str, -1);
2103 search->search_type = MC_SEARCH_T_GLOB;
2104 search->is_entire_line = TRUE;
2105 switch (panels_options.qsearch_mode)
2107 case QSEARCH_CASE_SENSITIVE:
2108 search->is_case_sensitive = TRUE;
2109 break;
2110 case QSEARCH_CASE_INSENSITIVE:
2111 search->is_case_sensitive = FALSE;
2112 break;
2113 default:
2114 search->is_case_sensitive = panel->case_sensitive;
2115 break;
2117 sel = panel->selected;
2118 for (i = panel->selected; !wrapped || i != panel->selected; i++)
2120 if (i >= panel->count)
2122 i = 0;
2123 if (wrapped)
2124 break;
2125 wrapped = TRUE;
2127 if (mc_search_run (search, panel->dir.list[i].fname, 0, panel->dir.list[i].fnamelen, NULL))
2129 sel = i;
2130 is_found = TRUE;
2131 break;
2134 if (is_found)
2136 unselect_item (panel);
2137 panel->selected = sel;
2138 select_item (panel);
2139 paint_panel (panel);
2141 else if (c_code != KEY_BACKSPACE)
2143 act = panel->search_buffer + l;
2144 str_prev_noncomb_char (&act, panel->search_buffer);
2145 act[0] = '\0';
2147 mc_search_free (search);
2148 g_free (reg_exp);
2149 g_free (esc_str);
2152 /* --------------------------------------------------------------------------------------------- */
2153 /** Start new search.
2154 * @param panel instance of WPanel structure
2157 static void
2158 start_search (WPanel * panel)
2160 if (panel->searching)
2162 if (panel->selected + 1 == panel->count)
2163 panel->selected = 0;
2164 else
2165 move_down (panel);
2167 /* in case if there was no search string we need to recall
2168 previous string, with which we ended previous searching */
2169 if (panel->search_buffer[0] == '\0')
2170 g_strlcpy (panel->search_buffer, panel->prev_search_buffer,
2171 sizeof (panel->search_buffer));
2173 do_search (panel, 0);
2175 else
2177 panel->searching = TRUE;
2178 panel->search_buffer[0] = '\0';
2179 panel->search_char[0] = '\0';
2180 panel->search_chpoint = 0;
2181 display_mini_info (panel);
2182 mc_refresh ();
2186 /* --------------------------------------------------------------------------------------------- */
2188 static void
2189 stop_search (WPanel * panel)
2191 panel->searching = FALSE;
2193 /* if user had overrdied search string, we need to store it
2194 to the previous_search_buffer */
2195 if (panel->search_buffer[0] != '\0')
2196 g_strlcpy (panel->prev_search_buffer, panel->search_buffer,
2197 sizeof (panel->prev_search_buffer));
2199 display_mini_info (panel);
2202 /* --------------------------------------------------------------------------------------------- */
2203 /** Return 1 if the Enter key has been processed, 0 otherwise */
2205 static int
2206 do_enter_on_file_entry (file_entry * fe)
2208 char *full_name;
2211 * Directory or link to directory - change directory.
2212 * Try the same for the entries on which mc_lstat() has failed.
2214 if (S_ISDIR (fe->st.st_mode) || link_isdir (fe) || (fe->st.st_mode == 0))
2216 if (!do_cd (fe->fname, cd_exact))
2217 message (D_ERROR, MSG_ERROR, _("Cannot change directory"));
2218 return 1;
2221 /* Try associated command */
2222 if (regex_command (fe->fname, "Open", NULL) != 0)
2223 return 1;
2225 /* Check if the file is executable */
2226 full_name = concat_dir_and_file (current_panel->cwd, fe->fname);
2227 if (!is_exe (fe->st.st_mode) || !if_link_is_exe (full_name, fe))
2229 g_free (full_name);
2230 return 0;
2232 g_free (full_name);
2234 if (confirm_execute)
2236 if (query_dialog
2237 (_("The Midnight Commander"),
2238 _("Do you really want to execute?"), D_NORMAL, 2, _("&Yes"), _("&No")) != 0)
2239 return 1;
2242 if (!vfs_current_is_local ())
2244 char *tmp;
2245 int ret;
2247 tmp = concat_dir_and_file (vfs_get_current_dir (), fe->fname);
2248 ret = mc_setctl (tmp, VFS_SETCTL_RUN, NULL);
2249 g_free (tmp);
2250 /* We took action only if the dialog was shown or the execution
2251 * was successful */
2252 return confirm_execute || (ret == 0);
2256 char *tmp = name_quote (fe->fname, 0);
2257 char *cmd = g_strconcat (".", PATH_SEP_STR, tmp, (char *) NULL);
2258 g_free (tmp);
2259 shell_execute (cmd, 0);
2260 g_free (cmd);
2263 #if HAVE_CHARSET
2264 source_codepage = default_source_codepage;
2265 #endif
2267 return 1;
2270 /* --------------------------------------------------------------------------------------------- */
2272 static int
2273 do_enter (WPanel * panel)
2275 return do_enter_on_file_entry (selection (panel));
2278 /* --------------------------------------------------------------------------------------------- */
2280 static void
2281 chdir_other_panel (WPanel * panel)
2283 char *new_dir;
2284 char *sel_entry = NULL;
2286 if (get_other_type () != view_listing)
2288 set_display_type (get_other_index (), view_listing);
2291 if (!S_ISDIR (panel->dir.list[panel->selected].st.st_mode))
2293 new_dir = concat_dir_and_file (panel->cwd, "..");
2294 sel_entry = strrchr (panel->cwd, PATH_SEP);
2296 else
2297 new_dir = concat_dir_and_file (panel->cwd, panel->dir.list[panel->selected].fname);
2299 change_panel ();
2300 do_cd (new_dir, cd_exact);
2301 if (sel_entry)
2302 try_to_select (current_panel, sel_entry);
2303 change_panel ();
2305 move_down (panel);
2307 g_free (new_dir);
2310 /* --------------------------------------------------------------------------------------------- */
2312 * Make the current directory of the current panel also the current
2313 * directory of the other panel. Put the other panel to the listing
2314 * mode if needed. If the current panel is panelized, the other panel
2315 * doesn't become panelized.
2318 static void
2319 sync_other_panel (WPanel * panel)
2321 if (get_other_type () != view_listing)
2323 set_display_type (get_other_index (), view_listing);
2326 do_panel_cd (other_panel, current_panel->cwd, cd_exact);
2328 /* try to select current filename on the other panel */
2329 if (!panel->is_panelized)
2331 try_to_select (other_panel, selection (panel)->fname);
2335 /* --------------------------------------------------------------------------------------------- */
2337 static void
2338 chdir_to_readlink (WPanel * panel)
2340 char *new_dir;
2342 if (get_other_type () != view_listing)
2343 return;
2345 if (S_ISLNK (panel->dir.list[panel->selected].st.st_mode))
2347 char buffer[MC_MAXPATHLEN], *p;
2348 int i;
2349 struct stat st;
2351 i = readlink (selection (panel)->fname, buffer, MC_MAXPATHLEN - 1);
2352 if (i < 0)
2353 return;
2354 if (mc_stat (selection (panel)->fname, &st) < 0)
2355 return;
2356 buffer[i] = 0;
2357 if (!S_ISDIR (st.st_mode))
2359 p = strrchr (buffer, PATH_SEP);
2360 if (p && !p[1])
2362 *p = 0;
2363 p = strrchr (buffer, PATH_SEP);
2365 if (!p)
2366 return;
2367 p[1] = 0;
2369 if (*buffer == PATH_SEP)
2370 new_dir = g_strdup (buffer);
2371 else
2372 new_dir = concat_dir_and_file (panel->cwd, buffer);
2374 change_panel ();
2375 do_cd (new_dir, cd_exact);
2376 change_panel ();
2378 move_down (panel);
2380 g_free (new_dir);
2384 /* --------------------------------------------------------------------------------------------- */
2386 static gsize
2387 panel_get_format_field_count (WPanel * panel)
2389 format_e *format;
2390 gsize lc_index;
2391 for (lc_index = 0, format = panel->format; format != NULL; format = format->next, lc_index++);
2392 return lc_index;
2395 /* --------------------------------------------------------------------------------------------- */
2397 function return 0 if not found and REAL_INDEX+1 if found
2400 static gsize
2401 panel_get_format_field_index_by_name (WPanel * panel, const char *name)
2403 format_e *format;
2404 gsize lc_index;
2406 for (lc_index = 1, format = panel->format;
2407 !(format == NULL || strcmp (format->title, name) == 0); format = format->next, lc_index++);
2408 if (format == NULL)
2409 lc_index = 0;
2411 return lc_index;
2414 /* --------------------------------------------------------------------------------------------- */
2416 static format_e *
2417 panel_get_format_field_by_index (WPanel * panel, gsize lc_index)
2419 format_e *format;
2420 for (format = panel->format;
2421 !(format == NULL || lc_index == 0); format = format->next, lc_index--);
2422 return format;
2425 /* --------------------------------------------------------------------------------------------- */
2427 static const panel_field_t *
2428 panel_get_sortable_field_by_format (WPanel * panel, gsize lc_index)
2430 const panel_field_t *pfield;
2431 format_e *format;
2433 format = panel_get_format_field_by_index (panel, lc_index);
2434 if (format == NULL)
2435 return NULL;
2436 pfield = panel_get_field_by_title (format->title);
2437 if (pfield == NULL)
2438 return NULL;
2439 if (pfield->sort_routine == NULL)
2440 return NULL;
2441 return pfield;
2444 /* --------------------------------------------------------------------------------------------- */
2446 static void
2447 panel_toggle_sort_order_prev (WPanel * panel)
2449 gsize lc_index, i;
2450 gchar *title;
2452 const panel_field_t *pfield = NULL;
2454 title = panel_get_title_without_hotkey (panel->current_sort_field->title_hotkey);
2455 lc_index = panel_get_format_field_index_by_name (panel, title);
2456 g_free (title);
2458 if (lc_index > 1)
2460 /* search for prev sortable column in panel format */
2461 for (i = lc_index - 1;
2462 i != 0 && (pfield = panel_get_sortable_field_by_format (panel, i - 1)) == NULL; i--);
2465 if (pfield == NULL)
2467 /* Sortable field not found. Try to search in each array */
2468 for (i = panel_get_format_field_count (panel);
2469 i != 0 && (pfield = panel_get_sortable_field_by_format (panel, i - 1)) == NULL; i--);
2471 if (pfield == NULL)
2472 return;
2473 panel->current_sort_field = pfield;
2474 panel_set_sort_order (panel, panel->current_sort_field);
2477 /* --------------------------------------------------------------------------------------------- */
2479 static void
2480 panel_toggle_sort_order_next (WPanel * panel)
2482 gsize lc_index, i;
2483 const panel_field_t *pfield = NULL;
2484 gsize format_field_count = panel_get_format_field_count (panel);
2485 gchar *title;
2487 title = panel_get_title_without_hotkey (panel->current_sort_field->title_hotkey);
2488 lc_index = panel_get_format_field_index_by_name (panel, title);
2489 g_free (title);
2491 if (lc_index != 0 && lc_index != format_field_count)
2493 /* search for prev sortable column in panel format */
2494 for (i = lc_index;
2495 i != format_field_count
2496 && (pfield = panel_get_sortable_field_by_format (panel, i)) == NULL; i++);
2499 if (pfield == NULL)
2501 /* Sortable field not found. Try to search in each array */
2502 for (i = 0;
2503 i != format_field_count
2504 && (pfield = panel_get_sortable_field_by_format (panel, i)) == NULL; i++);
2506 if (pfield == NULL)
2507 return;
2508 panel->current_sort_field = pfield;
2509 panel_set_sort_order (panel, panel->current_sort_field);
2512 /* --------------------------------------------------------------------------------------------- */
2514 static void
2515 panel_select_sort_order (WPanel * panel)
2517 const panel_field_t *sort_order;
2518 sort_order = sort_box (panel->current_sort_field, &panel->reverse,
2519 &panel->case_sensitive, &panel->exec_first);
2520 if (sort_order == NULL)
2521 return;
2522 panel->current_sort_field = sort_order;
2523 panel_set_sort_order (panel, panel->current_sort_field);
2527 /* --------------------------------------------------------------------------------------------- */
2529 static void
2530 panel_set_sort_type_by_id (WPanel * panel, const char *name)
2532 const panel_field_t *sort_order;
2534 if (strcmp (panel->current_sort_field->id, name) != 0)
2536 sort_order = panel_get_field_by_id (name);
2537 if (sort_order == NULL)
2538 return;
2539 panel->current_sort_field = sort_order;
2541 else
2543 panel->reverse = !panel->reverse;
2545 panel_set_sort_order (panel, panel->current_sort_field);
2548 /* --------------------------------------------------------------------------------------------- */
2550 * If we moved to the parent directory move the selection pointer to
2551 * the old directory name; If we leave VFS dir, remove FS specificator.
2553 * You do _NOT_ want to add any vfs aware code here. <pavel@ucw.cz>
2556 static const char *
2557 get_parent_dir_name (const char *cwd, const char *lwd)
2559 size_t llen, clen;
2561 llen = strlen (lwd);
2562 clen = strlen (cwd);
2564 if (llen > clen)
2566 const char *p;
2568 p = strrchr (lwd, PATH_SEP);
2570 if ((p != NULL)
2571 && (strncmp (cwd, lwd, (size_t) (p - lwd)) == 0)
2572 && (clen == (size_t) (p - lwd)
2573 || ((p == lwd) && (cwd[0] == PATH_SEP) && (cwd[1] == '\0'))))
2574 return (p + 1);
2577 return NULL;
2580 /* --------------------------------------------------------------------------------------------- */
2582 * Changes the current directory of the panel.
2583 * Don't record change in the directory history.
2586 static gboolean
2587 _do_panel_cd (WPanel * panel, const char *new_dir, enum cd_enum cd_type)
2589 const char *directory;
2590 char *olddir;
2591 char temp[MC_MAXPATHLEN];
2592 char *translated_url;
2594 if (cd_type == cd_parse_command)
2596 while (*new_dir == ' ')
2597 new_dir++;
2600 olddir = g_strdup (panel->cwd);
2601 new_dir = translated_url = vfs_translate_url (new_dir);
2603 /* Convert *new_path to a suitable pathname, handle ~user */
2605 if (cd_type == cd_parse_command)
2607 if (!strcmp (new_dir, "-"))
2609 strcpy (temp, panel->lwd);
2610 new_dir = temp;
2613 directory = *new_dir ? new_dir : home_dir;
2615 if (mc_chdir (directory) == -1)
2617 strcpy (panel->cwd, olddir);
2618 g_free (olddir);
2619 g_free (translated_url);
2620 return FALSE;
2622 g_free (translated_url);
2624 /* Success: save previous directory, shutdown status of previous dir */
2625 strcpy (panel->lwd, olddir);
2626 input_free_completions (cmdline);
2628 mc_get_current_wd (panel->cwd, sizeof (panel->cwd) - 2);
2630 vfs_release_path (olddir);
2632 subshell_chdir (panel->cwd);
2634 /* Reload current panel */
2635 panel_clean_dir (panel);
2636 panel->count =
2637 do_load_dir (panel->cwd, &panel->dir, panel->current_sort_field->sort_routine,
2638 panel->reverse, panel->case_sensitive, panel->exec_first, panel->filter);
2639 try_to_select (panel, get_parent_dir_name (panel->cwd, olddir));
2640 load_hint (0);
2641 panel->dirty = 1;
2642 update_xterm_title_path ();
2644 g_free (olddir);
2646 return TRUE;
2649 /* --------------------------------------------------------------------------------------------- */
2651 static void
2652 directory_history_next (WPanel * panel)
2654 GList *nextdir;
2656 nextdir = g_list_next (panel->dir_history);
2658 if ((nextdir != NULL) && (_do_panel_cd (panel, (char *) nextdir->data, cd_exact)))
2659 panel->dir_history = nextdir;
2662 /* --------------------------------------------------------------------------------------------- */
2664 static void
2665 directory_history_prev (WPanel * panel)
2667 GList *prevdir;
2669 prevdir = g_list_previous (panel->dir_history);
2671 if ((prevdir != NULL) && (_do_panel_cd (panel, (char *) prevdir->data, cd_exact)))
2672 panel->dir_history = prevdir;
2675 /* --------------------------------------------------------------------------------------------- */
2677 static void
2678 directory_history_list (WPanel * panel)
2680 char *s;
2682 s = history_show (&panel->dir_history, &panel->widget);
2684 if (s != NULL)
2686 if (_do_panel_cd (panel, s, cd_exact))
2687 directory_history_add (panel, panel->cwd);
2688 else
2689 message (D_ERROR, MSG_ERROR, _("Cannot change directory"));
2690 g_free (s);
2694 /* --------------------------------------------------------------------------------------------- */
2696 static cb_ret_t
2697 panel_execute_cmd (WPanel * panel, unsigned long command)
2699 int res = MSG_HANDLED;
2701 if (command != CK_PanelStartSearch)
2702 stop_search (panel);
2704 switch (command)
2706 case CK_PanelChdirOtherPanel:
2707 chdir_other_panel (panel);
2708 break;
2709 case CK_PanelChdirToReadlink:
2710 chdir_to_readlink (panel);
2711 break;
2712 case CK_PanelCmdCopyLocal:
2713 copy_cmd_local ();
2714 break;
2715 case CK_PanelCmdDeleteLocal:
2716 delete_cmd_local ();
2717 break;
2718 case CK_PanelCmdDoEnter:
2719 do_enter (panel);
2720 break;
2721 case CK_PanelCmdViewSimple:
2722 view_simple_cmd ();
2723 break;
2724 case CK_PanelCmdEditNew:
2725 edit_cmd_new ();
2726 break;
2727 case CK_PanelCmdRenameLocal:
2728 rename_cmd_local ();
2729 break;
2730 case CK_PanelCmdReverseSelection:
2731 reverse_selection_cmd ();
2732 break;
2733 case CK_PanelCmdSelect:
2734 select_cmd ();
2735 break;
2736 case CK_PanelCmdUnselect:
2737 unselect_cmd ();
2738 break;
2739 case CK_PanelNextPage:
2740 next_page (panel);
2741 break;
2742 case CK_PanelPrevPage:
2743 prev_page (panel);
2744 break;
2745 case CK_PanelCtrlNextPage:
2746 ctrl_next_page (panel);
2747 break;
2748 case CK_PanelCtrlPrevPage:
2749 ctrl_prev_page (panel);
2750 break;
2751 case CK_PanelDirectoryHistoryList:
2752 directory_history_list (panel);
2753 break;
2754 case CK_PanelDirectoryHistoryNext:
2755 directory_history_next (panel);
2756 break;
2757 case CK_PanelDirectoryHistoryPrev:
2758 directory_history_prev (panel);
2759 break;
2760 case CK_PanelGotoBottomFile:
2761 goto_bottom_file (panel);
2762 break;
2763 case CK_PanelGotoMiddleFile:
2764 goto_middle_file (panel);
2765 break;
2766 case CK_PanelGotoTopFile:
2767 goto_top_file (panel);
2768 break;
2769 case CK_PanelMarkFile:
2770 mark_file (panel);
2771 break;
2772 case CK_PanelMarkFileUp:
2773 mark_file_up (panel);
2774 break;
2775 case CK_PanelMarkFileDown:
2776 mark_file_down (panel);
2777 break;
2778 case CK_PanelMoveUp:
2779 move_up (panel);
2780 break;
2781 case CK_PanelMoveDown:
2782 move_down (panel);
2783 break;
2784 case CK_PanelMoveLeft:
2785 res = move_left (panel);
2786 break;
2787 case CK_PanelMoveRight:
2788 res = move_right (panel);
2789 break;
2790 case CK_PanelMoveEnd:
2791 move_end (panel);
2792 break;
2793 case CK_PanelMoveHome:
2794 move_home (panel);
2795 break;
2796 case CK_PanelSetPanelEncoding:
2797 panel_change_encoding (panel);
2798 break;
2799 case CK_PanelStartSearch:
2800 start_search (panel);
2801 break;
2802 case CK_PanelStopSearch:
2803 break;
2804 case CK_PanelSyncOtherPanel:
2805 sync_other_panel (panel);
2806 break;
2807 case CK_PanelSelectSortOrder:
2808 panel_select_sort_order (panel);
2809 break;
2810 case CK_PanelToggleSortOrderPrev:
2811 panel_toggle_sort_order_prev (panel);
2812 break;
2813 case CK_PanelToggleSortOrderNext:
2814 panel_toggle_sort_order_next (panel);
2815 break;
2816 case CK_PanelReverseSort:
2817 panel->reverse = !panel->reverse;
2818 panel_set_sort_order (panel, panel->current_sort_field);
2819 break;
2820 case CK_PanelSortOrderByName:
2821 panel_set_sort_type_by_id (panel, "name");
2822 break;
2823 case CK_PanelSortOrderByExt:
2824 panel_set_sort_type_by_id (panel, "extension");
2825 break;
2826 case CK_PanelSortOrderBySize:
2827 panel_set_sort_type_by_id (panel, "size");
2828 break;
2829 case CK_PanelSortOrderByMTime:
2830 panel_set_sort_type_by_id (panel, "mtime");
2831 break;
2833 return res;
2836 /* --------------------------------------------------------------------------------------------- */
2838 static cb_ret_t
2839 panel_key (WPanel * panel, int key)
2841 size_t i;
2843 for (i = 0; panel_map[i].key != 0; i++)
2844 if (key == panel_map[i].key)
2845 return panel_execute_cmd (panel, panel_map[i].command);
2847 if (torben_fj_mode && key == ALT ('h'))
2849 goto_middle_file (panel);
2850 return MSG_HANDLED;
2853 if (is_abort_char (key))
2855 stop_search (panel);
2856 return MSG_HANDLED;
2859 /* Do not eat characters not meant for the panel below ' ' (e.g. C-l). */
2860 if ((key >= ' ' && key <= 255) || key == KEY_BACKSPACE)
2862 if (panel->searching)
2864 do_search (panel, key);
2865 return MSG_HANDLED;
2868 if (!command_prompt)
2870 start_search (panel);
2871 do_search (panel, key);
2872 return MSG_HANDLED;
2876 return MSG_NOT_HANDLED;
2879 /* --------------------------------------------------------------------------------------------- */
2881 static cb_ret_t
2882 panel_callback (Widget * w, widget_msg_t msg, int parm)
2884 WPanel *panel = (WPanel *) w;
2885 WButtonBar *bb;
2887 switch (msg)
2889 case WIDGET_DRAW:
2890 paint_panel (panel);
2891 return MSG_HANDLED;
2893 case WIDGET_FOCUS:
2894 current_panel = panel;
2895 panel->active = 1;
2896 if (mc_chdir (panel->cwd) != 0)
2898 char *cwd = strip_password (g_strdup (panel->cwd), 1);
2899 message (D_ERROR, MSG_ERROR, _("Cannot chdir to \"%s\"\n%s"),
2900 cwd, unix_error_string (errno));
2901 g_free (cwd);
2903 else
2904 subshell_chdir (panel->cwd);
2906 update_xterm_title_path ();
2907 select_item (panel);
2908 show_dir (panel);
2909 paint_dir (panel);
2910 panel->dirty = 0;
2912 bb = find_buttonbar (panel->widget.owner);
2913 midnight_set_buttonbar (bb);
2914 buttonbar_redraw (bb);
2915 return MSG_HANDLED;
2917 case WIDGET_UNFOCUS:
2918 /* Janne: look at this for the multiple panel options */
2919 stop_search (panel);
2920 panel->active = 0;
2921 show_dir (panel);
2922 unselect_item (panel);
2923 return MSG_HANDLED;
2925 case WIDGET_KEY:
2926 return panel_key (panel, parm);
2928 case WIDGET_COMMAND:
2929 return panel_execute_cmd (panel, parm);
2931 case WIDGET_DESTROY:
2932 panel_destroy (panel);
2933 free_my_statfs ();
2934 return MSG_HANDLED;
2936 default:
2937 return default_proc (msg, parm);
2941 /* --------------------------------------------------------------------------------------------- */
2942 /* */
2943 /* Panel mouse events support routines */
2944 /* */
2946 static void
2947 mouse_toggle_mark (WPanel * panel)
2949 do_mark_file (panel, MARK_DONT_MOVE);
2950 mouse_marking = selection (panel)->f.marked;
2953 /* --------------------------------------------------------------------------------------------- */
2955 static void
2956 mouse_set_mark (WPanel * panel)
2958 if (mouse_marking && !(selection (panel)->f.marked))
2959 do_mark_file (panel, MARK_DONT_MOVE);
2960 else if (!mouse_marking && (selection (panel)->f.marked))
2961 do_mark_file (panel, MARK_DONT_MOVE);
2964 /* --------------------------------------------------------------------------------------------- */
2966 static int
2967 mark_if_marking (WPanel * panel, Gpm_Event * event)
2969 if (event->buttons & GPM_B_RIGHT)
2971 if (event->type & GPM_DOWN)
2972 mouse_toggle_mark (panel);
2973 else
2974 mouse_set_mark (panel);
2975 return 1;
2977 return 0;
2980 /* --------------------------------------------------------------------------------------------- */
2981 /** Determine which column was clicked, and sort the panel on
2982 * that column, or reverse sort on that column if already
2983 * sorted on that column.
2986 static void
2987 mouse_sort_col (Gpm_Event * event, WPanel * panel)
2989 int i;
2990 const char *lc_sort_name = NULL;
2991 panel_field_t *col_sort_format = NULL;
2992 format_e *format;
2993 gchar *title;
2995 for (i = 0, format = panel->format; format != NULL; format = format->next)
2997 i += format->field_len;
2998 if (event->x < i + 1)
3000 /* found column */
3001 lc_sort_name = format->title;
3002 break;
3006 if (lc_sort_name == NULL)
3007 return;
3009 for (i = 0; panel_fields[i].id != NULL; i++)
3011 title = panel_get_title_without_hotkey (panel_fields[i].title_hotkey);
3012 if (!strcmp (lc_sort_name, title) && panel_fields[i].sort_routine)
3014 col_sort_format = &panel_fields[i];
3015 g_free (title);
3016 break;
3018 g_free (title);
3021 if (!col_sort_format)
3022 return;
3024 if (panel->current_sort_field == col_sort_format)
3026 /* reverse the sort if clicked column is already the sorted column */
3027 panel->reverse = !panel->reverse;
3029 else
3031 /* new sort is forced to be ascending */
3032 panel->reverse = 0;
3034 panel_set_sort_order (panel, col_sort_format);
3038 /* --------------------------------------------------------------------------------------------- */
3040 * Mouse callback of the panel minus repainting.
3041 * If the event is redirected to the menu, *redir is set to TRUE.
3043 static int
3044 do_panel_event (Gpm_Event * event, WPanel * panel, gboolean * redir)
3046 const int lines = llines (panel);
3047 const gboolean is_active = dlg_widget_active (panel);
3048 const gboolean mouse_down = (event->type & GPM_DOWN) != 0;
3050 *redir = FALSE;
3052 /* 1st line */
3053 if (mouse_down && event->y == 1)
3055 /* "<" button */
3056 if (event->x == 2)
3058 directory_history_prev (panel);
3059 return MOU_NORMAL;
3062 /* "." button show/hide hidden files */
3063 if (event->x == panel->widget.cols - 5)
3065 toggle_show_hidden ();
3066 repaint_screen ();
3067 return MOU_NORMAL;
3070 /* ">" button */
3071 if (event->x == panel->widget.cols - 1)
3073 directory_history_next (panel);
3074 return MOU_NORMAL;
3077 /* "^" button */
3078 if (event->x >= panel->widget.cols - 4 && event->x <= panel->widget.cols - 2)
3080 directory_history_list (panel);
3081 return MOU_NORMAL;
3084 /* rest of the upper frame, the menu is invisible - call menu */
3085 if (!menubar_visible)
3087 *redir = TRUE;
3088 event->x += panel->widget.x;
3089 return the_menubar->widget.mouse (event, the_menubar);
3092 /* no other events on 1st line */
3093 return MOU_NORMAL;
3096 /* sort on clicked column; don't handle wheel events */
3097 if (mouse_down && (event->buttons & (GPM_B_UP | GPM_B_DOWN)) == 0 && event->y == 2)
3099 mouse_sort_col (event, panel);
3100 return MOU_NORMAL;
3103 /* Mouse wheel events */
3104 if (mouse_down && (event->buttons & GPM_B_UP))
3106 if (is_active)
3108 if (panels_options.mouse_move_pages && (panel->top_file > 0))
3109 prev_page (panel);
3110 else /* We are in first page */
3111 move_up (panel);
3113 return MOU_NORMAL;
3116 if (mouse_down && (event->buttons & GPM_B_DOWN))
3118 if (is_active)
3120 if (panels_options.mouse_move_pages && (panel->top_file + ITEMS (panel) < panel->count))
3121 next_page (panel);
3122 else /* We are in last page */
3123 move_down (panel);
3125 return MOU_NORMAL;
3128 event->y -= 2;
3129 if ((event->type & (GPM_DOWN | GPM_DRAG)))
3131 int my_index;
3133 if (!is_active)
3134 change_panel ();
3136 if (panel->top_file + event->y > panel->count)
3137 my_index = panel->count - 1;
3138 else
3140 my_index = panel->top_file + event->y - 1;
3141 if (panel->split && (event->x > ((panel->widget.cols - 2) / 2)))
3142 my_index += llines (panel);
3144 if (my_index >= panel->count)
3145 my_index = panel->count - 1;
3148 if (my_index != panel->selected)
3150 unselect_item (panel);
3151 panel->selected = my_index;
3152 select_item (panel);
3155 /* This one is new */
3156 mark_if_marking (panel, event);
3158 else if ((event->type & (GPM_UP | GPM_DOUBLE)) == (GPM_UP | GPM_DOUBLE))
3160 if (event->y > 0 && event->y <= lines)
3161 do_enter (panel);
3163 return MOU_NORMAL;
3166 /* --------------------------------------------------------------------------------------------- */
3167 /** Mouse callback of the panel */
3169 static int
3170 panel_event (Gpm_Event * event, void *data)
3172 WPanel *panel = data;
3173 int ret;
3174 gboolean redir;
3176 ret = do_panel_event (event, panel, &redir);
3177 if (!redir)
3178 paint_panel (panel);
3180 return ret;
3183 /* --------------------------------------------------------------------------------------------- */
3185 static void
3186 reload_panelized (WPanel * panel)
3188 int i, j;
3189 dir_list *list = &panel->dir;
3191 if (panel != current_panel)
3193 int ret;
3194 ret = mc_chdir (panel->cwd);
3197 for (i = 0, j = 0; i < panel->count; i++)
3199 if (list->list[i].f.marked)
3201 /* Unmark the file in advance. In case the following mc_lstat
3202 * fails we are done, else we have to mark the file again
3203 * (Note: do_file_mark depends on a valid "list->list [i].buf").
3204 * IMO that's the best way to update the panel's summary status
3205 * -- Norbert
3207 do_file_mark (panel, i, 0);
3209 if (mc_lstat (list->list[i].fname, &list->list[i].st))
3211 g_free (list->list[i].fname);
3212 continue;
3214 if (list->list[i].f.marked)
3215 do_file_mark (panel, i, 1);
3216 if (j != i)
3217 list->list[j] = list->list[i];
3218 j++;
3220 if (j == 0)
3221 panel->count = set_zero_dir (list) ? 1 : 0;
3222 else
3223 panel->count = j;
3225 if (panel != current_panel)
3227 int ret;
3228 ret = mc_chdir (current_panel->cwd);
3232 /* --------------------------------------------------------------------------------------------- */
3234 static void
3235 update_one_panel_widget (WPanel * panel, int force_update, const char *current_file)
3237 int free_pointer;
3238 char *my_current_file = NULL;
3240 if (force_update & UP_RELOAD)
3242 panel->is_panelized = 0;
3243 mc_setctl (panel->cwd, VFS_SETCTL_FLUSH, 0);
3244 memset (&(panel->dir_stat), 0, sizeof (panel->dir_stat));
3247 /* If current_file == -1 (an invalid pointer) then preserve selection */
3248 if (current_file == UP_KEEPSEL)
3250 free_pointer = 1;
3251 my_current_file = g_strdup (panel->dir.list[panel->selected].fname);
3252 current_file = my_current_file;
3254 else
3255 free_pointer = 0;
3257 if (panel->is_panelized)
3258 reload_panelized (panel);
3259 else
3260 panel_reload (panel);
3262 try_to_select (panel, current_file);
3263 panel->dirty = 1;
3265 if (free_pointer)
3266 g_free (my_current_file);
3269 /* --------------------------------------------------------------------------------------------- */
3271 static void
3272 update_one_panel (int which, int force_update, const char *current_file)
3274 if (get_display_type (which) == view_listing)
3276 WPanel *panel;
3277 panel = (WPanel *) get_panel_widget (which);
3278 update_one_panel_widget (panel, force_update, current_file);
3282 /* --------------------------------------------------------------------------------------------- */
3283 /*** public functions ****************************************************************************/
3284 /* --------------------------------------------------------------------------------------------- */
3286 char *
3287 remove_encoding_from_path (const char *path)
3289 GString *ret;
3290 GString *tmp_path, *tmp_conv;
3291 char *tmp, *tmp2;
3292 const char *enc;
3293 GIConv converter;
3295 ret = g_string_new ("");
3296 tmp_conv = g_string_new ("");
3298 tmp_path = g_string_new (path);
3300 while ((tmp = g_strrstr (tmp_path->str, PATH_SEP_STR VFS_ENCODING_PREFIX)) != NULL)
3302 enc = vfs_get_encoding ((const char *) tmp);
3303 converter = enc ? str_crt_conv_to (enc) : str_cnv_to_term;
3304 if (converter == INVALID_CONV)
3305 converter = str_cnv_to_term;
3307 tmp2 = tmp + 1;
3308 while (*tmp2 && *tmp2 != PATH_SEP)
3309 tmp2++;
3311 if (*tmp2)
3313 str_vfs_convert_from (converter, tmp2, tmp_conv);
3314 g_string_prepend (ret, tmp_conv->str);
3315 g_string_set_size (tmp_conv, 0);
3317 g_string_set_size (tmp_path, tmp - tmp_path->str);
3318 str_close_conv (converter);
3320 g_string_prepend (ret, tmp_path->str);
3321 g_string_free (tmp_path, TRUE);
3322 g_string_free (tmp_conv, TRUE);
3324 tmp = ret->str;
3325 g_string_free (ret, FALSE);
3326 return tmp;
3329 /* --------------------------------------------------------------------------------------------- */
3331 * Repaint the contents of the panels without frames. To schedule panel
3332 * for repainting, set panel->dirty to 1. There are many reasons why
3333 * the panels need to be repainted, and this is a costly operation, so
3334 * it's done once per event.
3337 void
3338 update_dirty_panels (void)
3340 if (current_panel->dirty)
3341 paint_panel (current_panel);
3343 if ((get_other_type () == view_listing) && other_panel->dirty)
3344 paint_panel (other_panel);
3347 /* --------------------------------------------------------------------------------------------- */
3349 static void
3350 do_select (WPanel * panel, int i)
3352 if (i != panel->selected)
3354 panel->dirty = 1;
3355 panel->selected = i;
3356 panel->top_file = panel->selected - (panel->widget.lines - 2) / 2;
3357 if (panel->top_file < 0)
3358 panel->top_file = 0;
3362 /* --------------------------------------------------------------------------------------------- */
3364 static void
3365 do_try_to_select (WPanel * panel, const char *name)
3367 int i;
3368 char *subdir;
3370 if (!name)
3372 do_select (panel, 0);
3373 return;
3376 /* We only want the last component of the directory,
3377 * and from this only the name without suffix. */
3378 subdir = vfs_strip_suffix_from_filename (x_basename (name));
3380 /* Search that subdirectory, if found select it */
3381 for (i = 0; i < panel->count; i++)
3383 if (strcmp (subdir, panel->dir.list[i].fname) == 0)
3385 do_select (panel, i);
3386 g_free (subdir);
3387 return;
3391 /* Try to select a file near the file that is missing */
3392 if (panel->selected >= panel->count)
3393 do_select (panel, panel->count - 1);
3394 g_free (subdir);
3397 /* --------------------------------------------------------------------------------------------- */
3399 void
3400 try_to_select (WPanel * panel, const char *name)
3402 do_try_to_select (panel, name);
3403 select_item (panel);
3406 /* --------------------------------------------------------------------------------------------- */
3408 void
3409 panel_update_cols (Widget * widget, int frame_size)
3411 int cols, origin;
3413 if (horizontal_split)
3415 widget->cols = COLS;
3416 return;
3419 if (frame_size == frame_full)
3421 cols = COLS;
3422 origin = 0;
3424 else
3426 if (widget == get_panel_widget (0))
3428 cols = first_panel_size;
3429 origin = 0;
3431 else
3433 cols = COLS - first_panel_size;
3434 origin = first_panel_size;
3438 widget->cols = cols;
3439 widget->x = origin;
3442 /* --------------------------------------------------------------------------------------------- */
3444 void
3445 panel_clean_dir (WPanel * panel)
3447 int count = panel->count;
3449 panel->count = 0;
3450 panel->top_file = 0;
3451 panel->selected = 0;
3452 panel->marked = 0;
3453 panel->dirs_marked = 0;
3454 panel->total = 0;
3455 panel->searching = FALSE;
3456 panel->is_panelized = 0;
3457 panel->dirty = 1;
3459 clean_dir (&panel->dir, count);
3462 /* --------------------------------------------------------------------------------------------- */
3463 /** Panel creation.
3464 * @param panel_name the name of the panel for setup retieving
3465 * @returns new instance of WPanel
3468 WPanel *
3469 panel_new (const char *panel_name)
3471 return panel_new_with_dir (panel_name, NULL);
3474 /* --------------------------------------------------------------------------------------------- */
3475 /** Panel creation for specified directory.
3476 * @param panel_name specifies the name of the panel for setup retieving
3477 * @param the path of working panel directory. If path is NULL then panel will be created for current directory
3478 * @returns new instance of WPanel
3481 WPanel *
3482 panel_new_with_dir (const char *panel_name, const char *wpath)
3484 WPanel *panel;
3485 char *section;
3486 int i, err;
3487 char curdir[MC_MAXPATHLEN] = "\0";
3489 panel = g_new0 (WPanel, 1);
3491 /* No know sizes of the panel at startup */
3492 init_widget (&panel->widget, 0, 0, 0, 0, panel_callback, panel_event);
3494 /* We do not want the cursor */
3495 widget_want_cursor (panel->widget, 0);
3497 if (wpath != NULL)
3499 g_strlcpy (panel->cwd, wpath, sizeof (panel->cwd));
3500 mc_get_current_wd (curdir, sizeof (curdir) - 2);
3502 else
3503 mc_get_current_wd (panel->cwd, sizeof (panel->cwd) - 2);
3505 strcpy (panel->lwd, ".");
3507 panel->hist_name = g_strconcat ("Dir Hist ", panel_name, (char *) NULL);
3508 panel->dir_history = history_get (panel->hist_name);
3509 directory_history_add (panel, panel->cwd);
3511 panel->dir.list = g_new (file_entry, MIN_FILES);
3512 panel->dir.size = MIN_FILES;
3513 panel->active = 0;
3514 panel->filter = 0;
3515 panel->split = 0;
3516 panel->top_file = 0;
3517 panel->selected = 0;
3518 panel->marked = 0;
3519 panel->total = 0;
3520 panel->reverse = 0;
3521 panel->dirty = 1;
3522 panel->searching = FALSE;
3523 panel->dirs_marked = 0;
3524 panel->is_panelized = 0;
3525 panel->format = 0;
3526 panel->status_format = 0;
3527 panel->format_modified = 1;
3529 panel->panel_name = g_strdup (panel_name);
3530 panel->user_format = g_strdup (DEFAULT_USER_FORMAT);
3532 panel->codepage = SELECT_CHARSET_NO_TRANSLATE;
3534 for (i = 0; i < LIST_TYPES; i++)
3535 panel->user_status_format[i] = g_strdup (DEFAULT_USER_FORMAT);
3537 panel->search_buffer[0] = '\0';
3538 panel->prev_search_buffer[0] = '\0';
3539 panel->frame_size = frame_half;
3541 section = g_strconcat ("Temporal:", panel->panel_name, (char *) NULL);
3542 if (!mc_config_has_group (mc_main_config, section))
3544 g_free (section);
3545 section = g_strdup (panel->panel_name);
3547 panel_load_setup (panel, section);
3548 g_free (section);
3550 /* Load format strings */
3551 err = set_panel_formats (panel);
3552 if (err != 0)
3553 set_panel_formats (panel);
3555 #ifdef HAVE_CHARSET
3557 const char *enc = vfs_get_encoding (panel->cwd);
3558 if (enc != NULL)
3559 panel->codepage = get_codepage_index (enc);
3561 #endif
3563 if (mc_chdir (panel->cwd) != 0)
3565 panel->codepage = SELECT_CHARSET_NO_TRANSLATE;
3566 mc_get_current_wd (panel->cwd, sizeof (panel->cwd) - 2);
3569 /* Load the default format */
3570 panel->count =
3571 do_load_dir (panel->cwd, &panel->dir, panel->current_sort_field->sort_routine,
3572 panel->reverse, panel->case_sensitive, panel->exec_first, panel->filter);
3574 /* Restore old right path */
3575 if (curdir[0] != '\0')
3576 err = mc_chdir (curdir);
3578 return panel;
3581 /* --------------------------------------------------------------------------------------------- */
3583 void
3584 panel_reload (WPanel * panel)
3586 struct stat current_stat;
3588 if (panels_options.fast_reload && !stat (panel->cwd, &current_stat)
3589 && current_stat.st_ctime == panel->dir_stat.st_ctime
3590 && current_stat.st_mtime == panel->dir_stat.st_mtime)
3591 return;
3593 while (mc_chdir (panel->cwd) == -1)
3595 char *last_slash;
3597 if (panel->cwd[0] == PATH_SEP && panel->cwd[1] == 0)
3599 panel_clean_dir (panel);
3600 panel->count = set_zero_dir (&panel->dir) ? 1 : 0;
3601 return;
3603 last_slash = strrchr (panel->cwd, PATH_SEP);
3604 if (!last_slash || last_slash == panel->cwd)
3605 strcpy (panel->cwd, PATH_SEP_STR);
3606 else
3607 *last_slash = 0;
3608 memset (&(panel->dir_stat), 0, sizeof (panel->dir_stat));
3609 show_dir (panel);
3612 panel->count =
3613 do_reload_dir (panel->cwd, &panel->dir, panel->current_sort_field->sort_routine,
3614 panel->count, panel->reverse, panel->case_sensitive,
3615 panel->exec_first, panel->filter);
3617 panel->dirty = 1;
3618 if (panel->selected >= panel->count)
3619 do_select (panel, panel->count - 1);
3621 recalculate_panel_summary (panel);
3624 /* --------------------------------------------------------------------------------------------- */
3625 /* Switches the panel to the mode specified in the format */
3626 /* Seting up both format and status string. Return: 0 - on success; */
3627 /* 1 - format error; 2 - status error; 3 - errors in both formats. */
3630 set_panel_formats (WPanel * p)
3632 format_e *form;
3633 char *err = NULL;
3634 int retcode = 0;
3636 form = use_display_format (p, panel_format (p), &err, 0);
3638 if (err != NULL)
3640 g_free (err);
3641 retcode = 1;
3643 else
3645 delete_format (p->format);
3646 p->format = form;
3649 if (show_mini_info)
3651 form = use_display_format (p, mini_status_format (p), &err, 1);
3653 if (err != NULL)
3655 g_free (err);
3656 retcode += 2;
3658 else
3660 delete_format (p->status_format);
3661 p->status_format = form;
3665 panel_format_modified (p);
3666 panel_update_cols (&(p->widget), p->frame_size);
3668 if (retcode)
3669 message (D_ERROR, _("Warning"),
3670 _("User supplied format looks invalid, reverting to default."));
3671 if (retcode & 0x01)
3673 g_free (p->user_format);
3674 p->user_format = g_strdup (DEFAULT_USER_FORMAT);
3676 if (retcode & 0x02)
3678 g_free (p->user_status_format[p->list_type]);
3679 p->user_status_format[p->list_type] = g_strdup (DEFAULT_USER_FORMAT);
3682 return retcode;
3685 /* --------------------------------------------------------------------------------------------- */
3687 /* Select current item and readjust the panel */
3688 void
3689 select_item (WPanel * panel)
3691 int items = ITEMS (panel);
3693 /* Although currently all over the code we set the selection and
3694 top file to decent values before calling select_item, I could
3695 forget it someday, so it's better to do the actual fitting here */
3697 if (panel->top_file < 0)
3698 panel->top_file = 0;
3700 if (panel->selected < 0)
3701 panel->selected = 0;
3703 if (panel->selected > panel->count - 1)
3704 panel->selected = panel->count - 1;
3706 if (panel->top_file > panel->count - 1)
3707 panel->top_file = panel->count - 1;
3709 if ((panel->count - panel->top_file) < items)
3711 panel->top_file = panel->count - items;
3712 if (panel->top_file < 0)
3713 panel->top_file = 0;
3716 if (panel->selected < panel->top_file)
3717 panel->top_file = panel->selected;
3719 if ((panel->selected - panel->top_file) >= items)
3720 panel->top_file = panel->selected - items + 1;
3722 panel->dirty = 1;
3724 execute_hooks (select_file_hook);
3727 /* --------------------------------------------------------------------------------------------- */
3728 /** Clears all files in the panel, used only when one file was marked */
3729 void
3730 unmark_files (WPanel * panel)
3732 int i;
3734 if (!panel->marked)
3735 return;
3736 for (i = 0; i < panel->count; i++)
3737 file_mark (panel, i, 0);
3739 panel->dirs_marked = 0;
3740 panel->marked = 0;
3741 panel->total = 0;
3744 /* --------------------------------------------------------------------------------------------- */
3745 /** Recalculate the panels summary information, used e.g. when marked
3746 files might have been removed by an external command */
3748 void
3749 recalculate_panel_summary (WPanel * panel)
3751 int i;
3753 panel->marked = 0;
3754 panel->dirs_marked = 0;
3755 panel->total = 0;
3757 for (i = 0; i < panel->count; i++)
3758 if (panel->dir.list[i].f.marked)
3760 /* do_file_mark will return immediately if newmark == oldmark.
3761 So we have to first unmark it to get panel's summary information
3762 updated. (Norbert) */
3763 panel->dir.list[i].f.marked = 0;
3764 do_file_mark (panel, i, 1);
3768 /* --------------------------------------------------------------------------------------------- */
3769 /** This routine marks a file or a directory */
3771 void
3772 do_file_mark (WPanel * panel, int idx, int mark)
3774 if (panel->dir.list[idx].f.marked == mark)
3775 return;
3777 /* Only '..' can't be marked, '.' isn't visible */
3778 if (!strcmp (panel->dir.list[idx].fname, ".."))
3779 return;
3781 file_mark (panel, idx, mark);
3782 if (panel->dir.list[idx].f.marked)
3784 panel->marked++;
3785 if (S_ISDIR (panel->dir.list[idx].st.st_mode))
3787 if (panel->dir.list[idx].f.dir_size_computed)
3788 panel->total += panel->dir.list[idx].st.st_size;
3789 panel->dirs_marked++;
3791 else
3792 panel->total += panel->dir.list[idx].st.st_size;
3793 set_colors (panel);
3795 else
3797 if (S_ISDIR (panel->dir.list[idx].st.st_mode))
3799 if (panel->dir.list[idx].f.dir_size_computed)
3800 panel->total -= panel->dir.list[idx].st.st_size;
3801 panel->dirs_marked--;
3803 else
3804 panel->total -= panel->dir.list[idx].st.st_size;
3805 panel->marked--;
3809 /* --------------------------------------------------------------------------------------------- */
3811 * Changes the current directory of the panel.
3812 * Record change in the directory history.
3814 gboolean
3815 do_panel_cd (struct WPanel *panel, const char *new_dir, enum cd_enum cd_type)
3817 gboolean r;
3819 r = _do_panel_cd (panel, new_dir, cd_type);
3820 if (r)
3821 directory_history_add (panel, panel->cwd);
3822 return r;
3825 /* --------------------------------------------------------------------------------------------- */
3827 void
3828 file_mark (WPanel * panel, int lc_index, int val)
3830 if (panel->dir.list[lc_index].f.marked != val)
3832 panel->dir.list[lc_index].f.marked = val;
3833 panel->dirty = 1;
3837 /* --------------------------------------------------------------------------------------------- */
3839 void
3840 panel_re_sort (WPanel * panel)
3842 char *filename;
3843 int i;
3845 if (panel == NULL)
3846 return;
3848 filename = g_strdup (selection (panel)->fname);
3849 unselect_item (panel);
3850 do_sort (&panel->dir, panel->current_sort_field->sort_routine, panel->count - 1, panel->reverse,
3851 panel->case_sensitive, panel->exec_first);
3852 panel->selected = -1;
3853 for (i = panel->count; i; i--)
3855 if (!strcmp (panel->dir.list[i - 1].fname, filename))
3857 panel->selected = i - 1;
3858 break;
3861 g_free (filename);
3862 panel->top_file = panel->selected - ITEMS (panel) / 2;
3863 select_item (panel);
3864 panel->dirty = 1;
3867 /* --------------------------------------------------------------------------------------------- */
3869 void
3870 panel_set_sort_order (WPanel * panel, const panel_field_t * sort_order)
3872 if (sort_order == 0)
3873 return;
3875 panel->current_sort_field = sort_order;
3877 /* The directory is already sorted, we have to load the unsorted stuff */
3878 if (sort_order->sort_routine == (sortfn *) unsorted)
3880 char *current_file;
3882 current_file = g_strdup (panel->dir.list[panel->selected].fname);
3883 panel_reload (panel);
3884 try_to_select (panel, current_file);
3885 g_free (current_file);
3887 panel_re_sort (panel);
3890 /* --------------------------------------------------------------------------------------------- */
3892 * Change panel encoding.
3893 * @param panel WPanel object
3896 void
3897 panel_change_encoding (WPanel * panel)
3899 const char *encoding = NULL;
3900 char *cd_path;
3901 #ifdef HAVE_CHARSET
3902 char *errmsg;
3903 int r;
3905 r = select_charset (-1, -1, panel->codepage, FALSE);
3907 if (r == SELECT_CHARSET_CANCEL)
3908 return; /* Cancel */
3910 panel->codepage = r;
3912 if (panel->codepage == SELECT_CHARSET_NO_TRANSLATE)
3914 /* No translation */
3915 g_free (init_translation_table (display_codepage, display_codepage));
3916 cd_path = remove_encoding_from_path (panel->cwd);
3917 do_panel_cd (panel, cd_path, cd_parse_command);
3918 g_free (cd_path);
3919 return;
3922 errmsg = init_translation_table (panel->codepage, display_codepage);
3923 if (errmsg != NULL)
3925 message (D_ERROR, MSG_ERROR, "%s", errmsg);
3926 g_free (errmsg);
3927 return;
3930 encoding = get_codepage_id (panel->codepage);
3931 #endif
3932 if (encoding != NULL)
3934 const char *enc;
3936 enc = vfs_get_encoding (panel->cwd);
3938 /* don't add current encoding */
3939 if ((enc == NULL) || (strcmp (encoding, enc) != 0))
3941 cd_path = add_encoding_to_path (panel->cwd, encoding);
3942 if (!do_panel_cd (panel, cd_path, cd_parse_command))
3943 message (D_ERROR, MSG_ERROR, _("Cannot chdir to \"%s\""), cd_path);
3944 g_free (cd_path);
3949 /* --------------------------------------------------------------------------------------------- */
3951 * This routine reloads the directory in both panels. It tries to
3952 * select current_file in current_panel and other_file in other_panel.
3953 * If current_file == -1 then it automatically sets current_file and
3954 * other_file to the currently selected files in the panels.
3956 * if force_update has the UP_ONLY_CURRENT bit toggled on, then it
3957 * will not reload the other panel.
3960 void
3961 update_panels (int force_update, const char *current_file)
3963 int reload_other = !(force_update & UP_ONLY_CURRENT);
3964 WPanel *panel;
3965 int ret;
3967 update_one_panel (get_current_index (), force_update, current_file);
3968 if (reload_other)
3969 update_one_panel (get_other_index (), force_update, UP_KEEPSEL);
3971 if (get_current_type () == view_listing)
3972 panel = (WPanel *) get_panel_widget (get_current_index ());
3973 else
3974 panel = (WPanel *) get_panel_widget (get_other_index ());
3976 ret = mc_chdir (panel->cwd);
3979 /* --------------------------------------------------------------------------------------------- */
3981 void
3982 directory_history_add (struct WPanel *panel, const char *dir)
3984 char *tmp;
3986 tmp = g_strdup (dir);
3987 strip_password (tmp, 1);
3989 panel->dir_history = list_append_unique (panel->dir_history, tmp);
3992 /* --------------------------------------------------------------------------------------------- */
3994 gsize
3995 panel_get_num_of_sortable_fields (void)
3997 gsize ret = 0, lc_index;
3999 for (lc_index = 0; panel_fields[lc_index].id != NULL; lc_index++)
4000 if (panel_fields[lc_index].is_user_choice)
4001 ret++;
4002 return ret;
4005 /* --------------------------------------------------------------------------------------------- */
4007 const char **
4008 panel_get_sortable_fields (gsize * array_size)
4010 char **ret;
4011 gsize lc_index, i;
4013 lc_index = panel_get_num_of_sortable_fields ();
4015 ret = g_try_new0 (char *, lc_index + 1);
4016 if (ret == NULL)
4017 return NULL;
4019 if (array_size != NULL)
4020 *array_size = lc_index;
4022 lc_index = 0;
4024 for (i = 0; panel_fields[i].id != NULL; i++)
4025 if (panel_fields[i].is_user_choice)
4026 ret[lc_index++] = g_strdup (_(panel_fields[i].title_hotkey));
4027 return (const char **) ret;
4030 /* --------------------------------------------------------------------------------------------- */
4032 const panel_field_t *
4033 panel_get_field_by_id (const char *name)
4035 gsize lc_index;
4036 for (lc_index = 0; panel_fields[lc_index].id != NULL; lc_index++)
4037 if (panel_fields[lc_index].id != NULL && strcmp (name, panel_fields[lc_index].id) == 0)
4038 return &panel_fields[lc_index];
4039 return NULL;
4042 /* --------------------------------------------------------------------------------------------- */
4044 const panel_field_t *
4045 panel_get_field_by_title_hotkey (const char *name)
4047 gsize lc_index;
4048 for (lc_index = 0; panel_fields[lc_index].id != NULL; lc_index++)
4049 if (panel_fields[lc_index].title_hotkey != NULL &&
4050 strcmp (name, _(panel_fields[lc_index].title_hotkey)) == 0)
4051 return &panel_fields[lc_index];
4052 return NULL;
4055 /* --------------------------------------------------------------------------------------------- */
4057 const panel_field_t *
4058 panel_get_field_by_title (const char *name)
4060 gsize lc_index;
4061 gchar *title = NULL;
4063 for (lc_index = 0; panel_fields[lc_index].id != NULL; lc_index++)
4065 title = panel_get_title_without_hotkey (panel_fields[lc_index].title_hotkey);
4066 if (panel_fields[lc_index].title_hotkey != NULL && strcmp (name, title) == 0)
4068 g_free (title);
4069 return &panel_fields[lc_index];
4072 g_free (title);
4073 return NULL;
4076 /* --------------------------------------------------------------------------------------------- */
4078 gsize
4079 panel_get_num_of_user_possible_fields (void)
4081 gsize ret = 0, lc_index;
4083 for (lc_index = 0; panel_fields[lc_index].id != NULL; lc_index++)
4084 if (panel_fields[lc_index].use_in_user_format)
4085 ret++;
4086 return ret;
4089 /* --------------------------------------------------------------------------------------------- */
4091 const char **
4092 panel_get_user_possible_fields (gsize * array_size)
4094 char **ret;
4095 gsize lc_index, i;
4097 lc_index = panel_get_num_of_user_possible_fields ();
4099 ret = g_try_new0 (char *, lc_index + 1);
4100 if (ret == NULL)
4101 return NULL;
4103 if (array_size != NULL)
4104 *array_size = lc_index;
4106 lc_index = 0;
4108 for (i = 0; panel_fields[i].id != NULL; i++)
4109 if (panel_fields[i].use_in_user_format)
4110 ret[lc_index++] = g_strdup (_(panel_fields[i].title_hotkey));
4111 return (const char **) ret;
4114 /* --------------------------------------------------------------------------------------------- */
4116 void
4117 panel_init (void)
4119 panel_sort_up_sign = mc_skin_get ("widget-common", "sort-sign-up", "'");
4120 panel_sort_down_sign = mc_skin_get ("widget-common", "sort-sign-down", ",");
4122 panel_hiddenfiles_sign_show = mc_skin_get ("widget-panel", "hiddenfiles-sign-show", ".");
4123 panel_hiddenfiles_sign_hide = mc_skin_get ("widget-panel", "hiddenfiles-sign-hide", ".");
4124 panel_history_prev_item_sign = mc_skin_get ("widget-panel", "history-prev-item-sign", "<");
4125 panel_history_next_item_sign = mc_skin_get ("widget-panel", "history-next-item-sign", ">");
4126 panel_history_show_list_sign = mc_skin_get ("widget-panel", "history-show-list-sign", "^");
4130 /* --------------------------------------------------------------------------------------------- */
4132 void
4133 panel_deinit (void)
4135 g_free (panel_sort_up_sign);
4136 g_free (panel_sort_down_sign);
4138 g_free (panel_hiddenfiles_sign_show);
4139 g_free (panel_hiddenfiles_sign_hide);
4140 g_free (panel_history_prev_item_sign);
4141 g_free (panel_history_next_item_sign);
4142 g_free (panel_history_show_list_sign);
4146 /* --------------------------------------------------------------------------------------------- */