2 Directory tree browser for the Midnight Commander
3 This module has been converted to be a widget.
5 The program load and saves the tree each time the tree widget is
6 created and destroyed. This is required for the future vfs layer,
7 it will be possible to have tree views over virtual file systems.
9 Copyright (C) 1994-2018
10 Free Software Foundation, Inc.
13 Janne Kukonlehto, 1994, 1996
15 Miguel de Icaza, 1996, 1999
16 Slava Zanko <slavazanko@gmail.com>, 2013
17 Andrew Borodin <aborodin@vmail.ru>, 2013, 2014, 2016
19 This file is part of the Midnight Commander.
21 The Midnight Commander is free software: you can redistribute it
22 and/or modify it under the terms of the GNU General Public License as
23 published by the Free Software Foundation, either version 3 of the License,
24 or (at your option) any later version.
26 The Midnight Commander is distributed in the hope that it will be useful,
27 but WITHOUT ANY WARRANTY; without even the implied warranty of
28 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 GNU General Public License for more details.
31 You should have received a copy of the GNU General Public License
32 along with this program. If not, see <http://www.gnu.org/licenses/>.
36 * \brief Source: directory tree browser
44 #include <sys/types.h>
46 #include "lib/global.h"
48 #include "lib/tty/tty.h"
49 #include "lib/tty/key.h"
51 #include "lib/vfs/vfs.h"
52 #include "lib/fileloc.h"
53 #include "lib/strutil.h"
55 #include "lib/widget.h"
56 #include "lib/event.h" /* mc_event_raise() */
58 #include "src/setup.h" /* confirm_delete, panels_options */
59 #include "src/keybind-defaults.h"
60 #include "src/history.h"
63 #include "midnight.h" /* the_menubar */
64 #include "file.h" /* copy_dir_dir(), move_dir_dir(), erase_dir() */
65 #include "layout.h" /* command_prompt */
66 #include "treestore.h"
72 /*** global variables ****************************************************************************/
74 /* The pointer to the tree */
75 WTree
*the_tree
= NULL
;
77 /* If this is true, then when browsing the tree the other window will
78 * automatically reload it's directory with the contents of the currently
81 gboolean xtree_mode
= FALSE
;
83 /*** file scope macro definitions ****************************************************************/
85 #define tlines(t) (t->is_panel ? WIDGET (t)->lines - 2 - \
86 (panels_options.show_mini_info ? 2 : 0) : WIDGET (t)->lines)
88 /* Use the color of the parent widget for the unselected entries */
89 #define TREE_NORMALC(h) (h->color[DLG_COLOR_NORMAL])
90 #define TREE_CURRENTC(h) (h->color[DLG_COLOR_FOCUS])
92 /*** file scope type declarations ****************************************************************/
97 struct TreeStore
*store
;
98 tree_entry
*selected_ptr
; /* The selected directory */
99 char search_buffer
[MC_MAXFILENAMELEN
]; /* Current search string */
100 tree_entry
**tree_shown
; /* Entries currently on screen */
101 int is_panel
; /* panel or plain widget flag */
102 int searching
; /* Are we on searching mode? */
103 int topdiff
; /* The difference between the topmost
104 shown and the selected */
107 /*** file scope variables ************************************************************************/
109 /* Specifies the display mode: 1d or 2d */
110 static gboolean tree_navigation_flag
= FALSE
;
112 /*** file scope functions ************************************************************************/
113 /* --------------------------------------------------------------------------------------------- */
115 static void tree_rescan (void *data
);
117 /* --------------------------------------------------------------------------------------------- */
120 back_ptr (tree_entry
* ptr
, int *count
)
124 while (ptr
&& ptr
->prev
&& i
< *count
)
133 /* --------------------------------------------------------------------------------------------- */
136 forw_ptr (tree_entry
* ptr
, int *count
)
140 while (ptr
&& ptr
->next
&& i
< *count
)
149 /* --------------------------------------------------------------------------------------------- */
152 remove_callback (tree_entry
* entry
, void *data
)
156 if (tree
->selected_ptr
== entry
)
158 if (tree
->selected_ptr
->next
)
159 tree
->selected_ptr
= tree
->selected_ptr
->next
;
161 tree
->selected_ptr
= tree
->selected_ptr
->prev
;
165 /* --------------------------------------------------------------------------------------------- */
166 /** Save the ${XDG_CACHE_HOME}/mc/Tree file */
169 save_tree (WTree
* tree
)
174 error
= tree_store_save ();
180 tree_name
= mc_config_get_full_path (MC_TREESTORE_FILE
);
181 fprintf (stderr
, _("Cannot open the %s file for writing:\n%s\n"), tree_name
,
182 unix_error_string (error
));
187 /* --------------------------------------------------------------------------------------------- */
190 tree_remove_entry (WTree
* tree
, const vfs_path_t
* name_vpath
)
193 tree_store_remove_entry (name_vpath
);
196 /* --------------------------------------------------------------------------------------------- */
199 tree_destroy (WTree
* tree
)
201 tree_store_remove_entry_remove_hook (remove_callback
);
204 g_free (tree
->tree_shown
);
205 tree
->tree_shown
= 0;
206 tree
->selected_ptr
= NULL
;
209 /* --------------------------------------------------------------------------------------------- */
210 /** Loads the .mc.tree file */
213 load_tree (WTree
* tree
)
219 tree
->selected_ptr
= tree
->store
->tree_first
;
220 vpath
= vfs_path_from_str (mc_config_get_home_dir ());
221 tree_chdir (tree
, vpath
);
222 vfs_path_free (vpath
);
225 /* --------------------------------------------------------------------------------------------- */
228 tree_show_mini_info (WTree
* tree
, int tree_lines
, int tree_cols
)
230 Widget
*w
= WIDGET (tree
);
236 if (!panels_options
.show_mini_info
)
238 line
= tree_lines
+ 2;
241 line
= tree_lines
+ 1;
245 /* Show search string */
246 tty_setcolor (INPUT_COLOR
);
247 tty_draw_hline (w
->y
+ line
, w
->x
+ 1, ' ', tree_cols
);
248 widget_move (w
, line
, 1);
249 tty_print_char (PATH_SEP
);
250 tty_print_string (str_fit_to_term (tree
->search_buffer
, tree_cols
- 2, J_LEFT_FIT
));
251 tty_print_char (' ');
255 /* Show full name of selected directory */
256 WDialog
*h
= w
->owner
;
258 tty_setcolor (tree
->is_panel
? NORMAL_COLOR
: TREE_NORMALC (h
));
259 tty_draw_hline (w
->y
+ line
, w
->x
+ 1, ' ', tree_cols
);
260 widget_move (w
, line
, 1);
261 tty_print_string (str_fit_to_term
262 (vfs_path_as_str (tree
->selected_ptr
->name
), tree_cols
, J_LEFT_FIT
));
266 /* --------------------------------------------------------------------------------------------- */
269 show_tree (WTree
* tree
)
271 Widget
*w
= WIDGET (tree
);
272 WDialog
*h
= w
->owner
;
274 int i
, j
, topsublevel
;
276 int tree_lines
, tree_cols
;
279 tree_lines
= tlines (tree
);
282 widget_move (w
, y
, x
);
289 g_free (tree
->tree_shown
);
290 tree
->tree_shown
= g_new0 (tree_entry
*, tree_lines
);
292 if (tree
->store
->tree_first
)
293 topsublevel
= tree
->store
->tree_first
->sublevel
;
296 if (!tree
->selected_ptr
)
298 tree
->selected_ptr
= tree
->store
->tree_first
;
301 current
= tree
->selected_ptr
;
303 /* Calculate the directory which is to be shown on the topmost line */
304 if (!tree_navigation_flag
)
305 current
= back_ptr (current
, &tree
->topdiff
);
309 while (current
->prev
&& i
< tree
->topdiff
)
311 current
= current
->prev
;
313 if (current
->sublevel
< tree
->selected_ptr
->sublevel
)
315 if (vfs_path_equal (current
->name
, tree
->selected_ptr
->name
))
318 else if (current
->sublevel
== tree
->selected_ptr
->sublevel
)
322 cname
= vfs_path_as_str (current
->name
);
323 for (j
= strlen (cname
) - 1; !IS_PATH_SEP (cname
[j
]); j
--)
325 if (vfs_path_equal_len (current
->name
, tree
->selected_ptr
->name
, j
))
328 else if (current
->sublevel
== tree
->selected_ptr
->sublevel
+ 1)
330 j
= vfs_path_len (tree
->selected_ptr
->name
);
331 if (j
> 1 && vfs_path_equal_len (current
->name
, tree
->selected_ptr
->name
, j
))
338 /* Loop for every line */
339 for (i
= 0; i
< tree_lines
; i
++)
341 tty_setcolor (tree
->is_panel
? NORMAL_COLOR
: TREE_NORMALC (h
));
343 /* Move to the beginning of the line */
344 tty_draw_hline (w
->y
+ y
+ i
, w
->x
+ x
, ' ', tree_cols
);
350 tty_setcolor (widget_get_state (w
, WST_FOCUSED
) && current
== tree
->selected_ptr
351 ? SELECTED_COLOR
: NORMAL_COLOR
);
353 tty_setcolor (current
== tree
->selected_ptr
? TREE_CURRENTC (h
) : TREE_NORMALC (h
));
355 tree
->tree_shown
[i
] = current
;
356 if (current
->sublevel
== topsublevel
)
358 tty_print_string (str_fit_to_term
359 (vfs_path_as_str (current
->name
),
360 tree_cols
+ (tree
->is_panel
? 0 : 1), J_LEFT_FIT
));
363 /* Sub level directory */
364 tty_set_alt_charset (TRUE
);
366 /* Output branch parts */
367 for (j
= 0; j
< current
->sublevel
- topsublevel
- 1; j
++)
369 if (tree_cols
- 8 - 3 * j
< 9)
371 tty_print_char (' ');
372 if (current
->submask
& (1 << (j
+ topsublevel
+ 1)))
373 tty_print_char (ACS_VLINE
);
375 tty_print_char (' ');
376 tty_print_char (' ');
378 tty_print_char (' ');
380 if (!current
->next
|| !(current
->next
->submask
& (1 << current
->sublevel
)))
381 tty_print_char (ACS_LLCORNER
);
383 tty_print_char (ACS_LTEE
);
384 tty_print_char (ACS_HLINE
);
385 tty_set_alt_charset (FALSE
);
388 tty_print_char (' ');
389 tty_print_string (str_fit_to_term
390 (current
->subname
, tree_cols
- x
- 3 * j
, J_LEFT_FIT
));
393 /* Calculate the next value for current */
394 current
= current
->next
;
395 if (tree_navigation_flag
)
397 while (current
!= NULL
)
399 if (current
->sublevel
< tree
->selected_ptr
->sublevel
)
401 if (vfs_path_equal_len (current
->name
, tree
->selected_ptr
->name
,
402 vfs_path_len (current
->name
)))
405 else if (current
->sublevel
== tree
->selected_ptr
->sublevel
)
409 cname
= vfs_path_as_str (current
->name
);
410 for (j
= strlen (cname
) - 1; !IS_PATH_SEP (cname
[j
]); j
--)
412 if (vfs_path_equal_len (current
->name
, tree
->selected_ptr
->name
, j
))
415 else if (current
->sublevel
== tree
->selected_ptr
->sublevel
+ 1
416 && vfs_path_len (tree
->selected_ptr
->name
) > 1)
418 if (vfs_path_equal_len (current
->name
, tree
->selected_ptr
->name
,
419 vfs_path_len (tree
->selected_ptr
->name
)))
422 current
= current
->next
;
427 tree_show_mini_info (tree
, tree_lines
, tree_cols
);
430 /* --------------------------------------------------------------------------------------------- */
433 tree_check_focus (WTree
* tree
)
435 if (tree
->topdiff
< 3)
437 else if (tree
->topdiff
>= tlines (tree
) - 3)
438 tree
->topdiff
= tlines (tree
) - 3 - 1;
441 /* --------------------------------------------------------------------------------------------- */
444 tree_move_backward (WTree
* tree
, int i
)
446 if (!tree_navigation_flag
)
447 tree
->selected_ptr
= back_ptr (tree
->selected_ptr
, &i
);
453 current
= tree
->selected_ptr
;
454 while (j
< i
&& current
->prev
&& current
->prev
->sublevel
>= tree
->selected_ptr
->sublevel
)
456 current
= current
->prev
;
457 if (current
->sublevel
== tree
->selected_ptr
->sublevel
)
459 tree
->selected_ptr
= current
;
467 tree_check_focus (tree
);
470 /* --------------------------------------------------------------------------------------------- */
473 tree_move_forward (WTree
* tree
, int i
)
475 if (!tree_navigation_flag
)
476 tree
->selected_ptr
= forw_ptr (tree
->selected_ptr
, &i
);
482 current
= tree
->selected_ptr
;
483 while (j
< i
&& current
->next
&& current
->next
->sublevel
>= tree
->selected_ptr
->sublevel
)
485 current
= current
->next
;
486 if (current
->sublevel
== tree
->selected_ptr
->sublevel
)
488 tree
->selected_ptr
= current
;
496 tree_check_focus (tree
);
499 /* --------------------------------------------------------------------------------------------- */
502 tree_move_to_child (WTree
* tree
)
506 /* Do we have a starting point? */
507 if (!tree
->selected_ptr
)
509 /* Take the next entry */
510 current
= tree
->selected_ptr
->next
;
511 /* Is it the child of the selected entry */
512 if (current
&& current
->sublevel
> tree
->selected_ptr
->sublevel
)
514 /* Yes -> select this entry */
515 tree
->selected_ptr
= current
;
517 tree_check_focus (tree
);
521 /* No -> rescan and try again */
523 current
= tree
->selected_ptr
->next
;
524 if (current
&& current
->sublevel
> tree
->selected_ptr
->sublevel
)
526 tree
->selected_ptr
= current
;
528 tree_check_focus (tree
);
533 /* --------------------------------------------------------------------------------------------- */
536 tree_move_to_parent (WTree
* tree
)
541 if (!tree
->selected_ptr
)
544 old
= tree
->selected_ptr
;
545 current
= tree
->selected_ptr
->prev
;
546 while (current
&& current
->sublevel
>= tree
->selected_ptr
->sublevel
)
548 current
= current
->prev
;
552 current
= tree
->store
->tree_first
;
553 tree
->selected_ptr
= current
;
554 tree_check_focus (tree
);
555 return tree
->selected_ptr
!= old
;
558 /* --------------------------------------------------------------------------------------------- */
561 tree_move_to_top (WTree
* tree
)
563 tree
->selected_ptr
= tree
->store
->tree_first
;
567 /* --------------------------------------------------------------------------------------------- */
570 tree_move_to_bottom (WTree
* tree
)
572 tree
->selected_ptr
= tree
->store
->tree_last
;
573 tree
->topdiff
= tlines (tree
) - 3 - 1;
576 /* --------------------------------------------------------------------------------------------- */
579 tree_chdir_sel (WTree
* tree
)
585 if (do_cd (tree
->selected_ptr
->name
, cd_exact
))
586 select_item (current_panel
);
588 message (D_ERROR
, MSG_ERROR
, _("Cannot chdir to \"%s\"\n%s"),
589 vfs_path_as_str (tree
->selected_ptr
->name
), unix_error_string (errno
));
591 widget_redraw (WIDGET (current_panel
));
597 WDialog
*h
= WIDGET (tree
)->owner
;
599 h
->ret_value
= B_ENTER
;
604 /* --------------------------------------------------------------------------------------------- */
607 maybe_chdir (WTree
* tree
)
609 if (xtree_mode
&& tree
->is_panel
&& is_idle ())
610 tree_chdir_sel (tree
);
613 /* --------------------------------------------------------------------------------------------- */
614 /** Search tree for text */
617 search_tree (WTree
* tree
, char *text
)
625 current
= tree
->selected_ptr
;
627 while (!wrapped
|| current
!= tree
->selected_ptr
)
629 if (strncmp (current
->subname
, text
, len
) == 0)
631 tree
->selected_ptr
= current
;
635 current
= current
->next
;
638 current
= tree
->store
->tree_first
;
643 tree_check_focus (tree
);
647 /* --------------------------------------------------------------------------------------------- */
650 tree_do_search (WTree
* tree
, int key
)
654 l
= strlen (tree
->search_buffer
);
655 if ((l
!= 0) && (key
== KEY_BACKSPACE
))
656 tree
->search_buffer
[--l
] = '\0';
657 else if (key
&& l
< sizeof (tree
->search_buffer
))
659 tree
->search_buffer
[l
] = key
;
660 tree
->search_buffer
[++l
] = '\0';
663 if (!search_tree (tree
, tree
->search_buffer
))
664 tree
->search_buffer
[--l
] = 0;
670 /* --------------------------------------------------------------------------------------------- */
673 tree_rescan (void *data
)
676 vfs_path_t
*old_vpath
;
678 old_vpath
= vfs_path_clone (vfs_get_raw_current_dir ());
679 if (old_vpath
== NULL
)
682 if (tree
->selected_ptr
!= NULL
&& mc_chdir (tree
->selected_ptr
->name
) == 0)
686 tree_store_rescan (tree
->selected_ptr
->name
);
687 ret
= mc_chdir (old_vpath
);
690 vfs_path_free (old_vpath
);
693 /* --------------------------------------------------------------------------------------------- */
696 tree_forget (void *data
)
699 if (tree
->selected_ptr
)
700 tree_remove_entry (tree
, tree
->selected_ptr
->name
);
703 /* --------------------------------------------------------------------------------------------- */
706 tree_copy (WTree
* tree
, const char *default_dest
)
708 char msg
[BUF_MEDIUM
];
711 if (tree
->selected_ptr
== NULL
)
714 g_snprintf (msg
, sizeof (msg
), _("Copy \"%s\" directory to:"),
715 str_trunc (vfs_path_as_str (tree
->selected_ptr
->name
), 50));
716 dest
= input_expand_dialog (Q_ ("DialogTitle|Copy"),
717 msg
, MC_HISTORY_FM_TREE_COPY
, default_dest
,
718 INPUT_COMPLETE_FILENAMES
| INPUT_COMPLETE_CD
);
720 if (dest
!= NULL
&& *dest
!= '\0')
722 file_op_context_t
*ctx
;
723 file_op_total_context_t
*tctx
;
725 ctx
= file_op_context_new (OP_COPY
);
726 tctx
= file_op_total_context_new ();
727 file_op_context_create_ui (ctx
, FALSE
, FILEGUI_DIALOG_MULTI_ITEM
);
728 tctx
->ask_overwrite
= FALSE
;
729 copy_dir_dir (tctx
, ctx
, vfs_path_as_str (tree
->selected_ptr
->name
), dest
, TRUE
, FALSE
,
731 file_op_total_context_destroy (tctx
);
732 file_op_context_destroy (ctx
);
738 /* --------------------------------------------------------------------------------------------- */
741 tree_move (WTree
* tree
, const char *default_dest
)
743 char msg
[BUF_MEDIUM
];
746 file_op_context_t
*ctx
;
747 file_op_total_context_t
*tctx
;
748 vfs_path_t
*dest_vpath
= NULL
;
750 if (tree
->selected_ptr
== NULL
)
753 g_snprintf (msg
, sizeof (msg
), _("Move \"%s\" directory to:"),
754 str_trunc (vfs_path_as_str (tree
->selected_ptr
->name
), 50));
756 input_expand_dialog (Q_ ("DialogTitle|Move"), msg
, MC_HISTORY_FM_TREE_MOVE
, default_dest
,
757 INPUT_COMPLETE_FILENAMES
| INPUT_COMPLETE_CD
);
759 if (dest
== NULL
|| *dest
== '\0')
762 dest_vpath
= vfs_path_from_str (dest
);
764 if (mc_stat (dest_vpath
, &buf
))
766 message (D_ERROR
, MSG_ERROR
, _("Cannot stat the destination\n%s"),
767 unix_error_string (errno
));
771 if (!S_ISDIR (buf
.st_mode
))
773 file_error (_("Destination \"%s\" must be a directory\n%s"), dest
);
777 ctx
= file_op_context_new (OP_MOVE
);
778 tctx
= file_op_total_context_new ();
779 file_op_context_create_ui (ctx
, FALSE
, FILEGUI_DIALOG_ONE_ITEM
);
780 move_dir_dir (tctx
, ctx
, vfs_path_as_str (tree
->selected_ptr
->name
), dest
);
781 file_op_total_context_destroy (tctx
);
782 file_op_context_destroy (ctx
);
785 vfs_path_free (dest_vpath
);
789 /* --------------------------------------------------------------------------------------------- */
793 tree_mkdir (WTree
* tree
)
795 char old_dir
[MC_MAXPATHLEN
];
797 if (!tree
->selected_ptr
)
799 if (chdir (tree
->selected_ptr
->name
))
809 /* --------------------------------------------------------------------------------------------- */
812 tree_rmdir (void *data
)
815 file_op_context_t
*ctx
;
816 file_op_total_context_t
*tctx
;
818 if (!tree
->selected_ptr
)
826 buf
= g_strdup_printf (_("Delete %s?"), vfs_path_as_str (tree
->selected_ptr
->name
));
828 result
= query_dialog (Q_ ("DialogTitle|Delete"), buf
, D_ERROR
, 2, _("&Yes"), _("&No"));
834 ctx
= file_op_context_new (OP_DELETE
);
835 tctx
= file_op_total_context_new ();
837 file_op_context_create_ui (ctx
, FALSE
, FILEGUI_DIALOG_ONE_ITEM
);
838 if (erase_dir (tctx
, ctx
, tree
->selected_ptr
->name
) == FILE_CONT
)
840 file_op_total_context_destroy (tctx
);
841 file_op_context_destroy (ctx
);
844 /* --------------------------------------------------------------------------------------------- */
847 tree_move_up (WTree
* tree
)
849 tree_move_backward (tree
, 1);
854 /* --------------------------------------------------------------------------------------------- */
857 tree_move_down (WTree
* tree
)
859 tree_move_forward (tree
, 1);
864 /* --------------------------------------------------------------------------------------------- */
867 tree_move_home (WTree
* tree
)
869 tree_move_to_top (tree
);
874 /* --------------------------------------------------------------------------------------------- */
877 tree_move_end (WTree
* tree
)
879 tree_move_to_bottom (tree
);
884 /* --------------------------------------------------------------------------------------------- */
887 tree_move_pgup (WTree
* tree
)
889 tree_move_backward (tree
, tlines (tree
) - 1);
894 /* --------------------------------------------------------------------------------------------- */
897 tree_move_pgdn (WTree
* tree
)
899 tree_move_forward (tree
, tlines (tree
) - 1);
904 /* --------------------------------------------------------------------------------------------- */
907 tree_move_left (WTree
* tree
)
911 if (tree_navigation_flag
)
913 v
= tree_move_to_parent (tree
);
921 /* --------------------------------------------------------------------------------------------- */
924 tree_move_right (WTree
* tree
)
928 if (tree_navigation_flag
)
930 tree_move_to_child (tree
);
939 /* --------------------------------------------------------------------------------------------- */
942 tree_start_search (WTree
* tree
)
948 if (tree
->selected_ptr
== tree
->store
->tree_last
)
949 tree_move_to_top (tree
);
952 /* set navigation mode temporarily to 'Static' because in
953 * dynamic navigation mode tree_move_forward will not move
954 * to a lower sublevel if necessary (sequent searches must
955 * start with the directory followed the last found directory)
957 i
= tree_navigation_flag
;
958 tree_navigation_flag
= 0;
959 tree_move_forward (tree
, 1);
960 tree_navigation_flag
= i
;
962 tree_do_search (tree
, 0);
967 tree
->search_buffer
[0] = 0;
971 /* --------------------------------------------------------------------------------------------- */
974 tree_toggle_navig (WTree
* tree
)
978 tree_navigation_flag
= !tree_navigation_flag
;
980 b
= find_buttonbar (WIDGET (tree
)->owner
);
981 buttonbar_set_label (b
, 4,
982 tree_navigation_flag
? Q_ ("ButtonBar|Static") : Q_ ("ButtonBar|Dynamc"),
983 tree_map
, WIDGET (tree
));
984 widget_redraw (WIDGET (b
));
987 /* --------------------------------------------------------------------------------------------- */
990 tree_execute_cmd (WTree
* tree
, long command
)
992 cb_ret_t res
= MSG_HANDLED
;
994 if (command
!= CK_Search
)
1001 ev_help_t event_data
= { NULL
, "[Directory Tree]" };
1002 mc_event_raise (MCEVENT_GROUP_CORE
, "help", &event_data
);
1008 case CK_ToggleNavigation
:
1009 tree_toggle_navig (tree
);
1012 tree_copy (tree
, "");
1015 tree_move (tree
, "");
1018 tree_move_up (tree
);
1021 tree_move_down (tree
);
1024 tree_move_home (tree
);
1027 tree_move_end (tree
);
1030 tree_move_pgup (tree
);
1033 tree_move_pgdn (tree
);
1036 tree_chdir_sel (tree
);
1042 tree_start_search (tree
);
1048 if (!tree
->is_panel
)
1049 dlg_stop (WIDGET (tree
)->owner
);
1052 res
= MSG_NOT_HANDLED
;
1060 /* --------------------------------------------------------------------------------------------- */
1063 tree_key (WTree
* tree
, int key
)
1067 if (is_abort_char (key
))
1071 tree
->searching
= 0;
1073 return MSG_HANDLED
; /* eat abort char */
1075 /* modal tree dialog: let upper layer see the
1076 abort character and close the dialog */
1077 return MSG_NOT_HANDLED
;
1080 if (tree
->searching
&& ((key
>= ' ' && key
<= 255) || key
== KEY_BACKSPACE
))
1082 tree_do_search (tree
, key
);
1087 for (i
= 0; tree_map
[i
].key
!= 0; i
++)
1088 if (key
== tree_map
[i
].key
)
1089 switch (tree_map
[i
].command
)
1092 return tree_move_left (tree
) ? MSG_HANDLED
: MSG_NOT_HANDLED
;
1094 return tree_move_right (tree
) ? MSG_HANDLED
: MSG_NOT_HANDLED
;
1096 tree_execute_cmd (tree
, tree_map
[i
].command
);
1100 /* Do not eat characters not meant for the tree below ' ' (e.g. C-l). */
1101 if (!command_prompt
&& ((key
>= ' ' && key
<= 255) || key
== KEY_BACKSPACE
))
1103 tree_start_search (tree
);
1104 tree_do_search (tree
, key
);
1108 return MSG_NOT_HANDLED
;
1111 /* --------------------------------------------------------------------------------------------- */
1114 tree_frame (WDialog
* h
, WTree
* tree
)
1116 Widget
*w
= WIDGET (tree
);
1120 tty_setcolor (NORMAL_COLOR
);
1124 const char *title
= _("Directory tree");
1125 const int len
= str_term_width1 (title
);
1127 tty_draw_box (w
->y
, w
->x
, w
->lines
, w
->cols
, FALSE
);
1129 widget_move (w
, 0, (w
->cols
- len
- 2) / 2);
1130 tty_printf (" %s ", title
);
1132 if (panels_options
.show_mini_info
)
1137 widget_move (w
, y
, 0);
1138 tty_print_alt_char (ACS_LTEE
, FALSE
);
1139 widget_move (w
, y
, w
->cols
- 1);
1140 tty_print_alt_char (ACS_RTEE
, FALSE
);
1141 tty_draw_hline (w
->y
+ y
, w
->x
+ 1, ACS_HLINE
, w
->cols
- 2);
1146 /* --------------------------------------------------------------------------------------------- */
1149 tree_callback (Widget
* w
, Widget
* sender
, widget_msg_t msg
, int parm
, void *data
)
1151 WTree
*tree
= (WTree
*) w
;
1152 WDialog
*h
= w
->owner
;
1158 tree_frame (h
, tree
);
1160 if (widget_get_state (w
, WST_FOCUSED
))
1162 b
= find_buttonbar (h
);
1163 widget_redraw (WIDGET (b
));
1168 b
= find_buttonbar (h
);
1169 buttonbar_set_label (b
, 1, Q_ ("ButtonBar|Help"), tree_map
, w
);
1170 buttonbar_set_label (b
, 2, Q_ ("ButtonBar|Rescan"), tree_map
, w
);
1171 buttonbar_set_label (b
, 3, Q_ ("ButtonBar|Forget"), tree_map
, w
);
1172 buttonbar_set_label (b
, 4, tree_navigation_flag
? Q_ ("ButtonBar|Static")
1173 : Q_ ("ButtonBar|Dynamc"), tree_map
, w
);
1174 buttonbar_set_label (b
, 5, Q_ ("ButtonBar|Copy"), tree_map
, w
);
1175 buttonbar_set_label (b
, 6, Q_ ("ButtonBar|RenMov"), tree_map
, w
);
1177 /* FIXME: mkdir is currently defunct */
1178 buttonbar_set_label (b
, 7, Q_ ("ButtonBar|Mkdir"), tree_map
, w
);
1180 buttonbar_clear_label (b
, 7, w
);
1182 buttonbar_set_label (b
, 8, Q_ ("ButtonBar|Rmdir"), tree_map
, w
);
1187 tree
->searching
= 0;
1191 return tree_key (tree
, parm
);
1194 /* command from buttonbar */
1195 return tree_execute_cmd (tree
, parm
);
1198 tree_destroy (tree
);
1202 return widget_default_callback (w
, sender
, msg
, parm
, data
);
1206 /* --------------------------------------------------------------------------------------------- */
1212 tree_mouse_callback (Widget
* w
, mouse_msg_t msg
, mouse_event_t
* event
)
1214 WTree
*tree
= (WTree
*) w
;
1223 case MSG_MOUSE_DOWN
:
1224 /* rest of the upper frame - call menu */
1225 if (tree
->is_panel
&& event
->y
== WIDGET (w
->owner
)->y
)
1227 /* return MOU_UNHANDLED */
1228 event
->result
.abort
= TRUE
;
1230 else if (!widget_get_state (w
, WST_FOCUSED
))
1234 case MSG_MOUSE_CLICK
:
1238 lines
= tlines (tree
);
1242 tree_move_backward (tree
, lines
- 1);
1245 else if (y
>= lines
)
1247 tree_move_forward (tree
, lines
- 1);
1250 else if ((event
->count
& GPM_DOUBLE
) != 0)
1252 if (tree
->tree_shown
[y
] != NULL
)
1254 tree
->selected_ptr
= tree
->tree_shown
[y
];
1258 tree_chdir_sel (tree
);
1263 case MSG_MOUSE_SCROLL_UP
:
1264 case MSG_MOUSE_SCROLL_DOWN
:
1265 /* TODO: Ticket #2218 */
1273 /* --------------------------------------------------------------------------------------------- */
1274 /*** public functions ****************************************************************************/
1275 /* --------------------------------------------------------------------------------------------- */
1278 tree_new (int y
, int x
, int lines
, int cols
, gboolean is_panel
)
1283 tree
= g_new (WTree
, 1);
1286 widget_init (w
, y
, x
, lines
, cols
, tree_callback
, tree_mouse_callback
);
1287 w
->options
|= WOP_SELECTABLE
| WOP_TOP_SELECT
;
1288 tree
->is_panel
= is_panel
;
1289 tree
->selected_ptr
= 0;
1291 tree
->store
= tree_store_get ();
1292 tree_store_add_entry_remove_hook (remove_callback
, tree
);
1293 tree
->tree_shown
= 0;
1294 tree
->search_buffer
[0] = 0;
1295 tree
->topdiff
= w
->lines
/ 2;
1296 tree
->searching
= 0;
1302 /* --------------------------------------------------------------------------------------------- */
1305 tree_chdir (WTree
* tree
, const vfs_path_t
* dir
)
1307 tree_entry
*current
;
1309 current
= tree_store_whereis (dir
);
1310 if (current
!= NULL
)
1312 tree
->selected_ptr
= current
;
1313 tree_check_focus (tree
);
1317 /* --------------------------------------------------------------------------------------------- */
1318 /** Return name of the currently selected entry */
1321 tree_selected_name (const WTree
* tree
)
1323 return tree
->selected_ptr
->name
;
1326 /* --------------------------------------------------------------------------------------------- */
1329 sync_tree (const vfs_path_t
* vpath
)
1331 tree_chdir (the_tree
, vpath
);
1334 /* --------------------------------------------------------------------------------------------- */
1337 find_tree (WDialog
* h
)
1339 return (WTree
*) find_widget_type (h
, tree_callback
);
1342 /* --------------------------------------------------------------------------------------------- */