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-2024
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-2022
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/keymap.h"
60 #include "src/history.h"
63 #include "filemanager.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"
69 #include "cd.h" /* cd_error_message() */
73 /*** global variables ****************************************************************************/
75 /* The pointer to the tree */
76 WTree
*the_tree
= NULL
;
78 /* If this is true, then when browsing the tree the other window will
79 * automatically reload it's directory with the contents of the currently
82 gboolean xtree_mode
= FALSE
;
84 /*** file scope macro definitions ****************************************************************/
86 #define tlines(t) (t->is_panel ? WIDGET (t)->rect.lines - 2 - \
87 (panels_options.show_mini_info ? 2 : 0) : WIDGET (t)->rect.lines)
89 /*** file scope type declarations ****************************************************************/
94 struct TreeStore
*store
;
95 tree_entry
*selected_ptr
; /* The selected directory */
96 GString
*search_buffer
; /* Current search string */
97 tree_entry
**tree_shown
; /* Entries currently on screen */
98 gboolean is_panel
; /* panel or plain widget flag */
99 gboolean searching
; /* Are we on searching mode? */
100 int topdiff
; /* The difference between the topmost
101 shown and the selected */
104 /*** forward declarations (file scope functions) *************************************************/
106 static void tree_rescan (void *data
);
108 /*** file scope variables ************************************************************************/
110 /* Specifies the display mode: 1d or 2d */
111 static gboolean tree_navigation_flag
= FALSE
;
113 /* --------------------------------------------------------------------------------------------- */
114 /*** file scope functions ************************************************************************/
115 /* --------------------------------------------------------------------------------------------- */
118 back_ptr (tree_entry
* ptr
, int *count
)
122 for (i
= 0; ptr
!= NULL
&& ptr
->prev
!= NULL
&& i
< *count
; ptr
= ptr
->prev
, i
++)
129 /* --------------------------------------------------------------------------------------------- */
132 forw_ptr (tree_entry
* ptr
, int *count
)
136 for (i
= 0; ptr
!= NULL
&& ptr
->next
!= NULL
&& i
< *count
; ptr
= ptr
->next
, i
++)
143 /* --------------------------------------------------------------------------------------------- */
146 remove_callback (tree_entry
* entry
, void *data
)
150 if (tree
->selected_ptr
== entry
)
152 if (tree
->selected_ptr
->next
!= NULL
)
153 tree
->selected_ptr
= tree
->selected_ptr
->next
;
155 tree
->selected_ptr
= tree
->selected_ptr
->prev
;
159 /* --------------------------------------------------------------------------------------------- */
160 /** Save the ${XDG_CACHE_HOME}/mc/Tree file */
163 save_tree (WTree
* tree
)
169 error
= tree_store_save ();
174 tree_name
= mc_config_get_full_path (MC_TREESTORE_FILE
);
175 fprintf (stderr
, _("Cannot open the %s file for writing:\n%s\n"), tree_name
,
176 unix_error_string (error
));
181 /* --------------------------------------------------------------------------------------------- */
184 tree_remove_entry (WTree
* tree
, const vfs_path_t
* name_vpath
)
187 tree_store_remove_entry (name_vpath
);
190 /* --------------------------------------------------------------------------------------------- */
193 tree_destroy (WTree
* tree
)
195 tree_store_remove_entry_remove_hook (remove_callback
);
198 MC_PTR_FREE (tree
->tree_shown
);
199 g_string_free (tree
->search_buffer
, TRUE
);
200 tree
->selected_ptr
= NULL
;
203 /* --------------------------------------------------------------------------------------------- */
204 /** Loads the .mc.tree file */
207 load_tree (WTree
* tree
)
213 tree
->selected_ptr
= tree
->store
->tree_first
;
214 vpath
= vfs_path_from_str (mc_config_get_home_dir ());
215 tree_chdir (tree
, vpath
);
216 vfs_path_free (vpath
, TRUE
);
219 /* --------------------------------------------------------------------------------------------- */
222 tree_show_mini_info (WTree
* tree
, int tree_lines
, int tree_cols
)
224 Widget
*w
= WIDGET (tree
);
230 if (!panels_options
.show_mini_info
)
232 line
= tree_lines
+ 2;
235 line
= tree_lines
+ 1;
239 /* Show search string */
240 tty_setcolor (INPUT_COLOR
);
241 tty_draw_hline (w
->rect
.y
+ line
, w
->rect
.x
+ 1, ' ', tree_cols
);
242 widget_gotoyx (w
, line
, 1);
243 tty_print_char (PATH_SEP
);
244 tty_print_string (str_fit_to_term (tree
->search_buffer
->str
, tree_cols
- 2, J_LEFT_FIT
));
245 tty_print_char (' ');
249 /* Show full name of selected directory */
253 colors
= widget_get_colors (w
);
254 tty_setcolor (tree
->is_panel
? NORMAL_COLOR
: colors
[DLG_COLOR_NORMAL
]);
255 tty_draw_hline (w
->rect
.y
+ line
, w
->rect
.x
+ 1, ' ', tree_cols
);
256 widget_gotoyx (w
, line
, 1);
257 tty_print_string (str_fit_to_term
258 (vfs_path_as_str (tree
->selected_ptr
->name
), tree_cols
, J_LEFT_FIT
));
262 /* --------------------------------------------------------------------------------------------- */
265 show_tree (WTree
* tree
)
267 Widget
*w
= WIDGET (tree
);
272 int tree_lines
, tree_cols
;
275 tree_lines
= tlines (tree
);
276 tree_cols
= w
->rect
.cols
;
278 widget_gotoyx (w
, y
, x
);
285 g_free (tree
->tree_shown
);
286 tree
->tree_shown
= g_new0 (tree_entry
*, tree_lines
);
288 if (tree
->store
->tree_first
!= NULL
)
289 topsublevel
= tree
->store
->tree_first
->sublevel
;
291 if (tree
->selected_ptr
== NULL
)
293 tree
->selected_ptr
= tree
->store
->tree_first
;
296 current
= tree
->selected_ptr
;
298 /* Calculate the directory which is to be shown on the topmost line */
299 if (!tree_navigation_flag
)
300 current
= back_ptr (current
, &tree
->topdiff
);
305 while (current
->prev
!= NULL
&& i
< tree
->topdiff
)
307 current
= current
->prev
;
309 if (current
->sublevel
< tree
->selected_ptr
->sublevel
)
311 if (vfs_path_equal (current
->name
, tree
->selected_ptr
->name
))
314 else if (current
->sublevel
== tree
->selected_ptr
->sublevel
)
318 cname
= vfs_path_as_str (current
->name
);
319 for (j
= strlen (cname
) - 1; !IS_PATH_SEP (cname
[j
]); j
--)
321 if (vfs_path_equal_len (current
->name
, tree
->selected_ptr
->name
, j
))
324 else if (current
->sublevel
== tree
->selected_ptr
->sublevel
+ 1)
326 j
= vfs_path_len (tree
->selected_ptr
->name
);
327 if (j
> 1 && vfs_path_equal_len (current
->name
, tree
->selected_ptr
->name
, j
))
334 /* Loop for every line */
335 for (i
= 0; i
< tree_lines
; i
++)
339 colors
= widget_get_colors (w
);
340 tty_setcolor (tree
->is_panel
? NORMAL_COLOR
: colors
[DLG_COLOR_NORMAL
]);
342 /* Move to the beginning of the line */
343 tty_draw_hline (w
->rect
.y
+ y
+ i
, w
->rect
.x
+ x
, ' ', tree_cols
);
352 selected
= widget_get_state (w
, WST_FOCUSED
) && current
== tree
->selected_ptr
;
353 tty_setcolor (selected
? SELECTED_COLOR
: NORMAL_COLOR
);
357 int idx
= current
== tree
->selected_ptr
? DLG_COLOR_FOCUS
: DLG_COLOR_NORMAL
;
359 tty_setcolor (colors
[idx
]);
362 tree
->tree_shown
[i
] = current
;
363 if (current
->sublevel
== topsublevel
)
365 tty_print_string (str_fit_to_term
366 (vfs_path_as_str (current
->name
),
367 tree_cols
+ (tree
->is_panel
? 0 : 1), J_LEFT_FIT
));
370 /* Sub level directory */
371 tty_set_alt_charset (TRUE
);
373 /* Output branch parts */
374 for (j
= 0; j
< current
->sublevel
- topsublevel
- 1; j
++)
376 if (tree_cols
- 8 - 3 * j
< 9)
378 tty_print_char (' ');
379 if ((current
->submask
& (1 << (j
+ topsublevel
+ 1))) != 0)
380 tty_print_char (ACS_VLINE
);
382 tty_print_char (' ');
383 tty_print_char (' ');
385 tty_print_char (' ');
387 if (current
->next
== NULL
|| (current
->next
->submask
& (1 << current
->sublevel
)) == 0)
388 tty_print_char (ACS_LLCORNER
);
390 tty_print_char (ACS_LTEE
);
391 tty_print_char (ACS_HLINE
);
392 tty_set_alt_charset (FALSE
);
395 tty_print_char (' ');
396 tty_print_string (str_fit_to_term
397 (current
->subname
, tree_cols
- x
- 3 * j
, J_LEFT_FIT
));
400 /* Calculate the next value for current */
401 current
= current
->next
;
402 if (tree_navigation_flag
)
403 for (; current
!= NULL
; current
= current
->next
)
405 if (current
->sublevel
< tree
->selected_ptr
->sublevel
)
407 if (vfs_path_equal_len (current
->name
, tree
->selected_ptr
->name
,
408 vfs_path_len (current
->name
)))
411 else if (current
->sublevel
== tree
->selected_ptr
->sublevel
)
415 cname
= vfs_path_as_str (current
->name
);
416 for (j
= strlen (cname
) - 1; !IS_PATH_SEP (cname
[j
]); j
--)
418 if (vfs_path_equal_len (current
->name
, tree
->selected_ptr
->name
, j
))
421 else if (current
->sublevel
== tree
->selected_ptr
->sublevel
+ 1
422 && vfs_path_len (tree
->selected_ptr
->name
) > 1)
424 if (vfs_path_equal_len (current
->name
, tree
->selected_ptr
->name
,
425 vfs_path_len (tree
->selected_ptr
->name
)))
431 tree_show_mini_info (tree
, tree_lines
, tree_cols
);
434 /* --------------------------------------------------------------------------------------------- */
437 tree_check_focus (WTree
* tree
)
439 if (tree
->topdiff
< 3)
441 else if (tree
->topdiff
>= tlines (tree
) - 3)
442 tree
->topdiff
= tlines (tree
) - 3 - 1;
445 /* --------------------------------------------------------------------------------------------- */
448 tree_move_backward (WTree
* tree
, int i
)
450 if (!tree_navigation_flag
)
451 tree
->selected_ptr
= back_ptr (tree
->selected_ptr
, &i
);
457 current
= tree
->selected_ptr
;
458 while (j
< i
&& current
->prev
!= NULL
459 && current
->prev
->sublevel
>= tree
->selected_ptr
->sublevel
)
461 current
= current
->prev
;
462 if (current
->sublevel
== tree
->selected_ptr
->sublevel
)
464 tree
->selected_ptr
= current
;
472 tree_check_focus (tree
);
475 /* --------------------------------------------------------------------------------------------- */
478 tree_move_forward (WTree
* tree
, int i
)
480 if (!tree_navigation_flag
)
481 tree
->selected_ptr
= forw_ptr (tree
->selected_ptr
, &i
);
487 current
= tree
->selected_ptr
;
488 while (j
< i
&& current
->next
!= NULL
489 && current
->next
->sublevel
>= tree
->selected_ptr
->sublevel
)
491 current
= current
->next
;
492 if (current
->sublevel
== tree
->selected_ptr
->sublevel
)
494 tree
->selected_ptr
= current
;
502 tree_check_focus (tree
);
505 /* --------------------------------------------------------------------------------------------- */
508 tree_move_to_child (WTree
* tree
)
512 /* Do we have a starting point? */
513 if (tree
->selected_ptr
== NULL
)
516 /* Take the next entry */
517 current
= tree
->selected_ptr
->next
;
518 /* Is it the child of the selected entry */
519 if (current
!= NULL
&& current
->sublevel
> tree
->selected_ptr
->sublevel
)
521 /* Yes -> select this entry */
522 tree
->selected_ptr
= current
;
524 tree_check_focus (tree
);
528 /* No -> rescan and try again */
530 current
= tree
->selected_ptr
->next
;
531 if (current
!= NULL
&& current
->sublevel
> tree
->selected_ptr
->sublevel
)
533 tree
->selected_ptr
= current
;
535 tree_check_focus (tree
);
540 /* --------------------------------------------------------------------------------------------- */
543 tree_move_to_parent (WTree
* tree
)
548 if (tree
->selected_ptr
== NULL
)
551 old
= tree
->selected_ptr
;
553 for (current
= tree
->selected_ptr
->prev
;
554 current
!= NULL
&& current
->sublevel
>= tree
->selected_ptr
->sublevel
;
555 current
= current
->prev
)
559 current
= tree
->store
->tree_first
;
560 tree
->selected_ptr
= current
;
561 tree_check_focus (tree
);
562 return tree
->selected_ptr
!= old
;
565 /* --------------------------------------------------------------------------------------------- */
568 tree_move_to_top (WTree
* tree
)
570 tree
->selected_ptr
= tree
->store
->tree_first
;
574 /* --------------------------------------------------------------------------------------------- */
577 tree_move_to_bottom (WTree
* tree
)
579 tree
->selected_ptr
= tree
->store
->tree_last
;
580 tree
->topdiff
= tlines (tree
) - 3 - 1;
583 /* --------------------------------------------------------------------------------------------- */
586 tree_chdir_sel (WTree
* tree
)
594 if (panel_cd (p
, tree
->selected_ptr
->name
, cd_exact
))
597 cd_error_message (vfs_path_as_str (tree
->selected_ptr
->name
));
599 widget_draw (WIDGET (p
));
600 (void) change_panel ();
605 WDialog
*h
= DIALOG (WIDGET (tree
)->owner
);
607 h
->ret_value
= B_ENTER
;
612 /* --------------------------------------------------------------------------------------------- */
615 maybe_chdir (WTree
* tree
)
617 if (xtree_mode
&& tree
->is_panel
&& is_idle ())
618 tree_chdir_sel (tree
);
621 /* --------------------------------------------------------------------------------------------- */
622 /** Search tree for text */
625 search_tree (WTree
* tree
, const GString
* text
)
627 tree_entry
*current
= tree
->selected_ptr
;
628 gboolean wrapped
= FALSE
;
629 gboolean found
= FALSE
;
631 while (!found
&& (!wrapped
|| current
!= tree
->selected_ptr
))
632 if (strncmp (current
->subname
, text
->str
, text
->len
) == 0)
634 tree
->selected_ptr
= current
;
639 current
= current
->next
;
642 current
= tree
->store
->tree_first
;
649 tree_check_focus (tree
);
653 /* --------------------------------------------------------------------------------------------- */
656 tree_do_search (WTree
* tree
, int key
)
658 /* TODO: support multi-byte characters, see do_search() in panel.c */
660 if (tree
->search_buffer
->len
!= 0 && key
== KEY_BACKSPACE
)
661 g_string_set_size (tree
->search_buffer
, tree
->search_buffer
->len
- 1);
663 g_string_append_c (tree
->search_buffer
, (gchar
) key
);
665 if (!search_tree (tree
, tree
->search_buffer
))
666 g_string_set_size (tree
->search_buffer
, tree
->search_buffer
->len
- 1);
672 /* --------------------------------------------------------------------------------------------- */
675 tree_rescan (void *data
)
678 vfs_path_t
*old_vpath
;
680 old_vpath
= vfs_path_clone (vfs_get_raw_current_dir ());
681 if (old_vpath
== NULL
)
684 if (tree
->selected_ptr
!= NULL
&& mc_chdir (tree
->selected_ptr
->name
) == 0)
688 tree_store_rescan (tree
->selected_ptr
->name
);
689 ret
= mc_chdir (old_vpath
);
692 vfs_path_free (old_vpath
, TRUE
);
695 /* --------------------------------------------------------------------------------------------- */
698 tree_forget (void *data
)
702 if (tree
->selected_ptr
!= NULL
)
703 tree_remove_entry (tree
, tree
->selected_ptr
->name
);
706 /* --------------------------------------------------------------------------------------------- */
709 tree_copy (WTree
* tree
, const char *default_dest
)
711 char msg
[BUF_MEDIUM
];
714 if (tree
->selected_ptr
== NULL
)
717 g_snprintf (msg
, sizeof (msg
), _("Copy \"%s\" directory to:"),
718 str_trunc (vfs_path_as_str (tree
->selected_ptr
->name
), 50));
719 dest
= input_expand_dialog (Q_ ("DialogTitle|Copy"),
720 msg
, MC_HISTORY_FM_TREE_COPY
, default_dest
,
721 INPUT_COMPLETE_FILENAMES
| INPUT_COMPLETE_CD
);
723 if (dest
!= NULL
&& *dest
!= '\0')
725 file_op_context_t
*ctx
;
726 file_op_total_context_t
*tctx
;
728 ctx
= file_op_context_new (OP_COPY
);
729 tctx
= file_op_total_context_new ();
730 file_op_context_create_ui (ctx
, FALSE
, FILEGUI_DIALOG_MULTI_ITEM
);
731 tctx
->ask_overwrite
= FALSE
;
732 copy_dir_dir (tctx
, ctx
, vfs_path_as_str (tree
->selected_ptr
->name
), dest
, TRUE
, FALSE
,
734 file_op_total_context_destroy (tctx
);
735 file_op_context_destroy (ctx
);
741 /* --------------------------------------------------------------------------------------------- */
744 tree_move (WTree
* tree
, const char *default_dest
)
746 char msg
[BUF_MEDIUM
];
749 if (tree
->selected_ptr
== NULL
)
752 g_snprintf (msg
, sizeof (msg
), _("Move \"%s\" directory to:"),
753 str_trunc (vfs_path_as_str (tree
->selected_ptr
->name
), 50));
755 input_expand_dialog (Q_ ("DialogTitle|Move"), msg
, MC_HISTORY_FM_TREE_MOVE
, default_dest
,
756 INPUT_COMPLETE_FILENAMES
| INPUT_COMPLETE_CD
);
758 if (dest
!= NULL
&& *dest
!= '\0')
760 vfs_path_t
*dest_vpath
;
763 dest_vpath
= vfs_path_from_str (dest
);
765 if (mc_stat (dest_vpath
, &buf
) != 0)
766 message (D_ERROR
, MSG_ERROR
, _("Cannot stat the destination\n%s"),
767 unix_error_string (errno
));
768 else if (!S_ISDIR (buf
.st_mode
))
769 file_error (TRUE
, _("Destination \"%s\" must be a directory\n%s"), dest
);
772 file_op_context_t
*ctx
;
773 file_op_total_context_t
*tctx
;
775 ctx
= file_op_context_new (OP_MOVE
);
776 tctx
= file_op_total_context_new ();
777 file_op_context_create_ui (ctx
, FALSE
, FILEGUI_DIALOG_ONE_ITEM
);
778 move_dir_dir (tctx
, ctx
, vfs_path_as_str (tree
->selected_ptr
->name
), dest
);
779 file_op_total_context_destroy (tctx
);
780 file_op_context_destroy (ctx
);
783 vfs_path_free (dest_vpath
, TRUE
);
789 /* --------------------------------------------------------------------------------------------- */
793 tree_mkdir (WTree
* tree
)
795 char old_dir
[MC_MAXPATHLEN
];
797 if (tree
->selected_ptr
== NULL
|| chdir (tree
->selected_ptr
->name
) != 0)
807 /* --------------------------------------------------------------------------------------------- */
810 tree_rmdir (void *data
)
813 file_op_context_t
*ctx
;
814 file_op_total_context_t
*tctx
;
816 if (tree
->selected_ptr
== NULL
)
824 buf
= g_strdup_printf (_("Delete %s?"), vfs_path_as_str (tree
->selected_ptr
->name
));
826 result
= query_dialog (Q_ ("DialogTitle|Delete"), buf
, D_ERROR
, 2, _("&Yes"), _("&No"));
832 ctx
= file_op_context_new (OP_DELETE
);
833 tctx
= file_op_total_context_new ();
835 file_op_context_create_ui (ctx
, FALSE
, FILEGUI_DIALOG_ONE_ITEM
);
836 if (erase_dir (tctx
, ctx
, tree
->selected_ptr
->name
) == FILE_CONT
)
838 file_op_total_context_destroy (tctx
);
839 file_op_context_destroy (ctx
);
842 /* --------------------------------------------------------------------------------------------- */
845 tree_move_up (WTree
* tree
)
847 tree_move_backward (tree
, 1);
852 /* --------------------------------------------------------------------------------------------- */
855 tree_move_down (WTree
* tree
)
857 tree_move_forward (tree
, 1);
862 /* --------------------------------------------------------------------------------------------- */
865 tree_move_home (WTree
* tree
)
867 tree_move_to_top (tree
);
872 /* --------------------------------------------------------------------------------------------- */
875 tree_move_end (WTree
* tree
)
877 tree_move_to_bottom (tree
);
882 /* --------------------------------------------------------------------------------------------- */
885 tree_move_pgup (WTree
* tree
)
887 tree_move_backward (tree
, tlines (tree
) - 1);
892 /* --------------------------------------------------------------------------------------------- */
895 tree_move_pgdn (WTree
* tree
)
897 tree_move_forward (tree
, tlines (tree
) - 1);
902 /* --------------------------------------------------------------------------------------------- */
905 tree_move_left (WTree
* tree
)
909 if (tree_navigation_flag
)
911 v
= tree_move_to_parent (tree
);
919 /* --------------------------------------------------------------------------------------------- */
922 tree_move_right (WTree
* tree
)
926 if (tree_navigation_flag
)
928 tree_move_to_child (tree
);
937 /* --------------------------------------------------------------------------------------------- */
940 tree_start_search (WTree
* tree
)
944 if (tree
->selected_ptr
== tree
->store
->tree_last
)
945 tree_move_to_top (tree
);
950 /* set navigation mode temporarily to 'Static' because in
951 * dynamic navigation mode tree_move_forward will not move
952 * to a lower sublevel if necessary (sequent searches must
953 * start with the directory followed the last found directory)
955 i
= tree_navigation_flag
;
956 tree_navigation_flag
= FALSE
;
957 tree_move_forward (tree
, 1);
958 tree_navigation_flag
= i
;
960 tree_do_search (tree
, 0);
964 tree
->searching
= TRUE
;
965 g_string_set_size (tree
->search_buffer
, 0);
969 /* --------------------------------------------------------------------------------------------- */
972 tree_toggle_navig (WTree
* tree
)
974 Widget
*w
= WIDGET (tree
);
977 tree_navigation_flag
= !tree_navigation_flag
;
979 b
= buttonbar_find (DIALOG (w
->owner
));
980 buttonbar_set_label (b
, 4,
981 tree_navigation_flag
? Q_ ("ButtonBar|Static") : Q_ ("ButtonBar|Dynamc"),
983 widget_draw (WIDGET (b
));
986 /* --------------------------------------------------------------------------------------------- */
991 ev_help_t event_data
= { NULL
, "[Directory Tree]" };
993 mc_event_raise (MCEVENT_GROUP_CORE
, "help", &event_data
);
996 /* --------------------------------------------------------------------------------------------- */
999 tree_execute_cmd (WTree
* tree
, long command
)
1001 cb_ret_t res
= MSG_HANDLED
;
1003 if (command
!= CK_Search
)
1004 tree
->searching
= FALSE
;
1014 case CK_ToggleNavigation
:
1015 tree_toggle_navig (tree
);
1018 tree_copy (tree
, "");
1021 tree_move (tree
, "");
1024 tree_move_up (tree
);
1027 tree_move_down (tree
);
1030 tree_move_home (tree
);
1033 tree_move_end (tree
);
1036 tree_move_pgup (tree
);
1039 tree_move_pgdn (tree
);
1042 tree_chdir_sel (tree
);
1048 tree_start_search (tree
);
1054 if (!tree
->is_panel
)
1055 dlg_close (DIALOG (WIDGET (tree
)->owner
));
1058 res
= MSG_NOT_HANDLED
;
1066 /* --------------------------------------------------------------------------------------------- */
1069 tree_key (WTree
* tree
, int key
)
1073 if (is_abort_char (key
))
1077 tree
->searching
= FALSE
;
1079 return MSG_HANDLED
; /* eat abort char */
1081 /* modal tree dialog: let upper layer see the
1082 abort character and close the dialog */
1083 return MSG_NOT_HANDLED
;
1086 if (tree
->searching
&& ((key
>= ' ' && key
<= 255) || key
== KEY_BACKSPACE
))
1088 tree_do_search (tree
, key
);
1093 command
= widget_lookup_key (WIDGET (tree
), key
);
1099 return tree_move_left (tree
) ? MSG_HANDLED
: MSG_NOT_HANDLED
;
1101 return tree_move_right (tree
) ? MSG_HANDLED
: MSG_NOT_HANDLED
;
1103 tree_execute_cmd (tree
, command
);
1107 /* Do not eat characters not meant for the tree below ' ' (e.g. C-l). */
1108 if (!command_prompt
&& ((key
>= ' ' && key
<= 255) || key
== KEY_BACKSPACE
))
1110 tree_start_search (tree
);
1111 tree_do_search (tree
, key
);
1115 return MSG_NOT_HANDLED
;
1118 /* --------------------------------------------------------------------------------------------- */
1121 tree_frame (WDialog
* h
, WTree
* tree
)
1123 Widget
*w
= WIDGET (tree
);
1127 tty_setcolor (NORMAL_COLOR
);
1131 const char *title
= _("Directory tree");
1132 const int len
= str_term_width1 (title
);
1134 tty_draw_box (w
->rect
.y
, w
->rect
.x
, w
->rect
.lines
, w
->rect
.cols
, FALSE
);
1136 widget_gotoyx (w
, 0, (w
->rect
.cols
- len
- 2) / 2);
1137 tty_printf (" %s ", title
);
1139 if (panels_options
.show_mini_info
)
1143 y
= w
->rect
.lines
- 3;
1144 widget_gotoyx (w
, y
, 0);
1145 tty_print_alt_char (ACS_LTEE
, FALSE
);
1146 widget_gotoyx (w
, y
, w
->rect
.cols
- 1);
1147 tty_print_alt_char (ACS_RTEE
, FALSE
);
1148 tty_draw_hline (w
->rect
.y
+ y
, w
->rect
.x
+ 1, ACS_HLINE
, w
->rect
.cols
- 2);
1153 /* --------------------------------------------------------------------------------------------- */
1156 tree_callback (Widget
* w
, Widget
* sender
, widget_msg_t msg
, int parm
, void *data
)
1158 WTree
*tree
= (WTree
*) w
;
1159 WDialog
*h
= DIALOG (w
->owner
);
1165 tree_frame (h
, tree
);
1167 if (widget_get_state (w
, WST_FOCUSED
))
1169 b
= buttonbar_find (h
);
1170 widget_draw (WIDGET (b
));
1175 b
= buttonbar_find (h
);
1176 buttonbar_set_label (b
, 1, Q_ ("ButtonBar|Help"), w
->keymap
, w
);
1177 buttonbar_set_label (b
, 2, Q_ ("ButtonBar|Rescan"), w
->keymap
, w
);
1178 buttonbar_set_label (b
, 3, Q_ ("ButtonBar|Forget"), w
->keymap
, w
);
1179 buttonbar_set_label (b
, 4, tree_navigation_flag
? Q_ ("ButtonBar|Static")
1180 : Q_ ("ButtonBar|Dynamc"), w
->keymap
, w
);
1181 buttonbar_set_label (b
, 5, Q_ ("ButtonBar|Copy"), w
->keymap
, w
);
1182 buttonbar_set_label (b
, 6, Q_ ("ButtonBar|RenMov"), w
->keymap
, w
);
1184 /* FIXME: mkdir is currently defunct */
1185 buttonbar_set_label (b
, 7, Q_ ("ButtonBar|Mkdir"), w
->keymap
, w
);
1187 buttonbar_clear_label (b
, 7, w
);
1189 buttonbar_set_label (b
, 8, Q_ ("ButtonBar|Rmdir"), w
->keymap
, w
);
1194 tree
->searching
= FALSE
;
1198 return tree_key (tree
, parm
);
1201 /* command from buttonbar */
1202 return tree_execute_cmd (tree
, parm
);
1205 tree_destroy (tree
);
1209 return widget_default_callback (w
, sender
, msg
, parm
, data
);
1213 /* --------------------------------------------------------------------------------------------- */
1219 tree_mouse_callback (Widget
* w
, mouse_msg_t msg
, mouse_event_t
* event
)
1221 WTree
*tree
= (WTree
*) w
;
1230 case MSG_MOUSE_DOWN
:
1231 /* rest of the upper frame - call menu */
1232 if (tree
->is_panel
&& event
->y
== WIDGET (w
->owner
)->rect
.y
)
1234 /* return MOU_UNHANDLED */
1235 event
->result
.abort
= TRUE
;
1237 else if (!widget_get_state (w
, WST_FOCUSED
))
1238 (void) change_panel ();
1241 case MSG_MOUSE_CLICK
:
1245 lines
= tlines (tree
);
1249 tree_move_backward (tree
, lines
- 1);
1252 else if (y
>= lines
)
1254 tree_move_forward (tree
, lines
- 1);
1257 else if ((event
->count
& GPM_DOUBLE
) != 0)
1259 if (tree
->tree_shown
[y
] != NULL
)
1261 tree
->selected_ptr
= tree
->tree_shown
[y
];
1265 tree_chdir_sel (tree
);
1270 case MSG_MOUSE_SCROLL_UP
:
1271 case MSG_MOUSE_SCROLL_DOWN
:
1272 /* TODO: Ticket #2218 */
1280 /* --------------------------------------------------------------------------------------------- */
1281 /*** public functions ****************************************************************************/
1282 /* --------------------------------------------------------------------------------------------- */
1285 tree_new (const WRect
* r
, gboolean is_panel
)
1290 tree
= g_new (WTree
, 1);
1293 widget_init (w
, r
, tree_callback
, tree_mouse_callback
);
1294 w
->options
|= WOP_SELECTABLE
| WOP_TOP_SELECT
;
1295 w
->keymap
= tree_map
;
1297 tree
->is_panel
= is_panel
;
1298 tree
->selected_ptr
= NULL
;
1300 tree
->store
= tree_store_get ();
1301 tree_store_add_entry_remove_hook (remove_callback
, tree
);
1302 tree
->tree_shown
= NULL
;
1303 tree
->search_buffer
= g_string_sized_new (MC_MAXPATHLEN
);
1304 tree
->topdiff
= w
->rect
.lines
/ 2;
1305 tree
->searching
= FALSE
;
1311 /* --------------------------------------------------------------------------------------------- */
1314 tree_chdir (WTree
* tree
, const vfs_path_t
* dir
)
1316 tree_entry
*current
;
1318 current
= tree_store_whereis (dir
);
1319 if (current
!= NULL
)
1321 tree
->selected_ptr
= current
;
1322 tree_check_focus (tree
);
1326 /* --------------------------------------------------------------------------------------------- */
1327 /** Return name of the currently selected entry */
1330 tree_selected_name (const WTree
* tree
)
1332 return tree
->selected_ptr
->name
;
1335 /* --------------------------------------------------------------------------------------------- */
1338 sync_tree (const vfs_path_t
* vpath
)
1340 tree_chdir (the_tree
, vpath
);
1343 /* --------------------------------------------------------------------------------------------- */
1346 find_tree (const WDialog
* h
)
1348 return (WTree
*) widget_find_by_type (CONST_WIDGET (h
), tree_callback
);
1351 /* --------------------------------------------------------------------------------------------- */