1 /* Directory tree browser for the Midnight Commander
2 Copyright (C) 1994, 1995, 1996, 1997 The Free Software Foundation
4 Written: 1994, 1996 Janne Kukonlehto
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 This module has been converted to be a widget.
24 The program load and saves the tree each time the tree widget is
25 created and destroyed. This is required for the future vfs layer,
26 it will be possible to have tree views over virtual file systems.
30 #include <sys/types.h>
35 #include <sys/param.h>
40 #include <stdlib.h> /* For free() and atoi() */
54 #include "file.h" /* For copy_dir_dir(), move_dir_dir(), erase_dir() */
56 #include "key.h" /* For mi_getch() */
59 #include "../vfs/vfs.h"
64 extern int command_prompt
;
66 #define TREE_NORMALC HOT_FOCUSC
68 /* Specifies the display mode: 1d or 2d */
69 int tree_navigation_flag
;
71 /* If this is true, then when browsing the tree the other window will
72 * automatically reload it's directory with the contents of the currently
78 static int tree_callback (Dlg_head
*h
, WTree
*tree
, int msg
, int par
);
79 #define tcallback (callback_fn) tree_callback
83 /* Returns number of common characters */
84 static inline int str_common (char *s1
, char *s2
)
88 while (*s1
++ == *s2
++)
93 static tree_entry
*back_ptr (tree_entry
*ptr
, int *count
)
97 while (ptr
&& ptr
->prev
&& i
< *count
){
105 static tree_entry
*forw_ptr (tree_entry
*ptr
, int *count
)
109 while (ptr
&& ptr
->next
&& i
< *count
){
117 /* The directory names are arranged in a single linked list in the same
118 order as they are displayed. When the tree is displayed the expected
129 i.e. the required collating sequence when comparing two directory names is
130 '\0' < PATH_SEP < all-other-characters-in-encoding-order
132 Since strcmp doesn't fulfil this requirement we use pathcmp when
133 inserting directory names into the list. The meaning of the return value
134 of pathcmp and strcmp are the same (an integer less than, equal to, or
135 greater than zero if p1 is found to be less than, to match, or be greater
139 pathcmp (const char *p1
, const char *p2
)
141 for ( ;*p1
== *p2
; p1
++, p2
++)
156 /* Searches for specified directory */
157 static tree_entry
*whereis (WTree
*tree
, char *name
)
159 tree_entry
*current
= tree
->tree_first
;
163 if (tree
->tree_last
){
164 flag
= strcmp (tree
->tree_last
->name
, name
);
166 current
= tree
->tree_last
;
167 } else if (tree
->selected_ptr
){
168 flag
= strcmp (tree
->selected_ptr
->name
, name
);
170 current
= tree
->selected_ptr
;
175 while (current
&& (flag
= pathcmp (current
->name
, name
)) < 0)
176 current
= current
->next
;
184 /* Add a directory to the list of directories */
185 tree_entry
*tree_add_entry (WTree
*tree
, char *name
)
188 tree_entry
*current
= tree
->tree_first
;
189 tree_entry
*old
= NULL
;
197 if (tree
->tree_last
&& tree
->tree_last
->next
)
200 if (tree
->tree_last
){
201 flag
= strcmp (tree
->tree_last
->name
, name
);
203 current
= tree
->tree_last
;
205 } else if (tree
->selected_ptr
){
206 flag
= strcmp (tree
->selected_ptr
->name
, name
);
208 current
= tree
->selected_ptr
;
214 /* Search for the correct place */
215 while (current
&& (flag
= pathcmp (current
->name
, name
)) < 0){
217 current
= current
->next
;
221 return current
; /* Already in the list */
223 /* Not in the list -> add it */
224 new = xmalloc (sizeof (tree_entry
), "tree, tree_entry");
226 /* Append to the end of the list */
227 if (!tree
->tree_first
){
229 tree
->tree_first
= new;
236 tree
->tree_last
= new;
238 /* Insert in to the middle of the list */
241 /* Yes, in the middle */
242 new->next
= old
->next
;
245 /* Nope, in the beginning of the list */
246 new->next
= tree
->tree_first
;
247 tree
->tree_first
= new;
249 new->next
->prev
= new;
253 /* Calculate attributes */
254 new->name
= strdup (name
);
255 len
= strlen (new->name
);
257 for (i
= 0; i
< len
; i
++)
258 if (new->name
[i
] == PATH_SEP
){
260 new->subname
= new->name
+ i
+ 1;
263 submask
= new->next
->submask
;
266 submask
|= 1 << new->sublevel
;
267 submask
&= (2 << new->sublevel
) - 1;
268 new->submask
= submask
;
271 /* Correct the submasks of the previous entries */
273 while (current
&& current
->sublevel
> new->sublevel
){
274 current
->submask
|= 1 << new->sublevel
;
275 current
= current
->prev
;
278 /* The entry has now been added */
280 if (new->sublevel
> 1){
281 /* Let's check if the parent directory is in the tree */
282 char *parent
= strdup (new->name
);
285 for (i
= strlen (parent
) - 1; i
> 1; i
--){
286 if (parent
[i
] == PATH_SEP
){
288 tree_add_entry (tree
, parent
);
299 /* Append a directory to the list of directories */
300 static tree_entry
*tree_append_entry (WTree
*tree
, char *name
)
302 tree_entry
*current
, *new;
306 /* We assume the directory is not yet in the list */
308 new = xmalloc (sizeof (tree_entry
), "tree, tree_entry");
309 if (!tree
->tree_first
){
311 tree
->tree_first
= new;
314 tree
->tree_last
->next
= new;
315 new->prev
= tree
->tree_last
;
318 tree
->tree_last
= new;
320 /* Calculate attributes */
321 new->name
= strdup (name
);
322 len
= strlen (new->name
);
324 for (i
= 0; i
< len
; i
++)
325 if (new->name
[i
] == PATH_SEP
){
327 new->subname
= new->name
+ i
+ 1;
329 submask
= 1 << new->sublevel
;
330 submask
&= (2 << new->sublevel
) - 1;
331 new->submask
= submask
;
334 /* Correct the submasks of the previous entries */
336 while (current
&& current
->sublevel
> new->sublevel
){
337 current
->submask
|= 1 << new->sublevel
;
338 current
= current
->prev
;
341 /* The entry has now been appended */
346 static void remove_entry (WTree
*tree
, tree_entry
*entry
)
348 tree_entry
*current
= entry
->prev
;
351 if (tree
->selected_ptr
== entry
){
352 if (tree
->selected_ptr
->next
)
353 tree
->selected_ptr
= tree
->selected_ptr
->next
;
355 tree
->selected_ptr
= tree
->selected_ptr
->prev
;
358 /* Correct the submasks of the previous entries */
360 submask
= entry
->next
->submask
;
361 while (current
&& current
->sublevel
> entry
->sublevel
){
362 submask
|= 1 << current
->sublevel
;
363 submask
&= (2 << current
->sublevel
) - 1;
364 current
->submask
= submask
;
365 current
= current
->prev
;
368 /* Unlink the entry from the list */
370 entry
->prev
->next
= entry
->next
;
372 tree
->tree_first
= entry
->next
;
374 entry
->next
->prev
= entry
->prev
;
376 tree
->tree_last
= entry
->prev
;
379 /* Free the memory used by the entry */
384 void tree_remove_entry (WTree
*tree
, char *name
)
386 tree_entry
*current
, *base
, *old
;
387 int len
, base_sublevel
;
389 /* Miguel Ugly hack */
390 if (name
[0] == PATH_SEP
&& name
[1] == 0)
392 /* Miguel Ugly hack end */
394 base
= whereis (tree
, name
);
396 return; /* Doesn't exist */
397 if (tree
->check_name
[0] == PATH_SEP
&& tree
->check_name
[1] == 0)
398 base_sublevel
= base
->sublevel
;
400 base_sublevel
= base
->sublevel
+ 1;
401 len
= strlen (base
->name
);
402 current
= base
->next
;
404 && strncmp (current
->name
, base
->name
, len
) == 0
405 && (current
->name
[len
] == '\0' || current
->name
[len
] == PATH_SEP
)){
407 current
= current
->next
;
408 remove_entry (tree
, old
);
410 remove_entry (tree
, base
);
413 void tree_destroy (WTree
*tree
)
415 tree_entry
*current
, *old
;
418 current
= tree
->tree_first
;
421 current
= current
->next
;
425 if (tree
->tree_shown
){
426 free (tree
->tree_shown
);
427 tree
->tree_shown
= 0;
429 tree
->selected_ptr
= tree
->tree_first
= tree
->tree_last
= NULL
;
432 /* Mark the subdirectories of the current directory for delete */
433 void start_tree_check (WTree
*tree
)
439 tree
= (WTree
*) find_widget_type (current_dlg
, tcallback
);
443 /* Search for the start of subdirectories */
444 mc_get_current_wd (tree
->check_name
, MC_MAXPATHLEN
);
445 tree
->check_start
= NULL
;
446 current
= whereis (tree
, tree
->check_name
);
448 /* Cwd doesn't exist -> add it */
449 current
= tree_add_entry (tree
, tree
->check_name
);
453 /* Mark old subdirectories for delete */
454 tree
->check_start
= current
->next
;
455 len
= strlen (tree
->check_name
);
457 current
= tree
->check_start
;
459 && strncmp (current
->name
, tree
->check_name
, len
) == 0
460 && (current
->name
[len
] == '\0' || current
->name
[len
] == PATH_SEP
|| len
== 1)){
462 current
= current
->next
;
466 /* This subdirectory exists -> clear deletion mark */
467 void do_tree_check (WTree
*tree
, const char *subname
)
470 tree_entry
*current
, *base
;
473 /* Calculate the full name of the subdirectory */
474 if (subname
[0] == '.' &&
475 (subname
[1] == 0 || (subname
[1] == '.' && subname
[2] == 0)))
477 if (tree
->check_name
[0] == PATH_SEP
&& tree
->check_name
[1] == 0)
478 name
= copy_strings (PATH_SEP_STR
, subname
, 0);
480 name
= concat_dir_and_file (tree
->check_name
, subname
);
482 /* Search for the subdirectory */
483 current
= tree
->check_start
;
484 while (current
&& (flag
= pathcmp (current
->name
, name
)) < 0)
485 current
= current
->next
;
488 /* Doesn't exist -> add it */
489 current
= tree_add_entry (tree
, name
);
492 /* Clear the deletion mark from the subdirectory and its children */
495 len
= strlen (base
->name
);
497 current
= base
->next
;
499 && strncmp (current
->name
, base
->name
, len
) == 0
500 && (current
->name
[len
] == '\0' || current
->name
[len
] == PATH_SEP
|| len
== 1)){
502 current
= current
->next
;
507 /* Tree check searchs a tree widget in the current dialog and
508 * if it finds it, it calls do_tree_check on the subname
510 void tree_check (const char *subname
)
514 tree
= (WTree
*) find_widget_type (current_dlg
, tcallback
);
517 do_tree_check (tree
, subname
);
521 /* Delete subdirectories which still have the deletion mark */
522 void end_tree_check (WTree
*tree
)
524 tree_entry
*current
, *old
;
528 tree
= (WTree
*) find_widget_type (current_dlg
, tcallback
);
532 /* Check delete marks and delete if found */
533 len
= strlen (tree
->check_name
);
535 current
= tree
->check_start
;
537 && strncmp (current
->name
, tree
->check_name
, len
) == 0
538 && (current
->name
[len
] == '\0' || current
->name
[len
] == PATH_SEP
|| len
== 1)){
540 current
= current
->next
;
542 remove_entry (tree
, old
);
546 /* Loads the .mc.tree file */
547 void load_tree (WTree
*tree
)
551 char name
[MC_MAXPATHLEN
], oldname
[MC_MAXPATHLEN
];
555 filename
= concat_dir_and_file (home_dir
, MC_TREE
);
556 file
= fopen (filename
, "r");
559 /* No new tree file -> let's try the old file */
560 filename
= concat_dir_and_file (home_dir
, MC_TREE
);
561 file
= fopen (filename
, "r");
566 /* File open -> read contents */
568 while (fgets (name
, MC_MAXPATHLEN
, file
)){
570 if (name
[len
- 1] == '\n'){
574 /* .ado: Drives for NT and OS/2 */
579 tree_add_entry (tree
, name
);
580 strcpy (oldname
, name
);
584 if (name
[0] != PATH_SEP
){
585 /* Clear-text decompression */
586 char *s
= strtok (name
, " ");
590 different
= strtok (NULL
, "");
592 strcpy (oldname
+ common
, different
);
593 tree_add_entry (tree
, oldname
);
597 tree_add_entry (tree
, name
);
598 strcpy (oldname
, name
);
603 if (!tree
->tree_first
){
604 /* Nothing loaded -> let's add some standard directories */
605 tree_add_entry (tree
, PATH_SEP_STR
);
606 tree
->selected_ptr
= tree
->tree_first
;
607 tree_rescan_cmd (tree
);
608 tree_add_entry (tree
, home_dir
);
609 tree_chdir (tree
, home_dir
);
610 tree_rescan_cmd (tree
);
614 /* Save the .mc.tree file */
615 void save_tree (WTree
*tree
)
622 filename
= concat_dir_and_file (home_dir
, MC_TREE
);
623 file
= fopen (filename
, "w");
626 fprintf (stderr
, _("Can't open the %s file for writing:\n%s\n"), MC_TREE
,
627 unix_error_string (errno
));
631 current
= tree
->tree_first
;
633 if (current
->prev
&& (common
= str_common (current
->prev
->name
, current
->name
)) > 2)
634 /* Clear-text compression */
635 i
= fprintf (file
, "%d %s\n", common
, current
->name
+ common
);
637 i
= fprintf (file
, "%s\n", current
->name
);
639 fprintf (stderr
, _("Can't write to the %s file:\n%s\n"), MC_TREE
,
640 unix_error_string (errno
));
643 current
= current
->next
;
648 static void tree_show_mini_info (WTree
*tree
, int tree_lines
, int tree_cols
)
650 Dlg_head
*h
= tree
->widget
.parent
;
661 widget_move (&tree
->widget
, line
, 1);
662 hline (' ', tree_cols
);
663 widget_move (&tree
->widget
, line
, 1);
665 if (tree
->searching
){
666 /* Show search string */
667 attrset (TREE_NORMALC
);
671 addstr (name_trunc (tree
->search_buffer
, tree_cols
-2));
675 /* Show full name of selected directory */
676 addstr (name_trunc (tree
->selected_ptr
->name
, tree_cols
));
680 void show_tree (WTree
*tree
)
682 Dlg_head
*h
= tree
->widget
.parent
;
684 int i
, j
, topsublevel
;
686 int tree_lines
, tree_cols
;
690 tree_lines
= tlines (tree
);
691 tree_cols
= tree
->widget
.cols
;
693 attrset (TREE_NORMALC
);
694 widget_move ((Widget
*)tree
, y
, x
);
700 if (tree
->tree_shown
)
701 free (tree
->tree_shown
);
702 tree
->tree_shown
= (tree_entry
**)xmalloc (sizeof (tree_entry
*)*tree_lines
,
704 for (i
= 0; i
< tree_lines
; i
++)
705 tree
->tree_shown
[i
] = NULL
;
706 if (tree
->tree_first
)
707 topsublevel
= tree
->tree_first
->sublevel
;
710 if (!tree
->selected_ptr
){
711 tree
->selected_ptr
= tree
->tree_first
;
714 current
= tree
->selected_ptr
;
716 /* Calculate the directory which is to be shown on the topmost line */
717 if (tree_navigation_flag
){
719 while (current
->prev
&& i
< tree
->topdiff
){
720 current
= current
->prev
;
721 if (current
->sublevel
< tree
->selected_ptr
->sublevel
){
722 if (strncmp (current
->name
, tree
->selected_ptr
->name
,
723 strlen (current
->name
)) == 0)
725 } else if (current
->sublevel
== tree
->selected_ptr
->sublevel
){
726 for (j
= strlen (current
->name
) - 1; current
->name
[j
] != PATH_SEP
; j
--);
727 if (strncmp (current
->name
, tree
->selected_ptr
->name
, j
) == 0)
729 } else if (current
->sublevel
== tree
->selected_ptr
->sublevel
+ 1
730 && strlen (tree
->selected_ptr
->name
) > 1){
731 if (strncmp (current
->name
, tree
->selected_ptr
->name
,
732 strlen (tree
->selected_ptr
->name
)) == 0)
738 current
= back_ptr (current
, &tree
->topdiff
);
740 /* Loop for every line */
741 for (i
= 0; i
< tree_lines
; i
++){
742 /* Move to the beginning of the line */
743 widget_move (&tree
->widget
, y
+i
, x
);
745 hline (' ', tree_cols
);
746 widget_move (&tree
->widget
, y
+i
, x
);
751 tree
->tree_shown
[i
] = current
;
752 if (current
->sublevel
== topsublevel
){
754 /* Top level directory */
755 if (tree
->active
&& current
== tree
->selected_ptr
)
756 if (!use_colors
&& !tree
->is_panel
)
757 attrset (MARKED_COLOR
);
759 attrset (SELECTED_COLOR
);
762 addstr (name_trunc (current
->name
, tree_cols
- 6));
764 /* Sub level directory */
767 /* Output branch parts */
768 for (j
= 0; j
< current
->sublevel
- topsublevel
- 1; j
++){
769 if (tree_cols
- 8 - 3 * j
< 9)
772 if (current
->submask
& (1 << (j
+ topsublevel
+ 1)))
779 if (!current
->next
|| !(current
->next
->submask
& (1 << current
->sublevel
)))
780 addch (ACS_LLCORNER
);
786 if (tree
->active
&& current
== tree
->selected_ptr
)
787 /* Selected directory -> change color */
788 if (!use_colors
&& !tree
->is_panel
)
789 attrset (MARKED_COLOR
);
791 attrset (SELECTED_COLOR
);
795 addstr (name_trunc (current
->subname
,
796 tree_cols
- 2 - 4 - 3 * j
));
800 /* Return to normal color */
801 attrset (TREE_NORMALC
);
803 /* Calculate the next value for current */
804 if (tree_navigation_flag
){
805 current
= current
->next
;
807 if (current
->sublevel
< tree
->selected_ptr
->sublevel
){
808 if (strncmp (current
->name
, tree
->selected_ptr
->name
,
809 strlen (current
->name
)) == 0)
811 } else if (current
->sublevel
== tree
->selected_ptr
->sublevel
){
812 for (j
= strlen (current
->name
) - 1; current
->name
[j
] != PATH_SEP
; j
--);
813 if (strncmp (current
->name
,tree
->selected_ptr
->name
,j
)== 0)
815 } else if (current
->sublevel
== tree
->selected_ptr
->sublevel
+1
816 && strlen (tree
->selected_ptr
->name
) > 1){
817 if (strncmp (current
->name
, tree
->selected_ptr
->name
,
818 strlen (tree
->selected_ptr
->name
)) == 0)
821 current
= current
->next
;
824 current
= current
->next
;
826 tree_show_mini_info (tree
, tree_lines
, tree_cols
);
829 static void check_focus (WTree
*tree
)
831 if (tree
->topdiff
< 3)
833 else if (tree
->topdiff
>= tlines (tree
) - 3)
834 tree
->topdiff
= tlines (tree
) - 3 - 1;
837 void tree_move_backward (WTree
*tree
, int i
)
842 if (tree_navigation_flag
){
843 current
= tree
->selected_ptr
;
844 while (j
< i
&& current
->prev
845 && current
->prev
->sublevel
>= tree
->selected_ptr
->sublevel
){
846 current
= current
->prev
;
847 if (current
->sublevel
== tree
->selected_ptr
->sublevel
){
848 tree
->selected_ptr
= current
;
854 tree
->selected_ptr
= back_ptr (tree
->selected_ptr
, &i
);
859 void tree_move_forward (WTree
*tree
, int i
)
864 if (tree_navigation_flag
){
865 current
= tree
->selected_ptr
;
866 while (j
< i
&& current
->next
867 && current
->next
->sublevel
>= tree
->selected_ptr
->sublevel
){
868 current
= current
->next
;
869 if (current
->sublevel
== tree
->selected_ptr
->sublevel
){
870 tree
->selected_ptr
= current
;
876 tree
->selected_ptr
= forw_ptr (tree
->selected_ptr
, &i
);
881 void tree_move_to_child (WTree
*tree
)
885 /* Do we have a starting point? */
886 if (!tree
->selected_ptr
)
888 /* Take the next entry */
889 current
= tree
->selected_ptr
->next
;
890 /* Is it the child of the selected entry */
891 if (current
&& current
->sublevel
> tree
->selected_ptr
->sublevel
){
892 /* Yes -> select this entry */
893 tree
->selected_ptr
= current
;
897 /* No -> rescan and try again */
898 tree_rescan_cmd (tree
);
899 current
= tree
->selected_ptr
->next
;
900 if (current
&& current
->sublevel
> tree
->selected_ptr
->sublevel
){
901 tree
->selected_ptr
= current
;
908 int tree_move_to_parent (WTree
*tree
)
913 if (!tree
->selected_ptr
)
915 old
= tree
->selected_ptr
;
916 current
= tree
->selected_ptr
->prev
;
917 while (current
&& current
->sublevel
>= tree
->selected_ptr
->sublevel
){
918 current
= current
->prev
;
922 current
= tree
->tree_first
;
923 tree
->selected_ptr
= current
;
925 return tree
->selected_ptr
!= old
;
928 void tree_move_to_top (WTree
*tree
)
930 tree
->selected_ptr
= tree
->tree_first
;
934 void tree_move_to_bottom (WTree
*tree
)
936 tree
->selected_ptr
= tree
->tree_last
;
937 tree
->topdiff
= tlines (tree
) - 3 - 1;
940 void tree_chdir (WTree
*tree
, char *dir
)
944 current
= whereis (tree
, dir
);
946 tree
->selected_ptr
= current
;
951 /* Handle mouse click */
952 void tree_event (WTree
*tree
, int y
)
954 if (tree
->tree_shown
[y
]){
955 tree
->selected_ptr
= tree
->tree_shown
[y
];
961 static void chdir_sel (WTree
*tree
);
963 static void maybe_chdir (WTree
*tree
)
965 if (!(xtree_mode
&& tree
->is_panel
))
972 static int event_callback (Gpm_Event
*event
, WTree
*tree
)
974 if (!(event
->type
& GPM_UP
))
986 tree_move_backward (tree
, tlines (tree
) - 1);
989 else if (event
->y
>= tlines (tree
)){
990 tree_move_forward (tree
, tlines (tree
) - 1);
993 tree_event (tree
, event
->y
);
994 if ((event
->type
& (GPM_UP
|GPM_DOUBLE
)) == (GPM_UP
|GPM_DOUBLE
)){
1001 /* Search tree for text */
1002 int search_tree (WTree
*tree
, char *text
)
1004 tree_entry
*current
;
1009 len
= strlen (text
);
1010 current
= tree
->selected_ptr
;
1012 while (!wrapped
|| current
!= tree
->selected_ptr
){
1013 if (strncmp (current
->subname
, text
, len
) == 0){
1014 tree
->selected_ptr
= current
;
1018 current
= current
->next
;
1020 current
= tree
->tree_first
;
1029 static void tree_do_search (WTree
*tree
, int key
)
1033 l
= strlen (tree
->search_buffer
);
1034 if (l
&& (key
== 8 || key
== 0177 || key
== KEY_BACKSPACE
))
1035 tree
->search_buffer
[--l
] = 0;
1037 if (key
&& l
< sizeof (tree
->search_buffer
)){
1038 tree
->search_buffer
[l
] = key
;
1039 tree
->search_buffer
[l
+1] = 0;
1044 if (!search_tree (tree
, tree
->search_buffer
))
1045 tree
->search_buffer
[--l
] = 0;
1051 void tree_rescan_cmd (WTree
*tree
)
1056 char old_dir
[MC_MAXPATHLEN
];
1058 if (!tree
->selected_ptr
|| !mc_get_current_wd (old_dir
, MC_MAXPATHLEN
) ||
1059 mc_chdir (tree
->selected_ptr
->name
))
1062 start_tree_check (tree
);
1063 dirp
= opendir (".");
1065 for (dp
= readdir (dirp
); dp
; dp
= readdir (dirp
)){
1066 lstat (dp
->d_name
, &buf
);
1067 if (S_ISDIR (buf
.st_mode
))
1068 do_tree_check (tree
, dp
->d_name
);
1072 end_tree_check (tree
);
1076 int tree_forget_cmd (WTree
*tree
)
1078 if (tree
->selected_ptr
)
1079 tree_remove_entry (tree
, tree
->selected_ptr
->name
);
1084 static int toggle_nav_mode (void)
1086 tree_navigation_flag
= 1 - tree_navigation_flag
;
1092 void tree_copy (WTree
*tree
, char *default_dest
)
1096 if (!tree
->selected_ptr
)
1098 sprintf (cmd_buf
, _("Copy \"%s\" directory to:"),
1099 name_trunc (tree
->selected_ptr
->name
, 50));
1100 dest
= input_expand_dialog (_(" Copy "), cmd_buf
, default_dest
);
1101 if (!dest
|| !*dest
){
1104 create_op_win (OP_COPY
, 0);
1105 file_mask_defaults ();
1106 copy_dir_dir (tree
->selected_ptr
->name
, dest
, 1, 0, 0, 0);
1111 static void tree_help_cmd (void)
1113 char *hlpfile
= concat_dir_and_file (mc_home
, "mc.hlp");
1114 interactive_display (hlpfile
, "[Directory Tree]");
1118 static int tree_copy_cmd (WTree
*tree
)
1120 tree_copy (tree
, "");
1124 void tree_move (WTree
*tree
, char *default_dest
)
1129 if (!tree
->selected_ptr
)
1131 sprintf (cmd_buf
, _("Move \"%s\" directory to:"),
1132 name_trunc (tree
->selected_ptr
->name
, 50));
1133 dest
= input_expand_dialog (_(" Move "), cmd_buf
, default_dest
);
1134 if (!dest
|| !*dest
){
1137 if (stat (dest
, &buf
)){
1138 message (1, _(" Error "), _(" Can't stat the destination \n %s "),
1139 unix_error_string (errno
));
1143 if (!S_ISDIR (buf
.st_mode
)){
1144 message (1, _(" Error "), _(" The destination isn't a directory "));
1148 create_op_win (OP_MOVE
, 0);
1149 file_mask_defaults ();
1150 move_dir_dir (tree
->selected_ptr
->name
, dest
);
1155 static int tree_move_cmd (WTree
*tree
)
1157 tree_move (tree
, "");
1161 static int tree_mkdir_cmd (WTree
*tree
)
1163 char old_dir
[MC_MAXPATHLEN
];
1165 if (!tree
->selected_ptr
)
1167 if (!mc_get_current_wd (old_dir
, MC_MAXPATHLEN
))
1169 if (chdir (tree
->selected_ptr
->name
))
1174 tree_rescan_cmd (tree
);
1179 static void tree_rmdir_cmd (WTree
*tree
)
1181 char old_dir
[MC_MAXPATHLEN
];
1183 if (tree
->selected_ptr
){
1184 if (!mc_get_current_wd (old_dir
, MC_MAXPATHLEN
))
1186 if (mc_chdir (PATH_SEP_STR
))
1188 if (confirm_delete
){
1192 cmd_buf
= xmalloc (strlen (tree
->selected_ptr
->name
) + 20,
1194 sprintf (cmd_buf
, _(" Delete %s? "), tree
->selected_ptr
->name
);
1195 result
= query_dialog (_(" Delete "), cmd_buf
, 3, 2, _("&Yes"), _("&No"));
1201 create_op_win (OP_DELETE
, 0);
1202 if (erase_dir (tree
->selected_ptr
->name
) == FILE_CONT
)
1203 tree_forget_cmd (tree
);
1212 static int tree_quit_cmd (void)
1222 static void set_navig_label (Dlg_head
*h
);
1223 static void tree_toggle_navig (Dlg_head
*h
)
1225 tree_navigation_flag
= 1 - tree_navigation_flag
;
1226 set_navig_label (h
);
1229 void set_navig_label (Dlg_head
*h
)
1231 define_label_data (h
, (Widget
*)tree
,
1232 4, tree_navigation_flag
? _("Static") : _("Dynamc"),
1233 (void (*)(void *))tree_toggle_navig
, h
);
1236 static void move_down (WTree
*tree
)
1238 tree_move_forward (tree
, 1);
1243 static void move_up (WTree
*tree
)
1245 tree_move_backward (tree
, 1);
1250 static void move_home (WTree
*tree
)
1252 tree_move_to_top (tree
);
1257 static void move_end (WTree
*tree
)
1259 tree_move_to_bottom (tree
);
1264 static int move_left (WTree
*tree
)
1268 if (tree_navigation_flag
){
1269 v
= tree_move_to_parent (tree
);
1277 static int move_right (WTree
*tree
)
1279 if (tree_navigation_flag
){
1280 tree_move_to_child (tree
);
1288 static void move_prevp (WTree
*tree
)
1290 tree_move_backward (tree
, tlines (tree
) - 1);
1295 static void move_nextp (WTree
*tree
)
1297 tree_move_forward (tree
, tlines (tree
) - 1);
1302 static void chdir_sel (WTree
*tree
)
1304 if (!tree
->is_panel
){
1309 if (do_cd (tree
->selected_ptr
->name
, cd_exact
)){
1310 paint_panel (cpanel
);
1311 select_item (cpanel
);
1313 message (1, MSG_ERROR
, _(" Can't chdir to \"%s\" \n %s "),
1314 tree
->selected_ptr
->name
, unix_error_string (errno
));
1321 static void start_search (WTree
*tree
)
1325 if (tree
->searching
){
1327 if (tree
->selected_ptr
== tree
->tree_last
)
1328 tree_move_to_top(tree
);
1330 /* set navigation mode temporarily to 'Static' because in
1331 * dynamic navigation mode tree_move_forward will not move
1332 * to a lower sublevel if necessary (sequent searches must
1333 * start with the directory followed the last found directory)
1335 i
= tree_navigation_flag
;
1336 tree_navigation_flag
= 0;
1337 tree_move_forward (tree
, 1);
1338 tree_navigation_flag
= i
;
1340 tree_do_search (tree
, 0);
1343 tree
->searching
= 1;
1344 tree
->search_buffer
[0] = 0;
1348 static key_map tree_keymap
[] = {
1349 { XCTRL('n'), move_down
},
1350 { XCTRL('p'), move_up
},
1351 { KEY_DOWN
, move_down
},
1352 { KEY_UP
, move_up
},
1353 { '\n', chdir_sel
},
1354 { KEY_ENTER
, chdir_sel
},
1355 { KEY_HOME
, move_home
},
1356 { KEY_C1
, move_end
},
1357 { KEY_END
, move_end
},
1358 { KEY_A1
, move_home
},
1359 { KEY_NPAGE
, move_nextp
},
1360 { KEY_PPAGE
, move_prevp
},
1361 { XCTRL('v'), move_nextp
},
1362 { ALT('v'), move_prevp
},
1363 { XCTRL('p'), move_up
},
1364 { XCTRL('p'), move_down
},
1365 { XCTRL('s'), start_search
},
1366 { ALT('s'), start_search
},
1367 { XCTRL('r'), tree_rescan_cmd
},
1368 { KEY_DC
, tree_rmdir_cmd
},
1372 static inline int tree_key (WTree
*tree
, int key
)
1376 for (i
= 0; tree_keymap
[i
].key_code
; i
++){
1377 if (key
== tree_keymap
[i
].key_code
){
1378 if (tree_keymap
[i
].fn
!= start_search
)
1379 tree
->searching
= 0;
1380 (*tree_keymap
[i
].fn
)(tree
);
1386 /* We do not want to use them if we do not need to */
1387 /* Input line may want to take the motion key event */
1388 if (key
== KEY_LEFT
)
1389 return move_left (tree
);
1391 if (key
== KEY_RIGHT
)
1392 return move_right (tree
);
1394 if (is_abort_char (key
)) {
1395 if (tree
->is_panel
) {
1396 tree
->searching
= 0;
1398 return 1; /* eat abort char */
1400 return 0; /* modal tree dialog: let upper layer see the
1401 abort character and close the dialog */
1404 /* Do not eat characters not meant for the tree below ' ' (e.g. C-l). */
1405 if ((key
>= ' '&& key
<= 255) || key
== 8 || key
== KEY_BACKSPACE
) {
1406 if (tree
->searching
){
1407 tree_do_search (tree
, key
);
1412 if (!command_prompt
) {
1413 start_search (tree
);
1414 tree_do_search (tree
, key
);
1417 return tree
->is_panel
;
1423 static void tree_frame (Dlg_head
*h
, WTree
*tree
)
1425 attrset (NORMAL_COLOR
);
1426 widget_erase ((Widget
*) tree
);
1428 draw_double_box (h
, tree
->widget
.y
, tree
->widget
.x
, tree
->widget
.lines
,
1431 if (show_mini_info
&& tree
->is_panel
){
1432 widget_move (tree
, tlines (tree
) + 1, 1);
1433 hline (ACS_HLINE
, tree
->widget
.cols
- 2);
1438 static int tree_callback (Dlg_head
*h
, WTree
*tree
, int msg
, int par
)
1442 tree_frame (h
, tree
);
1447 return tree_key (tree
, par
);
1451 define_label (h
, (Widget
*)tree
, 1, _("Help"), (voidfn
) tree_help_cmd
);
1452 define_label_data (h
, (Widget
*)tree
,
1453 2, _("Rescan"), (buttonbarfn
)tree_rescan_cmd
, tree
);
1454 define_label_data (h
, (Widget
*)tree
,
1455 3, _("Forget"), (buttonbarfn
)tree_forget_cmd
, tree
);
1456 define_label_data (h
, (Widget
*)tree
,
1457 5, _("Copy"), (buttonbarfn
) tree_copy_cmd
, tree
);
1458 define_label_data (h
, (Widget
*)tree
,
1459 6, _("RenMov"), (buttonbarfn
) tree_move_cmd
, tree
);
1461 /* FIXME: mkdir is currently defunct */
1462 define_label_data (h
, (Widget
*)tree
,
1463 7, _("Mkdir"), (buttonbarfn
) tree_mkdir_cmd
, tree
);
1465 define_label (h
, (Widget
*)tree
, 7, "", 0);
1467 define_label_data (h
, (Widget
*)tree
,
1468 8, _("Rmdir"), (buttonbarfn
) tree_rmdir_cmd
, tree
);
1469 set_navig_label (h
);
1470 redraw_labels (h
, (Widget
*)tree
);
1473 /* FIXME: Should find a better way of only displaying the
1474 currently selected item */
1478 /* FIXME: Should find a better way of changing the color of the
1480 case WIDGET_UNFOCUS
:
1485 return default_proc (h
, msg
, par
);
1488 WTree
*tree_new (int is_panel
, int y
, int x
, int lines
, int cols
)
1490 WTree
*tree
= xmalloc (sizeof (WTree
), "tree_new");
1492 init_widget (&tree
->widget
, y
, x
, lines
, cols
, tcallback
,
1493 (destroy_fn
) tree_destroy
, (mouse_h
) event_callback
, NULL
);
1494 tree
->is_panel
= is_panel
;
1495 tree
->selected_ptr
= 0;
1496 tree
->tree_shown
= 0;
1497 tree
->search_buffer
[0] = 0;
1498 tree
->tree_first
= tree
->tree_last
= 0;
1499 tree
->topdiff
= tree
->widget
.lines
/ 2;
1500 tree
->searching
= 0;
1504 /* We do not want to keep the cursor */
1505 widget_want_cursor (tree
->widget
, 0);
1510 static char *get_absolute_name (char *file
)
1512 char dir
[MC_MAXPATHLEN
];
1514 if (file
[0] == PATH_SEP
)
1515 return strdup (file
);
1516 mc_get_current_wd (dir
, MC_MAXPATHLEN
);
1517 return get_full_name (dir
, file
);
1520 static int my_mkdir_rec (char *s
, mode_t mode
)
1525 if (!mc_mkdir (s
, mode
))
1528 /* FIXME: should check instead if s is at the root of that filesystem */
1529 if (!vfs_file_is_local (s
))
1531 if (!strcmp (vfs_path(s
), PATH_SEP_STR
))
1533 p
= concat_dir_and_file (s
, "..");
1536 if (!(result
= my_mkdir_rec (q
, mode
))) {
1537 result
= mc_mkdir (s
, mode
);
1543 int my_mkdir (char *s
, mode_t mode
)
1550 result
= mc_mkdir (s
, mode
);
1552 /* .ado: it will be disabled in OS/2 and NT */
1553 /* otherwise crash if directory already exists. */
1557 char *p
= vfs_canon (s
);
1559 result
= my_mkdir_rec (p
, mode
);
1563 s
= get_absolute_name (s
);
1565 /* FIXME: Should receive a Wtree! */
1567 tree_add_entry (tree
, s
);
1574 int my_rmdir (char *s
)
1581 /* FIXME: Should receive a Wtree! */
1582 result
= mc_rmdir (s
);
1584 s
= get_absolute_name (s
);
1586 tree_remove_entry (tree
, s
);