Ticket #4536: skins: add root variant of julia256 skin.
[midnight-commander.git] / src / filemanager / tree.c
blob2bfcf5051b5c0c3acf9c318c1eec95a878a283de
1 /*
2 Directory tree browser for the Midnight Commander
3 This module has been converted to be a widget.
5 The program load and saves the tree each time the tree widget is
6 created and destroyed. This is required for the future vfs layer,
7 it will be possible to have tree views over virtual file systems.
9 Copyright (C) 1994-2024
10 Free Software Foundation, Inc.
12 Written by:
13 Janne Kukonlehto, 1994, 1996
14 Norbert Warmuth, 1997
15 Miguel de Icaza, 1996, 1999
16 Slava Zanko <slavazanko@gmail.com>, 2013
17 Andrew Borodin <aborodin@vmail.ru>, 2013-2022
19 This file is part of the Midnight Commander.
21 The Midnight Commander is free software: you can redistribute it
22 and/or modify it under the terms of the GNU General Public License as
23 published by the Free Software Foundation, either version 3 of the License,
24 or (at your option) any later version.
26 The Midnight Commander is distributed in the hope that it will be useful,
27 but WITHOUT ANY WARRANTY; without even the implied warranty of
28 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 GNU General Public License for more details.
31 You should have received a copy of the GNU General Public License
32 along with this program. If not, see <http://www.gnu.org/licenses/>.
35 /** \file tree.c
36 * \brief Source: directory tree browser
39 #include <config.h>
41 #include <errno.h>
42 #include <stdio.h>
43 #include <string.h>
44 #include <sys/types.h>
46 #include "lib/global.h"
48 #include "lib/tty/tty.h"
49 #include "lib/tty/key.h"
50 #include "lib/skin.h"
51 #include "lib/vfs/vfs.h"
52 #include "lib/fileloc.h"
53 #include "lib/strutil.h"
54 #include "lib/util.h"
55 #include "lib/widget.h"
56 #include "lib/event.h" /* mc_event_raise() */
58 #include "src/setup.h" /* confirm_delete, panels_options */
59 #include "src/keymap.h"
60 #include "src/history.h"
62 #include "dir.h"
63 #include "filemanager.h" /* the_menubar */
64 #include "file.h" /* copy_dir_dir(), move_dir_dir(), erase_dir() */
65 #include "layout.h" /* command_prompt */
66 #include "treestore.h"
67 #include "cmd.h"
68 #include "filegui.h"
69 #include "cd.h" /* cd_error_message() */
71 #include "tree.h"
73 /*** global variables ****************************************************************************/
75 /* The pointer to the tree */
76 WTree *the_tree = NULL;
78 /* If this is true, then when browsing the tree the other window will
79 * automatically reload it's directory with the contents of the currently
80 * selected directory.
82 gboolean xtree_mode = FALSE;
84 /*** file scope macro definitions ****************************************************************/
86 #define tlines(t) (t->is_panel ? WIDGET (t)->rect.lines - 2 - \
87 (panels_options.show_mini_info ? 2 : 0) : WIDGET (t)->rect.lines)
89 /*** file scope type declarations ****************************************************************/
91 struct WTree
93 Widget widget;
94 struct TreeStore *store;
95 tree_entry *selected_ptr; /* The selected directory */
96 GString *search_buffer; /* Current search string */
97 tree_entry **tree_shown; /* Entries currently on screen */
98 gboolean is_panel; /* panel or plain widget flag */
99 gboolean searching; /* Are we on searching mode? */
100 int topdiff; /* The difference between the topmost
101 shown and the selected */
104 /*** forward declarations (file scope functions) *************************************************/
106 static void tree_rescan (void *data);
108 /*** file scope variables ************************************************************************/
110 /* Specifies the display mode: 1d or 2d */
111 static gboolean tree_navigation_flag = FALSE;
113 /* --------------------------------------------------------------------------------------------- */
114 /*** file scope functions ************************************************************************/
115 /* --------------------------------------------------------------------------------------------- */
117 static tree_entry *
118 back_ptr (tree_entry * ptr, int *count)
120 int i;
122 for (i = 0; ptr != NULL && ptr->prev != NULL && i < *count; ptr = ptr->prev, i++)
125 *count = i;
126 return ptr;
129 /* --------------------------------------------------------------------------------------------- */
131 static tree_entry *
132 forw_ptr (tree_entry * ptr, int *count)
134 int i;
136 for (i = 0; ptr != NULL && ptr->next != NULL && i < *count; ptr = ptr->next, i++)
139 *count = i;
140 return ptr;
143 /* --------------------------------------------------------------------------------------------- */
145 static void
146 remove_callback (tree_entry * entry, void *data)
148 WTree *tree = data;
150 if (tree->selected_ptr == entry)
152 if (tree->selected_ptr->next != NULL)
153 tree->selected_ptr = tree->selected_ptr->next;
154 else
155 tree->selected_ptr = tree->selected_ptr->prev;
159 /* --------------------------------------------------------------------------------------------- */
160 /** Save the ${XDG_CACHE_HOME}/mc/Tree file */
162 static void
163 save_tree (WTree * tree)
165 int error;
167 (void) tree;
169 error = tree_store_save ();
170 if (error != 0)
172 char *tree_name;
174 tree_name = mc_config_get_full_path (MC_TREESTORE_FILE);
175 fprintf (stderr, _("Cannot open the %s file for writing:\n%s\n"), tree_name,
176 unix_error_string (error));
177 g_free (tree_name);
181 /* --------------------------------------------------------------------------------------------- */
183 static void
184 tree_remove_entry (WTree * tree, const vfs_path_t * name_vpath)
186 (void) tree;
187 tree_store_remove_entry (name_vpath);
190 /* --------------------------------------------------------------------------------------------- */
192 static void
193 tree_destroy (WTree * tree)
195 tree_store_remove_entry_remove_hook (remove_callback);
196 save_tree (tree);
198 MC_PTR_FREE (tree->tree_shown);
199 g_string_free (tree->search_buffer, TRUE);
200 tree->selected_ptr = NULL;
203 /* --------------------------------------------------------------------------------------------- */
204 /** Loads the .mc.tree file */
206 static void
207 load_tree (WTree * tree)
209 vfs_path_t *vpath;
211 tree_store_load ();
213 tree->selected_ptr = tree->store->tree_first;
214 vpath = vfs_path_from_str (mc_config_get_home_dir ());
215 tree_chdir (tree, vpath);
216 vfs_path_free (vpath, TRUE);
219 /* --------------------------------------------------------------------------------------------- */
221 static void
222 tree_show_mini_info (WTree * tree, int tree_lines, int tree_cols)
224 Widget *w = WIDGET (tree);
225 int line;
227 /* Show mini info */
228 if (tree->is_panel)
230 if (!panels_options.show_mini_info)
231 return;
232 line = tree_lines + 2;
234 else
235 line = tree_lines + 1;
237 if (tree->searching)
239 /* Show search string */
240 tty_setcolor (INPUT_COLOR);
241 tty_draw_hline (w->rect.y + line, w->rect.x + 1, ' ', tree_cols);
242 widget_gotoyx (w, line, 1);
243 tty_print_char (PATH_SEP);
244 tty_print_string (str_fit_to_term (tree->search_buffer->str, tree_cols - 2, J_LEFT_FIT));
245 tty_print_char (' ');
247 else
249 /* Show full name of selected directory */
251 const int *colors;
253 colors = widget_get_colors (w);
254 tty_setcolor (tree->is_panel ? NORMAL_COLOR : colors[DLG_COLOR_NORMAL]);
255 tty_draw_hline (w->rect.y + line, w->rect.x + 1, ' ', tree_cols);
256 widget_gotoyx (w, line, 1);
257 tty_print_string (str_fit_to_term
258 (vfs_path_as_str (tree->selected_ptr->name), tree_cols, J_LEFT_FIT));
262 /* --------------------------------------------------------------------------------------------- */
264 static void
265 show_tree (WTree * tree)
267 Widget *w = WIDGET (tree);
268 tree_entry *current;
269 int i, j;
270 int topsublevel = 0;
271 int x = 0, y = 0;
272 int tree_lines, tree_cols;
274 /* Initialize */
275 tree_lines = tlines (tree);
276 tree_cols = w->rect.cols;
278 widget_gotoyx (w, y, x);
279 if (tree->is_panel)
281 tree_cols -= 2;
282 x = y = 1;
285 g_free (tree->tree_shown);
286 tree->tree_shown = g_new0 (tree_entry *, tree_lines);
288 if (tree->store->tree_first != NULL)
289 topsublevel = tree->store->tree_first->sublevel;
291 if (tree->selected_ptr == NULL)
293 tree->selected_ptr = tree->store->tree_first;
294 tree->topdiff = 0;
296 current = tree->selected_ptr;
298 /* Calculate the directory which is to be shown on the topmost line */
299 if (!tree_navigation_flag)
300 current = back_ptr (current, &tree->topdiff);
301 else
303 i = 0;
305 while (current->prev != NULL && i < tree->topdiff)
307 current = current->prev;
309 if (current->sublevel < tree->selected_ptr->sublevel)
311 if (vfs_path_equal (current->name, tree->selected_ptr->name))
312 i++;
314 else if (current->sublevel == tree->selected_ptr->sublevel)
316 const char *cname;
318 cname = vfs_path_as_str (current->name);
319 for (j = strlen (cname) - 1; !IS_PATH_SEP (cname[j]); j--)
321 if (vfs_path_equal_len (current->name, tree->selected_ptr->name, j))
322 i++;
324 else if (current->sublevel == tree->selected_ptr->sublevel + 1)
326 j = vfs_path_len (tree->selected_ptr->name);
327 if (j > 1 && vfs_path_equal_len (current->name, tree->selected_ptr->name, j))
328 i++;
331 tree->topdiff = i;
334 /* Loop for every line */
335 for (i = 0; i < tree_lines; i++)
337 const int *colors;
339 colors = widget_get_colors (w);
340 tty_setcolor (tree->is_panel ? NORMAL_COLOR : colors[DLG_COLOR_NORMAL]);
342 /* Move to the beginning of the line */
343 tty_draw_hline (w->rect.y + y + i, w->rect.x + x, ' ', tree_cols);
345 if (current == NULL)
346 continue;
348 if (tree->is_panel)
350 gboolean selected;
352 selected = widget_get_state (w, WST_FOCUSED) && current == tree->selected_ptr;
353 tty_setcolor (selected ? SELECTED_COLOR : NORMAL_COLOR);
355 else
357 int idx = current == tree->selected_ptr ? DLG_COLOR_FOCUS : DLG_COLOR_NORMAL;
359 tty_setcolor (colors[idx]);
362 tree->tree_shown[i] = current;
363 if (current->sublevel == topsublevel)
364 /* Show full name */
365 tty_print_string (str_fit_to_term
366 (vfs_path_as_str (current->name),
367 tree_cols + (tree->is_panel ? 0 : 1), J_LEFT_FIT));
368 else
370 /* Sub level directory */
371 tty_set_alt_charset (TRUE);
373 /* Output branch parts */
374 for (j = 0; j < current->sublevel - topsublevel - 1; j++)
376 if (tree_cols - 8 - 3 * j < 9)
377 break;
378 tty_print_char (' ');
379 if ((current->submask & (1 << (j + topsublevel + 1))) != 0)
380 tty_print_char (ACS_VLINE);
381 else
382 tty_print_char (' ');
383 tty_print_char (' ');
385 tty_print_char (' ');
386 j++;
387 if (current->next == NULL || (current->next->submask & (1 << current->sublevel)) == 0)
388 tty_print_char (ACS_LLCORNER);
389 else
390 tty_print_char (ACS_LTEE);
391 tty_print_char (ACS_HLINE);
392 tty_set_alt_charset (FALSE);
394 /* Show sub-name */
395 tty_print_char (' ');
396 tty_print_string (str_fit_to_term
397 (current->subname, tree_cols - x - 3 * j, J_LEFT_FIT));
400 /* Calculate the next value for current */
401 current = current->next;
402 if (tree_navigation_flag)
403 for (; current != NULL; current = current->next)
405 if (current->sublevel < tree->selected_ptr->sublevel)
407 if (vfs_path_equal_len (current->name, tree->selected_ptr->name,
408 vfs_path_len (current->name)))
409 break;
411 else if (current->sublevel == tree->selected_ptr->sublevel)
413 const char *cname;
415 cname = vfs_path_as_str (current->name);
416 for (j = strlen (cname) - 1; !IS_PATH_SEP (cname[j]); j--)
418 if (vfs_path_equal_len (current->name, tree->selected_ptr->name, j))
419 break;
421 else if (current->sublevel == tree->selected_ptr->sublevel + 1
422 && vfs_path_len (tree->selected_ptr->name) > 1)
424 if (vfs_path_equal_len (current->name, tree->selected_ptr->name,
425 vfs_path_len (tree->selected_ptr->name)))
426 break;
431 tree_show_mini_info (tree, tree_lines, tree_cols);
434 /* --------------------------------------------------------------------------------------------- */
436 static void
437 tree_check_focus (WTree * tree)
439 if (tree->topdiff < 3)
440 tree->topdiff = 3;
441 else if (tree->topdiff >= tlines (tree) - 3)
442 tree->topdiff = tlines (tree) - 3 - 1;
445 /* --------------------------------------------------------------------------------------------- */
447 static void
448 tree_move_backward (WTree * tree, int i)
450 if (!tree_navigation_flag)
451 tree->selected_ptr = back_ptr (tree->selected_ptr, &i);
452 else
454 tree_entry *current;
455 int j = 0;
457 current = tree->selected_ptr;
458 while (j < i && current->prev != NULL
459 && current->prev->sublevel >= tree->selected_ptr->sublevel)
461 current = current->prev;
462 if (current->sublevel == tree->selected_ptr->sublevel)
464 tree->selected_ptr = current;
465 j++;
468 i = j;
471 tree->topdiff -= i;
472 tree_check_focus (tree);
475 /* --------------------------------------------------------------------------------------------- */
477 static void
478 tree_move_forward (WTree * tree, int i)
480 if (!tree_navigation_flag)
481 tree->selected_ptr = forw_ptr (tree->selected_ptr, &i);
482 else
484 tree_entry *current;
485 int j = 0;
487 current = tree->selected_ptr;
488 while (j < i && current->next != NULL
489 && current->next->sublevel >= tree->selected_ptr->sublevel)
491 current = current->next;
492 if (current->sublevel == tree->selected_ptr->sublevel)
494 tree->selected_ptr = current;
495 j++;
498 i = j;
501 tree->topdiff += i;
502 tree_check_focus (tree);
505 /* --------------------------------------------------------------------------------------------- */
507 static void
508 tree_move_to_child (WTree * tree)
510 tree_entry *current;
512 /* Do we have a starting point? */
513 if (tree->selected_ptr == NULL)
514 return;
516 /* Take the next entry */
517 current = tree->selected_ptr->next;
518 /* Is it the child of the selected entry */
519 if (current != NULL && current->sublevel > tree->selected_ptr->sublevel)
521 /* Yes -> select this entry */
522 tree->selected_ptr = current;
523 tree->topdiff++;
524 tree_check_focus (tree);
526 else
528 /* No -> rescan and try again */
529 tree_rescan (tree);
530 current = tree->selected_ptr->next;
531 if (current != NULL && current->sublevel > tree->selected_ptr->sublevel)
533 tree->selected_ptr = current;
534 tree->topdiff++;
535 tree_check_focus (tree);
540 /* --------------------------------------------------------------------------------------------- */
542 static gboolean
543 tree_move_to_parent (WTree * tree)
545 tree_entry *current;
546 tree_entry *old;
548 if (tree->selected_ptr == NULL)
549 return FALSE;
551 old = tree->selected_ptr;
553 for (current = tree->selected_ptr->prev;
554 current != NULL && current->sublevel >= tree->selected_ptr->sublevel;
555 current = current->prev)
556 tree->topdiff--;
558 if (current == NULL)
559 current = tree->store->tree_first;
560 tree->selected_ptr = current;
561 tree_check_focus (tree);
562 return tree->selected_ptr != old;
565 /* --------------------------------------------------------------------------------------------- */
567 static void
568 tree_move_to_top (WTree * tree)
570 tree->selected_ptr = tree->store->tree_first;
571 tree->topdiff = 0;
574 /* --------------------------------------------------------------------------------------------- */
576 static void
577 tree_move_to_bottom (WTree * tree)
579 tree->selected_ptr = tree->store->tree_last;
580 tree->topdiff = tlines (tree) - 3 - 1;
583 /* --------------------------------------------------------------------------------------------- */
585 static void
586 tree_chdir_sel (WTree * tree)
588 if (tree->is_panel)
590 WPanel *p;
592 p = change_panel ();
594 if (panel_cd (p, tree->selected_ptr->name, cd_exact))
595 select_item (p);
596 else
597 cd_error_message (vfs_path_as_str (tree->selected_ptr->name));
599 widget_draw (WIDGET (p));
600 (void) change_panel ();
601 show_tree (tree);
603 else
605 WDialog *h = DIALOG (WIDGET (tree)->owner);
607 h->ret_value = B_ENTER;
608 dlg_close (h);
612 /* --------------------------------------------------------------------------------------------- */
614 static void
615 maybe_chdir (WTree * tree)
617 if (xtree_mode && tree->is_panel && is_idle ())
618 tree_chdir_sel (tree);
621 /* --------------------------------------------------------------------------------------------- */
622 /** Search tree for text */
624 static gboolean
625 search_tree (WTree * tree, const GString * text)
627 tree_entry *current = tree->selected_ptr;
628 gboolean wrapped = FALSE;
629 gboolean found = FALSE;
631 while (!found && (!wrapped || current != tree->selected_ptr))
632 if (strncmp (current->subname, text->str, text->len) == 0)
634 tree->selected_ptr = current;
635 found = TRUE;
637 else
639 current = current->next;
640 if (current == NULL)
642 current = tree->store->tree_first;
643 wrapped = TRUE;
646 tree->topdiff++;
649 tree_check_focus (tree);
650 return found;
653 /* --------------------------------------------------------------------------------------------- */
655 static void
656 tree_do_search (WTree * tree, int key)
658 /* TODO: support multi-byte characters, see do_search() in panel.c */
660 if (tree->search_buffer->len != 0 && key == KEY_BACKSPACE)
661 g_string_set_size (tree->search_buffer, tree->search_buffer->len - 1);
662 else if (key != 0)
663 g_string_append_c (tree->search_buffer, (gchar) key);
665 if (!search_tree (tree, tree->search_buffer))
666 g_string_set_size (tree->search_buffer, tree->search_buffer->len - 1);
668 show_tree (tree);
669 maybe_chdir (tree);
672 /* --------------------------------------------------------------------------------------------- */
674 static void
675 tree_rescan (void *data)
677 WTree *tree = data;
678 vfs_path_t *old_vpath;
680 old_vpath = vfs_path_clone (vfs_get_raw_current_dir ());
681 if (old_vpath == NULL)
682 return;
684 if (tree->selected_ptr != NULL && mc_chdir (tree->selected_ptr->name) == 0)
686 int ret;
688 tree_store_rescan (tree->selected_ptr->name);
689 ret = mc_chdir (old_vpath);
690 (void) ret;
692 vfs_path_free (old_vpath, TRUE);
695 /* --------------------------------------------------------------------------------------------- */
697 static void
698 tree_forget (void *data)
700 WTree *tree = data;
702 if (tree->selected_ptr != NULL)
703 tree_remove_entry (tree, tree->selected_ptr->name);
706 /* --------------------------------------------------------------------------------------------- */
708 static void
709 tree_copy (WTree * tree, const char *default_dest)
711 char msg[BUF_MEDIUM];
712 char *dest;
714 if (tree->selected_ptr == NULL)
715 return;
717 g_snprintf (msg, sizeof (msg), _("Copy \"%s\" directory to:"),
718 str_trunc (vfs_path_as_str (tree->selected_ptr->name), 50));
719 dest = input_expand_dialog (Q_ ("DialogTitle|Copy"),
720 msg, MC_HISTORY_FM_TREE_COPY, default_dest,
721 INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_CD);
723 if (dest != NULL && *dest != '\0')
725 file_op_context_t *ctx;
726 file_op_total_context_t *tctx;
728 ctx = file_op_context_new (OP_COPY);
729 tctx = file_op_total_context_new ();
730 file_op_context_create_ui (ctx, FALSE, FILEGUI_DIALOG_MULTI_ITEM);
731 tctx->ask_overwrite = FALSE;
732 copy_dir_dir (tctx, ctx, vfs_path_as_str (tree->selected_ptr->name), dest, TRUE, FALSE,
733 FALSE, NULL);
734 file_op_total_context_destroy (tctx);
735 file_op_context_destroy (ctx);
738 g_free (dest);
741 /* --------------------------------------------------------------------------------------------- */
743 static void
744 tree_move (WTree * tree, const char *default_dest)
746 char msg[BUF_MEDIUM];
747 char *dest;
749 if (tree->selected_ptr == NULL)
750 return;
752 g_snprintf (msg, sizeof (msg), _("Move \"%s\" directory to:"),
753 str_trunc (vfs_path_as_str (tree->selected_ptr->name), 50));
754 dest =
755 input_expand_dialog (Q_ ("DialogTitle|Move"), msg, MC_HISTORY_FM_TREE_MOVE, default_dest,
756 INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_CD);
758 if (dest != NULL && *dest != '\0')
760 vfs_path_t *dest_vpath;
761 struct stat buf;
763 dest_vpath = vfs_path_from_str (dest);
765 if (mc_stat (dest_vpath, &buf) != 0)
766 message (D_ERROR, MSG_ERROR, _("Cannot stat the destination\n%s"),
767 unix_error_string (errno));
768 else if (!S_ISDIR (buf.st_mode))
769 file_error (TRUE, _("Destination \"%s\" must be a directory\n%s"), dest);
770 else
772 file_op_context_t *ctx;
773 file_op_total_context_t *tctx;
775 ctx = file_op_context_new (OP_MOVE);
776 tctx = file_op_total_context_new ();
777 file_op_context_create_ui (ctx, FALSE, FILEGUI_DIALOG_ONE_ITEM);
778 move_dir_dir (tctx, ctx, vfs_path_as_str (tree->selected_ptr->name), dest);
779 file_op_total_context_destroy (tctx);
780 file_op_context_destroy (ctx);
783 vfs_path_free (dest_vpath, TRUE);
786 g_free (dest);
789 /* --------------------------------------------------------------------------------------------- */
791 #if 0
792 static void
793 tree_mkdir (WTree * tree)
795 char old_dir[MC_MAXPATHLEN];
797 if (tree->selected_ptr == NULL || chdir (tree->selected_ptr->name) != 0)
798 return;
799 /* FIXME
800 mkdir_cmd (tree);
802 tree_rescan (tree);
803 chdir (old_dir);
805 #endif
807 /* --------------------------------------------------------------------------------------------- */
809 static void
810 tree_rmdir (void *data)
812 WTree *tree = data;
813 file_op_context_t *ctx;
814 file_op_total_context_t *tctx;
816 if (tree->selected_ptr == NULL)
817 return;
819 if (confirm_delete)
821 char *buf;
822 int result;
824 buf = g_strdup_printf (_("Delete %s?"), vfs_path_as_str (tree->selected_ptr->name));
826 result = query_dialog (Q_ ("DialogTitle|Delete"), buf, D_ERROR, 2, _("&Yes"), _("&No"));
827 g_free (buf);
828 if (result != 0)
829 return;
832 ctx = file_op_context_new (OP_DELETE);
833 tctx = file_op_total_context_new ();
835 file_op_context_create_ui (ctx, FALSE, FILEGUI_DIALOG_ONE_ITEM);
836 if (erase_dir (tctx, ctx, tree->selected_ptr->name) == FILE_CONT)
837 tree_forget (tree);
838 file_op_total_context_destroy (tctx);
839 file_op_context_destroy (ctx);
842 /* --------------------------------------------------------------------------------------------- */
844 static inline void
845 tree_move_up (WTree * tree)
847 tree_move_backward (tree, 1);
848 show_tree (tree);
849 maybe_chdir (tree);
852 /* --------------------------------------------------------------------------------------------- */
854 static inline void
855 tree_move_down (WTree * tree)
857 tree_move_forward (tree, 1);
858 show_tree (tree);
859 maybe_chdir (tree);
862 /* --------------------------------------------------------------------------------------------- */
864 static inline void
865 tree_move_home (WTree * tree)
867 tree_move_to_top (tree);
868 show_tree (tree);
869 maybe_chdir (tree);
872 /* --------------------------------------------------------------------------------------------- */
874 static inline void
875 tree_move_end (WTree * tree)
877 tree_move_to_bottom (tree);
878 show_tree (tree);
879 maybe_chdir (tree);
882 /* --------------------------------------------------------------------------------------------- */
884 static void
885 tree_move_pgup (WTree * tree)
887 tree_move_backward (tree, tlines (tree) - 1);
888 show_tree (tree);
889 maybe_chdir (tree);
892 /* --------------------------------------------------------------------------------------------- */
894 static void
895 tree_move_pgdn (WTree * tree)
897 tree_move_forward (tree, tlines (tree) - 1);
898 show_tree (tree);
899 maybe_chdir (tree);
902 /* --------------------------------------------------------------------------------------------- */
904 static gboolean
905 tree_move_left (WTree * tree)
907 gboolean v = FALSE;
909 if (tree_navigation_flag)
911 v = tree_move_to_parent (tree);
912 show_tree (tree);
913 maybe_chdir (tree);
916 return v;
919 /* --------------------------------------------------------------------------------------------- */
921 static gboolean
922 tree_move_right (WTree * tree)
924 gboolean v = FALSE;
926 if (tree_navigation_flag)
928 tree_move_to_child (tree);
929 show_tree (tree);
930 maybe_chdir (tree);
931 v = TRUE;
934 return v;
937 /* --------------------------------------------------------------------------------------------- */
939 static void
940 tree_start_search (WTree * tree)
942 if (tree->searching)
944 if (tree->selected_ptr == tree->store->tree_last)
945 tree_move_to_top (tree);
946 else
948 gboolean i;
950 /* set navigation mode temporarily to 'Static' because in
951 * dynamic navigation mode tree_move_forward will not move
952 * to a lower sublevel if necessary (sequent searches must
953 * start with the directory followed the last found directory)
955 i = tree_navigation_flag;
956 tree_navigation_flag = FALSE;
957 tree_move_forward (tree, 1);
958 tree_navigation_flag = i;
960 tree_do_search (tree, 0);
962 else
964 tree->searching = TRUE;
965 g_string_set_size (tree->search_buffer, 0);
969 /* --------------------------------------------------------------------------------------------- */
971 static void
972 tree_toggle_navig (WTree * tree)
974 Widget *w = WIDGET (tree);
975 WButtonBar *b;
977 tree_navigation_flag = !tree_navigation_flag;
979 b = buttonbar_find (DIALOG (w->owner));
980 buttonbar_set_label (b, 4,
981 tree_navigation_flag ? Q_ ("ButtonBar|Static") : Q_ ("ButtonBar|Dynamc"),
982 w->keymap, w);
983 widget_draw (WIDGET (b));
986 /* --------------------------------------------------------------------------------------------- */
988 static void
989 tree_help (void)
991 ev_help_t event_data = { NULL, "[Directory Tree]" };
993 mc_event_raise (MCEVENT_GROUP_CORE, "help", &event_data);
996 /* --------------------------------------------------------------------------------------------- */
998 static cb_ret_t
999 tree_execute_cmd (WTree * tree, long command)
1001 cb_ret_t res = MSG_HANDLED;
1003 if (command != CK_Search)
1004 tree->searching = FALSE;
1006 switch (command)
1008 case CK_Help:
1009 tree_help ();
1010 break;
1011 case CK_Forget:
1012 tree_forget (tree);
1013 break;
1014 case CK_ToggleNavigation:
1015 tree_toggle_navig (tree);
1016 break;
1017 case CK_Copy:
1018 tree_copy (tree, "");
1019 break;
1020 case CK_Move:
1021 tree_move (tree, "");
1022 break;
1023 case CK_Up:
1024 tree_move_up (tree);
1025 break;
1026 case CK_Down:
1027 tree_move_down (tree);
1028 break;
1029 case CK_Top:
1030 tree_move_home (tree);
1031 break;
1032 case CK_Bottom:
1033 tree_move_end (tree);
1034 break;
1035 case CK_PageUp:
1036 tree_move_pgup (tree);
1037 break;
1038 case CK_PageDown:
1039 tree_move_pgdn (tree);
1040 break;
1041 case CK_Enter:
1042 tree_chdir_sel (tree);
1043 break;
1044 case CK_Reread:
1045 tree_rescan (tree);
1046 break;
1047 case CK_Search:
1048 tree_start_search (tree);
1049 break;
1050 case CK_Delete:
1051 tree_rmdir (tree);
1052 break;
1053 case CK_Quit:
1054 if (!tree->is_panel)
1055 dlg_close (DIALOG (WIDGET (tree)->owner));
1056 return res;
1057 default:
1058 res = MSG_NOT_HANDLED;
1061 show_tree (tree);
1063 return res;
1066 /* --------------------------------------------------------------------------------------------- */
1068 static cb_ret_t
1069 tree_key (WTree * tree, int key)
1071 long command;
1073 if (is_abort_char (key))
1075 if (tree->is_panel)
1077 tree->searching = FALSE;
1078 show_tree (tree);
1079 return MSG_HANDLED; /* eat abort char */
1081 /* modal tree dialog: let upper layer see the
1082 abort character and close the dialog */
1083 return MSG_NOT_HANDLED;
1086 if (tree->searching && ((key >= ' ' && key <= 255) || key == KEY_BACKSPACE))
1088 tree_do_search (tree, key);
1089 show_tree (tree);
1090 return MSG_HANDLED;
1093 command = widget_lookup_key (WIDGET (tree), key);
1094 switch (command)
1096 case CK_IgnoreKey:
1097 break;
1098 case CK_Left:
1099 return tree_move_left (tree) ? MSG_HANDLED : MSG_NOT_HANDLED;
1100 case CK_Right:
1101 return tree_move_right (tree) ? MSG_HANDLED : MSG_NOT_HANDLED;
1102 default:
1103 tree_execute_cmd (tree, command);
1104 return MSG_HANDLED;
1107 /* Do not eat characters not meant for the tree below ' ' (e.g. C-l). */
1108 if (!command_prompt && ((key >= ' ' && key <= 255) || key == KEY_BACKSPACE))
1110 tree_start_search (tree);
1111 tree_do_search (tree, key);
1112 return MSG_HANDLED;
1115 return MSG_NOT_HANDLED;
1118 /* --------------------------------------------------------------------------------------------- */
1120 static void
1121 tree_frame (WDialog * h, WTree * tree)
1123 Widget *w = WIDGET (tree);
1125 (void) h;
1127 tty_setcolor (NORMAL_COLOR);
1128 widget_erase (w);
1129 if (tree->is_panel)
1131 const char *title = _("Directory tree");
1132 const int len = str_term_width1 (title);
1134 tty_draw_box (w->rect.y, w->rect.x, w->rect.lines, w->rect.cols, FALSE);
1136 widget_gotoyx (w, 0, (w->rect.cols - len - 2) / 2);
1137 tty_printf (" %s ", title);
1139 if (panels_options.show_mini_info)
1141 int y;
1143 y = w->rect.lines - 3;
1144 widget_gotoyx (w, y, 0);
1145 tty_print_alt_char (ACS_LTEE, FALSE);
1146 widget_gotoyx (w, y, w->rect.cols - 1);
1147 tty_print_alt_char (ACS_RTEE, FALSE);
1148 tty_draw_hline (w->rect.y + y, w->rect.x + 1, ACS_HLINE, w->rect.cols - 2);
1153 /* --------------------------------------------------------------------------------------------- */
1155 static cb_ret_t
1156 tree_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
1158 WTree *tree = (WTree *) w;
1159 WDialog *h = DIALOG (w->owner);
1160 WButtonBar *b;
1162 switch (msg)
1164 case MSG_DRAW:
1165 tree_frame (h, tree);
1166 show_tree (tree);
1167 if (widget_get_state (w, WST_FOCUSED))
1169 b = buttonbar_find (h);
1170 widget_draw (WIDGET (b));
1172 return MSG_HANDLED;
1174 case MSG_FOCUS:
1175 b = buttonbar_find (h);
1176 buttonbar_set_label (b, 1, Q_ ("ButtonBar|Help"), w->keymap, w);
1177 buttonbar_set_label (b, 2, Q_ ("ButtonBar|Rescan"), w->keymap, w);
1178 buttonbar_set_label (b, 3, Q_ ("ButtonBar|Forget"), w->keymap, w);
1179 buttonbar_set_label (b, 4, tree_navigation_flag ? Q_ ("ButtonBar|Static")
1180 : Q_ ("ButtonBar|Dynamc"), w->keymap, w);
1181 buttonbar_set_label (b, 5, Q_ ("ButtonBar|Copy"), w->keymap, w);
1182 buttonbar_set_label (b, 6, Q_ ("ButtonBar|RenMov"), w->keymap, w);
1183 #if 0
1184 /* FIXME: mkdir is currently defunct */
1185 buttonbar_set_label (b, 7, Q_ ("ButtonBar|Mkdir"), w->keymap, w);
1186 #else
1187 buttonbar_clear_label (b, 7, w);
1188 #endif
1189 buttonbar_set_label (b, 8, Q_ ("ButtonBar|Rmdir"), w->keymap, w);
1191 return MSG_HANDLED;
1193 case MSG_UNFOCUS:
1194 tree->searching = FALSE;
1195 return MSG_HANDLED;
1197 case MSG_KEY:
1198 return tree_key (tree, parm);
1200 case MSG_ACTION:
1201 /* command from buttonbar */
1202 return tree_execute_cmd (tree, parm);
1204 case MSG_DESTROY:
1205 tree_destroy (tree);
1206 return MSG_HANDLED;
1208 default:
1209 return widget_default_callback (w, sender, msg, parm, data);
1213 /* --------------------------------------------------------------------------------------------- */
1216 * Mouse callback
1218 static void
1219 tree_mouse_callback (Widget * w, mouse_msg_t msg, mouse_event_t * event)
1221 WTree *tree = (WTree *) w;
1222 int y;
1224 y = event->y;
1225 if (tree->is_panel)
1226 y--;
1228 switch (msg)
1230 case MSG_MOUSE_DOWN:
1231 /* rest of the upper frame - call menu */
1232 if (tree->is_panel && event->y == WIDGET (w->owner)->rect.y)
1234 /* return MOU_UNHANDLED */
1235 event->result.abort = TRUE;
1237 else if (!widget_get_state (w, WST_FOCUSED))
1238 (void) change_panel ();
1239 break;
1241 case MSG_MOUSE_CLICK:
1243 int lines;
1245 lines = tlines (tree);
1247 if (y < 0)
1249 tree_move_backward (tree, lines - 1);
1250 show_tree (tree);
1252 else if (y >= lines)
1254 tree_move_forward (tree, lines - 1);
1255 show_tree (tree);
1257 else if ((event->count & GPM_DOUBLE) != 0)
1259 if (tree->tree_shown[y] != NULL)
1261 tree->selected_ptr = tree->tree_shown[y];
1262 tree->topdiff = y;
1265 tree_chdir_sel (tree);
1268 break;
1270 case MSG_MOUSE_SCROLL_UP:
1271 case MSG_MOUSE_SCROLL_DOWN:
1272 /* TODO: Ticket #2218 */
1273 break;
1275 default:
1276 break;
1280 /* --------------------------------------------------------------------------------------------- */
1281 /*** public functions ****************************************************************************/
1282 /* --------------------------------------------------------------------------------------------- */
1284 WTree *
1285 tree_new (const WRect * r, gboolean is_panel)
1287 WTree *tree;
1288 Widget *w;
1290 tree = g_new (WTree, 1);
1292 w = WIDGET (tree);
1293 widget_init (w, r, tree_callback, tree_mouse_callback);
1294 w->options |= WOP_SELECTABLE | WOP_TOP_SELECT;
1295 w->keymap = tree_map;
1297 tree->is_panel = is_panel;
1298 tree->selected_ptr = NULL;
1300 tree->store = tree_store_get ();
1301 tree_store_add_entry_remove_hook (remove_callback, tree);
1302 tree->tree_shown = NULL;
1303 tree->search_buffer = g_string_sized_new (MC_MAXPATHLEN);
1304 tree->topdiff = w->rect.lines / 2;
1305 tree->searching = FALSE;
1307 load_tree (tree);
1308 return tree;
1311 /* --------------------------------------------------------------------------------------------- */
1313 void
1314 tree_chdir (WTree * tree, const vfs_path_t * dir)
1316 tree_entry *current;
1318 current = tree_store_whereis (dir);
1319 if (current != NULL)
1321 tree->selected_ptr = current;
1322 tree_check_focus (tree);
1326 /* --------------------------------------------------------------------------------------------- */
1327 /** Return name of the currently selected entry */
1329 const vfs_path_t *
1330 tree_selected_name (const WTree * tree)
1332 return tree->selected_ptr->name;
1335 /* --------------------------------------------------------------------------------------------- */
1337 void
1338 sync_tree (const vfs_path_t * vpath)
1340 tree_chdir (the_tree, vpath);
1343 /* --------------------------------------------------------------------------------------------- */
1345 WTree *
1346 find_tree (const WDialog * h)
1348 return (WTree *) widget_find_by_type (CONST_WIDGET (h), tree_callback);
1351 /* --------------------------------------------------------------------------------------------- */