added PCI interrupt link device
[reactos.git] / rosapps / mc / src / tree.c
1 /* Directory tree browser for the Midnight Commander
2 Copyright (C) 1994, 1995, 1996, 1997 The Free Software Foundation
3
4 Written: 1994, 1996 Janne Kukonlehto
5 1997 Norbert Warmuth
6 1996 Miguel de Icaza
7
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.
12
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.
17
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.
21
22 This module has been converted to be a widget.
23
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.
27
28 */
29 #include <config.h>
30 #include <sys/types.h>
31 #ifdef HAVE_UNISTD_H
32 #include <unistd.h>
33 #endif
34 #include <fcntl.h>
35 #include <sys/param.h>
36 #include <sys/stat.h>
37 #include <errno.h>
38 #include <dirent.h>
39 #include <stdio.h>
40 #include <stdlib.h> /* For free() and atoi() */
41 #include <string.h>
42 #include "tty.h"
43 #include "mad.h"
44 #include "global.h"
45 #include "util.h"
46 #include "color.h"
47 #include "dialog.h"
48 #include "dir.h"
49 #include "dlg.h"
50 #include "widget.h"
51 #include "panel.h"
52 #include "mouse.h"
53 #include "main.h"
54 #include "file.h" /* For copy_dir_dir(), move_dir_dir(), erase_dir() */
55 #include "help.h"
56 #include "key.h" /* For mi_getch() */
57 #include "tree.h"
58 #include "cmd.h"
59 #include "../vfs/vfs.h"
60 #ifdef OS2_NT
61 # include <io.h>
62 #endif
63
64 extern int command_prompt;
65
66 #define TREE_NORMALC HOT_FOCUSC
67
68 /* Specifies the display mode: 1d or 2d */
69 int tree_navigation_flag;
70
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
73 * selected directory.
74 */
75 int xtree_mode = 0;
76
77 /* Forwards */
78 static int tree_callback (Dlg_head *h, WTree *tree, int msg, int par);
79 #define tcallback (callback_fn) tree_callback
80
81 /* "$Id$" */
82
83 /* Returns number of common characters */
84 static inline int str_common (char *s1, char *s2)
85 {
86 int result = 0;
87
88 while (*s1++ == *s2++)
89 result++;
90 return result;
91 }
92
93 static tree_entry *back_ptr (tree_entry *ptr, int *count)
94 {
95 int i = 0;
96
97 while (ptr && ptr->prev && i < *count){
98 ptr = ptr->prev;
99 i ++;
100 }
101 *count = i;
102 return ptr;
103 }
104
105 static tree_entry *forw_ptr (tree_entry *ptr, int *count)
106 {
107 int i = 0;
108
109 while (ptr && ptr->next && i < *count){
110 ptr = ptr->next;
111 i ++;
112 }
113 *count = i;
114 return ptr;
115 }
116
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
119 order is like this:
120 /
121 /bin
122 /etc
123 /etc/X11
124 /etc/rc.d
125 /etc.old/X11
126 /etc.old/rc.d
127 /usr
128
129 i.e. the required collating sequence when comparing two directory names is
130 '\0' < PATH_SEP < all-other-characters-in-encoding-order
131
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
136 than p2.
137 */
138 int
139 pathcmp (const char *p1, const char *p2)
140 {
141 for ( ;*p1 == *p2; p1++, p2++)
142 if (*p1 == '\0' )
143 return 0;
144
145 if (*p1 == '\0')
146 return -1;
147 if (*p2 == '\0')
148 return 1;
149 if (*p1 == PATH_SEP)
150 return -1;
151 if (*p2 == PATH_SEP)
152 return 1;
153 return (*p1 - *p2);
154 }
155
156 /* Searches for specified directory */
157 static tree_entry *whereis (WTree *tree, char *name)
158 {
159 tree_entry *current = tree->tree_first;
160 int flag = -1;
161
162 #if 0
163 if (tree->tree_last){
164 flag = strcmp (tree->tree_last->name, name);
165 if (flag <= 0){
166 current = tree->tree_last;
167 } else if (tree->selected_ptr){
168 flag = strcmp (tree->selected_ptr->name, name);
169 if (flag <= 0){
170 current = tree->selected_ptr;
171 }
172 }
173 }
174 #endif
175 while (current && (flag = pathcmp (current->name, name)) < 0)
176 current = current->next;
177
178 if (flag == 0)
179 return current;
180 else
181 return NULL;
182 }
183
184 /* Add a directory to the list of directories */
185 tree_entry *tree_add_entry (WTree *tree, char *name)
186 {
187 int flag = -1;
188 tree_entry *current = tree->tree_first;
189 tree_entry *old = NULL;
190 tree_entry *new;
191 int i, len;
192 int submask = 0;
193
194 if (!tree)
195 return 0;
196
197 if (tree->tree_last && tree->tree_last->next)
198 abort ();
199 #if 0
200 if (tree->tree_last){
201 flag = strcmp (tree->tree_last->name, name);
202 if (flag <= 0){
203 current = tree->tree_last;
204 old = current->prev;
205 } else if (tree->selected_ptr){
206 flag = strcmp (tree->selected_ptr->name, name);
207 if (flag <= 0){
208 current = tree->selected_ptr;
209 old = current->prev;
210 }
211 }
212 }
213 #endif
214 /* Search for the correct place */
215 while (current && (flag = pathcmp (current->name, name)) < 0){
216 old = current;
217 current = current->next;
218 }
219
220 if (flag == 0)
221 return current; /* Already in the list */
222
223 /* Not in the list -> add it */
224 new = xmalloc (sizeof (tree_entry), "tree, tree_entry");
225 if (!current){
226 /* Append to the end of the list */
227 if (!tree->tree_first){
228 /* Empty list */
229 tree->tree_first = new;
230 new->prev = NULL;
231 } else {
232 old->next = new;
233 new->prev = old;
234 }
235 new->next = NULL;
236 tree->tree_last = new;
237 } else {
238 /* Insert in to the middle of the list */
239 new->prev = old;
240 if (old){
241 /* Yes, in the middle */
242 new->next = old->next;
243 old->next = new;
244 } else {
245 /* Nope, in the beginning of the list */
246 new->next = tree->tree_first;
247 tree->tree_first = new;
248 }
249 new->next->prev = new;
250 }
251 /* tree_count++; */
252
253 /* Calculate attributes */
254 new->name = strdup (name);
255 len = strlen (new->name);
256 new->sublevel = 0;
257 for (i = 0; i < len; i++)
258 if (new->name [i] == PATH_SEP){
259 new->sublevel++;
260 new->subname = new->name + i + 1;
261 }
262 if (new->next)
263 submask = new->next->submask;
264 else
265 submask = 0;
266 submask |= 1 << new->sublevel;
267 submask &= (2 << new->sublevel) - 1;
268 new->submask = submask;
269 new->mark = 0;
270
271 /* Correct the submasks of the previous entries */
272 current = new->prev;
273 while (current && current->sublevel > new->sublevel){
274 current->submask |= 1 << new->sublevel;
275 current = current->prev;
276 }
277
278 /* The entry has now been added */
279
280 if (new->sublevel > 1){
281 /* Let's check if the parent directory is in the tree */
282 char *parent = strdup (new->name);
283 int i;
284
285 for (i = strlen (parent) - 1; i > 1; i--){
286 if (parent [i] == PATH_SEP){
287 parent [i] = 0;
288 tree_add_entry (tree, parent);
289 break;
290 }
291 }
292 free (parent);
293 }
294
295 return new;
296 }
297
298 #if 0
299 /* Append a directory to the list of directories */
300 static tree_entry *tree_append_entry (WTree *tree, char *name)
301 {
302 tree_entry *current, *new;
303 int i, len;
304 int submask = 0;
305
306 /* We assume the directory is not yet in the list */
307
308 new = xmalloc (sizeof (tree_entry), "tree, tree_entry");
309 if (!tree->tree_first){
310 /* Empty list */
311 tree->tree_first = new;
312 new->prev = NULL;
313 } else {
314 tree->tree_last->next = new;
315 new->prev = tree->tree_last;
316 }
317 new->next = NULL;
318 tree->tree_last = new;
319
320 /* Calculate attributes */
321 new->name = strdup (name);
322 len = strlen (new->name);
323 new->sublevel = 0;
324 for (i = 0; i < len; i++)
325 if (new->name [i] == PATH_SEP){
326 new->sublevel++;
327 new->subname = new->name + i + 1;
328 }
329 submask = 1 << new->sublevel;
330 submask &= (2 << new->sublevel) - 1;
331 new->submask = submask;
332 new->mark = 0;
333
334 /* Correct the submasks of the previous entries */
335 current = new->prev;
336 while (current && current->sublevel > new->sublevel){
337 current->submask |= 1 << new->sublevel;
338 current = current->prev;
339 }
340
341 /* The entry has now been appended */
342 return new;
343 }
344 #endif
345
346 static void remove_entry (WTree *tree, tree_entry *entry)
347 {
348 tree_entry *current = entry->prev;
349 long submask = 0;
350
351 if (tree->selected_ptr == entry){
352 if (tree->selected_ptr->next)
353 tree->selected_ptr = tree->selected_ptr->next;
354 else
355 tree->selected_ptr = tree->selected_ptr->prev;
356 }
357
358 /* Correct the submasks of the previous entries */
359 if (entry->next)
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;
366 }
367
368 /* Unlink the entry from the list */
369 if (entry->prev)
370 entry->prev->next = entry->next;
371 else
372 tree->tree_first = entry->next;
373 if (entry->next)
374 entry->next->prev = entry->prev;
375 else
376 tree->tree_last = entry->prev;
377 /* tree_count--; */
378
379 /* Free the memory used by the entry */
380 free (entry->name);
381 free (entry);
382 }
383
384 void tree_remove_entry (WTree *tree, char *name)
385 {
386 tree_entry *current, *base, *old;
387 int len, base_sublevel;
388
389 /* Miguel Ugly hack */
390 if (name [0] == PATH_SEP && name [1] == 0)
391 return;
392 /* Miguel Ugly hack end */
393
394 base = whereis (tree, name);
395 if (!base)
396 return; /* Doesn't exist */
397 if (tree->check_name [0] == PATH_SEP && tree->check_name [1] == 0)
398 base_sublevel = base->sublevel;
399 else
400 base_sublevel = base->sublevel + 1;
401 len = strlen (base->name);
402 current = base->next;
403 while (current
404 && strncmp (current->name, base->name, len) == 0
405 && (current->name[len] == '\0' || current->name[len] == PATH_SEP)){
406 old = current;
407 current = current->next;
408 remove_entry (tree, old);
409 }
410 remove_entry (tree, base);
411 }
412
413 void tree_destroy (WTree *tree)
414 {
415 tree_entry *current, *old;
416
417 save_tree (tree);
418 current = tree->tree_first;
419 while (current){
420 old = current;
421 current = current->next;
422 free (old->name);
423 free (old);
424 }
425 if (tree->tree_shown){
426 free (tree->tree_shown);
427 tree->tree_shown = 0;
428 }
429 tree->selected_ptr = tree->tree_first = tree->tree_last = NULL;
430 }
431
432 /* Mark the subdirectories of the current directory for delete */
433 void start_tree_check (WTree *tree)
434 {
435 tree_entry *current;
436 int len;
437
438 if (!tree)
439 tree = (WTree *) find_widget_type (current_dlg, tcallback);
440 if (!tree)
441 return;
442
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);
447 if (!current){
448 /* Cwd doesn't exist -> add it */
449 current = tree_add_entry (tree, tree->check_name);
450 return;
451 }
452
453 /* Mark old subdirectories for delete */
454 tree->check_start = current->next;
455 len = strlen (tree->check_name);
456
457 current = tree->check_start;
458 while (current
459 && strncmp (current->name, tree->check_name, len) == 0
460 && (current->name[len] == '\0' || current->name[len] == PATH_SEP || len == 1)){
461 current->mark = 1;
462 current = current->next;
463 }
464 }
465
466 /* This subdirectory exists -> clear deletion mark */
467 void do_tree_check (WTree *tree, const char *subname)
468 {
469 char *name;
470 tree_entry *current, *base;
471 int flag = 1, len;
472
473 /* Calculate the full name of the subdirectory */
474 if (subname [0] == '.' &&
475 (subname [1] == 0 || (subname [1] == '.' && subname [2] == 0)))
476 return;
477 if (tree->check_name [0] == PATH_SEP && tree->check_name [1] == 0)
478 name = copy_strings (PATH_SEP_STR, subname, 0);
479 else
480 name = concat_dir_and_file (tree->check_name, subname);
481
482 /* Search for the subdirectory */
483 current = tree->check_start;
484 while (current && (flag = pathcmp (current->name, name)) < 0)
485 current = current->next;
486
487 if (flag != 0)
488 /* Doesn't exist -> add it */
489 current = tree_add_entry (tree, name);
490 free (name);
491
492 /* Clear the deletion mark from the subdirectory and its children */
493 base = current;
494 if (base){
495 len = strlen (base->name);
496 base->mark = 0;
497 current = base->next;
498 while (current
499 && strncmp (current->name, base->name, len) == 0
500 && (current->name[len] == '\0' || current->name[len] == PATH_SEP || len == 1)){
501 current->mark = 0;
502 current = current->next;
503 }
504 }
505 }
506
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
509 */
510 void tree_check (const char *subname)
511 {
512 WTree *tree;
513
514 tree = (WTree *) find_widget_type (current_dlg, tcallback);
515 if (!tree)
516 return;
517 do_tree_check (tree, subname);
518 }
519
520
521 /* Delete subdirectories which still have the deletion mark */
522 void end_tree_check (WTree *tree)
523 {
524 tree_entry *current, *old;
525 int len;
526
527 if (!tree)
528 tree = (WTree *) find_widget_type (current_dlg, tcallback);
529 if (!tree)
530 return;
531
532 /* Check delete marks and delete if found */
533 len = strlen (tree->check_name);
534
535 current = tree->check_start;
536 while (current
537 && strncmp (current->name, tree->check_name, len) == 0
538 && (current->name[len] == '\0' || current->name[len] == PATH_SEP || len == 1)){
539 old = current;
540 current = current->next;
541 if (old->mark)
542 remove_entry (tree, old);
543 }
544 }
545
546 /* Loads the .mc.tree file */
547 void load_tree (WTree *tree)
548 {
549 char *filename;
550 FILE *file;
551 char name [MC_MAXPATHLEN], oldname[MC_MAXPATHLEN];
552 char *different;
553 int len, common;
554
555 filename = concat_dir_and_file (home_dir, MC_TREE);
556 file = fopen (filename, "r");
557 free (filename);
558 if (!file){
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");
562 free (filename);
563 }
564
565 if (file){
566 /* File open -> read contents */
567 oldname [0] = 0;
568 while (fgets (name, MC_MAXPATHLEN, file)){
569 len = strlen (name);
570 if (name [len - 1] == '\n'){
571 name [--len] = 0;
572 }
573 #ifdef OS2_NT
574 /* .ado: Drives for NT and OS/2 */
575 if ((len > 2) &&
576 isalpha(name[0]) &&
577 (name[1] == ':') &&
578 (name[2] == '\\')) {
579 tree_add_entry (tree, name);
580 strcpy (oldname, name);
581 } else
582 #endif
583 /* UNIX Version */
584 if (name [0] != PATH_SEP){
585 /* Clear-text decompression */
586 char *s = strtok (name, " ");
587
588 if (s){
589 common = atoi (s);
590 different = strtok (NULL, "");
591 if (different){
592 strcpy (oldname + common, different);
593 tree_add_entry (tree, oldname);
594 }
595 }
596 } else {
597 tree_add_entry (tree, name);
598 strcpy (oldname, name);
599 }
600 }
601 fclose (file);
602 }
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);
611 }
612 }
613
614 /* Save the .mc.tree file */
615 void save_tree (WTree *tree)
616 {
617 tree_entry *current;
618 char *filename;
619 FILE *file;
620 int i, common;
621
622 filename = concat_dir_and_file (home_dir, MC_TREE);
623 file = fopen (filename, "w");
624 free (filename);
625 if (!file){
626 fprintf (stderr, _("Can't open the %s file for writing:\n%s\n"), MC_TREE,
627 unix_error_string (errno));
628 return;
629 }
630
631 current = tree->tree_first;
632 while (current){
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);
636 else
637 i = fprintf (file, "%s\n", current->name);
638 if (i == EOF){
639 fprintf (stderr, _("Can't write to the %s file:\n%s\n"), MC_TREE,
640 unix_error_string (errno));
641 break;
642 }
643 current = current->next;
644 }
645 fclose (file);
646 }
647
648 static void tree_show_mini_info (WTree *tree, int tree_lines, int tree_cols)
649 {
650 Dlg_head *h = tree->widget.parent;
651 int line;
652
653 /* Show mini info */
654 if (tree->is_panel){
655 if (!show_mini_info)
656 return;
657 line = tree_lines+2;
658 } else
659 line = tree_lines+1;
660
661 widget_move (&tree->widget, line, 1);
662 hline (' ', tree_cols);
663 widget_move (&tree->widget, line, 1);
664
665 if (tree->searching){
666 /* Show search string */
667 attrset (TREE_NORMALC);
668 attrset (FOCUSC);
669 addch (PATH_SEP);
670
671 addstr (name_trunc (tree->search_buffer, tree_cols-2));
672 addch (' ');
673 attrset (FOCUSC);
674 } else {
675 /* Show full name of selected directory */
676 addstr (name_trunc (tree->selected_ptr->name, tree_cols));
677 }
678 }
679
680 void show_tree (WTree *tree)
681 {
682 Dlg_head *h = tree->widget.parent;
683 tree_entry *current;
684 int i, j, topsublevel;
685 int x, y;
686 int tree_lines, tree_cols;
687
688 /* Initialize */
689 x = y = 0;
690 tree_lines = tlines (tree);
691 tree_cols = tree->widget.cols;
692
693 attrset (TREE_NORMALC);
694 widget_move ((Widget*)tree, y, x);
695 if (tree->is_panel){
696 tree_cols -= 2;
697 x = y = 1;
698 }
699
700 if (tree->tree_shown)
701 free (tree->tree_shown);
702 tree->tree_shown = (tree_entry**)xmalloc (sizeof (tree_entry*)*tree_lines,
703 "tree, show_tree");
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;
708 else
709 topsublevel = 0;
710 if (!tree->selected_ptr){
711 tree->selected_ptr = tree->tree_first;
712 tree->topdiff = 0;
713 }
714 current = tree->selected_ptr;
715
716 /* Calculate the directory which is to be shown on the topmost line */
717 if (tree_navigation_flag){
718 i = 0;
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)
724 i++;
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)
728 i++;
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)
733 i++;
734 }
735 }
736 tree->topdiff = i;
737 } else
738 current = back_ptr (current, &tree->topdiff);
739
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);
744
745 hline (' ', tree_cols);
746 widget_move (&tree->widget, y+i, x);
747
748 if (!current)
749 continue;
750
751 tree->tree_shown [i] = current;
752 if (current->sublevel == topsublevel){
753
754 /* Top level directory */
755 if (tree->active && current == tree->selected_ptr)
756 if (!use_colors && !tree->is_panel)
757 attrset (MARKED_COLOR);
758 else
759 attrset (SELECTED_COLOR);
760
761 /* Show full name */
762 addstr (name_trunc (current->name, tree_cols - 6));
763 } else{
764 /* Sub level directory */
765
766 acs ();
767 /* Output branch parts */
768 for (j = 0; j < current->sublevel - topsublevel - 1; j++){
769 if (tree_cols - 8 - 3 * j < 9)
770 break;
771 addch (' ');
772 if (current->submask & (1 << (j + topsublevel + 1)))
773 addch (ACS_VLINE);
774 else
775 addch (' ');
776 addch (' ');
777 }
778 addch (' '); j++;
779 if (!current->next || !(current->next->submask & (1 << current->sublevel)))
780 addch (ACS_LLCORNER);
781 else
782 addch (ACS_LTEE);
783 addch (ACS_HLINE);
784 noacs ();
785
786 if (tree->active && current == tree->selected_ptr)
787 /* Selected directory -> change color */
788 if (!use_colors && !tree->is_panel)
789 attrset (MARKED_COLOR);
790 else
791 attrset (SELECTED_COLOR);
792
793 /* Show sub-name */
794 addch (' ');
795 addstr (name_trunc (current->subname,
796 tree_cols - 2 - 4 - 3 * j));
797 }
798 addch (' ');
799
800 /* Return to normal color */
801 attrset (TREE_NORMALC);
802
803 /* Calculate the next value for current */
804 if (tree_navigation_flag){
805 current = current->next;
806 while (current){
807 if (current->sublevel < tree->selected_ptr->sublevel){
808 if (strncmp (current->name, tree->selected_ptr->name,
809 strlen (current->name)) == 0)
810 break;
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)
814 break;
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)
819 break;
820 }
821 current = current->next;
822 }
823 } else
824 current = current->next;
825 }
826 tree_show_mini_info (tree, tree_lines, tree_cols);
827 }
828
829 static void check_focus (WTree *tree)
830 {
831 if (tree->topdiff < 3)
832 tree->topdiff = 3;
833 else if (tree->topdiff >= tlines (tree) - 3)
834 tree->topdiff = tlines (tree) - 3 - 1;
835 }
836
837 void tree_move_backward (WTree *tree, int i)
838 {
839 tree_entry *current;
840 int j = 0;
841
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;
849 j ++;
850 }
851 }
852 i = j;
853 } else
854 tree->selected_ptr = back_ptr (tree->selected_ptr, &i);
855 tree->topdiff -= i;
856 check_focus (tree);
857 }
858
859 void tree_move_forward (WTree *tree, int i)
860 {
861 tree_entry *current;
862 int j = 0;
863
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;
871 j ++;
872 }
873 }
874 i = j;
875 } else
876 tree->selected_ptr = forw_ptr (tree->selected_ptr, &i);
877 tree->topdiff += i;
878 check_focus (tree);
879 }
880
881 void tree_move_to_child (WTree *tree)
882 {
883 tree_entry *current;
884
885 /* Do we have a starting point? */
886 if (!tree->selected_ptr)
887 return;
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;
894 tree->topdiff++;
895 check_focus (tree);
896 } else {
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;
902 tree->topdiff++;
903 check_focus (tree);
904 }
905 }
906 }
907
908 int tree_move_to_parent (WTree *tree)
909 {
910 tree_entry *current;
911 tree_entry *old;
912
913 if (!tree->selected_ptr)
914 return 0;
915 old = tree->selected_ptr;
916 current = tree->selected_ptr->prev;
917 while (current && current->sublevel >= tree->selected_ptr->sublevel){
918 current = current->prev;
919 tree->topdiff--;
920 }
921 if (!current)
922 current = tree->tree_first;
923 tree->selected_ptr = current;
924 check_focus (tree);
925 return tree->selected_ptr != old;
926 }
927
928 void tree_move_to_top (WTree *tree)
929 {
930 tree->selected_ptr = tree->tree_first;
931 tree->topdiff = 0;
932 }
933
934 void tree_move_to_bottom (WTree *tree)
935 {
936 tree->selected_ptr = tree->tree_last;
937 tree->topdiff = tlines (tree) - 3 - 1;
938 }
939
940 void tree_chdir (WTree *tree, char *dir)
941 {
942 tree_entry *current;
943
944 current = whereis (tree, dir);
945 if (current){
946 tree->selected_ptr = current;
947 check_focus (tree);
948 }
949 }
950
951 /* Handle mouse click */
952 void tree_event (WTree *tree, int y)
953 {
954 if (tree->tree_shown [y]){
955 tree->selected_ptr = tree->tree_shown [y];
956 tree->topdiff = y;
957 }
958 show_tree (tree);
959 }
960
961 static void chdir_sel (WTree *tree);
962
963 static void maybe_chdir (WTree *tree)
964 {
965 if (!(xtree_mode && tree->is_panel))
966 return;
967 if (is_idle ())
968 chdir_sel (tree);
969 }
970
971 /* Mouse callback */
972 static int event_callback (Gpm_Event *event, WTree *tree)
973 {
974 if (!(event->type & GPM_UP))
975 return MOU_ENDLOOP;
976
977 if (tree->is_panel)
978 event->y--;
979
980 event->y--;
981
982 if (!tree->active)
983 change_panel ();
984
985 if (event->y < 0){
986 tree_move_backward (tree, tlines (tree) - 1);
987 show_tree (tree);
988 }
989 else if (event->y >= tlines (tree)){
990 tree_move_forward (tree, tlines (tree) - 1);
991 show_tree (tree);
992 } else {
993 tree_event (tree, event->y);
994 if ((event->type & (GPM_UP|GPM_DOUBLE)) == (GPM_UP|GPM_DOUBLE)){
995 chdir_sel (tree);
996 }
997 }
998 return MOU_ENDLOOP;
999 }
1000
1001 /* Search tree for text */
1002 int search_tree (WTree *tree, char *text)
1003 {
1004 tree_entry *current;
1005 int len;
1006 int wrapped = 0;
1007 int found = 0;
1008
1009 len = strlen (text);
1010 current = tree->selected_ptr;
1011 found = 0;
1012 while (!wrapped || current != tree->selected_ptr){
1013 if (strncmp (current->subname, text, len) == 0){
1014 tree->selected_ptr = current;
1015 found = 1;
1016 break;
1017 }
1018 current = current->next;
1019 if (!current){
1020 current = tree->tree_first;
1021 wrapped = 1;
1022 }
1023 tree->topdiff++;
1024 }
1025 check_focus (tree);
1026 return found;
1027 }
1028
1029 static void tree_do_search (WTree *tree, int key)
1030 {
1031 int l;
1032
1033 l = strlen (tree->search_buffer);
1034 if (l && (key == 8 || key == 0177 || key == KEY_BACKSPACE))
1035 tree->search_buffer [--l] = 0;
1036 else {
1037 if (key && l < sizeof (tree->search_buffer)){
1038 tree->search_buffer [l] = key;
1039 tree->search_buffer [l+1] = 0;
1040 l++;
1041 }
1042 }
1043
1044 if (!search_tree (tree, tree->search_buffer))
1045 tree->search_buffer [--l] = 0;
1046
1047 show_tree (tree);
1048 maybe_chdir (tree);
1049 }
1050
1051 void tree_rescan_cmd (WTree *tree)
1052 {
1053 DIR *dirp;
1054 struct dirent *dp;
1055 struct stat buf;
1056 char old_dir [MC_MAXPATHLEN];
1057
1058 if (!tree->selected_ptr || !mc_get_current_wd (old_dir, MC_MAXPATHLEN) ||
1059 mc_chdir (tree->selected_ptr->name))
1060 return;
1061
1062 start_tree_check (tree);
1063 dirp = opendir (".");
1064 if (dirp){
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);
1069 }
1070 closedir (dirp);
1071 }
1072 end_tree_check (tree);
1073 mc_chdir (old_dir);
1074 }
1075
1076 int tree_forget_cmd (WTree *tree)
1077 {
1078 if (tree->selected_ptr)
1079 tree_remove_entry (tree, tree->selected_ptr->name);
1080 return 1;
1081 }
1082
1083 #if 0
1084 static int toggle_nav_mode (void)
1085 {
1086 tree_navigation_flag = 1 - tree_navigation_flag;
1087
1088 return 1;
1089 }
1090 #endif
1091
1092 void tree_copy (WTree *tree, char *default_dest)
1093 {
1094 char *dest;
1095
1096 if (!tree->selected_ptr)
1097 return;
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){
1102 return;
1103 }
1104 create_op_win (OP_COPY, 0);
1105 file_mask_defaults ();
1106 copy_dir_dir (tree->selected_ptr->name, dest, 1, 0, 0, 0);
1107 destroy_op_win ();
1108 free (dest);
1109 }
1110
1111 static void tree_help_cmd (void)
1112 {
1113 char *hlpfile = concat_dir_and_file (mc_home, "mc.hlp");
1114 interactive_display (hlpfile, "[Directory Tree]");
1115 free (hlpfile);
1116 }
1117
1118 static int tree_copy_cmd (WTree *tree)
1119 {
1120 tree_copy (tree, "");
1121 return 1;
1122 }
1123
1124 void tree_move (WTree *tree, char *default_dest)
1125 {
1126 char *dest;
1127 struct stat buf;
1128
1129 if (!tree->selected_ptr)
1130 return;
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){
1135 return;
1136 }
1137 if (stat (dest, &buf)){
1138 message (1, _(" Error "), _(" Can't stat the destination \n %s "),
1139 unix_error_string (errno));
1140 free (dest);
1141 return;
1142 }
1143 if (!S_ISDIR (buf.st_mode)){
1144 message (1, _(" Error "), _(" The destination isn't a directory "));
1145 free (dest);
1146 return;
1147 }
1148 create_op_win (OP_MOVE, 0);
1149 file_mask_defaults ();
1150 move_dir_dir (tree->selected_ptr->name, dest);
1151 destroy_op_win ();
1152 free (dest);
1153 }
1154
1155 static int tree_move_cmd (WTree *tree)
1156 {
1157 tree_move (tree, "");
1158 return 1;
1159 }
1160
1161 static int tree_mkdir_cmd (WTree *tree)
1162 {
1163 char old_dir [MC_MAXPATHLEN];
1164
1165 if (!tree->selected_ptr)
1166 return 0;
1167 if (!mc_get_current_wd (old_dir, MC_MAXPATHLEN))
1168 return 0;
1169 if (chdir (tree->selected_ptr->name))
1170 return 0;
1171 /* FIXME
1172 mkdir_cmd (tree);
1173 */
1174 tree_rescan_cmd (tree);
1175 chdir (old_dir);
1176 return 1;
1177 }
1178
1179 static void tree_rmdir_cmd (WTree *tree)
1180 {
1181 char old_dir [MC_MAXPATHLEN];
1182
1183 if (tree->selected_ptr){
1184 if (!mc_get_current_wd (old_dir, MC_MAXPATHLEN))
1185 return;
1186 if (mc_chdir (PATH_SEP_STR))
1187 return;
1188 if (confirm_delete){
1189 char *cmd_buf;
1190 int result;
1191
1192 cmd_buf = xmalloc (strlen (tree->selected_ptr->name) + 20,
1193 "tree, rmdir_cmd");
1194 sprintf (cmd_buf, _(" Delete %s? "), tree->selected_ptr->name);
1195 result = query_dialog (_(" Delete "), cmd_buf, 3, 2, _("&Yes"), _("&No"));
1196 free (cmd_buf);
1197 if (result != 0){
1198 return;
1199 }
1200 }
1201 create_op_win (OP_DELETE, 0);
1202 if (erase_dir (tree->selected_ptr->name) == FILE_CONT)
1203 tree_forget_cmd (tree);
1204 destroy_op_win ();
1205 mc_chdir (old_dir);
1206 return;
1207 } else
1208 return;
1209 }
1210
1211 #if 0
1212 static int tree_quit_cmd (void)
1213 {
1214 /*
1215 FIXME
1216 return done = 1;
1217 */
1218 return 1;
1219 }
1220 #endif
1221
1222 static void set_navig_label (Dlg_head *h);
1223 static void tree_toggle_navig (Dlg_head *h)
1224 {
1225 tree_navigation_flag = 1 - tree_navigation_flag;
1226 set_navig_label (h);
1227 }
1228
1229 void set_navig_label (Dlg_head *h)
1230 {
1231 define_label_data (h, (Widget *)tree,
1232 4, tree_navigation_flag ? _("Static") : _("Dynamc"),
1233 (void (*)(void *))tree_toggle_navig, h);
1234 }
1235
1236 static void move_down (WTree *tree)
1237 {
1238 tree_move_forward (tree, 1);
1239 show_tree (tree);
1240 maybe_chdir (tree);
1241 }
1242
1243 static void move_up (WTree *tree)
1244 {
1245 tree_move_backward (tree, 1);
1246 show_tree (tree);
1247 maybe_chdir (tree);
1248 }
1249
1250 static void move_home (WTree *tree)
1251 {
1252 tree_move_to_top (tree);
1253 show_tree (tree);
1254 maybe_chdir (tree);
1255 }
1256
1257 static void move_end (WTree *tree)
1258 {
1259 tree_move_to_bottom (tree);
1260 show_tree (tree);
1261 maybe_chdir (tree);
1262 }
1263
1264 static int move_left (WTree *tree)
1265 {
1266 int v;
1267
1268 if (tree_navigation_flag){
1269 v = tree_move_to_parent (tree);
1270 show_tree (tree);
1271 maybe_chdir (tree);
1272 return v;
1273 }
1274 return 0;
1275 }
1276
1277 static int move_right (WTree *tree)
1278 {
1279 if (tree_navigation_flag){
1280 tree_move_to_child (tree);
1281 show_tree (tree);
1282 maybe_chdir (tree);
1283 return 1;
1284 }
1285 return 0;
1286 }
1287
1288 static void move_prevp (WTree *tree)
1289 {
1290 tree_move_backward (tree, tlines (tree) - 1);
1291 show_tree (tree);
1292 maybe_chdir (tree);
1293 }
1294
1295 static void move_nextp (WTree *tree)
1296 {
1297 tree_move_forward (tree, tlines (tree) - 1);
1298 show_tree (tree);
1299 maybe_chdir (tree);
1300 }
1301
1302 static void chdir_sel (WTree *tree)
1303 {
1304 if (!tree->is_panel){
1305 tree->done = 1;
1306 return;
1307 }
1308 change_panel ();
1309 if (do_cd (tree->selected_ptr->name, cd_exact)){
1310 paint_panel (cpanel);
1311 select_item (cpanel);
1312 } else {
1313 message (1, MSG_ERROR, _(" Can't chdir to \"%s\" \n %s "),
1314 tree->selected_ptr->name, unix_error_string (errno));
1315 }
1316 change_panel ();
1317 show_tree (tree);
1318 return;
1319 }
1320
1321 static void start_search (WTree *tree)
1322 {
1323 int i;
1324
1325 if (tree->searching){
1326
1327 if (tree->selected_ptr == tree->tree_last)
1328 tree_move_to_top(tree);
1329 else {
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)
1334 */
1335 i = tree_navigation_flag;
1336 tree_navigation_flag = 0;
1337 tree_move_forward (tree, 1);
1338 tree_navigation_flag = i;
1339 }
1340 tree_do_search (tree, 0);
1341 }
1342 else {
1343 tree->searching = 1;
1344 tree->search_buffer[0] = 0;
1345 }
1346 }
1347
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 },
1369 { 0, 0 }
1370 };
1371
1372 static inline int tree_key (WTree *tree, int key)
1373 {
1374 int i;
1375
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);
1381 show_tree (tree);
1382 return 1;
1383 }
1384 }
1385
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);
1390
1391 if (key == KEY_RIGHT)
1392 return move_right (tree);
1393
1394 if (is_abort_char (key)) {
1395 if (tree->is_panel) {
1396 tree->searching = 0;
1397 show_tree (tree);
1398 return 1; /* eat abort char */
1399 }
1400 return 0; /* modal tree dialog: let upper layer see the
1401 abort character and close the dialog */
1402 }
1403
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);
1408 show_tree (tree);
1409 return 1;
1410 }
1411
1412 if (!command_prompt) {
1413 start_search (tree);
1414 tree_do_search (tree, key);
1415 return 1;
1416 }
1417 return tree->is_panel;
1418 }
1419
1420 return 0;
1421 }
1422
1423 static void tree_frame (Dlg_head *h, WTree *tree)
1424 {
1425 attrset (NORMAL_COLOR);
1426 widget_erase ((Widget*) tree);
1427 if (tree->is_panel)
1428 draw_double_box (h, tree->widget.y, tree->widget.x, tree->widget.lines,
1429 tree->widget.cols);
1430
1431 if (show_mini_info && tree->is_panel){
1432 widget_move (tree, tlines (tree) + 1, 1);
1433 hline (ACS_HLINE, tree->widget.cols - 2);
1434 }
1435 }
1436
1437
1438 static int tree_callback (Dlg_head *h, WTree *tree, int msg, int par)
1439 {
1440 switch (msg){
1441 case WIDGET_DRAW:
1442 tree_frame (h, tree);
1443 show_tree (tree);
1444 return 1;
1445
1446 case WIDGET_KEY:
1447 return tree_key (tree, par);
1448
1449 case WIDGET_FOCUS:
1450 tree->active = 1;
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);
1460 #if 0
1461 /* FIXME: mkdir is currently defunct */
1462 define_label_data (h, (Widget *)tree,
1463 7, _("Mkdir"), (buttonbarfn) tree_mkdir_cmd, tree);
1464 #else
1465 define_label (h, (Widget *)tree, 7, "", 0);
1466 #endif
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);
1471
1472
1473 /* FIXME: Should find a better way of only displaying the
1474 currently selected item */
1475 show_tree (tree);
1476 return 1;
1477
1478 /* FIXME: Should find a better way of changing the color of the
1479 selected item */
1480 case WIDGET_UNFOCUS:
1481 tree->active = 0;
1482 show_tree (tree);
1483 return 1;
1484 }
1485 return default_proc (h, msg, par);
1486 }
1487
1488 WTree *tree_new (int is_panel, int y, int x, int lines, int cols)
1489 {
1490 WTree *tree = xmalloc (sizeof (WTree), "tree_new");
1491
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;
1501 tree->done = 0;
1502 tree->active = 0;
1503
1504 /* We do not want to keep the cursor */
1505 widget_want_cursor (tree->widget, 0);
1506 load_tree (tree);
1507 return tree;
1508 }
1509
1510 static char *get_absolute_name (char *file)
1511 {
1512 char dir [MC_MAXPATHLEN];
1513
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);
1518 }
1519
1520 static int my_mkdir_rec (char *s, mode_t mode)
1521 {
1522 char *p, *q;
1523 int result;
1524
1525 if (!mc_mkdir (s, mode))
1526 return 0;
1527
1528 /* FIXME: should check instead if s is at the root of that filesystem */
1529 if (!vfs_file_is_local (s))
1530 return -1;
1531 if (!strcmp (vfs_path(s), PATH_SEP_STR))
1532 return ENOTDIR;
1533 p = concat_dir_and_file (s, "..");
1534 q = vfs_canon (p);
1535 free (p);
1536 if (!(result = my_mkdir_rec (q, mode))) {
1537 result = mc_mkdir (s, mode);
1538 }
1539 free (q);
1540 return result;
1541 }
1542
1543 int my_mkdir (char *s, mode_t mode)
1544 {
1545 int result;
1546 #if FIXME
1547 WTree *tree = 0;
1548 #endif
1549
1550 result = mc_mkdir (s, mode);
1551 #ifdef OS2_NT
1552 /* .ado: it will be disabled in OS/2 and NT */
1553 /* otherwise crash if directory already exists. */
1554 return result;
1555 #endif
1556 if (result) {
1557 char *p = vfs_canon (s);
1558
1559 result = my_mkdir_rec (p, mode);
1560 free (p);
1561 }
1562 if (result == 0){
1563 s = get_absolute_name (s);
1564 #if FIXME
1565 /* FIXME: Should receive a Wtree! */
1566
1567 tree_add_entry (tree, s);
1568 #endif
1569 free (s);
1570 }
1571 return result;
1572 }
1573
1574 int my_rmdir (char *s)
1575 {
1576 int result;
1577 #if FIXME
1578 WTree *tree = 0;
1579 #endif
1580
1581 /* FIXME: Should receive a Wtree! */
1582 result = mc_rmdir (s);
1583 if (result == 0){
1584 s = get_absolute_name (s);
1585 #if FIXME
1586 tree_remove_entry (tree, s);
1587 #endif
1588 free (s);
1589 }
1590 return result;
1591 }
1592
1593