Merge remote branch 'osp/only-directories' into homework
[pantumic.git] / src / filemanager / tree.c
blob431c1be6bdda607d8e1a4c8462c6cd45ab415e8b
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 "src/setup.h" /* confirm_delete, panels_options */
55 #include "src/keybind-defaults.h"
56 #include "src/history.h"
57 #include "src/help.h"
59 #include "dir.h"
60 #include "midnight.h" /* the_menubar */
61 #include "file.h" /* copy_dir_dir(), move_dir_dir(), erase_dir() */
62 #include "layout.h" /* command_prompt */
63 #include "treestore.h"
64 #include "cmd.h"
65 #include "filegui.h"
67 #include "tree.h"
69 /*** global variables ****************************************************************************/
71 /* The pointer to the tree */
72 WTree *the_tree = NULL;
74 /* If this is true, then when browsing the tree the other window will
75 * automatically reload it's directory with the contents of the currently
76 * selected directory.
78 int xtree_mode = 0;
80 /*** file scope macro definitions ****************************************************************/
82 #define tlines(t) (t->is_panel ? t->widget.lines - 2 - (panels_options.show_mini_info ? 2 : 0) : t->widget.lines)
84 /* Use the color of the parent widget for the unselected entries */
85 #define TREE_NORMALC(h) (h->color[DLG_COLOR_NORMAL])
86 #define TREE_CURRENTC(h) (h->color[DLG_COLOR_FOCUS])
88 /*** file scope type declarations ****************************************************************/
90 struct WTree
92 Widget widget;
93 struct TreeStore *store;
94 tree_entry *selected_ptr; /* The selected directory */
95 char search_buffer[MC_MAXFILENAMELEN]; /* Current search string */
96 tree_entry **tree_shown; /* Entries currently on screen */
97 int is_panel; /* panel or plain widget flag */
98 int active; /* if it's currently selected */
99 int searching; /* Are we on searching mode? */
100 int topdiff; /* The difference between the topmost
101 shown and the selected */
104 /*** file scope variables ************************************************************************/
106 /* Specifies the display mode: 1d or 2d */
107 static gboolean tree_navigation_flag = FALSE;
109 /*** file scope functions ************************************************************************/
110 /* --------------------------------------------------------------------------------------------- */
112 static void tree_rescan (void *data);
114 /* --------------------------------------------------------------------------------------------- */
116 static tree_entry *
117 back_ptr (tree_entry * ptr, int *count)
119 int i = 0;
121 while (ptr && ptr->prev && i < *count)
123 ptr = ptr->prev;
124 i++;
126 *count = i;
127 return ptr;
130 /* --------------------------------------------------------------------------------------------- */
132 static tree_entry *
133 forw_ptr (tree_entry * ptr, int *count)
135 int i = 0;
137 while (ptr && ptr->next && i < *count)
139 ptr = ptr->next;
140 i++;
142 *count = i;
143 return ptr;
146 /* --------------------------------------------------------------------------------------------- */
148 static void
149 remove_callback (tree_entry * entry, void *data)
151 WTree *tree = data;
153 if (tree->selected_ptr == entry)
155 if (tree->selected_ptr->next)
156 tree->selected_ptr = tree->selected_ptr->next;
157 else
158 tree->selected_ptr = tree->selected_ptr->prev;
162 /* --------------------------------------------------------------------------------------------- */
163 /** Save the ${XDG_CACHE_HOME}/mc/Tree file */
165 static void
166 save_tree (WTree * tree)
168 int error;
169 char *tree_name;
171 (void) tree;
172 error = tree_store_save ();
175 if (error)
177 tree_name =
178 g_build_filename (mc_config_get_cache_path (), MC_TREESTORE_FILE, (char *) NULL);
179 fprintf (stderr, _("Cannot open the %s file for writing:\n%s\n"), tree_name,
180 unix_error_string (error));
181 g_free (tree_name);
185 /* --------------------------------------------------------------------------------------------- */
187 static void
188 tree_remove_entry (WTree * tree, char *name)
190 (void) tree;
191 tree_store_remove_entry (name);
194 /* --------------------------------------------------------------------------------------------- */
196 static void
197 tree_destroy (WTree * tree)
199 tree_store_remove_entry_remove_hook (remove_callback);
200 save_tree (tree);
202 g_free (tree->tree_shown);
203 tree->tree_shown = 0;
204 tree->selected_ptr = NULL;
207 /* --------------------------------------------------------------------------------------------- */
208 /** Loads the .mc.tree file */
210 static void
211 load_tree (WTree * tree)
213 tree_store_load ();
215 tree->selected_ptr = tree->store->tree_first;
216 tree_chdir (tree, mc_config_get_home_dir ());
219 /* --------------------------------------------------------------------------------------------- */
221 static void
222 tree_show_mini_info (WTree * tree, int tree_lines, int tree_cols)
224 Dlg_head *h = tree->widget.owner;
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 (tree->widget.y + line, tree->widget.x + 1, ' ', tree_cols);
242 widget_move (&tree->widget, line, 1);
243 tty_print_char (PATH_SEP);
244 tty_print_string (str_fit_to_term (tree->search_buffer, tree_cols - 2, J_LEFT_FIT));
245 tty_print_char (' ');
247 else
249 /* Show full name of selected directory */
250 tty_setcolor (tree->is_panel ? NORMAL_COLOR : TREE_NORMALC (h));
251 tty_draw_hline (tree->widget.y + line, tree->widget.x + 1, ' ', tree_cols);
252 widget_move (&tree->widget, line, 1);
253 tty_print_string (str_fit_to_term (tree->selected_ptr->name, tree_cols, J_LEFT_FIT));
257 /* --------------------------------------------------------------------------------------------- */
259 static void
260 show_tree (WTree * tree)
262 Dlg_head *h = tree->widget.owner;
263 tree_entry *current;
264 int i, j, topsublevel;
265 int x = 0, y = 0;
266 int tree_lines, tree_cols;
268 /* Initialize */
269 tree_lines = tlines (tree);
270 tree_cols = tree->widget.cols;
272 widget_move ((Widget *) tree, y, x);
273 if (tree->is_panel)
275 tree_cols -= 2;
276 x = y = 1;
279 g_free (tree->tree_shown);
280 tree->tree_shown = g_new0 (tree_entry *, tree_lines);
282 if (tree->store->tree_first)
283 topsublevel = tree->store->tree_first->sublevel;
284 else
285 topsublevel = 0;
286 if (!tree->selected_ptr)
288 tree->selected_ptr = tree->store->tree_first;
289 tree->topdiff = 0;
291 current = tree->selected_ptr;
293 /* Calculate the directory which is to be shown on the topmost line */
294 if (!tree_navigation_flag)
295 current = back_ptr (current, &tree->topdiff);
296 else
298 i = 0;
299 while (current->prev && i < tree->topdiff)
301 current = current->prev;
302 if (current->sublevel < tree->selected_ptr->sublevel)
304 if (strncmp (current->name, tree->selected_ptr->name, strlen (current->name)) == 0)
305 i++;
307 else if (current->sublevel == tree->selected_ptr->sublevel)
309 for (j = strlen (current->name) - 1; current->name[j] != PATH_SEP; j--);
310 if (strncmp (current->name, tree->selected_ptr->name, j) == 0)
311 i++;
313 else if (current->sublevel == tree->selected_ptr->sublevel + 1
314 && strlen (tree->selected_ptr->name) > 1)
316 if (strncmp (current->name, tree->selected_ptr->name,
317 strlen (tree->selected_ptr->name)) == 0)
318 i++;
321 tree->topdiff = i;
324 /* Loop for every line */
325 for (i = 0; i < tree_lines; i++)
327 tty_setcolor (tree->is_panel ? NORMAL_COLOR : TREE_NORMALC (h));
329 /* Move to the beginning of the line */
330 tty_draw_hline (tree->widget.y + y + i, tree->widget.x + x, ' ', tree_cols);
332 if (current == NULL)
333 continue;
335 if (tree->is_panel)
336 tty_setcolor (tree->active && current == tree->selected_ptr
337 ? SELECTED_COLOR : NORMAL_COLOR);
338 else
339 tty_setcolor (current == tree->selected_ptr ? TREE_CURRENTC (h) : TREE_NORMALC (h));
341 tree->tree_shown[i] = current;
342 if (current->sublevel == topsublevel)
344 /* Show full name */
345 tty_print_string (str_fit_to_term
346 (current->name, tree_cols + (tree->is_panel ? 0 : 1), J_LEFT_FIT));
348 else
350 /* Sub level directory */
351 tty_set_alt_charset (TRUE);
353 /* Output branch parts */
354 for (j = 0; j < current->sublevel - topsublevel - 1; j++)
356 if (tree_cols - 8 - 3 * j < 9)
357 break;
358 tty_print_char (' ');
359 if (current->submask & (1 << (j + topsublevel + 1)))
360 tty_print_char (ACS_VLINE);
361 else
362 tty_print_char (' ');
363 tty_print_char (' ');
365 tty_print_char (' ');
366 j++;
367 if (!current->next || !(current->next->submask & (1 << current->sublevel)))
368 tty_print_char (ACS_LLCORNER);
369 else
370 tty_print_char (ACS_LTEE);
371 tty_print_char (ACS_HLINE);
372 tty_set_alt_charset (FALSE);
374 /* Show sub-name */
375 tty_print_char (' ');
376 tty_print_string (str_fit_to_term
377 (current->subname, tree_cols - x - 3 * j, J_LEFT_FIT));
380 /* Calculate the next value for current */
381 current = current->next;
382 if (tree_navigation_flag)
384 while (current != NULL)
386 if (current->sublevel < tree->selected_ptr->sublevel)
388 if (strncmp (current->name, tree->selected_ptr->name,
389 strlen (current->name)) == 0)
390 break;
392 else if (current->sublevel == tree->selected_ptr->sublevel)
394 for (j = strlen (current->name) - 1; current->name[j] != PATH_SEP; j--)
396 if (strncmp (current->name, tree->selected_ptr->name, j) == 0)
397 break;
399 else if (current->sublevel == tree->selected_ptr->sublevel + 1
400 && strlen (tree->selected_ptr->name) > 1)
402 if (strncmp (current->name, tree->selected_ptr->name,
403 strlen (tree->selected_ptr->name)) == 0)
404 break;
406 current = current->next;
411 tree_show_mini_info (tree, tree_lines, tree_cols);
414 /* --------------------------------------------------------------------------------------------- */
416 static void
417 tree_check_focus (WTree * tree)
419 if (tree->topdiff < 3)
420 tree->topdiff = 3;
421 else if (tree->topdiff >= tlines (tree) - 3)
422 tree->topdiff = tlines (tree) - 3 - 1;
425 /* --------------------------------------------------------------------------------------------- */
427 static void
428 tree_move_backward (WTree * tree, int i)
430 if (!tree_navigation_flag)
431 tree->selected_ptr = back_ptr (tree->selected_ptr, &i);
432 else
434 tree_entry *current;
435 int j = 0;
437 current = tree->selected_ptr;
438 while (j < i && current->prev && current->prev->sublevel >= tree->selected_ptr->sublevel)
440 current = current->prev;
441 if (current->sublevel == tree->selected_ptr->sublevel)
443 tree->selected_ptr = current;
444 j++;
447 i = j;
450 tree->topdiff -= i;
451 tree_check_focus (tree);
454 /* --------------------------------------------------------------------------------------------- */
456 static void
457 tree_move_forward (WTree * tree, int i)
459 if (!tree_navigation_flag)
460 tree->selected_ptr = forw_ptr (tree->selected_ptr, &i);
461 else
463 tree_entry *current;
464 int j = 0;
466 current = tree->selected_ptr;
467 while (j < i && current->next && current->next->sublevel >= tree->selected_ptr->sublevel)
469 current = current->next;
470 if (current->sublevel == tree->selected_ptr->sublevel)
472 tree->selected_ptr = current;
473 j++;
476 i = j;
479 tree->topdiff += i;
480 tree_check_focus (tree);
483 /* --------------------------------------------------------------------------------------------- */
485 static void
486 tree_move_to_child (WTree * tree)
488 tree_entry *current;
490 /* Do we have a starting point? */
491 if (!tree->selected_ptr)
492 return;
493 /* Take the next entry */
494 current = tree->selected_ptr->next;
495 /* Is it the child of the selected entry */
496 if (current && current->sublevel > tree->selected_ptr->sublevel)
498 /* Yes -> select this entry */
499 tree->selected_ptr = current;
500 tree->topdiff++;
501 tree_check_focus (tree);
503 else
505 /* No -> rescan and try again */
506 tree_rescan (tree);
507 current = tree->selected_ptr->next;
508 if (current && current->sublevel > tree->selected_ptr->sublevel)
510 tree->selected_ptr = current;
511 tree->topdiff++;
512 tree_check_focus (tree);
517 /* --------------------------------------------------------------------------------------------- */
519 static gboolean
520 tree_move_to_parent (WTree * tree)
522 tree_entry *current;
523 tree_entry *old;
525 if (!tree->selected_ptr)
526 return FALSE;
528 old = tree->selected_ptr;
529 current = tree->selected_ptr->prev;
530 while (current && current->sublevel >= tree->selected_ptr->sublevel)
532 current = current->prev;
533 tree->topdiff--;
535 if (!current)
536 current = tree->store->tree_first;
537 tree->selected_ptr = current;
538 tree_check_focus (tree);
539 return tree->selected_ptr != old;
542 /* --------------------------------------------------------------------------------------------- */
544 static void
545 tree_move_to_top (WTree * tree)
547 tree->selected_ptr = tree->store->tree_first;
548 tree->topdiff = 0;
551 /* --------------------------------------------------------------------------------------------- */
553 static void
554 tree_move_to_bottom (WTree * tree)
556 tree->selected_ptr = tree->store->tree_last;
557 tree->topdiff = tlines (tree) - 3 - 1;
560 /* --------------------------------------------------------------------------------------------- */
561 /** Handle mouse click */
563 static void
564 tree_event (WTree * tree, int y)
566 if (tree->tree_shown[y])
568 tree->selected_ptr = tree->tree_shown[y];
569 tree->topdiff = y;
571 show_tree (tree);
574 /* --------------------------------------------------------------------------------------------- */
576 static void
577 tree_chdir_sel (WTree * tree)
579 if (!tree->is_panel)
580 return;
582 change_panel ();
584 if (do_cd (tree->selected_ptr->name, cd_exact))
585 select_item (current_panel);
586 else
587 message (D_ERROR, MSG_ERROR, _("Cannot chdir to \"%s\"\n%s"),
588 tree->selected_ptr->name, unix_error_string (errno));
590 change_panel ();
591 show_tree (tree);
594 /* --------------------------------------------------------------------------------------------- */
596 static void
597 maybe_chdir (WTree * tree)
599 if (xtree_mode && tree->is_panel && is_idle ())
600 tree_chdir_sel (tree);
603 /* --------------------------------------------------------------------------------------------- */
604 /** Mouse callback */
606 static int
607 event_callback (Gpm_Event * event, void *data)
609 WTree *tree = data;
611 /* rest of the upper frame, the menu is invisible - call menu */
612 if (tree->is_panel && (event->type & GPM_DOWN) && event->y == 1 && !menubar_visible)
614 event->x += tree->widget.x;
615 return the_menubar->widget.mouse (event, the_menubar);
618 if (!(event->type & GPM_UP))
619 return MOU_NORMAL;
621 if (tree->is_panel)
622 event->y--;
624 event->y--;
626 if (!tree->active)
627 change_panel ();
629 if (event->y < 0)
631 tree_move_backward (tree, tlines (tree) - 1);
632 show_tree (tree);
634 else if (event->y >= tlines (tree))
636 tree_move_forward (tree, tlines (tree) - 1);
637 show_tree (tree);
639 else
641 tree_event (tree, event->y);
642 if ((event->type & (GPM_UP | GPM_DOUBLE)) == (GPM_UP | GPM_DOUBLE))
644 tree_chdir_sel (tree);
647 return MOU_NORMAL;
650 /* --------------------------------------------------------------------------------------------- */
651 /** Search tree for text */
653 static int
654 search_tree (WTree * tree, char *text)
656 tree_entry *current;
657 int len;
658 int wrapped = 0;
659 int found = 0;
661 len = strlen (text);
662 current = tree->selected_ptr;
663 found = 0;
664 while (!wrapped || current != tree->selected_ptr)
666 if (strncmp (current->subname, text, len) == 0)
668 tree->selected_ptr = current;
669 found = 1;
670 break;
672 current = current->next;
673 if (!current)
675 current = tree->store->tree_first;
676 wrapped = 1;
678 tree->topdiff++;
680 tree_check_focus (tree);
681 return found;
684 /* --------------------------------------------------------------------------------------------- */
686 static void
687 tree_do_search (WTree * tree, int key)
689 size_t l;
691 l = strlen (tree->search_buffer);
692 if ((l != 0) && (key == KEY_BACKSPACE))
693 tree->search_buffer[--l] = '\0';
694 else if (key && l < sizeof (tree->search_buffer))
696 tree->search_buffer[l] = key;
697 tree->search_buffer[++l] = '\0';
700 if (!search_tree (tree, tree->search_buffer))
701 tree->search_buffer[--l] = 0;
703 show_tree (tree);
704 maybe_chdir (tree);
707 /* --------------------------------------------------------------------------------------------- */
709 static void
710 tree_rescan (void *data)
712 char old_dir[MC_MAXPATHLEN];
713 WTree *tree = data;
714 int ret;
716 if (tree->selected_ptr == NULL || mc_get_current_wd (old_dir, MC_MAXPATHLEN) == NULL
717 || mc_chdir (tree->selected_ptr->name))
718 return;
720 tree_store_rescan (tree->selected_ptr->name);
721 ret = mc_chdir (old_dir);
724 /* --------------------------------------------------------------------------------------------- */
726 static void
727 tree_forget (void *data)
729 WTree *tree = data;
730 if (tree->selected_ptr)
731 tree_remove_entry (tree, tree->selected_ptr->name);
734 /* --------------------------------------------------------------------------------------------- */
736 static void
737 tree_copy (WTree * tree, const char *default_dest)
739 char msg[BUF_MEDIUM];
740 char *dest;
742 if (tree->selected_ptr == NULL)
743 return;
745 g_snprintf (msg, sizeof (msg), _("Copy \"%s\" directory to:"),
746 str_trunc (tree->selected_ptr->name, 50));
747 dest = input_expand_dialog (Q_ ("DialogTitle|Copy"),
748 msg, MC_HISTORY_FM_TREE_COPY, default_dest);
750 if (dest != NULL && *dest != '\0')
752 FileOpContext *ctx;
753 FileOpTotalContext *tctx;
755 ctx = file_op_context_new (OP_COPY);
756 tctx = file_op_total_context_new ();
757 file_op_context_create_ui (ctx, FALSE, FILEGUI_DIALOG_MULTI_ITEM);
758 tctx->ask_overwrite = FALSE;
759 tctx->is_toplevel_file = FALSE;
760 copy_dir_dir (tctx, ctx, tree->selected_ptr->name, dest, TRUE, FALSE, FALSE, NULL);
761 file_op_total_context_destroy (tctx);
762 file_op_context_destroy (ctx);
765 g_free (dest);
768 /* --------------------------------------------------------------------------------------------- */
770 static void
771 tree_move (WTree * tree, const char *default_dest)
773 char msg[BUF_MEDIUM];
774 char *dest;
775 struct stat buf;
776 FileOpContext *ctx;
777 FileOpTotalContext *tctx;
779 if (tree->selected_ptr == NULL)
780 return;
782 g_snprintf (msg, sizeof (msg), _("Move \"%s\" directory to:"),
783 str_trunc (tree->selected_ptr->name, 50));
784 dest =
785 input_expand_dialog (Q_ ("DialogTitle|Move"), msg, MC_HISTORY_FM_TREE_MOVE, default_dest);
787 if (dest == NULL || *dest == '\0')
789 g_free (dest);
790 return;
793 if (stat (dest, &buf))
795 message (D_ERROR, MSG_ERROR, _("Cannot stat the destination\n%s"),
796 unix_error_string (errno));
797 g_free (dest);
798 return;
801 if (!S_ISDIR (buf.st_mode))
803 file_error (_("Destination \"%s\" must be a directory\n%s"), dest);
804 g_free (dest);
805 return;
808 ctx = file_op_context_new (OP_MOVE);
809 tctx = file_op_total_context_new ();
810 file_op_context_create_ui (ctx, FALSE, FILEGUI_DIALOG_ONE_ITEM);
811 move_dir_dir (tctx, ctx, tree->selected_ptr->name, dest);
812 file_op_total_context_destroy (tctx);
813 file_op_context_destroy (ctx);
815 g_free (dest);
818 /* --------------------------------------------------------------------------------------------- */
820 #if 0
821 static void
822 tree_mkdir (WTree * tree)
824 char old_dir[MC_MAXPATHLEN];
826 if (!tree->selected_ptr)
827 return;
828 if (mc_get_current_wd (old_dir, MC_MAXPATHLEN) == NULL)
829 return;
830 if (chdir (tree->selected_ptr->name))
831 return;
832 /* FIXME
833 mkdir_cmd (tree);
835 tree_rescan (tree);
836 chdir (old_dir);
838 #endif
840 /* --------------------------------------------------------------------------------------------- */
842 static void
843 tree_rmdir (void *data)
845 WTree *tree = data;
846 FileOpContext *ctx;
847 FileOpTotalContext *tctx;
849 if (!tree->selected_ptr)
850 return;
852 if (confirm_delete)
854 char *buf;
855 int result;
857 buf = g_strdup_printf (_("Delete %s?"), tree->selected_ptr->name);
858 result = query_dialog (Q_ ("DialogTitle|Delete"), buf, D_ERROR, 2, _("&Yes"), _("&No"));
859 g_free (buf);
860 if (result != 0)
861 return;
864 ctx = file_op_context_new (OP_DELETE);
865 tctx = file_op_total_context_new ();
867 file_op_context_create_ui (ctx, FALSE, FILEGUI_DIALOG_ONE_ITEM);
868 if (erase_dir (tctx, ctx, tree->selected_ptr->name) == FILE_CONT)
869 tree_forget (tree);
870 file_op_total_context_destroy (tctx);
871 file_op_context_destroy (ctx);
874 /* --------------------------------------------------------------------------------------------- */
876 static inline void
877 tree_move_up (WTree * tree)
879 tree_move_backward (tree, 1);
880 show_tree (tree);
881 maybe_chdir (tree);
884 /* --------------------------------------------------------------------------------------------- */
886 static inline void
887 tree_move_down (WTree * tree)
889 tree_move_forward (tree, 1);
890 show_tree (tree);
891 maybe_chdir (tree);
894 /* --------------------------------------------------------------------------------------------- */
896 static inline void
897 tree_move_home (WTree * tree)
899 tree_move_to_top (tree);
900 show_tree (tree);
901 maybe_chdir (tree);
904 /* --------------------------------------------------------------------------------------------- */
906 static inline void
907 tree_move_end (WTree * tree)
909 tree_move_to_bottom (tree);
910 show_tree (tree);
911 maybe_chdir (tree);
914 /* --------------------------------------------------------------------------------------------- */
916 static void
917 tree_move_pgup (WTree * tree)
919 tree_move_backward (tree, tlines (tree) - 1);
920 show_tree (tree);
921 maybe_chdir (tree);
924 /* --------------------------------------------------------------------------------------------- */
926 static void
927 tree_move_pgdn (WTree * tree)
929 tree_move_forward (tree, tlines (tree) - 1);
930 show_tree (tree);
931 maybe_chdir (tree);
934 /* --------------------------------------------------------------------------------------------- */
936 static gboolean
937 tree_move_left (WTree * tree)
939 gboolean v = FALSE;
941 if (tree_navigation_flag)
943 v = tree_move_to_parent (tree);
944 show_tree (tree);
945 maybe_chdir (tree);
948 return v;
951 /* --------------------------------------------------------------------------------------------- */
953 static gboolean
954 tree_move_right (WTree * tree)
956 gboolean v = FALSE;
958 if (tree_navigation_flag)
960 tree_move_to_child (tree);
961 show_tree (tree);
962 maybe_chdir (tree);
963 v = TRUE;
966 return v;
969 /* --------------------------------------------------------------------------------------------- */
971 static void
972 tree_start_search (WTree * tree)
974 gboolean i;
976 if (tree->searching)
978 if (tree->selected_ptr == tree->store->tree_last)
979 tree_move_to_top (tree);
980 else
982 /* set navigation mode temporarily to 'Static' because in
983 * dynamic navigation mode tree_move_forward will not move
984 * to a lower sublevel if necessary (sequent searches must
985 * start with the directory followed the last found directory)
987 i = tree_navigation_flag;
988 tree_navigation_flag = 0;
989 tree_move_forward (tree, 1);
990 tree_navigation_flag = i;
992 tree_do_search (tree, 0);
994 else
996 tree->searching = 1;
997 tree->search_buffer[0] = 0;
1001 /* --------------------------------------------------------------------------------------------- */
1003 static void
1004 tree_toggle_navig (WTree * tree)
1006 tree_navigation_flag = !tree_navigation_flag;
1007 buttonbar_set_label (find_buttonbar (tree->widget.owner), 4,
1008 tree_navigation_flag ? Q_ ("ButtonBar|Static")
1009 : Q_ ("ButtonBar|Dynamc"), tree_map, (Widget *) tree);
1012 /* --------------------------------------------------------------------------------------------- */
1014 static cb_ret_t
1015 tree_execute_cmd (WTree * tree, unsigned long command)
1017 cb_ret_t res = MSG_HANDLED;
1019 if (command != CK_TreeStartSearch)
1020 tree->searching = 0;
1022 switch (command)
1024 case CK_TreeHelp:
1025 interactive_display (NULL, "[Directory Tree]");
1026 break;
1027 case CK_TreeForget:
1028 tree_forget (tree);
1029 break;
1030 case CK_TreeToggleNav:
1031 tree_toggle_navig (tree);
1032 break;
1033 case CK_TreeCopy:
1034 tree_copy (tree, "");
1035 break;
1036 case CK_TreeMove:
1037 tree_move (tree, "");
1038 break;
1039 case CK_TreeMoveUp:
1040 tree_move_up (tree);
1041 break;
1042 case CK_TreeMoveDown:
1043 tree_move_down (tree);
1044 break;
1045 case CK_TreeMoveHome:
1046 tree_move_home (tree);
1047 break;
1048 case CK_TreeMoveEnd:
1049 tree_move_end (tree);
1050 break;
1051 case CK_TreeMovePgUp:
1052 tree_move_pgup (tree);
1053 break;
1054 case CK_TreeMovePgDn:
1055 tree_move_pgdn (tree);
1056 break;
1057 case CK_TreeOpen:
1058 tree_chdir_sel (tree);
1059 break;
1060 case CK_TreeRescan:
1061 tree_rescan (tree);
1062 break;
1063 case CK_TreeStartSearch:
1064 tree_start_search (tree);
1065 break;
1066 case CK_TreeRemove:
1067 tree_rmdir (tree);
1068 break;
1069 default:
1070 res = MSG_NOT_HANDLED;
1073 show_tree (tree);
1075 return res;
1078 /* --------------------------------------------------------------------------------------------- */
1080 static cb_ret_t
1081 tree_key (WTree * tree, int key)
1083 size_t i;
1085 for (i = 0; tree_map[i].key != 0; i++)
1086 if (key == tree_map[i].key)
1087 switch (tree_map[i].command)
1089 case CK_TreeMoveLeft:
1090 return tree_move_left (tree) ? MSG_HANDLED : MSG_NOT_HANDLED;
1091 case CK_TreeMoveRight:
1092 return tree_move_right (tree) ? MSG_HANDLED : MSG_NOT_HANDLED;
1093 default:
1094 tree_execute_cmd (tree, tree_map[i].command);
1095 return MSG_HANDLED;
1098 if (is_abort_char (key))
1100 if (tree->is_panel)
1102 tree->searching = 0;
1103 show_tree (tree);
1104 return MSG_HANDLED; /* eat abort char */
1106 /* modal tree dialog: let upper layer see the
1107 abort character and close the dialog */
1108 return MSG_NOT_HANDLED;
1111 /* Do not eat characters not meant for the tree below ' ' (e.g. C-l). */
1112 if ((key >= ' ' && key <= 255) || key == KEY_BACKSPACE)
1114 if (tree->searching)
1116 tree_do_search (tree, key);
1117 show_tree (tree);
1118 return MSG_HANDLED;
1121 if (!command_prompt)
1123 tree_start_search (tree);
1124 tree_do_search (tree, key);
1125 return MSG_HANDLED;
1127 return tree->is_panel ? MSG_HANDLED : MSG_NOT_HANDLED;
1130 return MSG_NOT_HANDLED;
1133 /* --------------------------------------------------------------------------------------------- */
1135 static void
1136 tree_frame (Dlg_head * h, WTree * tree)
1138 tty_setcolor (NORMAL_COLOR);
1139 widget_erase ((Widget *) tree);
1140 if (tree->is_panel)
1142 const char *title = _("Directory tree");
1143 const int len = str_term_width1 (title);
1145 draw_box (h, tree->widget.y, tree->widget.x, tree->widget.lines, tree->widget.cols, FALSE);
1147 widget_move (&tree->widget, 0, (tree->widget.cols - len - 2) / 2);
1148 tty_printf (" %s ", title);
1150 if (panels_options.show_mini_info)
1151 widget_move (&tree->widget, tlines (tree) + 1, 0);
1152 tty_print_alt_char (ACS_LTEE, FALSE);
1153 widget_move (&tree->widget, tlines (tree) + 1, tree->widget.cols - 1);
1154 tty_print_alt_char (ACS_RTEE, FALSE);
1155 tty_draw_hline (tree->widget.y + tlines (tree) + 1,
1156 tree->widget.x + 1, ACS_HLINE, tree->widget.cols - 2);
1160 /* --------------------------------------------------------------------------------------------- */
1162 static cb_ret_t
1163 tree_callback (Widget * w, widget_msg_t msg, int parm)
1165 WTree *tree = (WTree *) w;
1166 Dlg_head *h = tree->widget.owner;
1167 WButtonBar *b = find_buttonbar (h);
1169 switch (msg)
1171 case WIDGET_DRAW:
1172 tree_frame (h, tree);
1173 show_tree (tree);
1174 return MSG_HANDLED;
1176 case WIDGET_FOCUS:
1177 tree->active = 1;
1178 buttonbar_set_label (b, 1, Q_ ("ButtonBar|Help"), tree_map, (Widget *) tree);
1179 buttonbar_set_label (b, 2, Q_ ("ButtonBar|Rescan"), tree_map, (Widget *) tree);
1180 buttonbar_set_label (b, 3, Q_ ("ButtonBar|Forget"), tree_map, (Widget *) tree);
1181 buttonbar_set_label (b, 4, tree_navigation_flag ? Q_ ("ButtonBar|Static")
1182 : Q_ ("ButtonBar|Dynamc"), tree_map, (Widget *) tree);
1183 buttonbar_set_label (b, 5, Q_ ("ButtonBar|Copy"), tree_map, (Widget *) tree);
1184 buttonbar_set_label (b, 6, Q_ ("ButtonBar|RenMov"), tree_map, (Widget *) tree);
1185 #if 0
1186 /* FIXME: mkdir is currently defunct */
1187 buttonbar_set_label (b, 7, Q_ ("ButtonBar|Mkdir"), tree_map, (Widget *) tree);
1188 #else
1189 buttonbar_clear_label (b, 7, (Widget *) tree);
1190 #endif
1191 buttonbar_set_label (b, 8, Q_ ("ButtonBar|Rmdir"), tree_map, (Widget *) tree);
1192 buttonbar_redraw (b);
1194 /* FIXME: Should find a better way of only displaying the
1195 currently selected item */
1196 show_tree (tree);
1197 return MSG_HANDLED;
1199 /* FIXME: Should find a better way of changing the color of the
1200 selected item */
1202 case WIDGET_UNFOCUS:
1203 tree->active = 0;
1204 tree->searching = 0;
1205 show_tree (tree);
1206 return MSG_HANDLED;
1208 case WIDGET_KEY:
1209 return tree_key (tree, parm);
1211 case WIDGET_COMMAND:
1212 /* command from buttonbar */
1213 return tree_execute_cmd (tree, parm);
1215 case WIDGET_DESTROY:
1216 tree_destroy (tree);
1217 return MSG_HANDLED;
1219 default:
1220 return default_proc (msg, parm);
1224 /* --------------------------------------------------------------------------------------------- */
1225 /*** public functions ****************************************************************************/
1226 /* --------------------------------------------------------------------------------------------- */
1228 WTree *
1229 tree_new (int y, int x, int lines, int cols, gboolean is_panel)
1231 WTree *tree = g_new (WTree, 1);
1233 init_widget (&tree->widget, y, x, lines, cols, tree_callback, event_callback);
1234 tree->is_panel = is_panel;
1235 tree->selected_ptr = 0;
1237 tree->store = tree_store_get ();
1238 tree_store_add_entry_remove_hook (remove_callback, tree);
1239 tree->tree_shown = 0;
1240 tree->search_buffer[0] = 0;
1241 tree->topdiff = tree->widget.lines / 2;
1242 tree->searching = 0;
1243 tree->active = 0;
1245 /* We do not want to keep the cursor */
1246 widget_want_cursor (tree->widget, 0);
1247 load_tree (tree);
1248 return tree;
1251 /* --------------------------------------------------------------------------------------------- */
1253 void
1254 tree_chdir (WTree * tree, const char *dir)
1256 tree_entry *current;
1258 current = tree_store_whereis (dir);
1260 if (current != NULL)
1262 tree->selected_ptr = current;
1263 tree_check_focus (tree);
1267 /* --------------------------------------------------------------------------------------------- */
1268 /** Return name of the currently selected entry */
1270 char *
1271 tree_selected_name (const WTree * tree)
1273 return tree->selected_ptr->name;
1276 /* --------------------------------------------------------------------------------------------- */
1278 void
1279 sync_tree (const char *path)
1281 tree_chdir (the_tree, path);
1284 /* --------------------------------------------------------------------------------------------- */
1286 WTree *
1287 find_tree (struct Dlg_head *h)
1289 return (WTree *) find_widget_type (h, tree_callback);
1292 /* --------------------------------------------------------------------------------------------- */