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