Split file src/keybind.[ch] to lib/keybind.[ch] and src/keybind-defaults.[ch].
[midnight-commander.git] / src / tree.c
blob035226cda586116f0788b51875b2d85836629037
1 /* Directory tree browser for the Midnight Commander
2 Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
3 2003, 2004, 2005, 2007 Free Software Foundation, Inc.
5 Written: 1994, 1996 Janne Kukonlehto
6 1997 Norbert Warmuth
7 1996, 1999 Miguel de Icaza
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 This module has been converted to be a widget.
25 The program load and saves the tree each time the tree widget is
26 created and destroyed. This is required for the future vfs layer,
27 it will be possible to have tree views over virtual file systems.
31 /** \file tree.c
32 * \brief Source: directory tree browser
35 #include <config.h>
37 #include <errno.h>
38 #include <stdio.h>
39 #include <string.h>
40 #include <sys/types.h>
42 #include "lib/global.h"
44 #include "lib/tty/tty.h"
45 #include "lib/tty/mouse.h"
46 #include "lib/tty/key.h"
47 #include "lib/skin.h"
48 #include "lib/vfs/mc-vfs/vfs.h"
49 #include "lib/fileloc.h"
50 #include "lib/strutil.h"
51 #include "lib/util.h"
52 #include "lib/widget.h"
54 #include "dir.h"
55 #include "panel.h"
56 #include "main.h"
57 #include "main-widgets.h" /* the_menubar */
58 #include "file.h" /* copy_dir_dir(), move_dir_dir(), erase_dir() */
59 #include "layout.h" /* command_prompt */
60 #include "help.h"
61 #include "treestore.h"
62 #include "cmd.h"
63 #include "keybind-defaults.h"
64 #include "history.h"
65 #include "tree.h"
66 #include "filegui.h"
68 /*** global variables ****************************************************************************/
70 /*** file scope macro definitions ****************************************************************/
72 #define tlines(t) (t->is_panel ? t->widget.lines - 2 - (show_mini_info ? 2 : 0) : t->widget.lines)
74 /* Use the color of the parent widget for the unselected entries */
75 #define TREE_NORMALC(h) (h->color[DLG_COLOR_NORMAL])
76 #define TREE_CURRENTC(h) (h->color[DLG_COLOR_FOCUS])
78 /*** file scope type declarations ****************************************************************/
80 struct WTree
82 Widget widget;
83 struct TreeStore *store;
84 tree_entry *selected_ptr; /* The selected directory */
85 char search_buffer[MC_MAXFILENAMELEN]; /* Current search string */
86 tree_entry **tree_shown; /* Entries currently on screen */
87 int is_panel; /* panel or plain widget flag */
88 int active; /* if it's currently selected */
89 int searching; /* Are we on searching mode? */
90 int topdiff; /* The difference between the topmost
91 shown and the selected */
94 /*** file scope variables ************************************************************************/
96 /* Specifies the display mode: 1d or 2d */
97 static gboolean tree_navigation_flag = FALSE;
99 /*** file scope functions ************************************************************************/
100 /* --------------------------------------------------------------------------------------------- */
102 static void tree_rescan (void *data);
104 /* --------------------------------------------------------------------------------------------- */
106 static tree_entry *
107 back_ptr (tree_entry * ptr, int *count)
109 int i = 0;
111 while (ptr && ptr->prev && i < *count)
113 ptr = ptr->prev;
114 i++;
116 *count = i;
117 return ptr;
120 /* --------------------------------------------------------------------------------------------- */
122 static tree_entry *
123 forw_ptr (tree_entry * ptr, int *count)
125 int i = 0;
127 while (ptr && ptr->next && i < *count)
129 ptr = ptr->next;
130 i++;
132 *count = i;
133 return ptr;
136 /* --------------------------------------------------------------------------------------------- */
138 static void
139 remove_callback (tree_entry * entry, void *data)
141 WTree *tree = data;
143 if (tree->selected_ptr == entry)
145 if (tree->selected_ptr->next)
146 tree->selected_ptr = tree->selected_ptr->next;
147 else
148 tree->selected_ptr = tree->selected_ptr->prev;
152 /* --------------------------------------------------------------------------------------------- */
153 /** Save the ~/.mc/Tree file */
155 static void
156 save_tree (WTree * tree)
158 int error;
159 char *tree_name;
161 (void) tree;
162 error = tree_store_save ();
165 if (error)
167 tree_name = g_build_filename (home_dir, MC_USERCONF_DIR, MC_TREESTORE_FILE, (char *) NULL);
168 fprintf (stderr, _("Cannot open the %s file for writing:\n%s\n"), tree_name,
169 unix_error_string (error));
170 g_free (tree_name);
174 /* --------------------------------------------------------------------------------------------- */
176 static void
177 tree_remove_entry (WTree * tree, char *name)
179 (void) tree;
180 tree_store_remove_entry (name);
183 /* --------------------------------------------------------------------------------------------- */
185 static void
186 tree_destroy (WTree * tree)
188 tree_store_remove_entry_remove_hook (remove_callback);
189 save_tree (tree);
191 g_free (tree->tree_shown);
192 tree->tree_shown = 0;
193 tree->selected_ptr = NULL;
196 /* --------------------------------------------------------------------------------------------- */
197 /** Loads the .mc.tree file */
199 static void
200 load_tree (WTree * tree)
202 tree_store_load ();
204 tree->selected_ptr = tree->store->tree_first;
205 tree_chdir (tree, home_dir);
208 /* --------------------------------------------------------------------------------------------- */
210 static void
211 tree_show_mini_info (WTree * tree, int tree_lines, int tree_cols)
213 Dlg_head *h = tree->widget.owner;
214 int line;
216 /* Show mini info */
217 if (tree->is_panel)
219 if (!show_mini_info)
220 return;
221 line = tree_lines + 2;
223 else
224 line = tree_lines + 1;
226 if (tree->searching)
228 /* Show search string */
229 tty_setcolor (INPUT_COLOR);
230 tty_draw_hline (tree->widget.y + line, tree->widget.x + 1, ' ', tree_cols);
231 widget_move (&tree->widget, line, 1);
232 tty_print_char (PATH_SEP);
233 tty_print_string (str_fit_to_term (tree->search_buffer, tree_cols - 2, J_LEFT_FIT));
234 tty_print_char (' ');
236 else
238 /* Show full name of selected directory */
239 tty_setcolor (tree->is_panel ? NORMAL_COLOR : TREE_NORMALC (h));
240 tty_draw_hline (tree->widget.y + line, tree->widget.x + 1, ' ', tree_cols);
241 widget_move (&tree->widget, line, 1);
242 tty_print_string (str_fit_to_term (tree->selected_ptr->name, tree_cols, J_LEFT_FIT));
246 /* --------------------------------------------------------------------------------------------- */
248 static void
249 show_tree (WTree * tree)
251 Dlg_head *h = tree->widget.owner;
252 tree_entry *current;
253 int i, j, topsublevel;
254 int x = 0, y = 0;
255 int tree_lines, tree_cols;
257 /* Initialize */
258 tree_lines = tlines (tree);
259 tree_cols = tree->widget.cols;
261 widget_move ((Widget *) tree, y, x);
262 if (tree->is_panel)
264 tree_cols -= 2;
265 x = y = 1;
268 g_free (tree->tree_shown);
269 tree->tree_shown = g_new0 (tree_entry *, tree_lines);
271 if (tree->store->tree_first)
272 topsublevel = tree->store->tree_first->sublevel;
273 else
274 topsublevel = 0;
275 if (!tree->selected_ptr)
277 tree->selected_ptr = tree->store->tree_first;
278 tree->topdiff = 0;
280 current = tree->selected_ptr;
282 /* Calculate the directory which is to be shown on the topmost line */
283 if (!tree_navigation_flag)
284 current = back_ptr (current, &tree->topdiff);
285 else
287 i = 0;
288 while (current->prev && i < tree->topdiff)
290 current = current->prev;
291 if (current->sublevel < tree->selected_ptr->sublevel)
293 if (strncmp (current->name, tree->selected_ptr->name, strlen (current->name)) == 0)
294 i++;
296 else if (current->sublevel == tree->selected_ptr->sublevel)
298 for (j = strlen (current->name) - 1; current->name[j] != PATH_SEP; j--);
299 if (strncmp (current->name, tree->selected_ptr->name, j) == 0)
300 i++;
302 else if (current->sublevel == tree->selected_ptr->sublevel + 1
303 && strlen (tree->selected_ptr->name) > 1)
305 if (strncmp (current->name, tree->selected_ptr->name,
306 strlen (tree->selected_ptr->name)) == 0)
307 i++;
310 tree->topdiff = i;
313 /* Loop for every line */
314 for (i = 0; i < tree_lines; i++)
316 tty_setcolor (tree->is_panel ? NORMAL_COLOR : TREE_NORMALC (h));
318 /* Move to the beginning of the line */
319 tty_draw_hline (tree->widget.y + y + i, tree->widget.x + x, ' ', tree_cols);
321 if (current == NULL)
322 continue;
324 if (tree->is_panel)
325 tty_setcolor (tree->active && current == tree->selected_ptr
326 ? SELECTED_COLOR : NORMAL_COLOR);
327 else
328 tty_setcolor (current == tree->selected_ptr ? TREE_CURRENTC (h) : TREE_NORMALC (h));
330 tree->tree_shown[i] = current;
331 if (current->sublevel == topsublevel)
333 /* Show full name */
334 tty_print_string (str_fit_to_term
335 (current->name, tree_cols + (tree->is_panel ? 0 : 1), J_LEFT_FIT));
337 else
339 /* Sub level directory */
340 tty_set_alt_charset (TRUE);
342 /* Output branch parts */
343 for (j = 0; j < current->sublevel - topsublevel - 1; j++)
345 if (tree_cols - 8 - 3 * j < 9)
346 break;
347 tty_print_char (' ');
348 if (current->submask & (1 << (j + topsublevel + 1)))
349 tty_print_char (ACS_VLINE);
350 else
351 tty_print_char (' ');
352 tty_print_char (' ');
354 tty_print_char (' ');
355 j++;
356 if (!current->next || !(current->next->submask & (1 << current->sublevel)))
357 tty_print_char (ACS_LLCORNER);
358 else
359 tty_print_char (ACS_LTEE);
360 tty_print_char (ACS_HLINE);
361 tty_set_alt_charset (FALSE);
363 /* Show sub-name */
364 tty_print_char (' ');
365 tty_print_string (str_fit_to_term
366 (current->subname, tree_cols - x - 3 * j, J_LEFT_FIT));
369 /* Calculate the next value for current */
370 current = current->next;
371 if (tree_navigation_flag)
373 while (current != NULL)
375 if (current->sublevel < tree->selected_ptr->sublevel)
377 if (strncmp (current->name, tree->selected_ptr->name,
378 strlen (current->name)) == 0)
379 break;
381 else if (current->sublevel == tree->selected_ptr->sublevel)
383 for (j = strlen (current->name) - 1; current->name[j] != PATH_SEP; j--)
385 if (strncmp (current->name, tree->selected_ptr->name, j) == 0)
386 break;
388 else if (current->sublevel == tree->selected_ptr->sublevel + 1
389 && strlen (tree->selected_ptr->name) > 1)
391 if (strncmp (current->name, tree->selected_ptr->name,
392 strlen (tree->selected_ptr->name)) == 0)
393 break;
395 current = current->next;
400 tree_show_mini_info (tree, tree_lines, tree_cols);
403 /* --------------------------------------------------------------------------------------------- */
405 static void
406 tree_check_focus (WTree * tree)
408 if (tree->topdiff < 3)
409 tree->topdiff = 3;
410 else if (tree->topdiff >= tlines (tree) - 3)
411 tree->topdiff = tlines (tree) - 3 - 1;
414 /* --------------------------------------------------------------------------------------------- */
416 static void
417 tree_move_backward (WTree * tree, int i)
419 if (!tree_navigation_flag)
420 tree->selected_ptr = back_ptr (tree->selected_ptr, &i);
421 else
423 tree_entry *current;
424 int j = 0;
426 current = tree->selected_ptr;
427 while (j < i && current->prev && current->prev->sublevel >= tree->selected_ptr->sublevel)
429 current = current->prev;
430 if (current->sublevel == tree->selected_ptr->sublevel)
432 tree->selected_ptr = current;
433 j++;
436 i = j;
439 tree->topdiff -= i;
440 tree_check_focus (tree);
443 /* --------------------------------------------------------------------------------------------- */
445 static void
446 tree_move_forward (WTree * tree, int i)
448 if (!tree_navigation_flag)
449 tree->selected_ptr = forw_ptr (tree->selected_ptr, &i);
450 else
452 tree_entry *current;
453 int j = 0;
455 current = tree->selected_ptr;
456 while (j < i && current->next && current->next->sublevel >= tree->selected_ptr->sublevel)
458 current = current->next;
459 if (current->sublevel == tree->selected_ptr->sublevel)
461 tree->selected_ptr = current;
462 j++;
465 i = j;
468 tree->topdiff += i;
469 tree_check_focus (tree);
472 /* --------------------------------------------------------------------------------------------- */
474 static void
475 tree_move_to_child (WTree * tree)
477 tree_entry *current;
479 /* Do we have a starting point? */
480 if (!tree->selected_ptr)
481 return;
482 /* Take the next entry */
483 current = tree->selected_ptr->next;
484 /* Is it the child of the selected entry */
485 if (current && current->sublevel > tree->selected_ptr->sublevel)
487 /* Yes -> select this entry */
488 tree->selected_ptr = current;
489 tree->topdiff++;
490 tree_check_focus (tree);
492 else
494 /* No -> rescan and try again */
495 tree_rescan (tree);
496 current = tree->selected_ptr->next;
497 if (current && current->sublevel > tree->selected_ptr->sublevel)
499 tree->selected_ptr = current;
500 tree->topdiff++;
501 tree_check_focus (tree);
506 /* --------------------------------------------------------------------------------------------- */
508 static gboolean
509 tree_move_to_parent (WTree * tree)
511 tree_entry *current;
512 tree_entry *old;
514 if (!tree->selected_ptr)
515 return FALSE;
517 old = tree->selected_ptr;
518 current = tree->selected_ptr->prev;
519 while (current && current->sublevel >= tree->selected_ptr->sublevel)
521 current = current->prev;
522 tree->topdiff--;
524 if (!current)
525 current = tree->store->tree_first;
526 tree->selected_ptr = current;
527 tree_check_focus (tree);
528 return tree->selected_ptr != old;
531 /* --------------------------------------------------------------------------------------------- */
533 static void
534 tree_move_to_top (WTree * tree)
536 tree->selected_ptr = tree->store->tree_first;
537 tree->topdiff = 0;
540 /* --------------------------------------------------------------------------------------------- */
542 static void
543 tree_move_to_bottom (WTree * tree)
545 tree->selected_ptr = tree->store->tree_last;
546 tree->topdiff = tlines (tree) - 3 - 1;
549 /* --------------------------------------------------------------------------------------------- */
550 /** Handle mouse click */
552 static void
553 tree_event (WTree * tree, int y)
555 if (tree->tree_shown[y])
557 tree->selected_ptr = tree->tree_shown[y];
558 tree->topdiff = y;
560 show_tree (tree);
563 /* --------------------------------------------------------------------------------------------- */
565 static void
566 tree_chdir_sel (WTree * tree)
568 if (!tree->is_panel)
569 return;
571 change_panel ();
573 if (do_cd (tree->selected_ptr->name, cd_exact))
574 select_item (current_panel);
575 else
576 message (D_ERROR, MSG_ERROR, _("Cannot chdir to \"%s\"\n%s"),
577 tree->selected_ptr->name, unix_error_string (errno));
579 change_panel ();
580 show_tree (tree);
583 /* --------------------------------------------------------------------------------------------- */
585 static void
586 maybe_chdir (WTree * tree)
588 if (xtree_mode && tree->is_panel && is_idle ())
589 tree_chdir_sel (tree);
592 /* --------------------------------------------------------------------------------------------- */
593 /** Mouse callback */
595 static int
596 event_callback (Gpm_Event * event, void *data)
598 WTree *tree = data;
600 /* rest of the upper frame, the menu is invisible - call menu */
601 if (tree->is_panel && (event->type & GPM_DOWN) && event->y == 1 && !menubar_visible)
603 event->x += tree->widget.x;
604 return the_menubar->widget.mouse (event, the_menubar);
607 if (!(event->type & GPM_UP))
608 return MOU_NORMAL;
610 if (tree->is_panel)
611 event->y--;
613 event->y--;
615 if (!tree->active)
616 change_panel ();
618 if (event->y < 0)
620 tree_move_backward (tree, tlines (tree) - 1);
621 show_tree (tree);
623 else if (event->y >= tlines (tree))
625 tree_move_forward (tree, tlines (tree) - 1);
626 show_tree (tree);
628 else
630 tree_event (tree, event->y);
631 if ((event->type & (GPM_UP | GPM_DOUBLE)) == (GPM_UP | GPM_DOUBLE))
633 tree_chdir_sel (tree);
636 return MOU_NORMAL;
639 /* --------------------------------------------------------------------------------------------- */
640 /** Search tree for text */
642 static int
643 search_tree (WTree * tree, char *text)
645 tree_entry *current;
646 int len;
647 int wrapped = 0;
648 int found = 0;
650 len = strlen (text);
651 current = tree->selected_ptr;
652 found = 0;
653 while (!wrapped || current != tree->selected_ptr)
655 if (strncmp (current->subname, text, len) == 0)
657 tree->selected_ptr = current;
658 found = 1;
659 break;
661 current = current->next;
662 if (!current)
664 current = tree->store->tree_first;
665 wrapped = 1;
667 tree->topdiff++;
669 tree_check_focus (tree);
670 return found;
673 /* --------------------------------------------------------------------------------------------- */
675 static void
676 tree_do_search (WTree * tree, int key)
678 size_t l;
680 l = strlen (tree->search_buffer);
681 if ((l != 0) && (key == KEY_BACKSPACE))
682 tree->search_buffer[--l] = '\0';
683 else if (key && l < sizeof (tree->search_buffer))
685 tree->search_buffer[l] = key;
686 tree->search_buffer[++l] = '\0';
689 if (!search_tree (tree, tree->search_buffer))
690 tree->search_buffer[--l] = 0;
692 show_tree (tree);
693 maybe_chdir (tree);
696 /* --------------------------------------------------------------------------------------------- */
698 static void
699 tree_rescan (void *data)
701 char old_dir[MC_MAXPATHLEN];
702 WTree *tree = data;
703 int ret;
705 if (tree->selected_ptr == NULL || mc_get_current_wd (old_dir, MC_MAXPATHLEN) == NULL
706 || mc_chdir (tree->selected_ptr->name))
707 return;
709 tree_store_rescan (tree->selected_ptr->name);
710 ret = mc_chdir (old_dir);
713 /* --------------------------------------------------------------------------------------------- */
715 static void
716 tree_forget (void *data)
718 WTree *tree = data;
719 if (tree->selected_ptr)
720 tree_remove_entry (tree, tree->selected_ptr->name);
723 /* --------------------------------------------------------------------------------------------- */
725 static void
726 tree_copy (WTree * tree, const char *default_dest)
728 char msg[BUF_MEDIUM];
729 char *dest;
731 if (tree->selected_ptr == NULL)
732 return;
734 g_snprintf (msg, sizeof (msg), _("Copy \"%s\" directory to:"),
735 str_trunc (tree->selected_ptr->name, 50));
736 dest = input_expand_dialog (Q_ ("DialogTitle|Copy"),
737 msg, MC_HISTORY_FM_TREE_COPY, default_dest);
739 if (dest != NULL && *dest != '\0')
741 FileOpContext *ctx;
742 FileOpTotalContext *tctx;
744 ctx = file_op_context_new (OP_COPY);
745 tctx = file_op_total_context_new ();
746 file_op_context_create_ui (ctx, FALSE, FILEGUI_DIALOG_MULTI_ITEM);
747 tctx->ask_overwrite = FALSE;
748 tctx->is_toplevel_file = FALSE;
749 copy_dir_dir (tctx, ctx, tree->selected_ptr->name, dest, TRUE, FALSE, FALSE, NULL);
750 file_op_total_context_destroy (tctx);
751 file_op_context_destroy (ctx);
754 g_free (dest);
757 /* --------------------------------------------------------------------------------------------- */
759 static void
760 tree_move (WTree * tree, const char *default_dest)
762 char msg[BUF_MEDIUM];
763 char *dest;
764 struct stat buf;
765 FileOpContext *ctx;
766 FileOpTotalContext *tctx;
768 if (tree->selected_ptr == NULL)
769 return;
771 g_snprintf (msg, sizeof (msg), _("Move \"%s\" directory to:"),
772 str_trunc (tree->selected_ptr->name, 50));
773 dest =
774 input_expand_dialog (Q_ ("DialogTitle|Move"), msg, MC_HISTORY_FM_TREE_MOVE, default_dest);
776 if (dest == NULL || *dest == '\0')
778 g_free (dest);
779 return;
782 if (stat (dest, &buf))
784 message (D_ERROR, MSG_ERROR, _("Cannot stat the destination\n%s"),
785 unix_error_string (errno));
786 g_free (dest);
787 return;
790 if (!S_ISDIR (buf.st_mode))
792 file_error (_("Destination \"%s\" must be a directory\n%s"), dest);
793 g_free (dest);
794 return;
797 ctx = file_op_context_new (OP_MOVE);
798 tctx = file_op_total_context_new ();
799 file_op_context_create_ui (ctx, FALSE, FILEGUI_DIALOG_ONE_ITEM);
800 move_dir_dir (tctx, ctx, tree->selected_ptr->name, dest);
801 file_op_total_context_destroy (tctx);
802 file_op_context_destroy (ctx);
804 g_free (dest);
807 /* --------------------------------------------------------------------------------------------- */
809 #if 0
810 static void
811 tree_mkdir (WTree * tree)
813 char old_dir[MC_MAXPATHLEN];
815 if (!tree->selected_ptr)
816 return;
817 if (mc_get_current_wd (old_dir, MC_MAXPATHLEN) == NULL)
818 return;
819 if (chdir (tree->selected_ptr->name))
820 return;
821 /* FIXME
822 mkdir_cmd (tree);
824 tree_rescan (tree);
825 chdir (old_dir);
827 #endif
829 /* --------------------------------------------------------------------------------------------- */
831 static void
832 tree_rmdir (void *data)
834 WTree *tree = data;
835 FileOpContext *ctx;
836 FileOpTotalContext *tctx;
838 if (!tree->selected_ptr)
839 return;
841 if (confirm_delete)
843 char *buf;
844 int result;
846 buf = g_strdup_printf (_("Delete %s?"), tree->selected_ptr->name);
847 result = query_dialog (Q_ ("DialogTitle|Delete"), buf, D_ERROR, 2, _("&Yes"), _("&No"));
848 g_free (buf);
849 if (result != 0)
850 return;
853 ctx = file_op_context_new (OP_DELETE);
854 tctx = file_op_total_context_new ();
856 file_op_context_create_ui (ctx, FALSE, FILEGUI_DIALOG_ONE_ITEM);
857 if (erase_dir (tctx, ctx, tree->selected_ptr->name) == FILE_CONT)
858 tree_forget (tree);
859 file_op_total_context_destroy (tctx);
860 file_op_context_destroy (ctx);
863 /* --------------------------------------------------------------------------------------------- */
865 static inline void
866 tree_move_up (WTree * tree)
868 tree_move_backward (tree, 1);
869 show_tree (tree);
870 maybe_chdir (tree);
873 /* --------------------------------------------------------------------------------------------- */
875 static inline void
876 tree_move_down (WTree * tree)
878 tree_move_forward (tree, 1);
879 show_tree (tree);
880 maybe_chdir (tree);
883 /* --------------------------------------------------------------------------------------------- */
885 static inline void
886 tree_move_home (WTree * tree)
888 tree_move_to_top (tree);
889 show_tree (tree);
890 maybe_chdir (tree);
893 /* --------------------------------------------------------------------------------------------- */
895 static inline void
896 tree_move_end (WTree * tree)
898 tree_move_to_bottom (tree);
899 show_tree (tree);
900 maybe_chdir (tree);
903 /* --------------------------------------------------------------------------------------------- */
905 static void
906 tree_move_pgup (WTree * tree)
908 tree_move_backward (tree, tlines (tree) - 1);
909 show_tree (tree);
910 maybe_chdir (tree);
913 /* --------------------------------------------------------------------------------------------- */
915 static void
916 tree_move_pgdn (WTree * tree)
918 tree_move_forward (tree, tlines (tree) - 1);
919 show_tree (tree);
920 maybe_chdir (tree);
923 /* --------------------------------------------------------------------------------------------- */
925 static gboolean
926 tree_move_left (WTree * tree)
928 gboolean v = FALSE;
930 if (tree_navigation_flag)
932 v = tree_move_to_parent (tree);
933 show_tree (tree);
934 maybe_chdir (tree);
937 return v;
940 /* --------------------------------------------------------------------------------------------- */
942 static gboolean
943 tree_move_right (WTree * tree)
945 gboolean v = FALSE;
947 if (tree_navigation_flag)
949 tree_move_to_child (tree);
950 show_tree (tree);
951 maybe_chdir (tree);
952 v = TRUE;
955 return v;
958 /* --------------------------------------------------------------------------------------------- */
960 static void
961 tree_start_search (WTree * tree)
963 gboolean i;
965 if (tree->searching)
967 if (tree->selected_ptr == tree->store->tree_last)
968 tree_move_to_top (tree);
969 else
971 /* set navigation mode temporarily to 'Static' because in
972 * dynamic navigation mode tree_move_forward will not move
973 * to a lower sublevel if necessary (sequent searches must
974 * start with the directory followed the last found directory)
976 i = tree_navigation_flag;
977 tree_navigation_flag = 0;
978 tree_move_forward (tree, 1);
979 tree_navigation_flag = i;
981 tree_do_search (tree, 0);
983 else
985 tree->searching = 1;
986 tree->search_buffer[0] = 0;
990 /* --------------------------------------------------------------------------------------------- */
992 static void
993 tree_toggle_navig (WTree * tree)
995 tree_navigation_flag = !tree_navigation_flag;
996 buttonbar_set_label (find_buttonbar (tree->widget.owner), 4,
997 tree_navigation_flag ? Q_ ("ButtonBar|Static")
998 : Q_ ("ButtonBar|Dynamc"), tree_map, (Widget *) tree);
1001 /* --------------------------------------------------------------------------------------------- */
1003 static cb_ret_t
1004 tree_execute_cmd (WTree * tree, unsigned long command)
1006 cb_ret_t res = MSG_HANDLED;
1008 if (command != CK_TreeStartSearch)
1009 tree->searching = 0;
1011 switch (command)
1013 case CK_TreeHelp:
1014 interactive_display (NULL, "[Directory Tree]");
1015 break;
1016 case CK_TreeForget:
1017 tree_forget (tree);
1018 break;
1019 case CK_TreeToggleNav:
1020 tree_toggle_navig (tree);
1021 break;
1022 case CK_TreeCopy:
1023 tree_copy (tree, "");
1024 break;
1025 case CK_TreeMove:
1026 tree_move (tree, "");
1027 break;
1028 case CK_TreeMoveUp:
1029 tree_move_up (tree);
1030 break;
1031 case CK_TreeMoveDown:
1032 tree_move_down (tree);
1033 break;
1034 case CK_TreeMoveHome:
1035 tree_move_home (tree);
1036 break;
1037 case CK_TreeMoveEnd:
1038 tree_move_end (tree);
1039 break;
1040 case CK_TreeMovePgUp:
1041 tree_move_pgup (tree);
1042 break;
1043 case CK_TreeMovePgDn:
1044 tree_move_pgdn (tree);
1045 break;
1046 case CK_TreeOpen:
1047 tree_chdir_sel (tree);
1048 break;
1049 case CK_TreeRescan:
1050 tree_rescan (tree);
1051 break;
1052 case CK_TreeStartSearch:
1053 tree_start_search (tree);
1054 break;
1055 case CK_TreeRemove:
1056 tree_rmdir (tree);
1057 break;
1058 default:
1059 res = MSG_NOT_HANDLED;
1062 show_tree (tree);
1064 return res;
1067 /* --------------------------------------------------------------------------------------------- */
1069 static cb_ret_t
1070 tree_key (WTree * tree, int key)
1072 size_t i;
1074 for (i = 0; tree_map[i].key != 0; i++)
1075 if (key == tree_map[i].key)
1076 switch (tree_map[i].command)
1078 case CK_TreeMoveLeft:
1079 return tree_move_left (tree) ? MSG_HANDLED : MSG_NOT_HANDLED;
1080 case CK_TreeMoveRight:
1081 return tree_move_right (tree) ? MSG_HANDLED : MSG_NOT_HANDLED;
1082 default:
1083 tree_execute_cmd (tree, tree_map[i].command);
1084 return MSG_HANDLED;
1087 if (is_abort_char (key))
1089 if (tree->is_panel)
1091 tree->searching = 0;
1092 show_tree (tree);
1093 return MSG_HANDLED; /* eat abort char */
1095 /* modal tree dialog: let upper layer see the
1096 abort character and close the dialog */
1097 return MSG_NOT_HANDLED;
1100 /* Do not eat characters not meant for the tree below ' ' (e.g. C-l). */
1101 if ((key >= ' ' && key <= 255) || key == KEY_BACKSPACE)
1103 if (tree->searching)
1105 tree_do_search (tree, key);
1106 show_tree (tree);
1107 return MSG_HANDLED;
1110 if (!command_prompt)
1112 tree_start_search (tree);
1113 tree_do_search (tree, key);
1114 return MSG_HANDLED;
1116 return tree->is_panel ? MSG_HANDLED : MSG_NOT_HANDLED;
1119 return MSG_NOT_HANDLED;
1122 /* --------------------------------------------------------------------------------------------- */
1124 static void
1125 tree_frame (Dlg_head * h, WTree * tree)
1127 tty_setcolor (NORMAL_COLOR);
1128 widget_erase ((Widget *) tree);
1129 if (tree->is_panel)
1131 const char *title = _("Directory tree");
1132 const int len = str_term_width1 (title);
1134 draw_box (h, tree->widget.y, tree->widget.x, tree->widget.lines, tree->widget.cols, FALSE);
1136 widget_move (&tree->widget, 0, (tree->widget.cols - len - 2) / 2);
1137 tty_printf (" %s ", title);
1139 if (show_mini_info)
1140 widget_move (&tree->widget, tlines (tree) + 1, 0);
1141 tty_print_alt_char (ACS_LTEE, FALSE);
1142 widget_move (&tree->widget, tlines (tree) + 1, tree->widget.cols - 1);
1143 tty_print_alt_char (ACS_RTEE, FALSE);
1144 tty_draw_hline (tree->widget.y + tlines (tree) + 1,
1145 tree->widget.x + 1, ACS_HLINE, tree->widget.cols - 2);
1149 /* --------------------------------------------------------------------------------------------- */
1151 static cb_ret_t
1152 tree_callback (Widget * w, widget_msg_t msg, int parm)
1154 WTree *tree = (WTree *) w;
1155 Dlg_head *h = tree->widget.owner;
1156 WButtonBar *b = find_buttonbar (h);
1158 switch (msg)
1160 case WIDGET_DRAW:
1161 tree_frame (h, tree);
1162 show_tree (tree);
1163 return MSG_HANDLED;
1165 case WIDGET_FOCUS:
1166 tree->active = 1;
1167 buttonbar_set_label (b, 1, Q_ ("ButtonBar|Help"), tree_map, (Widget *) tree);
1168 buttonbar_set_label (b, 2, Q_ ("ButtonBar|Rescan"), tree_map, (Widget *) tree);
1169 buttonbar_set_label (b, 3, Q_ ("ButtonBar|Forget"), tree_map, (Widget *) tree);
1170 buttonbar_set_label (b, 4, tree_navigation_flag ? Q_ ("ButtonBar|Static")
1171 : Q_ ("ButtonBar|Dynamc"), tree_map, (Widget *) tree);
1172 buttonbar_set_label (b, 5, Q_ ("ButtonBar|Copy"), tree_map, (Widget *) tree);
1173 buttonbar_set_label (b, 6, Q_ ("ButtonBar|RenMov"), tree_map, (Widget *) tree);
1174 #if 0
1175 /* FIXME: mkdir is currently defunct */
1176 buttonbar_set_label (b, 7, Q_ ("ButtonBar|Mkdir"), tree_map, (Widget *) tree);
1177 #else
1178 buttonbar_clear_label (b, 7, (Widget *) tree);
1179 #endif
1180 buttonbar_set_label (b, 8, Q_ ("ButtonBar|Rmdir"), tree_map, (Widget *) tree);
1181 buttonbar_redraw (b);
1183 /* FIXME: Should find a better way of only displaying the
1184 currently selected item */
1185 show_tree (tree);
1186 return MSG_HANDLED;
1188 /* FIXME: Should find a better way of changing the color of the
1189 selected item */
1191 case WIDGET_UNFOCUS:
1192 tree->active = 0;
1193 tree->searching = 0;
1194 show_tree (tree);
1195 return MSG_HANDLED;
1197 case WIDGET_KEY:
1198 return tree_key (tree, parm);
1200 case WIDGET_COMMAND:
1201 /* command from buttonbar */
1202 return tree_execute_cmd (tree, parm);
1204 case WIDGET_DESTROY:
1205 tree_destroy (tree);
1206 return MSG_HANDLED;
1208 default:
1209 return default_proc (msg, parm);
1213 /* --------------------------------------------------------------------------------------------- */
1214 /*** public functions ****************************************************************************/
1215 /* --------------------------------------------------------------------------------------------- */
1217 WTree *
1218 tree_new (int y, int x, int lines, int cols, gboolean is_panel)
1220 WTree *tree = g_new (WTree, 1);
1222 init_widget (&tree->widget, y, x, lines, cols, tree_callback, event_callback);
1223 tree->is_panel = is_panel;
1224 tree->selected_ptr = 0;
1226 tree->store = tree_store_get ();
1227 tree_store_add_entry_remove_hook (remove_callback, tree);
1228 tree->tree_shown = 0;
1229 tree->search_buffer[0] = 0;
1230 tree->topdiff = tree->widget.lines / 2;
1231 tree->searching = 0;
1232 tree->active = 0;
1234 /* We do not want to keep the cursor */
1235 widget_want_cursor (tree->widget, 0);
1236 load_tree (tree);
1237 return tree;
1240 /* --------------------------------------------------------------------------------------------- */
1242 void
1243 tree_chdir (WTree * tree, const char *dir)
1245 tree_entry *current;
1247 current = tree_store_whereis (dir);
1249 if (current != NULL)
1251 tree->selected_ptr = current;
1252 tree_check_focus (tree);
1256 /* --------------------------------------------------------------------------------------------- */
1257 /** Return name of the currently selected entry */
1259 char *
1260 tree_selected_name (const WTree * tree)
1262 return tree->selected_ptr->name;
1265 /* --------------------------------------------------------------------------------------------- */
1267 void
1268 sync_tree (const char *path)
1270 tree_chdir (the_tree, path);
1273 /* --------------------------------------------------------------------------------------------- */
1275 WTree *
1276 find_tree (struct Dlg_head *h)
1278 return (WTree *) find_widget_type (h, tree_callback);
1281 /* --------------------------------------------------------------------------------------------- */