root/src/filemanager/tree.c

/* [previous][next][first][last][top][bottom][index][help]  */

DEFINITIONS

This source file includes following definitions.
  1. back_ptr
  2. forw_ptr
  3. remove_callback
  4. save_tree
  5. tree_remove_entry
  6. tree_destroy
  7. load_tree
  8. tree_show_mini_info
  9. show_tree
  10. tree_check_focus
  11. tree_move_backward
  12. tree_move_forward
  13. tree_move_to_child
  14. tree_move_to_parent
  15. tree_move_to_top
  16. tree_move_to_bottom
  17. tree_chdir_sel
  18. maybe_chdir
  19. search_tree
  20. tree_do_search
  21. tree_rescan
  22. tree_forget
  23. tree_copy
  24. tree_move
  25. tree_mkdir
  26. tree_rmdir
  27. tree_move_up
  28. tree_move_down
  29. tree_move_home
  30. tree_move_end
  31. tree_move_pgup
  32. tree_move_pgdn
  33. tree_move_left
  34. tree_move_right
  35. tree_start_search
  36. tree_toggle_navig
  37. tree_execute_cmd
  38. tree_key
  39. tree_frame
  40. tree_callback
  41. tree_mouse_callback
  42. tree_new
  43. tree_chdir
  44. tree_selected_name
  45. sync_tree
  46. find_tree

   1 /*
   2    Directory tree browser for the Midnight Commander
   3    This module has been converted to be a widget.
   4 
   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.
   8 
   9    Copyright (C) 1994-2019
  10    Free Software Foundation, Inc.
  11 
  12    Written by:
  13    Janne Kukonlehto, 1994, 1996
  14    Norbert Warmuth, 1997
  15    Miguel de Icaza, 1996, 1999
  16    Slava Zanko <slavazanko@gmail.com>, 2013
  17    Andrew Borodin <aborodin@vmail.ru>, 2013, 2014, 2016
  18 
  19    This file is part of the Midnight Commander.
  20 
  21    The Midnight Commander is free software: you can redistribute it
  22    and/or modify it under the terms of the GNU General Public License as
  23    published by the Free Software Foundation, either version 3 of the License,
  24    or (at your option) any later version.
  25 
  26    The Midnight Commander is distributed in the hope that it will be useful,
  27    but WITHOUT ANY WARRANTY; without even the implied warranty of
  28    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  29    GNU General Public License for more details.
  30 
  31    You should have received a copy of the GNU General Public License
  32    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  33  */
  34 
  35 /** \file tree.c
  36  *  \brief Source: directory tree browser
  37  */
  38 
  39 #include <config.h>
  40 
  41 #include <errno.h>
  42 #include <stdio.h>
  43 #include <string.h>
  44 #include <sys/types.h>
  45 
  46 #include "lib/global.h"
  47 
  48 #include "lib/tty/tty.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() */
  57 
  58 #include "src/setup.h"          /* confirm_delete, panels_options */
  59 #include "src/keybind-defaults.h"
  60 #include "src/history.h"
  61 
  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"
  69 
  70 #include "tree.h"
  71 
  72 /*** global variables ****************************************************************************/
  73 
  74 /* The pointer to the tree */
  75 WTree *the_tree = NULL;
  76 
  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.
  80  */
  81 gboolean xtree_mode = FALSE;
  82 
  83 /*** file scope macro definitions ****************************************************************/
  84 
  85 #define tlines(t) (t->is_panel ? WIDGET (t)->lines - 2 - \
  86                     (panels_options.show_mini_info ? 2 : 0) : WIDGET (t)->lines)
  87 
  88 /* Use the color of the parent widget for the unselected entries */
  89 #define TREE_NORMALC(h) (h->color[DLG_COLOR_NORMAL])
  90 #define TREE_CURRENTC(h) (h->color[DLG_COLOR_FOCUS])
  91 
  92 /*** file scope type declarations ****************************************************************/
  93 
  94 struct WTree
  95 {
  96     Widget widget;
  97     struct TreeStore *store;
  98     tree_entry *selected_ptr;   /* The selected directory */
  99     char search_buffer[MC_MAXFILENAMELEN];      /* Current search string */
 100     tree_entry **tree_shown;    /* Entries currently on screen */
 101     gboolean is_panel;          /* panel or plain widget flag */
 102     gboolean searching;         /* Are we on searching mode? */
 103     int topdiff;                /* The difference between the topmost
 104                                    shown and the selected */
 105 };
 106 
 107 /*** file scope variables ************************************************************************/
 108 
 109 /* Specifies the display mode: 1d or 2d */
 110 static gboolean tree_navigation_flag = FALSE;
 111 
 112 /* --------------------------------------------------------------------------------------------- */
 113 /*** file scope functions ************************************************************************/
 114 /* --------------------------------------------------------------------------------------------- */
 115 
 116 static void tree_rescan (void *data);
 117 
 118 /* --------------------------------------------------------------------------------------------- */
 119 
 120 static tree_entry *
 121 back_ptr (tree_entry * ptr, int *count)
     /* [previous][next][first][last][top][bottom][index][help]  */
 122 {
 123     int i;
 124 
 125     for (i = 0; ptr != NULL && ptr->prev != NULL && i < *count; ptr = ptr->prev, i++)
 126         ;
 127 
 128     *count = i;
 129     return ptr;
 130 }
 131 
 132 /* --------------------------------------------------------------------------------------------- */
 133 
 134 static tree_entry *
 135 forw_ptr (tree_entry * ptr, int *count)
     /* [previous][next][first][last][top][bottom][index][help]  */
 136 {
 137     int i;
 138 
 139     for (i = 0; ptr != NULL && ptr->next != NULL && i < *count; ptr = ptr->next, i++)
 140         ;
 141 
 142     *count = i;
 143     return ptr;
 144 }
 145 
 146 /* --------------------------------------------------------------------------------------------- */
 147 
 148 static void
 149 remove_callback (tree_entry * entry, void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
 150 {
 151     WTree *tree = data;
 152 
 153     if (tree->selected_ptr == entry)
 154     {
 155         if (tree->selected_ptr->next != NULL)
 156             tree->selected_ptr = tree->selected_ptr->next;
 157         else
 158             tree->selected_ptr = tree->selected_ptr->prev;
 159     }
 160 }
 161 
 162 /* --------------------------------------------------------------------------------------------- */
 163 /** Save the ${XDG_CACHE_HOME}/mc/Tree file */
 164 
 165 static void
 166 save_tree (WTree * tree)
     /* [previous][next][first][last][top][bottom][index][help]  */
 167 {
 168     int error;
 169 
 170     (void) tree;
 171 
 172     error = tree_store_save ();
 173     if (error != 0)
 174     {
 175         char *tree_name;
 176 
 177         tree_name = mc_config_get_full_path (MC_TREESTORE_FILE);
 178         fprintf (stderr, _("Cannot open the %s file for writing:\n%s\n"), tree_name,
 179                  unix_error_string (error));
 180         g_free (tree_name);
 181     }
 182 }
 183 
 184 /* --------------------------------------------------------------------------------------------- */
 185 
 186 static void
 187 tree_remove_entry (WTree * tree, const vfs_path_t * name_vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
 188 {
 189     (void) tree;
 190     tree_store_remove_entry (name_vpath);
 191 }
 192 
 193 /* --------------------------------------------------------------------------------------------- */
 194 
 195 static void
 196 tree_destroy (WTree * tree)
     /* [previous][next][first][last][top][bottom][index][help]  */
 197 {
 198     tree_store_remove_entry_remove_hook (remove_callback);
 199     save_tree (tree);
 200 
 201     MC_PTR_FREE (tree->tree_shown);
 202     tree->selected_ptr = NULL;
 203 }
 204 
 205 /* --------------------------------------------------------------------------------------------- */
 206 /** Loads the .mc.tree file */
 207 
 208 static void
 209 load_tree (WTree * tree)
     /* [previous][next][first][last][top][bottom][index][help]  */
 210 {
 211     vfs_path_t *vpath;
 212 
 213     tree_store_load ();
 214 
 215     tree->selected_ptr = tree->store->tree_first;
 216     vpath = vfs_path_from_str (mc_config_get_home_dir ());
 217     tree_chdir (tree, vpath);
 218     vfs_path_free (vpath);
 219 }
 220 
 221 /* --------------------------------------------------------------------------------------------- */
 222 
 223 static void
 224 tree_show_mini_info (WTree * tree, int tree_lines, int tree_cols)
     /* [previous][next][first][last][top][bottom][index][help]  */
 225 {
 226     Widget *w = WIDGET (tree);
 227     int line;
 228 
 229     /* Show mini info */
 230     if (tree->is_panel)
 231     {
 232         if (!panels_options.show_mini_info)
 233             return;
 234         line = tree_lines + 2;
 235     }
 236     else
 237         line = tree_lines + 1;
 238 
 239     if (tree->searching)
 240     {
 241         /* Show search string */
 242         tty_setcolor (INPUT_COLOR);
 243         tty_draw_hline (w->y + line, w->x + 1, ' ', tree_cols);
 244         widget_move (w, 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 (' ');
 248     }
 249     else
 250     {
 251         /* Show full name of selected directory */
 252         WDialog *h = w->owner;
 253 
 254         tty_setcolor (tree->is_panel ? NORMAL_COLOR : TREE_NORMALC (h));
 255         tty_draw_hline (w->y + line, w->x + 1, ' ', tree_cols);
 256         widget_move (w, line, 1);
 257         tty_print_string (str_fit_to_term
 258                           (vfs_path_as_str (tree->selected_ptr->name), tree_cols, J_LEFT_FIT));
 259     }
 260 }
 261 
 262 /* --------------------------------------------------------------------------------------------- */
 263 
 264 static void
 265 show_tree (WTree * tree)
     /* [previous][next][first][last][top][bottom][index][help]  */
 266 {
 267     Widget *w = WIDGET (tree);
 268     WDialog *h = w->owner;
 269     tree_entry *current;
 270     int i, j;
 271     int topsublevel = 0;
 272     int x = 0, y = 0;
 273     int tree_lines, tree_cols;
 274 
 275     /* Initialize */
 276     tree_lines = tlines (tree);
 277     tree_cols = w->cols;
 278 
 279     widget_move (w, y, x);
 280     if (tree->is_panel)
 281     {
 282         tree_cols -= 2;
 283         x = y = 1;
 284     }
 285 
 286     g_free (tree->tree_shown);
 287     tree->tree_shown = g_new0 (tree_entry *, tree_lines);
 288 
 289     if (tree->store->tree_first != NULL)
 290         topsublevel = tree->store->tree_first->sublevel;
 291 
 292     if (tree->selected_ptr == NULL)
 293     {
 294         tree->selected_ptr = tree->store->tree_first;
 295         tree->topdiff = 0;
 296     }
 297     current = tree->selected_ptr;
 298 
 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
 303     {
 304         i = 0;
 305 
 306         while (current->prev != NULL && i < tree->topdiff)
 307         {
 308             current = current->prev;
 309 
 310             if (current->sublevel < tree->selected_ptr->sublevel)
 311             {
 312                 if (vfs_path_equal (current->name, tree->selected_ptr->name))
 313                     i++;
 314             }
 315             else if (current->sublevel == tree->selected_ptr->sublevel)
 316             {
 317                 const char *cname;
 318 
 319                 cname = vfs_path_as_str (current->name);
 320                 for (j = strlen (cname) - 1; !IS_PATH_SEP (cname[j]); j--)
 321                     ;
 322                 if (vfs_path_equal_len (current->name, tree->selected_ptr->name, j))
 323                     i++;
 324             }
 325             else if (current->sublevel == tree->selected_ptr->sublevel + 1)
 326             {
 327                 j = vfs_path_len (tree->selected_ptr->name);
 328                 if (j > 1 && vfs_path_equal_len (current->name, tree->selected_ptr->name, j))
 329                     i++;
 330             }
 331         }
 332         tree->topdiff = i;
 333     }
 334 
 335     /* Loop for every line */
 336     for (i = 0; i < tree_lines; i++)
 337     {
 338         tty_setcolor (tree->is_panel ? NORMAL_COLOR : TREE_NORMALC (h));
 339 
 340         /* Move to the beginning of the line */
 341         tty_draw_hline (w->y + y + i, w->x + x, ' ', tree_cols);
 342 
 343         if (current == NULL)
 344             continue;
 345 
 346         if (tree->is_panel)
 347             tty_setcolor (widget_get_state (w, WST_FOCUSED) && current == tree->selected_ptr
 348                           ? SELECTED_COLOR : NORMAL_COLOR);
 349         else
 350             tty_setcolor (current == tree->selected_ptr ? TREE_CURRENTC (h) : TREE_NORMALC (h));
 351 
 352         tree->tree_shown[i] = current;
 353         if (current->sublevel == topsublevel)
 354             /* Show full name */
 355             tty_print_string (str_fit_to_term
 356                               (vfs_path_as_str (current->name),
 357                                tree_cols + (tree->is_panel ? 0 : 1), J_LEFT_FIT));
 358         else
 359         {
 360             /* Sub level directory */
 361             tty_set_alt_charset (TRUE);
 362 
 363             /* Output branch parts */
 364             for (j = 0; j < current->sublevel - topsublevel - 1; j++)
 365             {
 366                 if (tree_cols - 8 - 3 * j < 9)
 367                     break;
 368                 tty_print_char (' ');
 369                 if ((current->submask & (1 << (j + topsublevel + 1))) != 0)
 370                     tty_print_char (ACS_VLINE);
 371                 else
 372                     tty_print_char (' ');
 373                 tty_print_char (' ');
 374             }
 375             tty_print_char (' ');
 376             j++;
 377             if (current->next == NULL || (current->next->submask & (1 << current->sublevel)) == 0)
 378                 tty_print_char (ACS_LLCORNER);
 379             else
 380                 tty_print_char (ACS_LTEE);
 381             tty_print_char (ACS_HLINE);
 382             tty_set_alt_charset (FALSE);
 383 
 384             /* Show sub-name */
 385             tty_print_char (' ');
 386             tty_print_string (str_fit_to_term
 387                               (current->subname, tree_cols - x - 3 * j, J_LEFT_FIT));
 388         }
 389 
 390         /* Calculate the next value for current */
 391         current = current->next;
 392         if (tree_navigation_flag)
 393             for (; current != NULL; current = current->next)
 394             {
 395                 if (current->sublevel < tree->selected_ptr->sublevel)
 396                 {
 397                     if (vfs_path_equal_len (current->name, tree->selected_ptr->name,
 398                                             vfs_path_len (current->name)))
 399                         break;
 400                 }
 401                 else if (current->sublevel == tree->selected_ptr->sublevel)
 402                 {
 403                     const char *cname;
 404 
 405                     cname = vfs_path_as_str (current->name);
 406                     for (j = strlen (cname) - 1; !IS_PATH_SEP (cname[j]); j--)
 407                         ;
 408                     if (vfs_path_equal_len (current->name, tree->selected_ptr->name, j))
 409                         break;
 410                 }
 411                 else if (current->sublevel == tree->selected_ptr->sublevel + 1
 412                          && vfs_path_len (tree->selected_ptr->name) > 1)
 413                 {
 414                     if (vfs_path_equal_len (current->name, tree->selected_ptr->name,
 415                                             vfs_path_len (tree->selected_ptr->name)))
 416                         break;
 417                 }
 418             }
 419     }
 420 
 421     tree_show_mini_info (tree, tree_lines, tree_cols);
 422 }
 423 
 424 /* --------------------------------------------------------------------------------------------- */
 425 
 426 static void
 427 tree_check_focus (WTree * tree)
     /* [previous][next][first][last][top][bottom][index][help]  */
 428 {
 429     if (tree->topdiff < 3)
 430         tree->topdiff = 3;
 431     else if (tree->topdiff >= tlines (tree) - 3)
 432         tree->topdiff = tlines (tree) - 3 - 1;
 433 }
 434 
 435 /* --------------------------------------------------------------------------------------------- */
 436 
 437 static void
 438 tree_move_backward (WTree * tree, int i)
     /* [previous][next][first][last][top][bottom][index][help]  */
 439 {
 440     if (!tree_navigation_flag)
 441         tree->selected_ptr = back_ptr (tree->selected_ptr, &i);
 442     else
 443     {
 444         tree_entry *current;
 445         int j = 0;
 446 
 447         current = tree->selected_ptr;
 448         while (j < i && current->prev != NULL
 449                && current->prev->sublevel >= tree->selected_ptr->sublevel)
 450         {
 451             current = current->prev;
 452             if (current->sublevel == tree->selected_ptr->sublevel)
 453             {
 454                 tree->selected_ptr = current;
 455                 j++;
 456             }
 457         }
 458         i = j;
 459     }
 460 
 461     tree->topdiff -= i;
 462     tree_check_focus (tree);
 463 }
 464 
 465 /* --------------------------------------------------------------------------------------------- */
 466 
 467 static void
 468 tree_move_forward (WTree * tree, int i)
     /* [previous][next][first][last][top][bottom][index][help]  */
 469 {
 470     if (!tree_navigation_flag)
 471         tree->selected_ptr = forw_ptr (tree->selected_ptr, &i);
 472     else
 473     {
 474         tree_entry *current;
 475         int j = 0;
 476 
 477         current = tree->selected_ptr;
 478         while (j < i && current->next != NULL
 479                && current->next->sublevel >= tree->selected_ptr->sublevel)
 480         {
 481             current = current->next;
 482             if (current->sublevel == tree->selected_ptr->sublevel)
 483             {
 484                 tree->selected_ptr = current;
 485                 j++;
 486             }
 487         }
 488         i = j;
 489     }
 490 
 491     tree->topdiff += i;
 492     tree_check_focus (tree);
 493 }
 494 
 495 /* --------------------------------------------------------------------------------------------- */
 496 
 497 static void
 498 tree_move_to_child (WTree * tree)
     /* [previous][next][first][last][top][bottom][index][help]  */
 499 {
 500     tree_entry *current;
 501 
 502     /* Do we have a starting point? */
 503     if (tree->selected_ptr == NULL)
 504         return;
 505 
 506     /* Take the next entry */
 507     current = tree->selected_ptr->next;
 508     /* Is it the child of the selected entry */
 509     if (current != NULL && current->sublevel > tree->selected_ptr->sublevel)
 510     {
 511         /* Yes -> select this entry */
 512         tree->selected_ptr = current;
 513         tree->topdiff++;
 514         tree_check_focus (tree);
 515     }
 516     else
 517     {
 518         /* No -> rescan and try again */
 519         tree_rescan (tree);
 520         current = tree->selected_ptr->next;
 521         if (current != NULL && current->sublevel > tree->selected_ptr->sublevel)
 522         {
 523             tree->selected_ptr = current;
 524             tree->topdiff++;
 525             tree_check_focus (tree);
 526         }
 527     }
 528 }
 529 
 530 /* --------------------------------------------------------------------------------------------- */
 531 
 532 static gboolean
 533 tree_move_to_parent (WTree * tree)
     /* [previous][next][first][last][top][bottom][index][help]  */
 534 {
 535     tree_entry *current;
 536     tree_entry *old;
 537 
 538     if (tree->selected_ptr == NULL)
 539         return FALSE;
 540 
 541     old = tree->selected_ptr;
 542 
 543     for (current = tree->selected_ptr->prev;
 544          current != NULL && current->sublevel >= tree->selected_ptr->sublevel;
 545          current = current->prev)
 546         tree->topdiff--;
 547 
 548     if (current == NULL)
 549         current = tree->store->tree_first;
 550     tree->selected_ptr = current;
 551     tree_check_focus (tree);
 552     return tree->selected_ptr != old;
 553 }
 554 
 555 /* --------------------------------------------------------------------------------------------- */
 556 
 557 static void
 558 tree_move_to_top (WTree * tree)
     /* [previous][next][first][last][top][bottom][index][help]  */
 559 {
 560     tree->selected_ptr = tree->store->tree_first;
 561     tree->topdiff = 0;
 562 }
 563 
 564 /* --------------------------------------------------------------------------------------------- */
 565 
 566 static void
 567 tree_move_to_bottom (WTree * tree)
     /* [previous][next][first][last][top][bottom][index][help]  */
 568 {
 569     tree->selected_ptr = tree->store->tree_last;
 570     tree->topdiff = tlines (tree) - 3 - 1;
 571 }
 572 
 573 /* --------------------------------------------------------------------------------------------- */
 574 
 575 static void
 576 tree_chdir_sel (WTree * tree)
     /* [previous][next][first][last][top][bottom][index][help]  */
 577 {
 578     if (tree->is_panel)
 579     {
 580         change_panel ();
 581 
 582         if (do_cd (tree->selected_ptr->name, cd_exact))
 583             select_item (current_panel);
 584         else
 585             message (D_ERROR, MSG_ERROR, _("Cannot chdir to \"%s\"\n%s"),
 586                      vfs_path_as_str (tree->selected_ptr->name), unix_error_string (errno));
 587 
 588         widget_redraw (WIDGET (current_panel));
 589         change_panel ();
 590         show_tree (tree);
 591     }
 592     else
 593     {
 594         WDialog *h = WIDGET (tree)->owner;
 595 
 596         h->ret_value = B_ENTER;
 597         dlg_stop (h);
 598     }
 599 }
 600 
 601 /* --------------------------------------------------------------------------------------------- */
 602 
 603 static void
 604 maybe_chdir (WTree * tree)
     /* [previous][next][first][last][top][bottom][index][help]  */
 605 {
 606     if (xtree_mode && tree->is_panel && is_idle ())
 607         tree_chdir_sel (tree);
 608 }
 609 
 610 /* --------------------------------------------------------------------------------------------- */
 611 /** Search tree for text */
 612 
 613 static int
 614 search_tree (WTree * tree, char *text)
     /* [previous][next][first][last][top][bottom][index][help]  */
 615 {
 616     tree_entry *current;
 617     size_t len;
 618     gboolean wrapped = FALSE;
 619     gboolean found = FALSE;
 620 
 621     len = strlen (text);
 622     current = tree->selected_ptr;
 623 
 624     while (!found && (!wrapped || current != tree->selected_ptr))
 625         if (strncmp (current->subname, text, len) == 0)
 626         {
 627             tree->selected_ptr = current;
 628             found = TRUE;
 629         }
 630         else
 631         {
 632             current = current->next;
 633             if (current == NULL)
 634             {
 635                 current = tree->store->tree_first;
 636                 wrapped = TRUE;
 637             }
 638 
 639             tree->topdiff++;
 640         }
 641 
 642     tree_check_focus (tree);
 643     return found;
 644 }
 645 
 646 /* --------------------------------------------------------------------------------------------- */
 647 
 648 static void
 649 tree_do_search (WTree * tree, int key)
     /* [previous][next][first][last][top][bottom][index][help]  */
 650 {
 651     size_t l;
 652 
 653     l = strlen (tree->search_buffer);
 654     if ((l != 0) && (key == KEY_BACKSPACE))
 655         tree->search_buffer[--l] = '\0';
 656     else if (key && l < sizeof (tree->search_buffer) - 1)
 657     {
 658         tree->search_buffer[l] = key;
 659         tree->search_buffer[++l] = '\0';
 660     }
 661 
 662     if (!search_tree (tree, tree->search_buffer))
 663         tree->search_buffer[--l] = '\0';
 664 
 665     show_tree (tree);
 666     maybe_chdir (tree);
 667 }
 668 
 669 /* --------------------------------------------------------------------------------------------- */
 670 
 671 static void
 672 tree_rescan (void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
 673 {
 674     WTree *tree = data;
 675     vfs_path_t *old_vpath;
 676 
 677     old_vpath = vfs_path_clone (vfs_get_raw_current_dir ());
 678     if (old_vpath == NULL)
 679         return;
 680 
 681     if (tree->selected_ptr != NULL && mc_chdir (tree->selected_ptr->name) == 0)
 682     {
 683         int ret;
 684 
 685         tree_store_rescan (tree->selected_ptr->name);
 686         ret = mc_chdir (old_vpath);
 687         (void) ret;
 688     }
 689     vfs_path_free (old_vpath);
 690 }
 691 
 692 /* --------------------------------------------------------------------------------------------- */
 693 
 694 static void
 695 tree_forget (void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
 696 {
 697     WTree *tree = data;
 698 
 699     if (tree->selected_ptr != NULL)
 700         tree_remove_entry (tree, tree->selected_ptr->name);
 701 }
 702 
 703 /* --------------------------------------------------------------------------------------------- */
 704 
 705 static void
 706 tree_copy (WTree * tree, const char *default_dest)
     /* [previous][next][first][last][top][bottom][index][help]  */
 707 {
 708     char msg[BUF_MEDIUM];
 709     char *dest;
 710 
 711     if (tree->selected_ptr == NULL)
 712         return;
 713 
 714     g_snprintf (msg, sizeof (msg), _("Copy \"%s\" directory to:"),
 715                 str_trunc (vfs_path_as_str (tree->selected_ptr->name), 50));
 716     dest = input_expand_dialog (Q_ ("DialogTitle|Copy"),
 717                                 msg, MC_HISTORY_FM_TREE_COPY, default_dest,
 718                                 INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_CD);
 719 
 720     if (dest != NULL && *dest != '\0')
 721     {
 722         file_op_context_t *ctx;
 723         file_op_total_context_t *tctx;
 724 
 725         ctx = file_op_context_new (OP_COPY);
 726         tctx = file_op_total_context_new ();
 727         file_op_context_create_ui (ctx, FALSE, FILEGUI_DIALOG_MULTI_ITEM);
 728         tctx->ask_overwrite = FALSE;
 729         copy_dir_dir (tctx, ctx, vfs_path_as_str (tree->selected_ptr->name), dest, TRUE, FALSE,
 730                       FALSE, NULL);
 731         file_op_total_context_destroy (tctx);
 732         file_op_context_destroy (ctx);
 733     }
 734 
 735     g_free (dest);
 736 }
 737 
 738 /* --------------------------------------------------------------------------------------------- */
 739 
 740 static void
 741 tree_move (WTree * tree, const char *default_dest)
     /* [previous][next][first][last][top][bottom][index][help]  */
 742 {
 743     char msg[BUF_MEDIUM];
 744     char *dest;
 745     struct stat buf;
 746     file_op_context_t *ctx;
 747     file_op_total_context_t *tctx;
 748     vfs_path_t *dest_vpath = NULL;
 749 
 750     if (tree->selected_ptr == NULL)
 751         return;
 752 
 753     g_snprintf (msg, sizeof (msg), _("Move \"%s\" directory to:"),
 754                 str_trunc (vfs_path_as_str (tree->selected_ptr->name), 50));
 755     dest =
 756         input_expand_dialog (Q_ ("DialogTitle|Move"), msg, MC_HISTORY_FM_TREE_MOVE, default_dest,
 757                              INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_CD);
 758 
 759     if (dest == NULL || *dest == '\0')
 760         goto ret;
 761 
 762     dest_vpath = vfs_path_from_str (dest);
 763 
 764     if (mc_stat (dest_vpath, &buf))
 765     {
 766         message (D_ERROR, MSG_ERROR, _("Cannot stat the destination\n%s"),
 767                  unix_error_string (errno));
 768         goto ret;
 769     }
 770 
 771     if (!S_ISDIR (buf.st_mode))
 772     {
 773         file_error (TRUE, _("Destination \"%s\" must be a directory\n%s"), dest);
 774         goto ret;
 775     }
 776 
 777     ctx = file_op_context_new (OP_MOVE);
 778     tctx = file_op_total_context_new ();
 779     file_op_context_create_ui (ctx, FALSE, FILEGUI_DIALOG_ONE_ITEM);
 780     move_dir_dir (tctx, ctx, vfs_path_as_str (tree->selected_ptr->name), dest);
 781     file_op_total_context_destroy (tctx);
 782     file_op_context_destroy (ctx);
 783 
 784   ret:
 785     vfs_path_free (dest_vpath);
 786     g_free (dest);
 787 }
 788 
 789 /* --------------------------------------------------------------------------------------------- */
 790 
 791 #if 0
 792 static void
 793 tree_mkdir (WTree * tree)
     /* [previous][next][first][last][top][bottom][index][help]  */
 794 {
 795     char old_dir[MC_MAXPATHLEN];
 796 
 797     if (tree->selected_ptr == NULL || chdir (tree->selected_ptr->name) != 0)
 798         return;
 799     /* FIXME
 800        mkdir_cmd (tree);
 801      */
 802     tree_rescan (tree);
 803     chdir (old_dir);
 804 }
 805 #endif
 806 
 807 /* --------------------------------------------------------------------------------------------- */
 808 
 809 static void
 810 tree_rmdir (void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
 811 {
 812     WTree *tree = data;
 813     file_op_context_t *ctx;
 814     file_op_total_context_t *tctx;
 815 
 816     if (tree->selected_ptr == NULL)
 817         return;
 818 
 819     if (confirm_delete)
 820     {
 821         char *buf;
 822         int result;
 823 
 824         buf = g_strdup_printf (_("Delete %s?"), vfs_path_as_str (tree->selected_ptr->name));
 825 
 826         result = query_dialog (Q_ ("DialogTitle|Delete"), buf, D_ERROR, 2, _("&Yes"), _("&No"));
 827         g_free (buf);
 828         if (result != 0)
 829             return;
 830     }
 831 
 832     ctx = file_op_context_new (OP_DELETE);
 833     tctx = file_op_total_context_new ();
 834 
 835     file_op_context_create_ui (ctx, FALSE, FILEGUI_DIALOG_ONE_ITEM);
 836     if (erase_dir (tctx, ctx, tree->selected_ptr->name) == FILE_CONT)
 837         tree_forget (tree);
 838     file_op_total_context_destroy (tctx);
 839     file_op_context_destroy (ctx);
 840 }
 841 
 842 /* --------------------------------------------------------------------------------------------- */
 843 
 844 static inline void
 845 tree_move_up (WTree * tree)
     /* [previous][next][first][last][top][bottom][index][help]  */
 846 {
 847     tree_move_backward (tree, 1);
 848     show_tree (tree);
 849     maybe_chdir (tree);
 850 }
 851 
 852 /* --------------------------------------------------------------------------------------------- */
 853 
 854 static inline void
 855 tree_move_down (WTree * tree)
     /* [previous][next][first][last][top][bottom][index][help]  */
 856 {
 857     tree_move_forward (tree, 1);
 858     show_tree (tree);
 859     maybe_chdir (tree);
 860 }
 861 
 862 /* --------------------------------------------------------------------------------------------- */
 863 
 864 static inline void
 865 tree_move_home (WTree * tree)
     /* [previous][next][first][last][top][bottom][index][help]  */
 866 {
 867     tree_move_to_top (tree);
 868     show_tree (tree);
 869     maybe_chdir (tree);
 870 }
 871 
 872 /* --------------------------------------------------------------------------------------------- */
 873 
 874 static inline void
 875 tree_move_end (WTree * tree)
     /* [previous][next][first][last][top][bottom][index][help]  */
 876 {
 877     tree_move_to_bottom (tree);
 878     show_tree (tree);
 879     maybe_chdir (tree);
 880 }
 881 
 882 /* --------------------------------------------------------------------------------------------- */
 883 
 884 static void
 885 tree_move_pgup (WTree * tree)
     /* [previous][next][first][last][top][bottom][index][help]  */
 886 {
 887     tree_move_backward (tree, tlines (tree) - 1);
 888     show_tree (tree);
 889     maybe_chdir (tree);
 890 }
 891 
 892 /* --------------------------------------------------------------------------------------------- */
 893 
 894 static void
 895 tree_move_pgdn (WTree * tree)
     /* [previous][next][first][last][top][bottom][index][help]  */
 896 {
 897     tree_move_forward (tree, tlines (tree) - 1);
 898     show_tree (tree);
 899     maybe_chdir (tree);
 900 }
 901 
 902 /* --------------------------------------------------------------------------------------------- */
 903 
 904 static gboolean
 905 tree_move_left (WTree * tree)
     /* [previous][next][first][last][top][bottom][index][help]  */
 906 {
 907     gboolean v = FALSE;
 908 
 909     if (tree_navigation_flag)
 910     {
 911         v = tree_move_to_parent (tree);
 912         show_tree (tree);
 913         maybe_chdir (tree);
 914     }
 915 
 916     return v;
 917 }
 918 
 919 /* --------------------------------------------------------------------------------------------- */
 920 
 921 static gboolean
 922 tree_move_right (WTree * tree)
     /* [previous][next][first][last][top][bottom][index][help]  */
 923 {
 924     gboolean v = FALSE;
 925 
 926     if (tree_navigation_flag)
 927     {
 928         tree_move_to_child (tree);
 929         show_tree (tree);
 930         maybe_chdir (tree);
 931         v = TRUE;
 932     }
 933 
 934     return v;
 935 }
 936 
 937 /* --------------------------------------------------------------------------------------------- */
 938 
 939 static void
 940 tree_start_search (WTree * tree)
     /* [previous][next][first][last][top][bottom][index][help]  */
 941 {
 942     if (tree->searching)
 943     {
 944         if (tree->selected_ptr == tree->store->tree_last)
 945             tree_move_to_top (tree);
 946         else
 947         {
 948             gboolean i;
 949 
 950             /* set navigation mode temporarily to 'Static' because in
 951              * dynamic navigation mode tree_move_forward will not move
 952              * to a lower sublevel if necessary (sequent searches must
 953              * start with the directory followed the last found directory)
 954              */
 955             i = tree_navigation_flag;
 956             tree_navigation_flag = FALSE;
 957             tree_move_forward (tree, 1);
 958             tree_navigation_flag = i;
 959         }
 960         tree_do_search (tree, 0);
 961     }
 962     else
 963     {
 964         tree->searching = TRUE;
 965         tree->search_buffer[0] = '\0';
 966     }
 967 }
 968 
 969 /* --------------------------------------------------------------------------------------------- */
 970 
 971 static void
 972 tree_toggle_navig (WTree * tree)
     /* [previous][next][first][last][top][bottom][index][help]  */
 973 {
 974     WButtonBar *b;
 975 
 976     tree_navigation_flag = !tree_navigation_flag;
 977 
 978     b = find_buttonbar (WIDGET (tree)->owner);
 979     buttonbar_set_label (b, 4,
 980                          tree_navigation_flag ? Q_ ("ButtonBar|Static") : Q_ ("ButtonBar|Dynamc"),
 981                          tree_map, WIDGET (tree));
 982     widget_redraw (WIDGET (b));
 983 }
 984 
 985 /* --------------------------------------------------------------------------------------------- */
 986 
 987 static cb_ret_t
 988 tree_execute_cmd (WTree * tree, long command)
     /* [previous][next][first][last][top][bottom][index][help]  */
 989 {
 990     cb_ret_t res = MSG_HANDLED;
 991 
 992     if (command != CK_Search)
 993         tree->searching = FALSE;
 994 
 995     switch (command)
 996     {
 997     case CK_Help:
 998         {
 999             ev_help_t event_data = { NULL, "[Directory Tree]" };
1000             mc_event_raise (MCEVENT_GROUP_CORE, "help", &event_data);
1001         }
1002         break;
1003     case CK_Forget:
1004         tree_forget (tree);
1005         break;
1006     case CK_ToggleNavigation:
1007         tree_toggle_navig (tree);
1008         break;
1009     case CK_Copy:
1010         tree_copy (tree, "");
1011         break;
1012     case CK_Move:
1013         tree_move (tree, "");
1014         break;
1015     case CK_Up:
1016         tree_move_up (tree);
1017         break;
1018     case CK_Down:
1019         tree_move_down (tree);
1020         break;
1021     case CK_Top:
1022         tree_move_home (tree);
1023         break;
1024     case CK_Bottom:
1025         tree_move_end (tree);
1026         break;
1027     case CK_PageUp:
1028         tree_move_pgup (tree);
1029         break;
1030     case CK_PageDown:
1031         tree_move_pgdn (tree);
1032         break;
1033     case CK_Enter:
1034         tree_chdir_sel (tree);
1035         break;
1036     case CK_Reread:
1037         tree_rescan (tree);
1038         break;
1039     case CK_Search:
1040         tree_start_search (tree);
1041         break;
1042     case CK_Delete:
1043         tree_rmdir (tree);
1044         break;
1045     case CK_Quit:
1046         if (!tree->is_panel)
1047             dlg_stop (WIDGET (tree)->owner);
1048         return res;
1049     default:
1050         res = MSG_NOT_HANDLED;
1051     }
1052 
1053     show_tree (tree);
1054 
1055     return res;
1056 }
1057 
1058 /* --------------------------------------------------------------------------------------------- */
1059 
1060 static cb_ret_t
1061 tree_key (WTree * tree, int key)
     /* [previous][next][first][last][top][bottom][index][help]  */
1062 {
1063     size_t i;
1064 
1065     if (is_abort_char (key))
1066     {
1067         if (tree->is_panel)
1068         {
1069             tree->searching = FALSE;
1070             show_tree (tree);
1071             return MSG_HANDLED; /* eat abort char */
1072         }
1073         /* modal tree dialog: let upper layer see the
1074            abort character and close the dialog */
1075         return MSG_NOT_HANDLED;
1076     }
1077 
1078     if (tree->searching && ((key >= ' ' && key <= 255) || key == KEY_BACKSPACE))
1079     {
1080         tree_do_search (tree, key);
1081         show_tree (tree);
1082         return MSG_HANDLED;
1083     }
1084 
1085     for (i = 0; tree_map[i].key != 0; i++)
1086         if (key == tree_map[i].key)
1087             switch (tree_map[i].command)
1088             {
1089             case CK_Left:
1090                 return tree_move_left (tree) ? MSG_HANDLED : MSG_NOT_HANDLED;
1091             case CK_Right:
1092                 return tree_move_right (tree) ? MSG_HANDLED : MSG_NOT_HANDLED;
1093             default:
1094                 tree_execute_cmd (tree, tree_map[i].command);
1095                 return MSG_HANDLED;
1096             }
1097 
1098     /* Do not eat characters not meant for the tree below ' ' (e.g. C-l). */
1099     if (!command_prompt && ((key >= ' ' && key <= 255) || key == KEY_BACKSPACE))
1100     {
1101         tree_start_search (tree);
1102         tree_do_search (tree, key);
1103         return MSG_HANDLED;
1104     }
1105 
1106     return MSG_NOT_HANDLED;
1107 }
1108 
1109 /* --------------------------------------------------------------------------------------------- */
1110 
1111 static void
1112 tree_frame (WDialog * h, WTree * tree)
     /* [previous][next][first][last][top][bottom][index][help]  */
1113 {
1114     Widget *w = WIDGET (tree);
1115 
1116     (void) h;
1117 
1118     tty_setcolor (NORMAL_COLOR);
1119     widget_erase (w);
1120     if (tree->is_panel)
1121     {
1122         const char *title = _("Directory tree");
1123         const int len = str_term_width1 (title);
1124 
1125         tty_draw_box (w->y, w->x, w->lines, w->cols, FALSE);
1126 
1127         widget_move (w, 0, (w->cols - len - 2) / 2);
1128         tty_printf (" %s ", title);
1129 
1130         if (panels_options.show_mini_info)
1131         {
1132             int y;
1133 
1134             y = w->lines - 3;
1135             widget_move (w, y, 0);
1136             tty_print_alt_char (ACS_LTEE, FALSE);
1137             widget_move (w, y, w->cols - 1);
1138             tty_print_alt_char (ACS_RTEE, FALSE);
1139             tty_draw_hline (w->y + y, w->x + 1, ACS_HLINE, w->cols - 2);
1140         }
1141     }
1142 }
1143 
1144 /* --------------------------------------------------------------------------------------------- */
1145 
1146 static cb_ret_t
1147 tree_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
1148 {
1149     WTree *tree = (WTree *) w;
1150     WDialog *h = w->owner;
1151     WButtonBar *b;
1152 
1153     switch (msg)
1154     {
1155     case MSG_DRAW:
1156         tree_frame (h, tree);
1157         show_tree (tree);
1158         if (widget_get_state (w, WST_FOCUSED))
1159         {
1160             b = find_buttonbar (h);
1161             widget_redraw (WIDGET (b));
1162         }
1163         return MSG_HANDLED;
1164 
1165     case MSG_FOCUS:
1166         b = find_buttonbar (h);
1167         buttonbar_set_label (b, 1, Q_ ("ButtonBar|Help"), tree_map, w);
1168         buttonbar_set_label (b, 2, Q_ ("ButtonBar|Rescan"), tree_map, w);
1169         buttonbar_set_label (b, 3, Q_ ("ButtonBar|Forget"), tree_map, w);
1170         buttonbar_set_label (b, 4, tree_navigation_flag ? Q_ ("ButtonBar|Static")
1171                              : Q_ ("ButtonBar|Dynamc"), tree_map, w);
1172         buttonbar_set_label (b, 5, Q_ ("ButtonBar|Copy"), tree_map, w);
1173         buttonbar_set_label (b, 6, Q_ ("ButtonBar|RenMov"), tree_map, w);
1174 #if 0
1175         /* FIXME: mkdir is currently defunct */
1176         buttonbar_set_label (b, 7, Q_ ("ButtonBar|Mkdir"), tree_map, w);
1177 #else
1178         buttonbar_clear_label (b, 7, w);
1179 #endif
1180         buttonbar_set_label (b, 8, Q_ ("ButtonBar|Rmdir"), tree_map, w);
1181 
1182         return MSG_HANDLED;
1183 
1184     case MSG_UNFOCUS:
1185         tree->searching = FALSE;
1186         return MSG_HANDLED;
1187 
1188     case MSG_KEY:
1189         return tree_key (tree, parm);
1190 
1191     case MSG_ACTION:
1192         /* command from buttonbar */
1193         return tree_execute_cmd (tree, parm);
1194 
1195     case MSG_DESTROY:
1196         tree_destroy (tree);
1197         return MSG_HANDLED;
1198 
1199     default:
1200         return widget_default_callback (w, sender, msg, parm, data);
1201     }
1202 }
1203 
1204 /* --------------------------------------------------------------------------------------------- */
1205 
1206 /**
1207   * Mouse callback
1208   */
1209 static void
1210 tree_mouse_callback (Widget * w, mouse_msg_t msg, mouse_event_t * event)
     /* [previous][next][first][last][top][bottom][index][help]  */
1211 {
1212     WTree *tree = (WTree *) w;
1213     int y;
1214 
1215     y = event->y;
1216     if (tree->is_panel)
1217         y--;
1218 
1219     switch (msg)
1220     {
1221     case MSG_MOUSE_DOWN:
1222         /* rest of the upper frame - call menu */
1223         if (tree->is_panel && event->y == WIDGET (w->owner)->y)
1224         {
1225             /* return MOU_UNHANDLED */
1226             event->result.abort = TRUE;
1227         }
1228         else if (!widget_get_state (w, WST_FOCUSED))
1229             change_panel ();
1230         break;
1231 
1232     case MSG_MOUSE_CLICK:
1233         {
1234             int lines;
1235 
1236             lines = tlines (tree);
1237 
1238             if (y < 0)
1239             {
1240                 tree_move_backward (tree, lines - 1);
1241                 show_tree (tree);
1242             }
1243             else if (y >= lines)
1244             {
1245                 tree_move_forward (tree, lines - 1);
1246                 show_tree (tree);
1247             }
1248             else if ((event->count & GPM_DOUBLE) != 0)
1249             {
1250                 if (tree->tree_shown[y] != NULL)
1251                 {
1252                     tree->selected_ptr = tree->tree_shown[y];
1253                     tree->topdiff = y;
1254                 }
1255 
1256                 tree_chdir_sel (tree);
1257             }
1258         }
1259         break;
1260 
1261     case MSG_MOUSE_SCROLL_UP:
1262     case MSG_MOUSE_SCROLL_DOWN:
1263         /* TODO: Ticket #2218 */
1264         break;
1265 
1266     default:
1267         break;
1268     }
1269 }
1270 
1271 /* --------------------------------------------------------------------------------------------- */
1272 /*** public functions ****************************************************************************/
1273 /* --------------------------------------------------------------------------------------------- */
1274 
1275 WTree *
1276 tree_new (int y, int x, int lines, int cols, gboolean is_panel)
     /* [previous][next][first][last][top][bottom][index][help]  */
1277 {
1278     WTree *tree;
1279     Widget *w;
1280 
1281     tree = g_new (WTree, 1);
1282     w = WIDGET (tree);
1283 
1284     widget_init (w, y, x, lines, cols, tree_callback, tree_mouse_callback);
1285     w->options |= WOP_SELECTABLE | WOP_TOP_SELECT;
1286     tree->is_panel = is_panel;
1287     tree->selected_ptr = NULL;
1288 
1289     tree->store = tree_store_get ();
1290     tree_store_add_entry_remove_hook (remove_callback, tree);
1291     tree->tree_shown = NULL;
1292     tree->search_buffer[0] = '\0';
1293     tree->topdiff = w->lines / 2;
1294     tree->searching = FALSE;
1295 
1296     load_tree (tree);
1297     return tree;
1298 }
1299 
1300 /* --------------------------------------------------------------------------------------------- */
1301 
1302 void
1303 tree_chdir (WTree * tree, const vfs_path_t * dir)
     /* [previous][next][first][last][top][bottom][index][help]  */
1304 {
1305     tree_entry *current;
1306 
1307     current = tree_store_whereis (dir);
1308     if (current != NULL)
1309     {
1310         tree->selected_ptr = current;
1311         tree_check_focus (tree);
1312     }
1313 }
1314 
1315 /* --------------------------------------------------------------------------------------------- */
1316 /** Return name of the currently selected entry */
1317 
1318 const vfs_path_t *
1319 tree_selected_name (const WTree * tree)
     /* [previous][next][first][last][top][bottom][index][help]  */
1320 {
1321     return tree->selected_ptr->name;
1322 }
1323 
1324 /* --------------------------------------------------------------------------------------------- */
1325 
1326 void
1327 sync_tree (const vfs_path_t * vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
1328 {
1329     tree_chdir (the_tree, vpath);
1330 }
1331 
1332 /* --------------------------------------------------------------------------------------------- */
1333 
1334 WTree *
1335 find_tree (WDialog * h)
     /* [previous][next][first][last][top][bottom][index][help]  */
1336 {
1337     return (WTree *) find_widget_type (h, tree_callback);
1338 }
1339 
1340 /* --------------------------------------------------------------------------------------------- */

/* [previous][next][first][last][top][bottom][index][help]  */