Drop old mouse API and use the new one.
[midnight-commander.git] / src / filemanager / tree.c
blob44b96447ba138031038175a7934a56f01ce93d0d
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-2016
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, 2014, 2016
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/keybind-defaults.h"
60 #include "src/history.h"
62 #include "dir.h"
63 #include "midnight.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"
70 #include "tree.h"
72 /*** global variables ****************************************************************************/
74 /* The pointer to the tree */
75 WTree *the_tree = NULL;
77 /* If this is true, then when browsing the tree the other window will
78 * automatically reload it's directory with the contents of the currently
79 * selected directory.
81 int xtree_mode = 0;
83 /*** file scope macro definitions ****************************************************************/
85 #define tlines(t) (t->is_panel ? WIDGET (t)->lines - 2 - \
86 (panels_options.show_mini_info ? 2 : 0) : WIDGET (t)->lines)
88 /* Use the color of the parent widget for the unselected entries */
89 #define TREE_NORMALC(h) (h->color[DLG_COLOR_NORMAL])
90 #define TREE_CURRENTC(h) (h->color[DLG_COLOR_FOCUS])
92 /*** file scope type declarations ****************************************************************/
94 struct WTree
96 Widget widget;
97 struct TreeStore *store;
98 tree_entry *selected_ptr; /* The selected directory */
99 char search_buffer[MC_MAXFILENAMELEN]; /* Current search string */
100 tree_entry **tree_shown; /* Entries currently on screen */
101 int is_panel; /* panel or plain widget flag */
102 int active; /* if it's currently selected */
103 int searching; /* Are we on searching mode? */
104 int topdiff; /* The difference between the topmost
105 shown and the selected */
108 /*** file scope variables ************************************************************************/
110 /* Specifies the display mode: 1d or 2d */
111 static gboolean tree_navigation_flag = FALSE;
113 /*** file scope functions ************************************************************************/
114 /* --------------------------------------------------------------------------------------------- */
116 static void tree_rescan (void *data);
118 /* --------------------------------------------------------------------------------------------- */
120 static tree_entry *
121 back_ptr (tree_entry * ptr, int *count)
123 int i = 0;
125 while (ptr && ptr->prev && i < *count)
127 ptr = ptr->prev;
128 i++;
130 *count = i;
131 return ptr;
134 /* --------------------------------------------------------------------------------------------- */
136 static tree_entry *
137 forw_ptr (tree_entry * ptr, int *count)
139 int i = 0;
141 while (ptr && ptr->next && i < *count)
143 ptr = ptr->next;
144 i++;
146 *count = i;
147 return ptr;
150 /* --------------------------------------------------------------------------------------------- */
152 static void
153 remove_callback (tree_entry * entry, void *data)
155 WTree *tree = data;
157 if (tree->selected_ptr == entry)
159 if (tree->selected_ptr->next)
160 tree->selected_ptr = tree->selected_ptr->next;
161 else
162 tree->selected_ptr = tree->selected_ptr->prev;
166 /* --------------------------------------------------------------------------------------------- */
167 /** Save the ${XDG_CACHE_HOME}/mc/Tree file */
169 static void
170 save_tree (WTree * tree)
172 int error;
174 (void) tree;
175 error = tree_store_save ();
177 if (error)
179 char *tree_name;
181 tree_name = mc_config_get_full_path (MC_TREESTORE_FILE);
182 fprintf (stderr, _("Cannot open the %s file for writing:\n%s\n"), tree_name,
183 unix_error_string (error));
184 g_free (tree_name);
188 /* --------------------------------------------------------------------------------------------- */
190 static void
191 tree_remove_entry (WTree * tree, const vfs_path_t * name_vpath)
193 (void) tree;
194 tree_store_remove_entry (name_vpath);
197 /* --------------------------------------------------------------------------------------------- */
199 static void
200 tree_destroy (WTree * tree)
202 tree_store_remove_entry_remove_hook (remove_callback);
203 save_tree (tree);
205 g_free (tree->tree_shown);
206 tree->tree_shown = 0;
207 tree->selected_ptr = NULL;
210 /* --------------------------------------------------------------------------------------------- */
211 /** Loads the .mc.tree file */
213 static void
214 load_tree (WTree * tree)
216 vfs_path_t *vpath;
218 tree_store_load ();
220 tree->selected_ptr = tree->store->tree_first;
221 vpath = vfs_path_from_str (mc_config_get_home_dir ());
222 tree_chdir (tree, vpath);
223 vfs_path_free (vpath);
226 /* --------------------------------------------------------------------------------------------- */
228 static void
229 tree_show_mini_info (WTree * tree, int tree_lines, int tree_cols)
231 Widget *w = WIDGET (tree);
232 int line;
234 /* Show mini info */
235 if (tree->is_panel)
237 if (!panels_options.show_mini_info)
238 return;
239 line = tree_lines + 2;
241 else
242 line = tree_lines + 1;
244 if (tree->searching)
246 /* Show search string */
247 tty_setcolor (INPUT_COLOR);
248 tty_draw_hline (w->y + line, w->x + 1, ' ', tree_cols);
249 widget_move (w, line, 1);
250 tty_print_char (PATH_SEP);
251 tty_print_string (str_fit_to_term (tree->search_buffer, tree_cols - 2, J_LEFT_FIT));
252 tty_print_char (' ');
254 else
256 /* Show full name of selected directory */
257 WDialog *h = w->owner;
259 tty_setcolor (tree->is_panel ? NORMAL_COLOR : TREE_NORMALC (h));
260 tty_draw_hline (w->y + line, w->x + 1, ' ', tree_cols);
261 widget_move (w, line, 1);
262 tty_print_string (str_fit_to_term
263 (vfs_path_as_str (tree->selected_ptr->name), tree_cols, J_LEFT_FIT));
267 /* --------------------------------------------------------------------------------------------- */
269 static void
270 show_tree (WTree * tree)
272 Widget *w = WIDGET (tree);
273 WDialog *h = w->owner;
274 tree_entry *current;
275 int i, j, topsublevel;
276 int x = 0, y = 0;
277 int tree_lines, tree_cols;
279 /* Initialize */
280 tree_lines = tlines (tree);
281 tree_cols = w->cols;
283 widget_move (w, y, x);
284 if (tree->is_panel)
286 tree_cols -= 2;
287 x = y = 1;
290 g_free (tree->tree_shown);
291 tree->tree_shown = g_new0 (tree_entry *, tree_lines);
293 if (tree->store->tree_first)
294 topsublevel = tree->store->tree_first->sublevel;
295 else
296 topsublevel = 0;
297 if (!tree->selected_ptr)
299 tree->selected_ptr = tree->store->tree_first;
300 tree->topdiff = 0;
302 current = tree->selected_ptr;
304 /* Calculate the directory which is to be shown on the topmost line */
305 if (!tree_navigation_flag)
306 current = back_ptr (current, &tree->topdiff);
307 else
309 i = 0;
310 while (current->prev && i < tree->topdiff)
312 current = current->prev;
314 if (current->sublevel < tree->selected_ptr->sublevel)
316 if (vfs_path_equal (current->name, tree->selected_ptr->name))
317 i++;
319 else if (current->sublevel == tree->selected_ptr->sublevel)
321 const char *cname;
323 cname = vfs_path_as_str (current->name);
324 for (j = strlen (cname) - 1; !IS_PATH_SEP (cname[j]); j--)
326 if (vfs_path_equal_len (current->name, tree->selected_ptr->name, j))
327 i++;
329 else if (current->sublevel == tree->selected_ptr->sublevel + 1)
331 j = vfs_path_len (tree->selected_ptr->name);
332 if (j > 1 && vfs_path_equal_len (current->name, tree->selected_ptr->name, j))
333 i++;
336 tree->topdiff = i;
339 /* Loop for every line */
340 for (i = 0; i < tree_lines; i++)
342 tty_setcolor (tree->is_panel ? NORMAL_COLOR : TREE_NORMALC (h));
344 /* Move to the beginning of the line */
345 tty_draw_hline (w->y + y + i, w->x + x, ' ', tree_cols);
347 if (current == NULL)
348 continue;
350 if (tree->is_panel)
351 tty_setcolor (tree->active && current == tree->selected_ptr
352 ? SELECTED_COLOR : NORMAL_COLOR);
353 else
354 tty_setcolor (current == tree->selected_ptr ? TREE_CURRENTC (h) : TREE_NORMALC (h));
356 tree->tree_shown[i] = current;
357 if (current->sublevel == topsublevel)
358 /* Show full name */
359 tty_print_string (str_fit_to_term
360 (vfs_path_as_str (current->name),
361 tree_cols + (tree->is_panel ? 0 : 1), J_LEFT_FIT));
362 else
364 /* Sub level directory */
365 tty_set_alt_charset (TRUE);
367 /* Output branch parts */
368 for (j = 0; j < current->sublevel - topsublevel - 1; j++)
370 if (tree_cols - 8 - 3 * j < 9)
371 break;
372 tty_print_char (' ');
373 if (current->submask & (1 << (j + topsublevel + 1)))
374 tty_print_char (ACS_VLINE);
375 else
376 tty_print_char (' ');
377 tty_print_char (' ');
379 tty_print_char (' ');
380 j++;
381 if (!current->next || !(current->next->submask & (1 << current->sublevel)))
382 tty_print_char (ACS_LLCORNER);
383 else
384 tty_print_char (ACS_LTEE);
385 tty_print_char (ACS_HLINE);
386 tty_set_alt_charset (FALSE);
388 /* Show sub-name */
389 tty_print_char (' ');
390 tty_print_string (str_fit_to_term
391 (current->subname, tree_cols - x - 3 * j, J_LEFT_FIT));
394 /* Calculate the next value for current */
395 current = current->next;
396 if (tree_navigation_flag)
398 while (current != NULL)
400 if (current->sublevel < tree->selected_ptr->sublevel)
402 if (vfs_path_equal_len (current->name, tree->selected_ptr->name,
403 vfs_path_len (current->name)))
404 break;
406 else if (current->sublevel == tree->selected_ptr->sublevel)
408 const char *cname;
410 cname = vfs_path_as_str (current->name);
411 for (j = strlen (cname) - 1; !IS_PATH_SEP (cname[j]); j--)
413 if (vfs_path_equal_len (current->name, tree->selected_ptr->name, j))
414 break;
416 else if (current->sublevel == tree->selected_ptr->sublevel + 1
417 && vfs_path_len (tree->selected_ptr->name) > 1)
419 if (vfs_path_equal_len (current->name, tree->selected_ptr->name,
420 vfs_path_len (tree->selected_ptr->name)))
421 break;
423 current = current->next;
428 tree_show_mini_info (tree, tree_lines, tree_cols);
431 /* --------------------------------------------------------------------------------------------- */
433 static void
434 tree_check_focus (WTree * tree)
436 if (tree->topdiff < 3)
437 tree->topdiff = 3;
438 else if (tree->topdiff >= tlines (tree) - 3)
439 tree->topdiff = tlines (tree) - 3 - 1;
442 /* --------------------------------------------------------------------------------------------- */
444 static void
445 tree_move_backward (WTree * tree, int i)
447 if (!tree_navigation_flag)
448 tree->selected_ptr = back_ptr (tree->selected_ptr, &i);
449 else
451 tree_entry *current;
452 int j = 0;
454 current = tree->selected_ptr;
455 while (j < i && current->prev && current->prev->sublevel >= tree->selected_ptr->sublevel)
457 current = current->prev;
458 if (current->sublevel == tree->selected_ptr->sublevel)
460 tree->selected_ptr = current;
461 j++;
464 i = j;
467 tree->topdiff -= i;
468 tree_check_focus (tree);
471 /* --------------------------------------------------------------------------------------------- */
473 static void
474 tree_move_forward (WTree * tree, int i)
476 if (!tree_navigation_flag)
477 tree->selected_ptr = forw_ptr (tree->selected_ptr, &i);
478 else
480 tree_entry *current;
481 int j = 0;
483 current = tree->selected_ptr;
484 while (j < i && current->next && current->next->sublevel >= tree->selected_ptr->sublevel)
486 current = current->next;
487 if (current->sublevel == tree->selected_ptr->sublevel)
489 tree->selected_ptr = current;
490 j++;
493 i = j;
496 tree->topdiff += i;
497 tree_check_focus (tree);
500 /* --------------------------------------------------------------------------------------------- */
502 static void
503 tree_move_to_child (WTree * tree)
505 tree_entry *current;
507 /* Do we have a starting point? */
508 if (!tree->selected_ptr)
509 return;
510 /* Take the next entry */
511 current = tree->selected_ptr->next;
512 /* Is it the child of the selected entry */
513 if (current && current->sublevel > tree->selected_ptr->sublevel)
515 /* Yes -> select this entry */
516 tree->selected_ptr = current;
517 tree->topdiff++;
518 tree_check_focus (tree);
520 else
522 /* No -> rescan and try again */
523 tree_rescan (tree);
524 current = tree->selected_ptr->next;
525 if (current && current->sublevel > tree->selected_ptr->sublevel)
527 tree->selected_ptr = current;
528 tree->topdiff++;
529 tree_check_focus (tree);
534 /* --------------------------------------------------------------------------------------------- */
536 static gboolean
537 tree_move_to_parent (WTree * tree)
539 tree_entry *current;
540 tree_entry *old;
542 if (!tree->selected_ptr)
543 return FALSE;
545 old = tree->selected_ptr;
546 current = tree->selected_ptr->prev;
547 while (current && current->sublevel >= tree->selected_ptr->sublevel)
549 current = current->prev;
550 tree->topdiff--;
552 if (!current)
553 current = tree->store->tree_first;
554 tree->selected_ptr = current;
555 tree_check_focus (tree);
556 return tree->selected_ptr != old;
559 /* --------------------------------------------------------------------------------------------- */
561 static void
562 tree_move_to_top (WTree * tree)
564 tree->selected_ptr = tree->store->tree_first;
565 tree->topdiff = 0;
568 /* --------------------------------------------------------------------------------------------- */
570 static void
571 tree_move_to_bottom (WTree * tree)
573 tree->selected_ptr = tree->store->tree_last;
574 tree->topdiff = tlines (tree) - 3 - 1;
577 /* --------------------------------------------------------------------------------------------- */
579 static void
580 tree_chdir_sel (WTree * tree)
582 if (tree->is_panel)
584 change_panel ();
586 if (do_cd (tree->selected_ptr->name, cd_exact))
587 select_item (current_panel);
588 else
589 message (D_ERROR, MSG_ERROR, _("Cannot chdir to \"%s\"\n%s"),
590 vfs_path_as_str (tree->selected_ptr->name), unix_error_string (errno));
592 widget_redraw (WIDGET (current_panel));
593 change_panel ();
594 show_tree (tree);
596 else
598 WDialog *h = WIDGET (tree)->owner;
600 h->ret_value = B_ENTER;
601 dlg_stop (h);
605 /* --------------------------------------------------------------------------------------------- */
607 static void
608 maybe_chdir (WTree * tree)
610 if (xtree_mode && tree->is_panel && is_idle ())
611 tree_chdir_sel (tree);
614 /* --------------------------------------------------------------------------------------------- */
615 /** Search tree for text */
617 static int
618 search_tree (WTree * tree, char *text)
620 tree_entry *current;
621 int len;
622 int wrapped = 0;
623 int found = 0;
625 len = strlen (text);
626 current = tree->selected_ptr;
627 found = 0;
628 while (!wrapped || current != tree->selected_ptr)
630 if (strncmp (current->subname, text, len) == 0)
632 tree->selected_ptr = current;
633 found = 1;
634 break;
636 current = current->next;
637 if (!current)
639 current = tree->store->tree_first;
640 wrapped = 1;
642 tree->topdiff++;
644 tree_check_focus (tree);
645 return found;
648 /* --------------------------------------------------------------------------------------------- */
650 static void
651 tree_do_search (WTree * tree, int key)
653 size_t l;
655 l = strlen (tree->search_buffer);
656 if ((l != 0) && (key == KEY_BACKSPACE))
657 tree->search_buffer[--l] = '\0';
658 else if (key && l < sizeof (tree->search_buffer))
660 tree->search_buffer[l] = key;
661 tree->search_buffer[++l] = '\0';
664 if (!search_tree (tree, tree->search_buffer))
665 tree->search_buffer[--l] = 0;
667 show_tree (tree);
668 maybe_chdir (tree);
671 /* --------------------------------------------------------------------------------------------- */
673 static void
674 tree_rescan (void *data)
676 WTree *tree = data;
677 vfs_path_t *old_vpath;
679 old_vpath = vfs_path_clone (vfs_get_raw_current_dir ());
680 if (old_vpath == NULL)
681 return;
683 if (tree->selected_ptr != NULL && mc_chdir (tree->selected_ptr->name) == 0)
685 int ret;
687 tree_store_rescan (tree->selected_ptr->name);
688 ret = mc_chdir (old_vpath);
689 (void) ret;
691 vfs_path_free (old_vpath);
694 /* --------------------------------------------------------------------------------------------- */
696 static void
697 tree_forget (void *data)
699 WTree *tree = data;
700 if (tree->selected_ptr)
701 tree_remove_entry (tree, tree->selected_ptr->name);
704 /* --------------------------------------------------------------------------------------------- */
706 static void
707 tree_copy (WTree * tree, const char *default_dest)
709 char msg[BUF_MEDIUM];
710 char *dest;
712 if (tree->selected_ptr == NULL)
713 return;
715 g_snprintf (msg, sizeof (msg), _("Copy \"%s\" directory to:"),
716 str_trunc (vfs_path_as_str (tree->selected_ptr->name), 50));
717 dest = input_expand_dialog (Q_ ("DialogTitle|Copy"),
718 msg, MC_HISTORY_FM_TREE_COPY, default_dest,
719 INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_CD);
721 if (dest != NULL && *dest != '\0')
723 file_op_context_t *ctx;
724 file_op_total_context_t *tctx;
726 ctx = file_op_context_new (OP_COPY);
727 tctx = file_op_total_context_new ();
728 file_op_context_create_ui (ctx, FALSE, FILEGUI_DIALOG_MULTI_ITEM);
729 tctx->ask_overwrite = FALSE;
730 copy_dir_dir (tctx, ctx, vfs_path_as_str (tree->selected_ptr->name), dest, TRUE, FALSE,
731 FALSE, NULL);
732 file_op_total_context_destroy (tctx);
733 file_op_context_destroy (ctx);
736 g_free (dest);
739 /* --------------------------------------------------------------------------------------------- */
741 static void
742 tree_move (WTree * tree, const char *default_dest)
744 char msg[BUF_MEDIUM];
745 char *dest;
746 struct stat buf;
747 file_op_context_t *ctx;
748 file_op_total_context_t *tctx;
750 if (tree->selected_ptr == NULL)
751 return;
753 g_snprintf (msg, sizeof (msg), _("Move \"%s\" directory to:"),
754 str_trunc (vfs_path_as_str (tree->selected_ptr->name), 50));
755 dest =
756 input_expand_dialog (Q_ ("DialogTitle|Move"), msg, MC_HISTORY_FM_TREE_MOVE, default_dest,
757 INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_CD);
759 if (dest == NULL || *dest == '\0')
760 goto ret;
762 if (stat (dest, &buf))
764 message (D_ERROR, MSG_ERROR, _("Cannot stat the destination\n%s"),
765 unix_error_string (errno));
766 goto ret;
769 if (!S_ISDIR (buf.st_mode))
771 file_error (_("Destination \"%s\" must be a directory\n%s"), dest);
772 goto ret;
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);
782 ret:
783 g_free (dest);
786 /* --------------------------------------------------------------------------------------------- */
788 #if 0
789 static void
790 tree_mkdir (WTree * tree)
792 char old_dir[MC_MAXPATHLEN];
794 if (!tree->selected_ptr)
795 return;
796 if (chdir (tree->selected_ptr->name))
797 return;
798 /* FIXME
799 mkdir_cmd (tree);
801 tree_rescan (tree);
802 chdir (old_dir);
804 #endif
806 /* --------------------------------------------------------------------------------------------- */
808 static void
809 tree_rmdir (void *data)
811 WTree *tree = data;
812 file_op_context_t *ctx;
813 file_op_total_context_t *tctx;
815 if (!tree->selected_ptr)
816 return;
818 if (confirm_delete)
820 char *buf;
821 int result;
823 buf = g_strdup_printf (_("Delete %s?"), vfs_path_as_str (tree->selected_ptr->name));
825 result = query_dialog (Q_ ("DialogTitle|Delete"), buf, D_ERROR, 2, _("&Yes"), _("&No"));
826 g_free (buf);
827 if (result != 0)
828 return;
831 ctx = file_op_context_new (OP_DELETE);
832 tctx = file_op_total_context_new ();
834 file_op_context_create_ui (ctx, FALSE, FILEGUI_DIALOG_ONE_ITEM);
835 if (erase_dir (tctx, ctx, tree->selected_ptr->name) == FILE_CONT)
836 tree_forget (tree);
837 file_op_total_context_destroy (tctx);
838 file_op_context_destroy (ctx);
841 /* --------------------------------------------------------------------------------------------- */
843 static inline void
844 tree_move_up (WTree * tree)
846 tree_move_backward (tree, 1);
847 show_tree (tree);
848 maybe_chdir (tree);
851 /* --------------------------------------------------------------------------------------------- */
853 static inline void
854 tree_move_down (WTree * tree)
856 tree_move_forward (tree, 1);
857 show_tree (tree);
858 maybe_chdir (tree);
861 /* --------------------------------------------------------------------------------------------- */
863 static inline void
864 tree_move_home (WTree * tree)
866 tree_move_to_top (tree);
867 show_tree (tree);
868 maybe_chdir (tree);
871 /* --------------------------------------------------------------------------------------------- */
873 static inline void
874 tree_move_end (WTree * tree)
876 tree_move_to_bottom (tree);
877 show_tree (tree);
878 maybe_chdir (tree);
881 /* --------------------------------------------------------------------------------------------- */
883 static void
884 tree_move_pgup (WTree * tree)
886 tree_move_backward (tree, tlines (tree) - 1);
887 show_tree (tree);
888 maybe_chdir (tree);
891 /* --------------------------------------------------------------------------------------------- */
893 static void
894 tree_move_pgdn (WTree * tree)
896 tree_move_forward (tree, tlines (tree) - 1);
897 show_tree (tree);
898 maybe_chdir (tree);
901 /* --------------------------------------------------------------------------------------------- */
903 static gboolean
904 tree_move_left (WTree * tree)
906 gboolean v = FALSE;
908 if (tree_navigation_flag)
910 v = tree_move_to_parent (tree);
911 show_tree (tree);
912 maybe_chdir (tree);
915 return v;
918 /* --------------------------------------------------------------------------------------------- */
920 static gboolean
921 tree_move_right (WTree * tree)
923 gboolean v = FALSE;
925 if (tree_navigation_flag)
927 tree_move_to_child (tree);
928 show_tree (tree);
929 maybe_chdir (tree);
930 v = TRUE;
933 return v;
936 /* --------------------------------------------------------------------------------------------- */
938 static void
939 tree_start_search (WTree * tree)
941 gboolean i;
943 if (tree->searching)
945 if (tree->selected_ptr == tree->store->tree_last)
946 tree_move_to_top (tree);
947 else
949 /* set navigation mode temporarily to 'Static' because in
950 * dynamic navigation mode tree_move_forward will not move
951 * to a lower sublevel if necessary (sequent searches must
952 * start with the directory followed the last found directory)
954 i = tree_navigation_flag;
955 tree_navigation_flag = 0;
956 tree_move_forward (tree, 1);
957 tree_navigation_flag = i;
959 tree_do_search (tree, 0);
961 else
963 tree->searching = 1;
964 tree->search_buffer[0] = 0;
968 /* --------------------------------------------------------------------------------------------- */
970 static void
971 tree_toggle_navig (WTree * tree)
973 tree_navigation_flag = !tree_navigation_flag;
974 buttonbar_set_label (find_buttonbar (WIDGET (tree)->owner), 4,
975 tree_navigation_flag ? Q_ ("ButtonBar|Static")
976 : Q_ ("ButtonBar|Dynamc"), tree_map, WIDGET (tree));
979 /* --------------------------------------------------------------------------------------------- */
981 static cb_ret_t
982 tree_execute_cmd (WTree * tree, long command)
984 cb_ret_t res = MSG_HANDLED;
986 if (command != CK_Search)
987 tree->searching = 0;
989 switch (command)
991 case CK_Help:
993 ev_help_t event_data = { NULL, "[Directory Tree]" };
994 mc_event_raise (MCEVENT_GROUP_CORE, "help", &event_data);
996 break;
997 case CK_Forget:
998 tree_forget (tree);
999 break;
1000 case CK_ToggleNavigation:
1001 tree_toggle_navig (tree);
1002 break;
1003 case CK_Copy:
1004 tree_copy (tree, "");
1005 break;
1006 case CK_Move:
1007 tree_move (tree, "");
1008 break;
1009 case CK_Up:
1010 tree_move_up (tree);
1011 break;
1012 case CK_Down:
1013 tree_move_down (tree);
1014 break;
1015 case CK_Top:
1016 tree_move_home (tree);
1017 break;
1018 case CK_Bottom:
1019 tree_move_end (tree);
1020 break;
1021 case CK_PageUp:
1022 tree_move_pgup (tree);
1023 break;
1024 case CK_PageDown:
1025 tree_move_pgdn (tree);
1026 break;
1027 case CK_Enter:
1028 tree_chdir_sel (tree);
1029 break;
1030 case CK_Reread:
1031 tree_rescan (tree);
1032 break;
1033 case CK_Search:
1034 tree_start_search (tree);
1035 break;
1036 case CK_Delete:
1037 tree_rmdir (tree);
1038 break;
1039 case CK_Quit:
1040 if (!tree->is_panel)
1041 dlg_stop (WIDGET (tree)->owner);
1042 return res;
1043 default:
1044 res = MSG_NOT_HANDLED;
1047 show_tree (tree);
1049 return res;
1052 /* --------------------------------------------------------------------------------------------- */
1054 static cb_ret_t
1055 tree_key (WTree * tree, int key)
1057 size_t i;
1059 if (is_abort_char (key))
1061 if (tree->is_panel)
1063 tree->searching = 0;
1064 show_tree (tree);
1065 return MSG_HANDLED; /* eat abort char */
1067 /* modal tree dialog: let upper layer see the
1068 abort character and close the dialog */
1069 return MSG_NOT_HANDLED;
1072 if (tree->searching && ((key >= ' ' && key <= 255) || key == KEY_BACKSPACE))
1074 tree_do_search (tree, key);
1075 show_tree (tree);
1076 return MSG_HANDLED;
1079 for (i = 0; tree_map[i].key != 0; i++)
1080 if (key == tree_map[i].key)
1081 switch (tree_map[i].command)
1083 case CK_Left:
1084 return tree_move_left (tree) ? MSG_HANDLED : MSG_NOT_HANDLED;
1085 case CK_Right:
1086 return tree_move_right (tree) ? MSG_HANDLED : MSG_NOT_HANDLED;
1087 default:
1088 tree_execute_cmd (tree, tree_map[i].command);
1089 return MSG_HANDLED;
1092 /* Do not eat characters not meant for the tree below ' ' (e.g. C-l). */
1093 if (!command_prompt && ((key >= ' ' && key <= 255) || key == KEY_BACKSPACE))
1095 tree_start_search (tree);
1096 tree_do_search (tree, key);
1097 return MSG_HANDLED;
1100 return MSG_NOT_HANDLED;
1103 /* --------------------------------------------------------------------------------------------- */
1105 static void
1106 tree_frame (WDialog * h, WTree * tree)
1108 Widget *w = WIDGET (tree);
1110 (void) h;
1112 tty_setcolor (NORMAL_COLOR);
1113 widget_erase (w);
1114 if (tree->is_panel)
1116 const char *title = _("Directory tree");
1117 const int len = str_term_width1 (title);
1119 tty_draw_box (w->y, w->x, w->lines, w->cols, FALSE);
1121 widget_move (w, 0, (w->cols - len - 2) / 2);
1122 tty_printf (" %s ", title);
1124 if (panels_options.show_mini_info)
1126 int y;
1128 y = w->lines - 3;
1129 widget_move (w, y, 0);
1130 tty_print_alt_char (ACS_LTEE, FALSE);
1131 widget_move (w, y, w->cols - 1);
1132 tty_print_alt_char (ACS_RTEE, FALSE);
1133 tty_draw_hline (w->y + y, w->x + 1, ACS_HLINE, w->cols - 2);
1138 /* --------------------------------------------------------------------------------------------- */
1140 static cb_ret_t
1141 tree_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
1143 WTree *tree = (WTree *) w;
1144 WDialog *h = w->owner;
1145 WButtonBar *b = find_buttonbar (h);
1147 switch (msg)
1149 case MSG_DRAW:
1150 tree_frame (h, tree);
1151 show_tree (tree);
1152 return MSG_HANDLED;
1154 case MSG_FOCUS:
1155 tree->active = 1;
1156 buttonbar_set_label (b, 1, Q_ ("ButtonBar|Help"), tree_map, w);
1157 buttonbar_set_label (b, 2, Q_ ("ButtonBar|Rescan"), tree_map, w);
1158 buttonbar_set_label (b, 3, Q_ ("ButtonBar|Forget"), tree_map, w);
1159 buttonbar_set_label (b, 4, tree_navigation_flag ? Q_ ("ButtonBar|Static")
1160 : Q_ ("ButtonBar|Dynamc"), tree_map, w);
1161 buttonbar_set_label (b, 5, Q_ ("ButtonBar|Copy"), tree_map, w);
1162 buttonbar_set_label (b, 6, Q_ ("ButtonBar|RenMov"), tree_map, w);
1163 #if 0
1164 /* FIXME: mkdir is currently defunct */
1165 buttonbar_set_label (b, 7, Q_ ("ButtonBar|Mkdir"), tree_map, w);
1166 #else
1167 buttonbar_clear_label (b, 7, WIDGET (tree));
1168 #endif
1169 buttonbar_set_label (b, 8, Q_ ("ButtonBar|Rmdir"), tree_map, w);
1170 widget_redraw (WIDGET (b));
1172 /* FIXME: Should find a better way of only displaying the
1173 currently selected item */
1174 show_tree (tree);
1175 return MSG_HANDLED;
1177 /* FIXME: Should find a better way of changing the color of the
1178 selected item */
1180 case MSG_UNFOCUS:
1181 tree->active = 0;
1182 tree->searching = 0;
1183 show_tree (tree);
1184 return MSG_HANDLED;
1186 case MSG_KEY:
1187 return tree_key (tree, parm);
1189 case MSG_ACTION:
1190 /* command from buttonbar */
1191 return tree_execute_cmd (tree, parm);
1193 case MSG_DESTROY:
1194 tree_destroy (tree);
1195 return MSG_HANDLED;
1197 default:
1198 return widget_default_callback (w, sender, msg, parm, data);
1202 /* --------------------------------------------------------------------------------------------- */
1205 * Mouse callback
1207 static void
1208 tree_mouse_callback (Widget * w, mouse_msg_t msg, mouse_event_t * event)
1210 WTree *tree = (WTree *) w;
1211 int y;
1213 y = event->y;
1214 if (tree->is_panel)
1215 y--;
1217 switch (msg)
1219 case MSG_MOUSE_DOWN:
1220 /* rest of the upper frame - call menu */
1221 if (tree->is_panel && event->y == WIDGET (w->owner)->y)
1223 /* return MOU_UNHANDLED */
1224 event->result.abort = TRUE;
1226 else if (!tree->active)
1227 change_panel ();
1228 break;
1230 case MSG_MOUSE_CLICK:
1232 int lines;
1234 lines = tlines (tree);
1236 if (y < 0)
1238 tree_move_backward (tree, lines - 1);
1239 show_tree (tree);
1241 else if (y >= lines)
1243 tree_move_forward (tree, lines - 1);
1244 show_tree (tree);
1246 else if ((event->count & GPM_DOUBLE) != 0)
1248 if (tree->tree_shown[y] != NULL)
1250 tree->selected_ptr = tree->tree_shown[y];
1251 tree->topdiff = y;
1254 tree_chdir_sel (tree);
1257 break;
1259 case MSG_MOUSE_SCROLL_UP:
1260 case MSG_MOUSE_SCROLL_DOWN:
1261 /* TODO: Ticket #2218 */
1262 break;
1264 default:
1265 break;
1269 /* --------------------------------------------------------------------------------------------- */
1270 /*** public functions ****************************************************************************/
1271 /* --------------------------------------------------------------------------------------------- */
1273 WTree *
1274 tree_new (int y, int x, int lines, int cols, gboolean is_panel)
1276 WTree *tree;
1277 Widget *w;
1279 tree = g_new (WTree, 1);
1280 w = WIDGET (tree);
1282 widget_init (w, y, x, lines, cols, tree_callback, tree_mouse_callback);
1283 tree->is_panel = is_panel;
1284 tree->selected_ptr = 0;
1286 tree->store = tree_store_get ();
1287 tree_store_add_entry_remove_hook (remove_callback, tree);
1288 tree->tree_shown = 0;
1289 tree->search_buffer[0] = 0;
1290 tree->topdiff = w->lines / 2;
1291 tree->searching = 0;
1292 tree->active = 0;
1294 /* We do not want to keep the cursor */
1295 widget_want_cursor (w, FALSE);
1296 load_tree (tree);
1297 return tree;
1300 /* --------------------------------------------------------------------------------------------- */
1302 void
1303 tree_chdir (WTree * tree, const vfs_path_t * dir)
1305 tree_entry *current;
1307 current = tree_store_whereis (dir);
1308 if (current != NULL)
1310 tree->selected_ptr = current;
1311 tree_check_focus (tree);
1315 /* --------------------------------------------------------------------------------------------- */
1316 /** Return name of the currently selected entry */
1318 const vfs_path_t *
1319 tree_selected_name (const WTree * tree)
1321 return tree->selected_ptr->name;
1324 /* --------------------------------------------------------------------------------------------- */
1326 void
1327 sync_tree (const vfs_path_t * vpath)
1329 tree_chdir (the_tree, vpath);
1332 /* --------------------------------------------------------------------------------------------- */
1334 WTree *
1335 find_tree (WDialog * h)
1337 return (WTree *) find_widget_type (h, tree_callback);
1340 /* --------------------------------------------------------------------------------------------- */