Ticket #2384: allow rebind Fx keys in the file manager.
[midnight-commander.git] / src / filemanager / tree.c
blob60a7e4a8797cb8549837168688c3eeef2d85714b
1 /*
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, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
10 2003, 2004, 2005, 2007, 2011
11 The Free Software Foundation, Inc.
13 Written by:
14 Janne Kukonlehto, 1994, 1996
15 Norbert Warmuth, 1997
16 Miguel de Icaza, 1996, 1999
18 This file is part of the Midnight Commander.
20 The Midnight Commander is free software: you can redistribute it
21 and/or modify it under the terms of the GNU General Public License as
22 published by the Free Software Foundation, either version 3 of the License,
23 or (at your option) any later version.
25 The Midnight Commander is distributed in the hope that it will be useful,
26 but WITHOUT ANY WARRANTY; without even the implied warranty of
27 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 GNU General Public License for more details.
30 You should have received a copy of the GNU General Public License
31 along with this program. If not, see <http://www.gnu.org/licenses/>.
34 /** \file tree.c
35 * \brief Source: directory tree browser
38 #include <config.h>
40 #include <errno.h>
41 #include <stdio.h>
42 #include <string.h>
43 #include <sys/types.h>
45 #include "lib/global.h"
47 #include "lib/tty/tty.h"
48 #include "lib/tty/mouse.h"
49 #include "lib/tty/key.h"
50 #include "lib/skin.h"
51 #include "lib/vfs/vfs.h"
52 #include "lib/fileloc.h"
53 #include "lib/strutil.h"
54 #include "lib/util.h"
55 #include "lib/widget.h"
56 #include "lib/event.h" /* mc_event_raise() */
58 #include "src/setup.h" /* confirm_delete, panels_options */
59 #include "src/keybind-defaults.h"
60 #include "src/history.h"
62 #include "dir.h"
63 #include "midnight.h" /* the_menubar */
64 #include "file.h" /* copy_dir_dir(), move_dir_dir(), erase_dir() */
65 #include "layout.h" /* command_prompt */
66 #include "treestore.h"
67 #include "cmd.h"
68 #include "filegui.h"
70 #include "tree.h"
72 /*** global variables ****************************************************************************/
74 /* The pointer to the tree */
75 WTree *the_tree = NULL;
77 /* If this is true, then when browsing the tree the other window will
78 * automatically reload it's directory with the contents of the currently
79 * selected directory.
81 int xtree_mode = 0;
83 /*** file scope macro definitions ****************************************************************/
85 #define tlines(t) (t->is_panel ? t->widget.lines - 2 - (panels_options.show_mini_info ? 2 : 0) : t->widget.lines)
87 /* Use the color of the parent widget for the unselected entries */
88 #define TREE_NORMALC(h) (h->color[DLG_COLOR_NORMAL])
89 #define TREE_CURRENTC(h) (h->color[DLG_COLOR_FOCUS])
91 /*** file scope type declarations ****************************************************************/
93 struct WTree
95 Widget widget;
96 struct TreeStore *store;
97 tree_entry *selected_ptr; /* The selected directory */
98 char search_buffer[MC_MAXFILENAMELEN]; /* Current search string */
99 tree_entry **tree_shown; /* Entries currently on screen */
100 int is_panel; /* panel or plain widget flag */
101 int active; /* if it's currently selected */
102 int searching; /* Are we on searching mode? */
103 int topdiff; /* The difference between the topmost
104 shown and the selected */
107 /*** file scope variables ************************************************************************/
109 /* Specifies the display mode: 1d or 2d */
110 static gboolean tree_navigation_flag = FALSE;
112 /*** file scope functions ************************************************************************/
113 /* --------------------------------------------------------------------------------------------- */
115 static void tree_rescan (void *data);
117 /* --------------------------------------------------------------------------------------------- */
119 static tree_entry *
120 back_ptr (tree_entry * ptr, int *count)
122 int i = 0;
124 while (ptr && ptr->prev && i < *count)
126 ptr = ptr->prev;
127 i++;
129 *count = i;
130 return ptr;
133 /* --------------------------------------------------------------------------------------------- */
135 static tree_entry *
136 forw_ptr (tree_entry * ptr, int *count)
138 int i = 0;
140 while (ptr && ptr->next && i < *count)
142 ptr = ptr->next;
143 i++;
145 *count = i;
146 return ptr;
149 /* --------------------------------------------------------------------------------------------- */
151 static void
152 remove_callback (tree_entry * entry, void *data)
154 WTree *tree = data;
156 if (tree->selected_ptr == entry)
158 if (tree->selected_ptr->next)
159 tree->selected_ptr = tree->selected_ptr->next;
160 else
161 tree->selected_ptr = tree->selected_ptr->prev;
165 /* --------------------------------------------------------------------------------------------- */
166 /** Save the ${XDG_CACHE_HOME}/mc/Tree file */
168 static void
169 save_tree (WTree * tree)
171 int error;
172 char *tree_name;
174 (void) tree;
175 error = tree_store_save ();
178 if (error)
180 tree_name = mc_config_get_full_path (MC_TREESTORE_FILE);
181 fprintf (stderr, _("Cannot open the %s file for writing:\n%s\n"), tree_name,
182 unix_error_string (error));
183 g_free (tree_name);
187 /* --------------------------------------------------------------------------------------------- */
189 static void
190 tree_remove_entry (WTree * tree, const vfs_path_t * name_vpath)
192 (void) tree;
193 tree_store_remove_entry (name_vpath);
196 /* --------------------------------------------------------------------------------------------- */
198 static void
199 tree_destroy (WTree * tree)
201 tree_store_remove_entry_remove_hook (remove_callback);
202 save_tree (tree);
204 g_free (tree->tree_shown);
205 tree->tree_shown = 0;
206 tree->selected_ptr = NULL;
209 /* --------------------------------------------------------------------------------------------- */
210 /** Loads the .mc.tree file */
212 static void
213 load_tree (WTree * tree)
215 tree_store_load ();
217 tree->selected_ptr = tree->store->tree_first;
218 tree_chdir (tree, mc_config_get_home_dir ());
221 /* --------------------------------------------------------------------------------------------- */
223 static void
224 tree_show_mini_info (WTree * tree, int tree_lines, int tree_cols)
226 Dlg_head *h = tree->widget.owner;
227 int line;
229 /* Show mini info */
230 if (tree->is_panel)
232 if (!panels_options.show_mini_info)
233 return;
234 line = tree_lines + 2;
236 else
237 line = tree_lines + 1;
239 if (tree->searching)
241 /* Show search string */
242 tty_setcolor (INPUT_COLOR);
243 tty_draw_hline (tree->widget.y + line, tree->widget.x + 1, ' ', tree_cols);
244 widget_move (&tree->widget, line, 1);
245 tty_print_char (PATH_SEP);
246 tty_print_string (str_fit_to_term (tree->search_buffer, tree_cols - 2, J_LEFT_FIT));
247 tty_print_char (' ');
249 else
251 /* Show full name of selected directory */
252 char *tmp_path;
254 tty_setcolor (tree->is_panel ? NORMAL_COLOR : TREE_NORMALC (h));
255 tty_draw_hline (tree->widget.y + line, tree->widget.x + 1, ' ', tree_cols);
256 widget_move (&tree->widget, line, 1);
257 tmp_path = vfs_path_to_str (tree->selected_ptr->name);
258 tty_print_string (str_fit_to_term (tmp_path, tree_cols, J_LEFT_FIT));
259 g_free (tmp_path);
263 /* --------------------------------------------------------------------------------------------- */
265 static void
266 show_tree (WTree * tree)
268 Dlg_head *h = tree->widget.owner;
269 tree_entry *current;
270 int i, j, topsublevel;
271 int x = 0, y = 0;
272 int tree_lines, tree_cols;
274 /* Initialize */
275 tree_lines = tlines (tree);
276 tree_cols = tree->widget.cols;
278 widget_move ((Widget *) tree, y, x);
279 if (tree->is_panel)
281 tree_cols -= 2;
282 x = y = 1;
285 g_free (tree->tree_shown);
286 tree->tree_shown = g_new0 (tree_entry *, tree_lines);
288 if (tree->store->tree_first)
289 topsublevel = tree->store->tree_first->sublevel;
290 else
291 topsublevel = 0;
292 if (!tree->selected_ptr)
294 tree->selected_ptr = tree->store->tree_first;
295 tree->topdiff = 0;
297 current = tree->selected_ptr;
299 /* Calculate the directory which is to be shown on the topmost line */
300 if (!tree_navigation_flag)
301 current = back_ptr (current, &tree->topdiff);
302 else
304 i = 0;
305 while (current->prev && i < tree->topdiff)
307 char *current_name;
309 current = current->prev;
310 current_name = vfs_path_to_str (current->name);
312 if (current->sublevel < tree->selected_ptr->sublevel)
314 if (vfs_path_cmp (current->name, tree->selected_ptr->name) == 0)
315 i++;
317 else if (current->sublevel == tree->selected_ptr->sublevel)
319 for (j = strlen (current_name) - 1; current_name[j] != PATH_SEP; j--);
320 if (vfs_path_ncmp (current->name, tree->selected_ptr->name, j) == 0)
321 i++;
323 else
325 if (current->sublevel == tree->selected_ptr->sublevel + 1
326 && vfs_path_len (tree->selected_ptr->name) > 1)
328 if (vfs_path_ncmp (current->name, tree->selected_ptr->name,
329 vfs_path_len (tree->selected_ptr->name)) == 0)
330 i++;
333 g_free (current_name);
335 tree->topdiff = i;
338 /* Loop for every line */
339 for (i = 0; i < tree_lines; i++)
341 tty_setcolor (tree->is_panel ? NORMAL_COLOR : TREE_NORMALC (h));
343 /* Move to the beginning of the line */
344 tty_draw_hline (tree->widget.y + y + i, tree->widget.x + x, ' ', tree_cols);
346 if (current == NULL)
347 continue;
349 if (tree->is_panel)
350 tty_setcolor (tree->active && current == tree->selected_ptr
351 ? SELECTED_COLOR : NORMAL_COLOR);
352 else
353 tty_setcolor (current == tree->selected_ptr ? TREE_CURRENTC (h) : TREE_NORMALC (h));
355 tree->tree_shown[i] = current;
356 if (current->sublevel == topsublevel)
358 /* Show full name */
359 char *current_name;
361 current_name = vfs_path_to_str (current->name);
362 tty_print_string (str_fit_to_term
363 (current_name, tree_cols + (tree->is_panel ? 0 : 1), J_LEFT_FIT));
364 g_free (current_name);
366 else
368 /* Sub level directory */
369 tty_set_alt_charset (TRUE);
371 /* Output branch parts */
372 for (j = 0; j < current->sublevel - topsublevel - 1; j++)
374 if (tree_cols - 8 - 3 * j < 9)
375 break;
376 tty_print_char (' ');
377 if (current->submask & (1 << (j + topsublevel + 1)))
378 tty_print_char (ACS_VLINE);
379 else
380 tty_print_char (' ');
381 tty_print_char (' ');
383 tty_print_char (' ');
384 j++;
385 if (!current->next || !(current->next->submask & (1 << current->sublevel)))
386 tty_print_char (ACS_LLCORNER);
387 else
388 tty_print_char (ACS_LTEE);
389 tty_print_char (ACS_HLINE);
390 tty_set_alt_charset (FALSE);
392 /* Show sub-name */
393 tty_print_char (' ');
394 tty_print_string (str_fit_to_term
395 (current->subname, tree_cols - x - 3 * j, J_LEFT_FIT));
398 /* Calculate the next value for current */
399 current = current->next;
400 if (tree_navigation_flag)
402 while (current != NULL)
404 if (current->sublevel < tree->selected_ptr->sublevel)
406 if (vfs_path_ncmp (current->name, tree->selected_ptr->name,
407 vfs_path_len (current->name)) == 0)
408 break;
410 else if (current->sublevel == tree->selected_ptr->sublevel)
412 char *current_name;
414 current_name = vfs_path_to_str (current->name);
415 for (j = strlen (current_name) - 1; current_name[j] != PATH_SEP; j--)
417 g_free (current_name);
418 if (vfs_path_ncmp (current->name, tree->selected_ptr->name, j) == 0)
419 break;
421 else if (current->sublevel == tree->selected_ptr->sublevel + 1
422 && vfs_path_len (tree->selected_ptr->name) > 1)
424 if (vfs_path_ncmp (current->name, tree->selected_ptr->name,
425 vfs_path_len (tree->selected_ptr->name)) == 0)
426 break;
428 current = current->next;
433 tree_show_mini_info (tree, tree_lines, tree_cols);
436 /* --------------------------------------------------------------------------------------------- */
438 static void
439 tree_check_focus (WTree * tree)
441 if (tree->topdiff < 3)
442 tree->topdiff = 3;
443 else if (tree->topdiff >= tlines (tree) - 3)
444 tree->topdiff = tlines (tree) - 3 - 1;
447 /* --------------------------------------------------------------------------------------------- */
449 static void
450 tree_move_backward (WTree * tree, int i)
452 if (!tree_navigation_flag)
453 tree->selected_ptr = back_ptr (tree->selected_ptr, &i);
454 else
456 tree_entry *current;
457 int j = 0;
459 current = tree->selected_ptr;
460 while (j < i && current->prev && current->prev->sublevel >= tree->selected_ptr->sublevel)
462 current = current->prev;
463 if (current->sublevel == tree->selected_ptr->sublevel)
465 tree->selected_ptr = current;
466 j++;
469 i = j;
472 tree->topdiff -= i;
473 tree_check_focus (tree);
476 /* --------------------------------------------------------------------------------------------- */
478 static void
479 tree_move_forward (WTree * tree, int i)
481 if (!tree_navigation_flag)
482 tree->selected_ptr = forw_ptr (tree->selected_ptr, &i);
483 else
485 tree_entry *current;
486 int j = 0;
488 current = tree->selected_ptr;
489 while (j < i && current->next && current->next->sublevel >= tree->selected_ptr->sublevel)
491 current = current->next;
492 if (current->sublevel == tree->selected_ptr->sublevel)
494 tree->selected_ptr = current;
495 j++;
498 i = j;
501 tree->topdiff += i;
502 tree_check_focus (tree);
505 /* --------------------------------------------------------------------------------------------- */
507 static void
508 tree_move_to_child (WTree * tree)
510 tree_entry *current;
512 /* Do we have a starting point? */
513 if (!tree->selected_ptr)
514 return;
515 /* Take the next entry */
516 current = tree->selected_ptr->next;
517 /* Is it the child of the selected entry */
518 if (current && current->sublevel > tree->selected_ptr->sublevel)
520 /* Yes -> select this entry */
521 tree->selected_ptr = current;
522 tree->topdiff++;
523 tree_check_focus (tree);
525 else
527 /* No -> rescan and try again */
528 tree_rescan (tree);
529 current = tree->selected_ptr->next;
530 if (current && current->sublevel > tree->selected_ptr->sublevel)
532 tree->selected_ptr = current;
533 tree->topdiff++;
534 tree_check_focus (tree);
539 /* --------------------------------------------------------------------------------------------- */
541 static gboolean
542 tree_move_to_parent (WTree * tree)
544 tree_entry *current;
545 tree_entry *old;
547 if (!tree->selected_ptr)
548 return FALSE;
550 old = tree->selected_ptr;
551 current = tree->selected_ptr->prev;
552 while (current && current->sublevel >= tree->selected_ptr->sublevel)
554 current = current->prev;
555 tree->topdiff--;
557 if (!current)
558 current = tree->store->tree_first;
559 tree->selected_ptr = current;
560 tree_check_focus (tree);
561 return tree->selected_ptr != old;
564 /* --------------------------------------------------------------------------------------------- */
566 static void
567 tree_move_to_top (WTree * tree)
569 tree->selected_ptr = tree->store->tree_first;
570 tree->topdiff = 0;
573 /* --------------------------------------------------------------------------------------------- */
575 static void
576 tree_move_to_bottom (WTree * tree)
578 tree->selected_ptr = tree->store->tree_last;
579 tree->topdiff = tlines (tree) - 3 - 1;
582 /* --------------------------------------------------------------------------------------------- */
584 /** Handle mouse click */
585 static void
586 tree_mouse_click (WTree * tree, int y)
588 if (tree->tree_shown[y])
590 tree->selected_ptr = tree->tree_shown[y];
591 tree->topdiff = y;
593 show_tree (tree);
596 /* --------------------------------------------------------------------------------------------- */
598 static void
599 tree_chdir_sel (WTree * tree)
601 char *tmp_path;
603 if (!tree->is_panel)
604 return;
606 change_panel ();
608 tmp_path = vfs_path_to_str (tree->selected_ptr->name);
609 if (do_cd (tree->selected_ptr->name, cd_exact))
610 select_item (current_panel);
611 else
612 message (D_ERROR, MSG_ERROR, _("Cannot chdir to \"%s\"\n%s"),
613 tmp_path, unix_error_string (errno));
615 g_free (tmp_path);
616 change_panel ();
617 show_tree (tree);
620 /* --------------------------------------------------------------------------------------------- */
622 static void
623 maybe_chdir (WTree * tree)
625 if (xtree_mode && tree->is_panel && is_idle ())
626 tree_chdir_sel (tree);
629 /* --------------------------------------------------------------------------------------------- */
630 /** Mouse callback */
632 static int
633 tree_event (Gpm_Event * event, void *data)
635 WTree *tree = (WTree *) data;
636 Widget *w = (Widget *) data;
637 Gpm_Event local;
639 if (!mouse_global_in_widget (event, w))
640 return MOU_UNHANDLED;
642 local = mouse_get_local (event, w);
644 /* rest of the upper frame, the menu is invisible - call menu */
645 if (tree->is_panel && (local.type & GPM_DOWN) != 0 && local.y == 1 && !menubar_visible)
646 return the_menubar->widget.mouse (event, the_menubar);
648 if ((local.type & GPM_UP) == 0)
649 return MOU_NORMAL;
651 if (tree->is_panel)
652 local.y--;
654 local.y--;
656 if (!tree->active)
657 change_panel ();
659 if (local.y < 0)
661 tree_move_backward (tree, tlines (tree) - 1);
662 show_tree (tree);
664 else if (local.y >= tlines (tree))
666 tree_move_forward (tree, tlines (tree) - 1);
667 show_tree (tree);
669 else
671 tree_mouse_click (tree, local.y);
672 if ((local.type & (GPM_UP | GPM_DOUBLE)) == (GPM_UP | GPM_DOUBLE))
673 tree_chdir_sel (tree);
676 return MOU_NORMAL;
679 /* --------------------------------------------------------------------------------------------- */
680 /** Search tree for text */
682 static int
683 search_tree (WTree * tree, char *text)
685 tree_entry *current;
686 int len;
687 int wrapped = 0;
688 int found = 0;
690 len = strlen (text);
691 current = tree->selected_ptr;
692 found = 0;
693 while (!wrapped || current != tree->selected_ptr)
695 if (strncmp (current->subname, text, len) == 0)
697 tree->selected_ptr = current;
698 found = 1;
699 break;
701 current = current->next;
702 if (!current)
704 current = tree->store->tree_first;
705 wrapped = 1;
707 tree->topdiff++;
709 tree_check_focus (tree);
710 return found;
713 /* --------------------------------------------------------------------------------------------- */
715 static void
716 tree_do_search (WTree * tree, int key)
718 size_t l;
720 l = strlen (tree->search_buffer);
721 if ((l != 0) && (key == KEY_BACKSPACE))
722 tree->search_buffer[--l] = '\0';
723 else if (key && l < sizeof (tree->search_buffer))
725 tree->search_buffer[l] = key;
726 tree->search_buffer[++l] = '\0';
729 if (!search_tree (tree, tree->search_buffer))
730 tree->search_buffer[--l] = 0;
732 show_tree (tree);
733 maybe_chdir (tree);
736 /* --------------------------------------------------------------------------------------------- */
738 static void
739 tree_rescan (void *data)
741 WTree *tree = data;
742 int ret;
743 vfs_path_t *old_vpath;
745 old_vpath = vfs_path_clone (vfs_get_raw_current_dir ());
746 if (old_vpath == NULL)
747 return;
749 if (tree->selected_ptr != NULL && mc_chdir (tree->selected_ptr->name) == 0)
751 tree_store_rescan (tree->selected_ptr->name);
752 ret = mc_chdir (old_vpath);
754 vfs_path_free (old_vpath);
757 /* --------------------------------------------------------------------------------------------- */
759 static void
760 tree_forget (void *data)
762 WTree *tree = data;
763 if (tree->selected_ptr)
764 tree_remove_entry (tree, tree->selected_ptr->name);
767 /* --------------------------------------------------------------------------------------------- */
769 static void
770 tree_copy (WTree * tree, const char *default_dest)
772 char msg[BUF_MEDIUM];
773 char *dest, *selected_ptr_name;
775 if (tree->selected_ptr == NULL)
776 return;
778 selected_ptr_name = vfs_path_to_str (tree->selected_ptr->name);
780 g_snprintf (msg, sizeof (msg), _("Copy \"%s\" directory to:"),
781 str_trunc (selected_ptr_name, 50));
782 dest = input_expand_dialog (Q_ ("DialogTitle|Copy"),
783 msg, MC_HISTORY_FM_TREE_COPY, default_dest);
785 if (dest != NULL && *dest != '\0')
787 FileOpContext *ctx;
788 FileOpTotalContext *tctx;
790 ctx = file_op_context_new (OP_COPY);
791 tctx = file_op_total_context_new ();
792 file_op_context_create_ui (ctx, FALSE, FILEGUI_DIALOG_MULTI_ITEM);
793 tctx->ask_overwrite = FALSE;
794 copy_dir_dir (tctx, ctx, selected_ptr_name, dest, TRUE, FALSE, FALSE, NULL);
795 file_op_total_context_destroy (tctx);
796 file_op_context_destroy (ctx);
799 g_free (dest);
800 g_free (selected_ptr_name);
803 /* --------------------------------------------------------------------------------------------- */
805 static void
806 tree_move (WTree * tree, const char *default_dest)
808 char msg[BUF_MEDIUM];
809 char *dest, *selected_ptr_name;
810 struct stat buf;
811 FileOpContext *ctx;
812 FileOpTotalContext *tctx;
814 if (tree->selected_ptr == NULL)
815 return;
817 selected_ptr_name = vfs_path_to_str (tree->selected_ptr->name);
819 g_snprintf (msg, sizeof (msg), _("Move \"%s\" directory to:"),
820 str_trunc (selected_ptr_name, 50));
821 dest =
822 input_expand_dialog (Q_ ("DialogTitle|Move"), msg, MC_HISTORY_FM_TREE_MOVE, default_dest);
824 if (dest == NULL || *dest == '\0')
825 goto ret;
827 if (stat (dest, &buf))
829 message (D_ERROR, MSG_ERROR, _("Cannot stat the destination\n%s"),
830 unix_error_string (errno));
831 goto ret;
834 if (!S_ISDIR (buf.st_mode))
836 file_error (_("Destination \"%s\" must be a directory\n%s"), dest);
837 goto ret;
840 ctx = file_op_context_new (OP_MOVE);
841 tctx = file_op_total_context_new ();
842 file_op_context_create_ui (ctx, FALSE, FILEGUI_DIALOG_ONE_ITEM);
843 move_dir_dir (tctx, ctx, selected_ptr_name, dest);
844 file_op_total_context_destroy (tctx);
845 file_op_context_destroy (ctx);
847 ret:
848 g_free (selected_ptr_name);
849 g_free (dest);
852 /* --------------------------------------------------------------------------------------------- */
854 #if 0
855 static void
856 tree_mkdir (WTree * tree)
858 char old_dir[MC_MAXPATHLEN];
860 if (!tree->selected_ptr)
861 return;
862 if (chdir (tree->selected_ptr->name))
863 return;
864 /* FIXME
865 mkdir_cmd (tree);
867 tree_rescan (tree);
868 chdir (old_dir);
870 #endif
872 /* --------------------------------------------------------------------------------------------- */
874 static void
875 tree_rmdir (void *data)
877 WTree *tree = data;
878 FileOpContext *ctx;
879 FileOpTotalContext *tctx;
881 if (!tree->selected_ptr)
882 return;
884 if (confirm_delete)
886 char *buf;
887 int result;
888 char *selected_ptr_name;
890 selected_ptr_name = vfs_path_to_str (tree->selected_ptr->name);
891 buf = g_strdup_printf (_("Delete %s?"), selected_ptr_name);
892 g_free (selected_ptr_name);
894 result = query_dialog (Q_ ("DialogTitle|Delete"), buf, D_ERROR, 2, _("&Yes"), _("&No"));
895 g_free (buf);
896 if (result != 0)
897 return;
900 ctx = file_op_context_new (OP_DELETE);
901 tctx = file_op_total_context_new ();
903 file_op_context_create_ui (ctx, FALSE, FILEGUI_DIALOG_ONE_ITEM);
904 if (erase_dir (tctx, ctx, tree->selected_ptr->name) == FILE_CONT)
905 tree_forget (tree);
906 file_op_total_context_destroy (tctx);
907 file_op_context_destroy (ctx);
910 /* --------------------------------------------------------------------------------------------- */
912 static inline void
913 tree_move_up (WTree * tree)
915 tree_move_backward (tree, 1);
916 show_tree (tree);
917 maybe_chdir (tree);
920 /* --------------------------------------------------------------------------------------------- */
922 static inline void
923 tree_move_down (WTree * tree)
925 tree_move_forward (tree, 1);
926 show_tree (tree);
927 maybe_chdir (tree);
930 /* --------------------------------------------------------------------------------------------- */
932 static inline void
933 tree_move_home (WTree * tree)
935 tree_move_to_top (tree);
936 show_tree (tree);
937 maybe_chdir (tree);
940 /* --------------------------------------------------------------------------------------------- */
942 static inline void
943 tree_move_end (WTree * tree)
945 tree_move_to_bottom (tree);
946 show_tree (tree);
947 maybe_chdir (tree);
950 /* --------------------------------------------------------------------------------------------- */
952 static void
953 tree_move_pgup (WTree * tree)
955 tree_move_backward (tree, tlines (tree) - 1);
956 show_tree (tree);
957 maybe_chdir (tree);
960 /* --------------------------------------------------------------------------------------------- */
962 static void
963 tree_move_pgdn (WTree * tree)
965 tree_move_forward (tree, tlines (tree) - 1);
966 show_tree (tree);
967 maybe_chdir (tree);
970 /* --------------------------------------------------------------------------------------------- */
972 static gboolean
973 tree_move_left (WTree * tree)
975 gboolean v = FALSE;
977 if (tree_navigation_flag)
979 v = tree_move_to_parent (tree);
980 show_tree (tree);
981 maybe_chdir (tree);
984 return v;
987 /* --------------------------------------------------------------------------------------------- */
989 static gboolean
990 tree_move_right (WTree * tree)
992 gboolean v = FALSE;
994 if (tree_navigation_flag)
996 tree_move_to_child (tree);
997 show_tree (tree);
998 maybe_chdir (tree);
999 v = TRUE;
1002 return v;
1005 /* --------------------------------------------------------------------------------------------- */
1007 static void
1008 tree_start_search (WTree * tree)
1010 gboolean i;
1012 if (tree->searching)
1014 if (tree->selected_ptr == tree->store->tree_last)
1015 tree_move_to_top (tree);
1016 else
1018 /* set navigation mode temporarily to 'Static' because in
1019 * dynamic navigation mode tree_move_forward will not move
1020 * to a lower sublevel if necessary (sequent searches must
1021 * start with the directory followed the last found directory)
1023 i = tree_navigation_flag;
1024 tree_navigation_flag = 0;
1025 tree_move_forward (tree, 1);
1026 tree_navigation_flag = i;
1028 tree_do_search (tree, 0);
1030 else
1032 tree->searching = 1;
1033 tree->search_buffer[0] = 0;
1037 /* --------------------------------------------------------------------------------------------- */
1039 static void
1040 tree_toggle_navig (WTree * tree)
1042 tree_navigation_flag = !tree_navigation_flag;
1043 buttonbar_set_label (find_buttonbar (tree->widget.owner), 4,
1044 tree_navigation_flag ? Q_ ("ButtonBar|Static")
1045 : Q_ ("ButtonBar|Dynamc"), tree_map, (Widget *) tree);
1048 /* --------------------------------------------------------------------------------------------- */
1050 static cb_ret_t
1051 tree_execute_cmd (WTree * tree, unsigned long command)
1053 cb_ret_t res = MSG_HANDLED;
1055 if (command != CK_Search)
1056 tree->searching = 0;
1058 switch (command)
1060 case CK_Help:
1062 ev_help_t event_data = { NULL, "[Directory Tree]" };
1063 mc_event_raise (MCEVENT_GROUP_CORE, "help", &event_data);
1065 break;
1066 case CK_Forget:
1067 tree_forget (tree);
1068 break;
1069 case CK_ToggleNavigation:
1070 tree_toggle_navig (tree);
1071 break;
1072 case CK_Copy:
1073 tree_copy (tree, "");
1074 break;
1075 case CK_Move:
1076 tree_move (tree, "");
1077 break;
1078 case CK_Up:
1079 tree_move_up (tree);
1080 break;
1081 case CK_Down:
1082 tree_move_down (tree);
1083 break;
1084 case CK_Top:
1085 tree_move_home (tree);
1086 break;
1087 case CK_Bottom:
1088 tree_move_end (tree);
1089 break;
1090 case CK_PageUp:
1091 tree_move_pgup (tree);
1092 break;
1093 case CK_PageDown:
1094 tree_move_pgdn (tree);
1095 break;
1096 case CK_Enter:
1097 tree_chdir_sel (tree);
1098 break;
1099 case CK_Reread:
1100 tree_rescan (tree);
1101 break;
1102 case CK_Search:
1103 tree_start_search (tree);
1104 break;
1105 case CK_Delete:
1106 tree_rmdir (tree);
1107 break;
1108 case CK_Quit:
1109 if (!tree->is_panel)
1110 dlg_stop (((Widget *) tree)->owner);
1111 return res;
1112 default:
1113 res = MSG_NOT_HANDLED;
1116 show_tree (tree);
1118 return res;
1121 /* --------------------------------------------------------------------------------------------- */
1123 static cb_ret_t
1124 tree_key (WTree * tree, int key)
1126 size_t i;
1128 if (is_abort_char (key))
1130 if (tree->is_panel)
1132 tree->searching = 0;
1133 show_tree (tree);
1134 return MSG_HANDLED; /* eat abort char */
1136 /* modal tree dialog: let upper layer see the
1137 abort character and close the dialog */
1138 return MSG_NOT_HANDLED;
1141 if (tree->searching && ((key >= ' ' && key <= 255) || key == KEY_BACKSPACE))
1143 tree_do_search (tree, key);
1144 show_tree (tree);
1145 return MSG_HANDLED;
1148 for (i = 0; tree_map[i].key != 0; i++)
1149 if (key == tree_map[i].key)
1150 switch (tree_map[i].command)
1152 case CK_Left:
1153 return tree_move_left (tree) ? MSG_HANDLED : MSG_NOT_HANDLED;
1154 case CK_Right:
1155 return tree_move_right (tree) ? MSG_HANDLED : MSG_NOT_HANDLED;
1156 default:
1157 tree_execute_cmd (tree, tree_map[i].command);
1158 return MSG_HANDLED;
1161 /* Do not eat characters not meant for the tree below ' ' (e.g. C-l). */
1162 if (!command_prompt && ((key >= ' ' && key <= 255) || key == KEY_BACKSPACE))
1164 tree_start_search (tree);
1165 tree_do_search (tree, key);
1166 return MSG_HANDLED;
1169 return MSG_NOT_HANDLED;
1172 /* --------------------------------------------------------------------------------------------- */
1174 static void
1175 tree_frame (Dlg_head * h, WTree * tree)
1177 tty_setcolor (NORMAL_COLOR);
1178 widget_erase ((Widget *) tree);
1179 if (tree->is_panel)
1181 const char *title = _("Directory tree");
1182 const int len = str_term_width1 (title);
1184 draw_box (h, tree->widget.y, tree->widget.x, tree->widget.lines, tree->widget.cols, FALSE);
1186 widget_move (&tree->widget, 0, (tree->widget.cols - len - 2) / 2);
1187 tty_printf (" %s ", title);
1189 if (panels_options.show_mini_info)
1190 widget_move (&tree->widget, tlines (tree) + 1, 0);
1191 tty_print_alt_char (ACS_LTEE, FALSE);
1192 widget_move (&tree->widget, tlines (tree) + 1, tree->widget.cols - 1);
1193 tty_print_alt_char (ACS_RTEE, FALSE);
1194 tty_draw_hline (tree->widget.y + tlines (tree) + 1,
1195 tree->widget.x + 1, ACS_HLINE, tree->widget.cols - 2);
1199 /* --------------------------------------------------------------------------------------------- */
1201 static cb_ret_t
1202 tree_callback (Widget * w, widget_msg_t msg, int parm)
1204 WTree *tree = (WTree *) w;
1205 Dlg_head *h = tree->widget.owner;
1206 WButtonBar *b = find_buttonbar (h);
1208 switch (msg)
1210 case WIDGET_DRAW:
1211 tree_frame (h, tree);
1212 show_tree (tree);
1213 return MSG_HANDLED;
1215 case WIDGET_FOCUS:
1216 tree->active = 1;
1217 buttonbar_set_label (b, 1, Q_ ("ButtonBar|Help"), tree_map, (Widget *) tree);
1218 buttonbar_set_label (b, 2, Q_ ("ButtonBar|Rescan"), tree_map, (Widget *) tree);
1219 buttonbar_set_label (b, 3, Q_ ("ButtonBar|Forget"), tree_map, (Widget *) tree);
1220 buttonbar_set_label (b, 4, tree_navigation_flag ? Q_ ("ButtonBar|Static")
1221 : Q_ ("ButtonBar|Dynamc"), tree_map, (Widget *) tree);
1222 buttonbar_set_label (b, 5, Q_ ("ButtonBar|Copy"), tree_map, (Widget *) tree);
1223 buttonbar_set_label (b, 6, Q_ ("ButtonBar|RenMov"), tree_map, (Widget *) tree);
1224 #if 0
1225 /* FIXME: mkdir is currently defunct */
1226 buttonbar_set_label (b, 7, Q_ ("ButtonBar|Mkdir"), tree_map, (Widget *) tree);
1227 #else
1228 buttonbar_clear_label (b, 7, (Widget *) tree);
1229 #endif
1230 buttonbar_set_label (b, 8, Q_ ("ButtonBar|Rmdir"), tree_map, (Widget *) tree);
1231 buttonbar_redraw (b);
1233 /* FIXME: Should find a better way of only displaying the
1234 currently selected item */
1235 show_tree (tree);
1236 return MSG_HANDLED;
1238 /* FIXME: Should find a better way of changing the color of the
1239 selected item */
1241 case WIDGET_UNFOCUS:
1242 tree->active = 0;
1243 tree->searching = 0;
1244 show_tree (tree);
1245 return MSG_HANDLED;
1247 case WIDGET_KEY:
1248 return tree_key (tree, parm);
1250 case WIDGET_COMMAND:
1251 /* command from buttonbar */
1252 return tree_execute_cmd (tree, parm);
1254 case WIDGET_DESTROY:
1255 tree_destroy (tree);
1256 return MSG_HANDLED;
1258 default:
1259 return default_proc (msg, parm);
1263 /* --------------------------------------------------------------------------------------------- */
1264 /*** public functions ****************************************************************************/
1265 /* --------------------------------------------------------------------------------------------- */
1267 WTree *
1268 tree_new (int y, int x, int lines, int cols, gboolean is_panel)
1270 WTree *tree = g_new (WTree, 1);
1272 init_widget (&tree->widget, y, x, lines, cols, tree_callback, tree_event);
1273 tree->is_panel = is_panel;
1274 tree->selected_ptr = 0;
1276 tree->store = tree_store_get ();
1277 tree_store_add_entry_remove_hook (remove_callback, tree);
1278 tree->tree_shown = 0;
1279 tree->search_buffer[0] = 0;
1280 tree->topdiff = tree->widget.lines / 2;
1281 tree->searching = 0;
1282 tree->active = 0;
1284 /* We do not want to keep the cursor */
1285 widget_want_cursor (tree->widget, 0);
1286 load_tree (tree);
1287 return tree;
1290 /* --------------------------------------------------------------------------------------------- */
1292 void
1293 tree_chdir (WTree * tree, const char *dir)
1295 vfs_path_t *vpath;
1296 tree_entry *current;
1298 vpath = vfs_path_from_str (dir);
1299 current = tree_store_whereis (vpath);
1300 if (current != NULL)
1302 tree->selected_ptr = current;
1303 tree_check_focus (tree);
1305 vfs_path_free (vpath);
1308 /* --------------------------------------------------------------------------------------------- */
1309 /** Return name of the currently selected entry */
1311 vfs_path_t *
1312 tree_selected_name (const WTree * tree)
1314 return tree->selected_ptr->name;
1317 /* --------------------------------------------------------------------------------------------- */
1319 void
1320 sync_tree (const char *path)
1322 tree_chdir (the_tree, path);
1325 /* --------------------------------------------------------------------------------------------- */
1327 WTree *
1328 find_tree (struct Dlg_head *h)
1330 return (WTree *) find_widget_type (h, tree_callback);
1333 /* --------------------------------------------------------------------------------------------- */