Merge branch '1858_segfault_in_search'
[midnight-commander.git] / src / tree.c
blob0542ef08fe1a1219ed3eca8028db53dd61e2d1df
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 "global.h"
44 #include "../src/tty/tty.h"
45 #include "../src/skin/skin.h"
46 #include "../src/tty/mouse.h"
47 #include "../src/tty/key.h"
49 #include "wtools.h" /* message() */
50 #include "dir.h"
51 #include "dialog.h"
52 #include "widget.h"
53 #include "panel.h"
54 #include "main.h"
55 #include "main-widgets.h" /* the_menubar */
56 #include "menu.h" /* menubar_visible */
57 #include "file.h" /* copy_dir_dir(), move_dir_dir(), erase_dir() */
58 #include "layout.h" /* command_prompt */
59 #include "help.h"
60 #include "treestore.h"
61 #include "cmd.h"
62 #include "cmddef.h"
63 #include "keybind.h"
64 #include "history.h"
65 #include "strutil.h"
66 #include "fileloc.h"
67 #include "tree.h"
69 const global_keymap_t *tree_map;
71 #define tlines(t) (t->is_panel ? t->widget.lines - 2 - (show_mini_info ? 2 : 0) : t->widget.lines)
73 /* Use the color of the parent widget for the unselected entries */
74 #define TREE_NORMALC(h) (DLG_NORMALC (h))
76 /* Specifies the display mode: 1d or 2d */
77 static gboolean tree_navigation_flag = FALSE;
79 struct WTree {
80 Widget widget;
81 struct TreeStore *store;
82 tree_entry *selected_ptr; /* The selected directory */
83 char search_buffer[256]; /* Current search string */
84 tree_entry **tree_shown; /* Entries currently on screen */
85 int is_panel; /* panel or plain widget flag */
86 int active; /* if it's currently selected */
87 int searching; /* Are we on searching mode? */
88 int topdiff; /* The difference between the topmost
89 shown and the selected */
92 /* Forwards */
93 static void tree_rescan (void *data);
95 static tree_entry *
96 back_ptr (tree_entry *ptr, int *count)
98 int i = 0;
100 while (ptr && ptr->prev && i < *count){
101 ptr = ptr->prev;
102 i ++;
104 *count = i;
105 return ptr;
108 static tree_entry *
109 forw_ptr (tree_entry *ptr, int *count)
111 int i = 0;
113 while (ptr && ptr->next && i < *count){
114 ptr = ptr->next;
115 i ++;
117 *count = i;
118 return ptr;
121 static void
122 remove_callback (tree_entry *entry, void *data)
124 WTree *tree = data;
126 if (tree->selected_ptr == entry){
127 if (tree->selected_ptr->next)
128 tree->selected_ptr = tree->selected_ptr->next;
129 else
130 tree->selected_ptr = tree->selected_ptr->prev;
134 /* Save the ~/.mc/Tree file */
135 static void
136 save_tree (WTree *tree)
138 int error;
139 char *tree_name;
141 (void) tree;
142 error = tree_store_save ();
145 if (error){
146 tree_name = g_build_filename (home_dir, MC_USERCONF_DIR,
147 MC_TREESTORE_FILE, (char *) NULL);
148 fprintf (stderr, _("Cannot open the %s file for writing:\n%s\n"), tree_name,
149 unix_error_string (error));
150 g_free (tree_name);
154 static void
155 tree_remove_entry (WTree *tree, char *name)
157 (void) tree;
158 tree_store_remove_entry (name);
161 static void
162 tree_destroy (WTree *tree)
164 tree_store_remove_entry_remove_hook (remove_callback);
165 save_tree (tree);
167 g_free (tree->tree_shown);
168 tree->tree_shown = 0;
169 tree->selected_ptr = NULL;
172 /* Loads the .mc.tree file */
173 static void
174 load_tree (WTree *tree)
176 tree_store_load ();
178 tree->selected_ptr = tree->store->tree_first;
179 tree_chdir (tree, home_dir);
182 static void
183 tree_show_mini_info (WTree *tree, int tree_lines, int tree_cols)
185 Dlg_head *h = tree->widget.parent;
186 int line;
188 /* Show mini info */
189 if (tree->is_panel){
190 if (!show_mini_info)
191 return;
192 line = tree_lines+2;
193 } else
194 line = tree_lines+1;
196 tty_draw_hline (tree->widget.y + line, tree->widget.x + 1, ' ', tree_cols);
197 widget_move (&tree->widget, line, 1);
199 if (tree->searching){
200 /* Show search string */
201 tty_setcolor (TREE_NORMALC (h));
202 tty_setcolor (DLG_FOCUSC (h));
203 tty_print_char (PATH_SEP);
205 tty_print_string (str_fit_to_term (tree->search_buffer,
206 tree_cols - 2, J_LEFT_FIT));
207 tty_print_char (' ');
208 tty_setcolor (DLG_FOCUSC (h));
209 } else {
210 /* Show full name of selected directory */
211 tty_print_string (str_fit_to_term (tree->selected_ptr->name,
212 tree_cols, J_LEFT_FIT));
216 static void
217 show_tree (WTree *tree)
219 Dlg_head *h = tree->widget.parent;
220 tree_entry *current;
221 int i, j, topsublevel;
222 int x, y;
223 int tree_lines, tree_cols;
225 /* Initialize */
226 x = y = 0;
227 tree_lines = tlines (tree);
228 tree_cols = tree->widget.cols;
230 tty_setcolor (TREE_NORMALC (h));
231 widget_move ((Widget*)tree, y, x);
232 if (tree->is_panel){
233 tree_cols -= 2;
234 x = y = 1;
237 g_free (tree->tree_shown);
238 tree->tree_shown = g_new (tree_entry*, tree_lines);
240 for (i = 0; i < tree_lines; i++)
241 tree->tree_shown [i] = NULL;
242 if (tree->store->tree_first)
243 topsublevel = tree->store->tree_first->sublevel;
244 else
245 topsublevel = 0;
246 if (!tree->selected_ptr){
247 tree->selected_ptr = tree->store->tree_first;
248 tree->topdiff = 0;
250 current = tree->selected_ptr;
252 /* Calculate the directory which is to be shown on the topmost line */
253 if (!tree_navigation_flag)
254 current = back_ptr (current, &tree->topdiff);
255 else {
256 i = 0;
257 while (current->prev && i < tree->topdiff){
258 current = current->prev;
259 if (current->sublevel < tree->selected_ptr->sublevel){
260 if (strncmp (current->name, tree->selected_ptr->name,
261 strlen (current->name)) == 0)
262 i++;
263 } else if (current->sublevel == tree->selected_ptr->sublevel){
264 for (j = strlen (current->name) - 1; current->name [j] != PATH_SEP; j--);
265 if (strncmp (current->name, tree->selected_ptr->name, j) == 0)
266 i++;
267 } else if (current->sublevel == tree->selected_ptr->sublevel + 1
268 && strlen (tree->selected_ptr->name) > 1){
269 if (strncmp (current->name, tree->selected_ptr->name,
270 strlen (tree->selected_ptr->name)) == 0)
271 i++;
274 tree->topdiff = i;
277 /* Loop for every line */
278 for (i = 0; i < tree_lines; i++){
279 /* Move to the beginning of the line */
280 tty_draw_hline (tree->widget.y + y + i, tree->widget.x + x, ' ', tree_cols);
282 if (!current)
283 continue;
285 tree->tree_shown [i] = current;
286 if (current->sublevel == topsublevel){
288 /* Top level directory */
289 if (tree->active && current == tree->selected_ptr) {
290 if (!tty_use_colors () && !tree->is_panel)
291 tty_setcolor (MARKED_COLOR);
292 else
293 tty_setcolor (SELECTED_COLOR);
296 /* Show full name */
297 tty_print_string (str_fit_to_term (current->name, tree_cols - 6, J_LEFT_FIT));
298 } else{
299 /* Sub level directory */
301 tty_set_alt_charset (TRUE);
302 /* Output branch parts */
303 for (j = 0; j < current->sublevel - topsublevel - 1; j++){
304 if (tree_cols - 8 - 3 * j < 9)
305 break;
306 tty_print_char (' ');
307 if (current->submask & (1 << (j + topsublevel + 1)))
308 tty_print_char (ACS_VLINE);
309 else
310 tty_print_char (' ');
311 tty_print_char (' ');
313 tty_print_char (' '); j++;
314 if (!current->next || !(current->next->submask & (1 << current->sublevel)))
315 tty_print_char (ACS_LLCORNER);
316 else
317 tty_print_char (ACS_LTEE);
318 tty_print_char (ACS_HLINE);
319 tty_set_alt_charset (FALSE);
321 if (tree->active && current == tree->selected_ptr) {
322 /* Selected directory -> change color */
323 if (!tty_use_colors () && !tree->is_panel)
324 tty_setcolor (MARKED_COLOR);
325 else
326 tty_setcolor (SELECTED_COLOR);
329 /* Show sub-name */
330 tty_print_char (' ');
331 tty_print_string (str_fit_to_term (current->subname,
332 tree_cols - 2 - 4 - 3 * j, J_LEFT_FIT));
334 tty_print_char (' ');
336 /* Return to normal color */
337 tty_setcolor (TREE_NORMALC (h));
339 /* Calculate the next value for current */
340 current = current->next;
341 if (tree_navigation_flag){
342 while (current){
343 if (current->sublevel < tree->selected_ptr->sublevel){
344 if (strncmp (current->name, tree->selected_ptr->name,
345 strlen (current->name)) == 0)
346 break;
347 } else if (current->sublevel == tree->selected_ptr->sublevel){
348 for (j = strlen (current->name) - 1; current->name [j] != PATH_SEP; j--);
349 if (strncmp (current->name,tree->selected_ptr->name,j)== 0)
350 break;
351 } else if (current->sublevel == tree->selected_ptr->sublevel+1
352 && strlen (tree->selected_ptr->name) > 1){
353 if (strncmp (current->name, tree->selected_ptr->name,
354 strlen (tree->selected_ptr->name)) == 0)
355 break;
357 current = current->next;
361 tree_show_mini_info (tree, tree_lines, tree_cols);
364 static void
365 tree_check_focus (WTree *tree)
367 if (tree->topdiff < 3)
368 tree->topdiff = 3;
369 else if (tree->topdiff >= tlines (tree) - 3)
370 tree->topdiff = tlines (tree) - 3 - 1;
373 static void
374 tree_move_backward (WTree *tree, int i)
376 if (!tree_navigation_flag)
377 tree->selected_ptr = back_ptr (tree->selected_ptr, &i);
378 else {
379 tree_entry *current;
380 int j = 0;
382 current = tree->selected_ptr;
383 while (j < i && current->prev
384 && current->prev->sublevel >= tree->selected_ptr->sublevel){
385 current = current->prev;
386 if (current->sublevel == tree->selected_ptr->sublevel){
387 tree->selected_ptr = current;
388 j++;
391 i = j;
394 tree->topdiff -= i;
395 tree_check_focus (tree);
398 static void
399 tree_move_forward (WTree *tree, int i)
401 if (!tree_navigation_flag)
402 tree->selected_ptr = forw_ptr (tree->selected_ptr, &i);
403 else {
404 tree_entry *current;
405 int j = 0;
407 current = tree->selected_ptr;
408 while (j < i && current->next
409 && current->next->sublevel >= tree->selected_ptr->sublevel){
410 current = current->next;
411 if (current->sublevel == tree->selected_ptr->sublevel){
412 tree->selected_ptr = current;
413 j ++;
416 i = j;
419 tree->topdiff += i;
420 tree_check_focus (tree);
423 static void
424 tree_move_to_child (WTree *tree)
426 tree_entry *current;
428 /* Do we have a starting point? */
429 if (!tree->selected_ptr)
430 return;
431 /* Take the next entry */
432 current = tree->selected_ptr->next;
433 /* Is it the child of the selected entry */
434 if (current && current->sublevel > tree->selected_ptr->sublevel){
435 /* Yes -> select this entry */
436 tree->selected_ptr = current;
437 tree->topdiff++;
438 tree_check_focus (tree);
439 } else {
440 /* No -> rescan and try again */
441 tree_rescan (tree);
442 current = tree->selected_ptr->next;
443 if (current && current->sublevel > tree->selected_ptr->sublevel){
444 tree->selected_ptr = current;
445 tree->topdiff++;
446 tree_check_focus (tree);
451 static gboolean
452 tree_move_to_parent (WTree *tree)
454 tree_entry *current;
455 tree_entry *old;
457 if (!tree->selected_ptr)
458 return FALSE;
460 old = tree->selected_ptr;
461 current = tree->selected_ptr->prev;
462 while (current && current->sublevel >= tree->selected_ptr->sublevel){
463 current = current->prev;
464 tree->topdiff--;
466 if (!current)
467 current = tree->store->tree_first;
468 tree->selected_ptr = current;
469 tree_check_focus (tree);
470 return tree->selected_ptr != old;
473 static void
474 tree_move_to_top (WTree *tree)
476 tree->selected_ptr = tree->store->tree_first;
477 tree->topdiff = 0;
480 static void
481 tree_move_to_bottom (WTree *tree)
483 tree->selected_ptr = tree->store->tree_last;
484 tree->topdiff = tlines (tree) - 3 - 1;
487 /* Handle mouse click */
488 static void
489 tree_event (WTree *tree, int y)
491 if (tree->tree_shown [y]){
492 tree->selected_ptr = tree->tree_shown [y];
493 tree->topdiff = y;
495 show_tree (tree);
498 static void
499 tree_chdir_sel (WTree *tree)
501 if (!tree->is_panel)
502 return;
504 change_panel ();
506 if (do_cd (tree->selected_ptr->name, cd_exact))
507 select_item (current_panel);
508 else
509 message (D_ERROR, MSG_ERROR, _(" Cannot chdir to \"%s\" \n %s "),
510 tree->selected_ptr->name, unix_error_string (errno));
512 change_panel ();
513 show_tree (tree);
516 static void
517 maybe_chdir (WTree *tree)
519 if (xtree_mode && tree->is_panel && is_idle ())
520 tree_chdir_sel (tree);
523 /* Mouse callback */
524 static int
525 event_callback (Gpm_Event *event, void *data)
527 WTree *tree = data;
529 /* rest of the upper frame, the menu is invisible - call menu */
530 if (tree->is_panel && (event->type & GPM_DOWN)
531 && event->y == 1 && !menubar_visible) {
532 event->x += tree->widget.x;
533 return the_menubar->widget.mouse (event, the_menubar);
536 if (!(event->type & GPM_UP))
537 return MOU_NORMAL;
539 if (tree->is_panel)
540 event->y--;
542 event->y--;
544 if (!tree->active)
545 change_panel ();
547 if (event->y < 0){
548 tree_move_backward (tree, tlines (tree) - 1);
549 show_tree (tree);
550 } else if (event->y >= tlines (tree)){
551 tree_move_forward (tree, tlines (tree) - 1);
552 show_tree (tree);
553 } else {
554 tree_event (tree, event->y);
555 if ((event->type & (GPM_UP|GPM_DOUBLE)) == (GPM_UP|GPM_DOUBLE)){
556 tree_chdir_sel (tree);
559 return MOU_NORMAL;
562 /* Search tree for text */
563 static int
564 search_tree (WTree *tree, char *text)
566 tree_entry *current;
567 int len;
568 int wrapped = 0;
569 int found = 0;
571 len = strlen (text);
572 current = tree->selected_ptr;
573 found = 0;
574 while (!wrapped || current != tree->selected_ptr){
575 if (strncmp (current->subname, text, len) == 0){
576 tree->selected_ptr = current;
577 found = 1;
578 break;
580 current = current->next;
581 if (!current){
582 current = tree->store->tree_first;
583 wrapped = 1;
585 tree->topdiff++;
587 tree_check_focus (tree);
588 return found;
591 static void
592 tree_do_search (WTree *tree, int key)
594 size_t l;
596 l = strlen (tree->search_buffer);
597 if ((l != 0) && (key == KEY_BACKSPACE))
598 tree->search_buffer [--l] = '\0';
599 else if (key && l < sizeof (tree->search_buffer)){
600 tree->search_buffer [l] = key;
601 tree->search_buffer [++l] = '\0';
604 if (!search_tree (tree, tree->search_buffer))
605 tree->search_buffer [--l] = 0;
607 show_tree (tree);
608 maybe_chdir (tree);
611 static void
612 tree_rescan (void *data)
614 char old_dir [MC_MAXPATHLEN];
615 WTree *tree = data;
617 if (!tree->selected_ptr || !mc_get_current_wd (old_dir, MC_MAXPATHLEN) ||
618 mc_chdir (tree->selected_ptr->name))
619 return;
621 tree_store_rescan (tree->selected_ptr->name);
622 mc_chdir (old_dir);
625 static void
626 tree_forget (void *data)
628 WTree *tree = data;
629 if (tree->selected_ptr)
630 tree_remove_entry (tree, tree->selected_ptr->name);
633 static void
634 tree_copy (WTree *tree, const char *default_dest)
636 char msg [BUF_MEDIUM];
637 char *dest;
638 off_t count = 0;
639 double bytes = 0;
640 FileOpContext *ctx;
642 if (tree->selected_ptr == NULL)
643 return;
645 g_snprintf (msg, sizeof (msg), _("Copy \"%s\" directory to:"),
646 str_trunc (tree->selected_ptr->name, 50));
647 dest = input_expand_dialog (Q_("DialogTitle|Copy"), msg, MC_HISTORY_FM_TREE_COPY, default_dest);
649 if (dest != NULL && *dest != '\0') {
650 ctx = file_op_context_new (OP_COPY);
651 file_op_context_create_ui (ctx, FALSE);
652 copy_dir_dir (ctx, tree->selected_ptr->name, dest, 1, 0, 0, 0, &count, &bytes);
653 file_op_context_destroy (ctx);
656 g_free (dest);
659 static void
660 tree_move (WTree *tree, const char *default_dest)
662 char msg [BUF_MEDIUM];
663 char *dest;
664 struct stat buf;
665 double bytes = 0;
666 off_t count = 0;
667 FileOpContext *ctx;
669 if (tree->selected_ptr == NULL)
670 return;
672 g_snprintf (msg, sizeof (msg), _("Move \"%s\" directory to:"),
673 str_trunc (tree->selected_ptr->name, 50));
674 dest = input_expand_dialog (Q_("DialogTitle|Move"), msg, MC_HISTORY_FM_TREE_MOVE, default_dest);
676 if (dest == NULL || *dest == '\0') {
677 g_free (dest);
678 return;
681 if (stat (dest, &buf)){
682 message (D_ERROR, MSG_ERROR, _(" Cannot stat the destination \n %s "),
683 unix_error_string (errno));
684 g_free (dest);
685 return;
688 if (!S_ISDIR (buf.st_mode)){
689 file_error (_(" Destination \"%s\" must be a directory \n %s "),
690 dest);
691 g_free (dest);
692 return;
695 ctx = file_op_context_new (OP_MOVE);
696 file_op_context_create_ui (ctx, FALSE);
697 move_dir_dir (ctx, tree->selected_ptr->name, dest, &count, &bytes);
698 file_op_context_destroy (ctx);
700 g_free (dest);
703 #if 0
704 static void
705 tree_mkdir (WTree *tree)
707 char old_dir [MC_MAXPATHLEN];
709 if (!tree->selected_ptr)
710 return;
711 if (!mc_get_current_wd (old_dir, MC_MAXPATHLEN))
712 return;
713 if (chdir (tree->selected_ptr->name))
714 return;
715 /* FIXME
716 mkdir_cmd (tree);
718 tree_rescan (tree);
719 chdir (old_dir);
721 #endif
723 static void
724 tree_rmdir (void *data)
726 WTree *tree = data;
727 off_t count = 0;
728 double bytes = 0;
729 FileOpContext *ctx;
731 if (!tree->selected_ptr)
732 return;
734 if (confirm_delete) {
735 char *buf;
736 int result;
738 buf =
739 g_strdup_printf (_(" Delete %s? "),
740 tree->selected_ptr->name);
741 result =
742 query_dialog (Q_("DialogTitle|Delete"), buf, D_ERROR, 2, _("&Yes"), _("&No"));
743 g_free (buf);
744 if (result != 0)
745 return;
748 ctx = file_op_context_new (OP_DELETE);
749 file_op_context_create_ui (ctx, FALSE);
750 if (erase_dir (ctx, tree->selected_ptr->name, &count, &bytes) == FILE_CONT)
751 tree_forget (tree);
752 file_op_context_destroy (ctx);
755 static inline void
756 tree_move_up (WTree *tree)
758 tree_move_backward (tree, 1);
759 show_tree (tree);
760 maybe_chdir (tree);
763 static inline void
764 tree_move_down (WTree *tree)
766 tree_move_forward (tree, 1);
767 show_tree (tree);
768 maybe_chdir (tree);
771 static inline void
772 tree_move_home (WTree *tree)
774 tree_move_to_top (tree);
775 show_tree (tree);
776 maybe_chdir (tree);
779 static inline void
780 tree_move_end (WTree *tree)
782 tree_move_to_bottom (tree);
783 show_tree (tree);
784 maybe_chdir (tree);
787 static void
788 tree_move_pgup (WTree *tree)
790 tree_move_backward (tree, tlines (tree) - 1);
791 show_tree (tree);
792 maybe_chdir (tree);
795 static void
796 tree_move_pgdn (WTree *tree)
798 tree_move_forward (tree, tlines (tree) - 1);
799 show_tree (tree);
800 maybe_chdir (tree);
803 static gboolean
804 tree_move_left (WTree *tree)
806 gboolean v = FALSE;
808 if (tree_navigation_flag) {
809 v = tree_move_to_parent (tree);
810 show_tree (tree);
811 maybe_chdir (tree);
814 return v;
817 static gboolean
818 tree_move_right (WTree *tree)
820 gboolean v = FALSE;
822 if (tree_navigation_flag) {
823 tree_move_to_child (tree);
824 show_tree (tree);
825 maybe_chdir (tree);
826 v = TRUE;
829 return v;
832 static void
833 tree_start_search (WTree *tree)
835 gboolean i;
837 if (tree->searching) {
838 if (tree->selected_ptr == tree->store->tree_last)
839 tree_move_to_top (tree);
840 else {
841 /* set navigation mode temporarily to 'Static' because in
842 * dynamic navigation mode tree_move_forward will not move
843 * to a lower sublevel if necessary (sequent searches must
844 * start with the directory followed the last found directory)
846 i = tree_navigation_flag;
847 tree_navigation_flag = 0;
848 tree_move_forward (tree, 1);
849 tree_navigation_flag = i;
851 tree_do_search (tree, 0);
852 } else {
853 tree->searching = 1;
854 tree->search_buffer[0] = 0;
858 static void
859 tree_toggle_navig (WTree *tree)
861 tree_navigation_flag = !tree_navigation_flag;
862 buttonbar_set_label (find_buttonbar (tree->widget.parent), 4,
863 tree_navigation_flag ? Q_("ButtonBar|Static")
864 : Q_("ButtonBar|Dynamc"),
865 tree_map, (Widget *) tree);
868 static cb_ret_t
869 tree_execute_cmd (WTree *tree, unsigned long command)
871 cb_ret_t res = MSG_HANDLED;
873 if (command != CK_TreeStartSearch)
874 tree->searching = 0;
876 switch (command) {
877 case CK_TreeHelp:
878 interactive_display (NULL, "[Directory Tree]");
879 break;
880 case CK_TreeForget:
881 tree_forget (tree);
882 break;
883 case CK_TreeToggleNav:
884 tree_toggle_navig (tree);
885 break;
886 case CK_TreeCopy:
887 tree_copy (tree, "");
888 break;
889 case CK_TreeMove:
890 tree_move (tree, "");
891 break;
892 case CK_TreeMoveUp:
893 tree_move_up (tree);
894 break;
895 case CK_TreeMoveDown:
896 tree_move_down (tree);
897 break;
898 case CK_TreeMoveHome:
899 tree_move_home (tree);
900 break;
901 case CK_TreeMoveEnd:
902 tree_move_end (tree);
903 break;
904 case CK_TreeMovePgUp:
905 tree_move_pgup (tree);
906 break;
907 case CK_TreeMovePgDn:
908 tree_move_pgdn (tree);
909 break;
910 case CK_TreeOpen:
911 tree_chdir_sel (tree);
912 break;
913 case CK_TreeRescan:
914 tree_rescan (tree);
915 break;
916 case CK_TreeStartSearch:
917 tree_start_search (tree);
918 break;
919 case CK_TreeRemove:
920 tree_rmdir (tree);
921 break;
922 default:
923 res = MSG_NOT_HANDLED;
926 show_tree (tree);
928 return res;
931 static cb_ret_t
932 tree_key (WTree *tree, int key)
934 size_t i;
936 for (i = 0; tree_map [i].key != 0; i++)
937 if (key == tree_map [i].key)
938 switch (tree_map [i].command) {
939 case CK_TreeMoveLeft:
940 return tree_move_left (tree) ? MSG_HANDLED : MSG_NOT_HANDLED;
941 case CK_TreeMoveRight:
942 return tree_move_right (tree) ? MSG_HANDLED : MSG_NOT_HANDLED;
943 default:
944 tree_execute_cmd (tree, tree_map [i].command);
945 return MSG_HANDLED;
948 if (is_abort_char (key)) {
949 if (tree->is_panel) {
950 tree->searching = 0;
951 show_tree (tree);
952 return MSG_HANDLED; /* eat abort char */
954 /* modal tree dialog: let upper layer see the
955 abort character and close the dialog */
956 return MSG_NOT_HANDLED;
959 /* Do not eat characters not meant for the tree below ' ' (e.g. C-l). */
960 if ((key >= ' ' && key <= 255) || key == KEY_BACKSPACE) {
961 if (tree->searching){
962 tree_do_search (tree, key);
963 show_tree (tree);
964 return MSG_HANDLED;
967 if (!command_prompt) {
968 tree_start_search (tree);
969 tree_do_search (tree, key);
970 return MSG_HANDLED;
972 return tree->is_panel ? MSG_HANDLED : MSG_NOT_HANDLED;
975 return MSG_NOT_HANDLED;
978 static void
979 tree_frame (Dlg_head *h, WTree *tree)
981 tty_setcolor (NORMAL_COLOR);
982 widget_erase ((Widget*) tree);
983 if (tree->is_panel) {
984 draw_box (h, tree->widget.y, tree->widget.x, tree->widget.lines,
985 tree->widget.cols);
987 if (show_mini_info)
988 tty_draw_hline (tree->widget.y + tlines (tree) + 1,
989 tree->widget.x + 1,
990 ACS_HLINE, tree->widget.cols - 2);
994 static cb_ret_t
995 tree_callback (Widget *w, widget_msg_t msg, int parm)
997 WTree *tree = (WTree *) w;
998 Dlg_head *h = tree->widget.parent;
999 WButtonBar *b = find_buttonbar (h);
1001 switch (msg) {
1002 case WIDGET_DRAW:
1003 tree_frame (h, tree);
1004 show_tree (tree);
1005 return MSG_HANDLED;
1007 case WIDGET_FOCUS:
1008 tree->active = 1;
1009 buttonbar_set_label (b, 1, Q_("ButtonBar|Help"), tree_map, (Widget *) tree);
1010 buttonbar_set_label (b, 2, Q_("ButtonBar|Rescan"), tree_map, (Widget *) tree);
1011 buttonbar_set_label (b, 3, Q_("ButtonBar|Forget"), tree_map, (Widget *) tree);
1012 buttonbar_set_label (b, 4, tree_navigation_flag ? Q_("ButtonBar|Static")
1013 : Q_("ButtonBar|Dynamc"),
1014 tree_map, (Widget *) tree);
1015 buttonbar_set_label (b, 5, Q_("ButtonBar|Copy"), tree_map, (Widget *) tree);
1016 buttonbar_set_label (b, 6, Q_("ButtonBar|RenMov"), tree_map, (Widget *) tree);
1017 #if 0
1018 /* FIXME: mkdir is currently defunct */
1019 buttonbar_set_label (b, 7, Q_("ButtonBar|Mkdir"), tree_map, (Widget *) tree);
1020 #else
1021 buttonbar_clear_label (b, 7, (Widget *) tree);
1022 #endif
1023 buttonbar_set_label (b, 8, Q_("ButtonBar|Rmdir"), tree_map, (Widget *) tree);
1024 buttonbar_redraw (b);
1026 /* FIXME: Should find a better way of only displaying the
1027 currently selected item */
1028 show_tree (tree);
1029 return MSG_HANDLED;
1031 /* FIXME: Should find a better way of changing the color of the
1032 selected item */
1034 case WIDGET_UNFOCUS:
1035 tree->active = 0;
1036 show_tree (tree);
1037 return MSG_HANDLED;
1039 case WIDGET_KEY:
1040 return tree_key (tree, parm);
1042 case WIDGET_COMMAND:
1043 /* command from buttonbar */
1044 return tree_execute_cmd (tree, parm);
1046 case WIDGET_DESTROY:
1047 tree_destroy (tree);
1048 return MSG_HANDLED;
1050 default:
1051 return default_proc (msg, parm);
1055 WTree *
1056 tree_new (int is_panel, int y, int x, int lines, int cols)
1058 WTree *tree = g_new (WTree, 1);
1060 init_widget (&tree->widget, y, x, lines, cols,
1061 tree_callback, event_callback);
1062 tree->is_panel = is_panel;
1063 tree->selected_ptr = 0;
1065 tree->store = tree_store_get ();
1066 tree_store_add_entry_remove_hook (remove_callback, tree);
1067 tree->tree_shown = 0;
1068 tree->search_buffer[0] = 0;
1069 tree->topdiff = tree->widget.lines / 2;
1070 tree->searching = 0;
1071 tree->active = 0;
1073 /* We do not want to keep the cursor */
1074 widget_want_cursor (tree->widget, 0);
1075 load_tree (tree);
1076 return tree;
1079 void
1080 tree_chdir (WTree *tree, const char *dir)
1082 tree_entry *current;
1084 current = tree_store_whereis (dir);
1086 if (current != NULL) {
1087 tree->selected_ptr = current;
1088 tree_check_focus (tree);
1092 /* Return name of the currently selected entry */
1093 char *
1094 tree_selected_name (const WTree *tree)
1096 return tree->selected_ptr->name;
1099 void
1100 sync_tree (const char *path)
1102 tree_chdir (the_tree, path);
1105 WTree *
1106 find_tree (struct Dlg_head *h)
1108 return (WTree *) find_widget_type (h, tree_callback);