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

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