tree.c 25 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079
  1. /* Directory tree browser for the Midnight Commander
  2. Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
  3. 2003, 2004, 2005, 2007 Free Software Foundation, Inc.
  4. Written: 1994, 1996 Janne Kukonlehto
  5. 1997 Norbert Warmuth
  6. 1996, 1999 Miguel de Icaza
  7. This program is free software; you can redistribute it and/or modify
  8. it under the terms of the GNU General Public License as published by
  9. the Free Software Foundation; either version 2 of the License, or
  10. (at your option) any later version.
  11. This program is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. GNU General Public License for more details.
  15. You should have received a copy of the GNU General Public License
  16. along with this program; if not, write to the Free Software
  17. Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  18. This module has been converted to be a widget.
  19. The program load and saves the tree each time the tree widget is
  20. created and destroyed. This is required for the future vfs layer,
  21. it will be possible to have tree views over virtual file systems.
  22. */
  23. /** \file tree.c
  24. * \brief Source: directory tree browser
  25. */
  26. #include <config.h>
  27. #include <errno.h>
  28. #include <stdio.h>
  29. #include <string.h>
  30. #include "global.h"
  31. #include "../src/tty/tty.h"
  32. #include "../src/tty/color.h"
  33. #include "../src/tty/mouse.h"
  34. #include "../src/tty/key.h"
  35. #include "wtools.h" /* message() */
  36. #include "dir.h"
  37. #include "dialog.h"
  38. #include "widget.h"
  39. #include "panel.h"
  40. #include "main.h"
  41. #include "file.h" /* For copy_dir_dir(), move_dir_dir(), erase_dir() */
  42. #include "help.h"
  43. #include "tree.h"
  44. #include "treestore.h"
  45. #include "cmd.h"
  46. #include "history.h"
  47. #include "strutil.h"
  48. #define tlines(t) (t->is_panel ? t->widget.lines-2 - (show_mini_info ? 2 : 0) : t->widget.lines)
  49. extern int command_prompt;
  50. /* Use the color of the parent widget for the unselected entries */
  51. #define TREE_NORMALC(h) (DLG_NORMALC (h))
  52. /* Specifies the display mode: 1d or 2d */
  53. static int tree_navigation_flag;
  54. struct WTree {
  55. Widget widget;
  56. struct TreeStore *store;
  57. tree_entry *selected_ptr; /* The selected directory */
  58. char search_buffer[256]; /* Current search string */
  59. tree_entry **tree_shown; /* Entries currently on screen */
  60. int is_panel; /* panel or plain widget flag */
  61. int active; /* if it's currently selected */
  62. int searching; /* Are we on searching mode? */
  63. int topdiff; /* The difference between the topmost
  64. shown and the selected */
  65. };
  66. /* Forwards */
  67. static void save_tree (WTree *tree);
  68. static void tree_rescan_cmd (WTree *);
  69. static tree_entry *back_ptr (tree_entry *ptr, int *count)
  70. {
  71. int i = 0;
  72. while (ptr && ptr->prev && i < *count){
  73. ptr = ptr->prev;
  74. i ++;
  75. }
  76. *count = i;
  77. return ptr;
  78. }
  79. static tree_entry *forw_ptr (tree_entry *ptr, int *count)
  80. {
  81. int i = 0;
  82. while (ptr && ptr->next && i < *count){
  83. ptr = ptr->next;
  84. i ++;
  85. }
  86. *count = i;
  87. return ptr;
  88. }
  89. static void
  90. remove_callback (tree_entry *entry, void *data)
  91. {
  92. WTree *tree = data;
  93. if (tree->selected_ptr == entry){
  94. if (tree->selected_ptr->next)
  95. tree->selected_ptr = tree->selected_ptr->next;
  96. else
  97. tree->selected_ptr = tree->selected_ptr->prev;
  98. }
  99. }
  100. static void tree_remove_entry (WTree *tree, char *name)
  101. {
  102. (void) tree;
  103. tree_store_remove_entry (name);
  104. }
  105. static void tree_destroy (WTree *tree)
  106. {
  107. tree_store_remove_entry_remove_hook (remove_callback);
  108. save_tree (tree);
  109. g_free (tree->tree_shown);
  110. tree->tree_shown = 0;
  111. tree->selected_ptr = NULL;
  112. }
  113. /* Loads the .mc.tree file */
  114. static void load_tree (WTree *tree)
  115. {
  116. tree_store_load ();
  117. tree->selected_ptr = tree->store->tree_first;
  118. tree_chdir (tree, home_dir);
  119. }
  120. /* Save the .mc.tree file */
  121. static void save_tree (WTree *tree)
  122. {
  123. int error;
  124. (void) tree;
  125. error = tree_store_save ();
  126. if (error){
  127. fprintf (stderr, _("Cannot open the %s file for writing:\n%s\n"), MC_TREE,
  128. unix_error_string (error));
  129. }
  130. }
  131. static void tree_show_mini_info (WTree *tree, int tree_lines, int tree_cols)
  132. {
  133. Dlg_head *h = tree->widget.parent;
  134. int line;
  135. /* Show mini info */
  136. if (tree->is_panel){
  137. if (!show_mini_info)
  138. return;
  139. line = tree_lines+2;
  140. } else
  141. line = tree_lines+1;
  142. tty_draw_hline (tree->widget.y + line, tree->widget.x + 1, ' ', tree_cols);
  143. widget_move (&tree->widget, line, 1);
  144. if (tree->searching){
  145. /* Show search string */
  146. tty_setcolor (TREE_NORMALC (h));
  147. tty_setcolor (DLG_FOCUSC (h));
  148. tty_print_char (PATH_SEP);
  149. tty_print_string (str_fit_to_term (tree->search_buffer,
  150. tree_cols - 2, J_LEFT_FIT));
  151. tty_print_char (' ');
  152. tty_setcolor (DLG_FOCUSC (h));
  153. } else {
  154. /* Show full name of selected directory */
  155. tty_print_string (str_fit_to_term (tree->selected_ptr->name,
  156. tree_cols, J_LEFT_FIT));
  157. }
  158. }
  159. static void show_tree (WTree *tree)
  160. {
  161. Dlg_head *h = tree->widget.parent;
  162. tree_entry *current;
  163. int i, j, topsublevel;
  164. int x, y;
  165. int tree_lines, tree_cols;
  166. /* Initialize */
  167. x = y = 0;
  168. tree_lines = tlines (tree);
  169. tree_cols = tree->widget.cols;
  170. tty_setcolor (TREE_NORMALC (h));
  171. widget_move ((Widget*)tree, y, x);
  172. if (tree->is_panel){
  173. tree_cols -= 2;
  174. x = y = 1;
  175. }
  176. g_free (tree->tree_shown);
  177. tree->tree_shown = g_new (tree_entry*, tree_lines);
  178. for (i = 0; i < tree_lines; i++)
  179. tree->tree_shown [i] = NULL;
  180. if (tree->store->tree_first)
  181. topsublevel = tree->store->tree_first->sublevel;
  182. else
  183. topsublevel = 0;
  184. if (!tree->selected_ptr){
  185. tree->selected_ptr = tree->store->tree_first;
  186. tree->topdiff = 0;
  187. }
  188. current = tree->selected_ptr;
  189. /* Calculate the directory which is to be shown on the topmost line */
  190. if (tree_navigation_flag){
  191. i = 0;
  192. while (current->prev && i < tree->topdiff){
  193. current = current->prev;
  194. if (current->sublevel < tree->selected_ptr->sublevel){
  195. if (strncmp (current->name, tree->selected_ptr->name,
  196. strlen (current->name)) == 0)
  197. i++;
  198. } else if (current->sublevel == tree->selected_ptr->sublevel){
  199. for (j = strlen (current->name) - 1; current->name [j] != PATH_SEP; j--);
  200. if (strncmp (current->name, tree->selected_ptr->name, j) == 0)
  201. i++;
  202. } else if (current->sublevel == tree->selected_ptr->sublevel + 1
  203. && strlen (tree->selected_ptr->name) > 1){
  204. if (strncmp (current->name, tree->selected_ptr->name,
  205. strlen (tree->selected_ptr->name)) == 0)
  206. i++;
  207. }
  208. }
  209. tree->topdiff = i;
  210. } else
  211. current = back_ptr (current, &tree->topdiff);
  212. /* Loop for every line */
  213. for (i = 0; i < tree_lines; i++){
  214. /* Move to the beginning of the line */
  215. tty_draw_hline (tree->widget.y + y + i, tree->widget.x + x,
  216. ' ', tree_cols);
  217. if (!current)
  218. continue;
  219. tree->tree_shown [i] = current;
  220. if (current->sublevel == topsublevel){
  221. /* Top level directory */
  222. if (tree->active && current == tree->selected_ptr) {
  223. if (!tty_use_colors () && !tree->is_panel)
  224. tty_setcolor (MARKED_COLOR);
  225. else
  226. tty_setcolor (SELECTED_COLOR);
  227. }
  228. /* Show full name */
  229. tty_print_string (str_fit_to_term (current->name, tree_cols - 6, J_LEFT_FIT));
  230. } else{
  231. /* Sub level directory */
  232. tty_set_alt_charset (TRUE);
  233. /* Output branch parts */
  234. for (j = 0; j < current->sublevel - topsublevel - 1; j++){
  235. if (tree_cols - 8 - 3 * j < 9)
  236. break;
  237. tty_print_char (' ');
  238. if (current->submask & (1 << (j + topsublevel + 1)))
  239. tty_print_char (ACS_VLINE);
  240. else
  241. tty_print_char (' ');
  242. tty_print_char (' ');
  243. }
  244. tty_print_char (' '); j++;
  245. if (!current->next || !(current->next->submask & (1 << current->sublevel)))
  246. tty_print_char (ACS_LLCORNER);
  247. else
  248. tty_print_char (ACS_LTEE);
  249. tty_print_char (ACS_HLINE);
  250. tty_set_alt_charset (FALSE);
  251. if (tree->active && current == tree->selected_ptr) {
  252. /* Selected directory -> change color */
  253. if (!tty_use_colors () && !tree->is_panel)
  254. tty_setcolor (MARKED_COLOR);
  255. else
  256. tty_setcolor (SELECTED_COLOR);
  257. }
  258. /* Show sub-name */
  259. tty_print_char (' ');
  260. tty_print_string (str_fit_to_term (current->subname,
  261. tree_cols - 2 - 4 - 3 * j, J_LEFT_FIT));
  262. }
  263. tty_print_char (' ');
  264. /* Return to normal color */
  265. tty_setcolor (TREE_NORMALC (h));
  266. /* Calculate the next value for current */
  267. current = current->next;
  268. if (tree_navigation_flag){
  269. while (current){
  270. if (current->sublevel < tree->selected_ptr->sublevel){
  271. if (strncmp (current->name, tree->selected_ptr->name,
  272. strlen (current->name)) == 0)
  273. break;
  274. } else if (current->sublevel == tree->selected_ptr->sublevel){
  275. for (j = strlen (current->name) - 1; current->name [j] != PATH_SEP; j--);
  276. if (strncmp (current->name,tree->selected_ptr->name,j)== 0)
  277. break;
  278. } else if (current->sublevel == tree->selected_ptr->sublevel+1
  279. && strlen (tree->selected_ptr->name) > 1){
  280. if (strncmp (current->name, tree->selected_ptr->name,
  281. strlen (tree->selected_ptr->name)) == 0)
  282. break;
  283. }
  284. current = current->next;
  285. }
  286. }
  287. }
  288. tree_show_mini_info (tree, tree_lines, tree_cols);
  289. }
  290. static void check_focus (WTree *tree)
  291. {
  292. if (tree->topdiff < 3)
  293. tree->topdiff = 3;
  294. else if (tree->topdiff >= tlines (tree) - 3)
  295. tree->topdiff = tlines (tree) - 3 - 1;
  296. }
  297. static void tree_move_backward (WTree *tree, int i)
  298. {
  299. tree_entry *current;
  300. int j = 0;
  301. if (tree_navigation_flag){
  302. current = tree->selected_ptr;
  303. while (j < i && current->prev
  304. && current->prev->sublevel >= tree->selected_ptr->sublevel){
  305. current = current->prev;
  306. if (current->sublevel == tree->selected_ptr->sublevel){
  307. tree->selected_ptr = current;
  308. j ++;
  309. }
  310. }
  311. i = j;
  312. } else
  313. tree->selected_ptr = back_ptr (tree->selected_ptr, &i);
  314. tree->topdiff -= i;
  315. check_focus (tree);
  316. }
  317. static void tree_move_forward (WTree *tree, int i)
  318. {
  319. tree_entry *current;
  320. int j = 0;
  321. if (tree_navigation_flag){
  322. current = tree->selected_ptr;
  323. while (j < i && current->next
  324. && current->next->sublevel >= tree->selected_ptr->sublevel){
  325. current = current->next;
  326. if (current->sublevel == tree->selected_ptr->sublevel){
  327. tree->selected_ptr = current;
  328. j ++;
  329. }
  330. }
  331. i = j;
  332. } else
  333. tree->selected_ptr = forw_ptr (tree->selected_ptr, &i);
  334. tree->topdiff += i;
  335. check_focus (tree);
  336. }
  337. static void tree_move_to_child (WTree *tree)
  338. {
  339. tree_entry *current;
  340. /* Do we have a starting point? */
  341. if (!tree->selected_ptr)
  342. return;
  343. /* Take the next entry */
  344. current = tree->selected_ptr->next;
  345. /* Is it the child of the selected entry */
  346. if (current && current->sublevel > tree->selected_ptr->sublevel){
  347. /* Yes -> select this entry */
  348. tree->selected_ptr = current;
  349. tree->topdiff++;
  350. check_focus (tree);
  351. } else {
  352. /* No -> rescan and try again */
  353. tree_rescan_cmd (tree);
  354. current = tree->selected_ptr->next;
  355. if (current && current->sublevel > tree->selected_ptr->sublevel){
  356. tree->selected_ptr = current;
  357. tree->topdiff++;
  358. check_focus (tree);
  359. }
  360. }
  361. }
  362. static int tree_move_to_parent (WTree *tree)
  363. {
  364. tree_entry *current;
  365. tree_entry *old;
  366. if (!tree->selected_ptr)
  367. return 0;
  368. old = tree->selected_ptr;
  369. current = tree->selected_ptr->prev;
  370. while (current && current->sublevel >= tree->selected_ptr->sublevel){
  371. current = current->prev;
  372. tree->topdiff--;
  373. }
  374. if (!current)
  375. current = tree->store->tree_first;
  376. tree->selected_ptr = current;
  377. check_focus (tree);
  378. return tree->selected_ptr != old;
  379. }
  380. static void tree_move_to_top (WTree *tree)
  381. {
  382. tree->selected_ptr = tree->store->tree_first;
  383. tree->topdiff = 0;
  384. }
  385. static void tree_move_to_bottom (WTree *tree)
  386. {
  387. tree->selected_ptr = tree->store->tree_last;
  388. tree->topdiff = tlines (tree) - 3 - 1;
  389. }
  390. void tree_chdir (WTree *tree, const char *dir)
  391. {
  392. tree_entry *current;
  393. current = tree_store_whereis (dir);
  394. if (current){
  395. tree->selected_ptr = current;
  396. check_focus (tree);
  397. }
  398. }
  399. void
  400. sync_tree (const char *path)
  401. {
  402. tree_chdir (the_tree, path);
  403. }
  404. /* Handle mouse click */
  405. static void
  406. tree_event (WTree *tree, int y)
  407. {
  408. if (tree->tree_shown [y]){
  409. tree->selected_ptr = tree->tree_shown [y];
  410. tree->topdiff = y;
  411. }
  412. show_tree (tree);
  413. }
  414. static void chdir_sel (WTree *tree);
  415. static void maybe_chdir (WTree *tree)
  416. {
  417. if (!(xtree_mode && tree->is_panel))
  418. return;
  419. if (is_idle ())
  420. chdir_sel (tree);
  421. }
  422. /* Mouse callback */
  423. static int
  424. event_callback (Gpm_Event *event, void *data)
  425. {
  426. WTree *tree = data;
  427. if (!(event->type & GPM_UP))
  428. return MOU_NORMAL;
  429. if (tree->is_panel)
  430. event->y--;
  431. event->y--;
  432. if (!tree->active)
  433. change_panel ();
  434. if (event->y < 0){
  435. tree_move_backward (tree, tlines (tree) - 1);
  436. show_tree (tree);
  437. }
  438. else if (event->y >= tlines (tree)){
  439. tree_move_forward (tree, tlines (tree) - 1);
  440. show_tree (tree);
  441. } else {
  442. tree_event (tree, event->y);
  443. if ((event->type & (GPM_UP|GPM_DOUBLE)) == (GPM_UP|GPM_DOUBLE)){
  444. chdir_sel (tree);
  445. }
  446. }
  447. return MOU_NORMAL;
  448. }
  449. /* Search tree for text */
  450. static int search_tree (WTree *tree, char *text)
  451. {
  452. tree_entry *current;
  453. int len;
  454. int wrapped = 0;
  455. int found = 0;
  456. len = strlen (text);
  457. current = tree->selected_ptr;
  458. found = 0;
  459. while (!wrapped || current != tree->selected_ptr){
  460. if (strncmp (current->subname, text, len) == 0){
  461. tree->selected_ptr = current;
  462. found = 1;
  463. break;
  464. }
  465. current = current->next;
  466. if (!current){
  467. current = tree->store->tree_first;
  468. wrapped = 1;
  469. }
  470. tree->topdiff++;
  471. }
  472. check_focus (tree);
  473. return found;
  474. }
  475. static void tree_do_search (WTree *tree, int key)
  476. {
  477. size_t l;
  478. l = strlen (tree->search_buffer);
  479. if (l && (key == KEY_BACKSPACE))
  480. tree->search_buffer [--l] = 0;
  481. else {
  482. if (key && l < sizeof (tree->search_buffer)){
  483. tree->search_buffer [l] = key;
  484. tree->search_buffer [l+1] = 0;
  485. l++;
  486. }
  487. }
  488. if (!search_tree (tree, tree->search_buffer))
  489. tree->search_buffer [--l] = 0;
  490. show_tree (tree);
  491. maybe_chdir (tree);
  492. }
  493. static void
  494. tree_rescan_cmd (WTree *tree)
  495. {
  496. char old_dir [MC_MAXPATHLEN];
  497. if (!tree->selected_ptr || !mc_get_current_wd (old_dir, MC_MAXPATHLEN) ||
  498. mc_chdir (tree->selected_ptr->name))
  499. return;
  500. tree_store_rescan (tree->selected_ptr->name);
  501. mc_chdir (old_dir);
  502. }
  503. static void
  504. tree_forget_cmd (void *data)
  505. {
  506. WTree *tree = data;
  507. if (tree->selected_ptr)
  508. tree_remove_entry (tree, tree->selected_ptr->name);
  509. }
  510. static void tree_copy (WTree *tree, const char *default_dest)
  511. {
  512. char *dest;
  513. off_t count = 0;
  514. double bytes = 0;
  515. FileOpContext *ctx;
  516. if (!tree->selected_ptr)
  517. return;
  518. g_snprintf (cmd_buf, sizeof(cmd_buf), _("Copy \"%s\" directory to:"),
  519. str_trunc (tree->selected_ptr->name, 50));
  520. dest = input_expand_dialog (_(" Copy "), cmd_buf, MC_HISTORY_FM_TREE_COPY, default_dest);
  521. if (!dest)
  522. return;
  523. if (!*dest){
  524. g_free (dest);
  525. return;
  526. }
  527. ctx = file_op_context_new (OP_COPY);
  528. file_op_context_create_ui (ctx, FALSE);
  529. copy_dir_dir (ctx, tree->selected_ptr->name, dest, 1, 0, 0, 0, &count, &bytes);
  530. file_op_context_destroy (ctx);
  531. g_free (dest);
  532. }
  533. static void
  534. tree_help_cmd (void)
  535. {
  536. interactive_display (NULL, "[Directory Tree]");
  537. }
  538. static void
  539. tree_copy_cmd (void *data)
  540. {
  541. WTree *tree = data;
  542. tree_copy (tree, "");
  543. }
  544. static void tree_move (WTree *tree, const char *default_dest)
  545. {
  546. char *dest;
  547. struct stat buf;
  548. double bytes = 0;
  549. off_t count = 0;
  550. FileOpContext *ctx;
  551. if (!tree->selected_ptr)
  552. return;
  553. g_snprintf (cmd_buf, sizeof (cmd_buf), _("Move \"%s\" directory to:"),
  554. str_trunc (tree->selected_ptr->name, 50));
  555. dest = input_expand_dialog (_(" Move "), cmd_buf, MC_HISTORY_FM_TREE_MOVE, default_dest);
  556. if (!dest)
  557. return;
  558. if (!*dest){
  559. g_free (dest);
  560. return;
  561. }
  562. if (stat (dest, &buf)){
  563. message (D_ERROR, MSG_ERROR, _(" Cannot stat the destination \n %s "),
  564. unix_error_string (errno));
  565. g_free (dest);
  566. return;
  567. }
  568. if (!S_ISDIR (buf.st_mode)){
  569. file_error (_(" Destination \"%s\" must be a directory \n %s "),
  570. dest);
  571. g_free (dest);
  572. return;
  573. }
  574. ctx = file_op_context_new (OP_MOVE);
  575. file_op_context_create_ui (ctx, FALSE);
  576. move_dir_dir (ctx, tree->selected_ptr->name, dest, &count, &bytes);
  577. file_op_context_destroy (ctx);
  578. g_free (dest);
  579. }
  580. static void
  581. tree_move_cmd (void *data)
  582. {
  583. WTree *tree = data;
  584. tree_move (tree, "");
  585. }
  586. #if 0
  587. static void
  588. tree_mkdir_cmd (WTree *tree)
  589. {
  590. char old_dir [MC_MAXPATHLEN];
  591. if (!tree->selected_ptr)
  592. return;
  593. if (!mc_get_current_wd (old_dir, MC_MAXPATHLEN))
  594. return;
  595. if (chdir (tree->selected_ptr->name))
  596. return;
  597. /* FIXME
  598. mkdir_cmd (tree);
  599. */
  600. tree_rescan_cmd (tree);
  601. chdir (old_dir);
  602. }
  603. #endif
  604. static void
  605. tree_rmdir_cmd (WTree *tree)
  606. {
  607. off_t count = 0;
  608. double bytes = 0;
  609. FileOpContext *ctx;
  610. if (!tree->selected_ptr)
  611. return;
  612. if (confirm_delete) {
  613. char *buf;
  614. int result;
  615. buf =
  616. g_strdup_printf (_(" Delete %s? "),
  617. tree->selected_ptr->name);
  618. result =
  619. query_dialog (_(" Delete "), buf, D_ERROR, 2, _("&Yes"), _("&No"));
  620. g_free (buf);
  621. if (result != 0)
  622. return;
  623. }
  624. ctx = file_op_context_new (OP_DELETE);
  625. file_op_context_create_ui (ctx, FALSE);
  626. if (erase_dir (ctx, tree->selected_ptr->name, &count, &bytes) ==
  627. FILE_CONT)
  628. tree_forget_cmd (tree);
  629. file_op_context_destroy (ctx);
  630. }
  631. static void set_navig_label (WTree *tree);
  632. static void
  633. tree_toggle_navig (void *data)
  634. {
  635. WTree *tree = data;
  636. /* FIXME: invalid use of boolean variable */
  637. tree_navigation_flag = 1 - tree_navigation_flag;
  638. set_navig_label (tree);
  639. }
  640. static void
  641. set_navig_label (WTree *tree)
  642. {
  643. buttonbar_set_label_data (tree->widget.parent, 4,
  644. tree_navigation_flag ? _("Static") : _("Dynamc"),
  645. tree_toggle_navig, tree);
  646. }
  647. static void
  648. move_down (WTree *tree)
  649. {
  650. tree_move_forward (tree, 1);
  651. show_tree (tree);
  652. maybe_chdir (tree);
  653. }
  654. static void
  655. move_up (WTree *tree)
  656. {
  657. tree_move_backward (tree, 1);
  658. show_tree (tree);
  659. maybe_chdir (tree);
  660. }
  661. static void
  662. move_home (WTree *tree)
  663. {
  664. tree_move_to_top (tree);
  665. show_tree (tree);
  666. maybe_chdir (tree);
  667. }
  668. static void
  669. move_end (WTree *tree)
  670. {
  671. tree_move_to_bottom (tree);
  672. show_tree (tree);
  673. maybe_chdir (tree);
  674. }
  675. static int
  676. move_left (WTree *tree)
  677. {
  678. int v;
  679. if (tree_navigation_flag){
  680. v = tree_move_to_parent (tree);
  681. show_tree (tree);
  682. maybe_chdir (tree);
  683. return v;
  684. }
  685. return 0;
  686. }
  687. static int
  688. move_right (WTree *tree)
  689. {
  690. if (tree_navigation_flag){
  691. tree_move_to_child (tree);
  692. show_tree (tree);
  693. maybe_chdir (tree);
  694. return 1;
  695. }
  696. return 0;
  697. }
  698. static void
  699. move_prevp (WTree *tree)
  700. {
  701. tree_move_backward (tree, tlines (tree) - 1);
  702. show_tree (tree);
  703. maybe_chdir (tree);
  704. }
  705. static void
  706. move_nextp (WTree *tree)
  707. {
  708. tree_move_forward (tree, tlines (tree) - 1);
  709. show_tree (tree);
  710. maybe_chdir (tree);
  711. }
  712. static void
  713. chdir_sel (WTree *tree)
  714. {
  715. if (!tree->is_panel) {
  716. return;
  717. }
  718. change_panel ();
  719. if (do_cd (tree->selected_ptr->name, cd_exact)) {
  720. select_item (current_panel);
  721. } else {
  722. message (D_ERROR, MSG_ERROR, _(" Cannot chdir to \"%s\" \n %s "),
  723. tree->selected_ptr->name, unix_error_string (errno));
  724. }
  725. change_panel ();
  726. show_tree (tree);
  727. return;
  728. }
  729. static void
  730. tree_start_search (WTree *tree)
  731. {
  732. int i;
  733. if (tree->searching){
  734. if (tree->selected_ptr == tree->store->tree_last)
  735. tree_move_to_top(tree);
  736. else {
  737. /* set navigation mode temporarily to 'Static' because in
  738. * dynamic navigation mode tree_move_forward will not move
  739. * to a lower sublevel if necessary (sequent searches must
  740. * start with the directory followed the last found directory)
  741. */
  742. i = tree_navigation_flag;
  743. tree_navigation_flag = 0;
  744. tree_move_forward (tree, 1);
  745. tree_navigation_flag = i;
  746. }
  747. tree_do_search (tree, 0);
  748. }
  749. else {
  750. tree->searching = 1;
  751. tree->search_buffer[0] = 0;
  752. }
  753. }
  754. typedef void (*tree_key_action) (WTree *);
  755. typedef struct {
  756. int key_code;
  757. tree_key_action fn;
  758. } tree_key_map;
  759. static const tree_key_map tree_keymap [] = {
  760. { XCTRL('n'), move_down },
  761. { XCTRL('p'), move_up },
  762. { KEY_DOWN, move_down },
  763. { KEY_UP, move_up },
  764. { '\n', chdir_sel },
  765. { KEY_ENTER, chdir_sel },
  766. { KEY_HOME, move_home },
  767. { KEY_A1, move_home },
  768. { ALT ('<'), move_home },
  769. { KEY_END, move_end },
  770. { KEY_C1, move_end },
  771. { ALT ('>'), move_end },
  772. { KEY_NPAGE, move_nextp },
  773. { KEY_PPAGE, move_prevp },
  774. { XCTRL('v'), move_nextp },
  775. { ALT('v'), move_prevp },
  776. { XCTRL('p'), move_up },
  777. { XCTRL('p'), move_down },
  778. { XCTRL('s'), tree_start_search },
  779. { ALT('s'), tree_start_search },
  780. { XCTRL('r'), tree_rescan_cmd },
  781. { KEY_DC, tree_rmdir_cmd },
  782. { 0, 0 }
  783. };
  784. static inline cb_ret_t
  785. tree_key (WTree *tree, int key)
  786. {
  787. int i;
  788. for (i = 0; tree_keymap [i].key_code; i++){
  789. if (key == tree_keymap [i].key_code){
  790. if (tree_keymap [i].fn != tree_start_search)
  791. tree->searching = 0;
  792. (*tree_keymap [i].fn)(tree);
  793. show_tree (tree);
  794. return MSG_HANDLED;
  795. }
  796. }
  797. /* We do not want to use them if we do not need to */
  798. /* Input line may want to take the motion key event */
  799. if (key == KEY_LEFT)
  800. return move_left (tree) ? MSG_HANDLED : MSG_NOT_HANDLED;
  801. if (key == KEY_RIGHT)
  802. return move_right (tree) ? MSG_HANDLED : MSG_NOT_HANDLED;
  803. if (is_abort_char (key)) {
  804. if (tree->is_panel) {
  805. tree->searching = 0;
  806. show_tree (tree);
  807. return MSG_HANDLED; /* eat abort char */
  808. }
  809. /* modal tree dialog: let upper layer see the
  810. abort character and close the dialog */
  811. return MSG_NOT_HANDLED;
  812. }
  813. /* Do not eat characters not meant for the tree below ' ' (e.g. C-l). */
  814. if ((key >= ' ' && key <= 255) || key == KEY_BACKSPACE) {
  815. if (tree->searching){
  816. tree_do_search (tree, key);
  817. show_tree (tree);
  818. return MSG_HANDLED;
  819. }
  820. if (!command_prompt) {
  821. tree_start_search (tree);
  822. tree_do_search (tree, key);
  823. return MSG_HANDLED;
  824. }
  825. return tree->is_panel ? MSG_HANDLED : MSG_NOT_HANDLED;
  826. }
  827. return MSG_NOT_HANDLED;
  828. }
  829. static void
  830. tree_frame (Dlg_head *h, WTree *tree)
  831. {
  832. tty_setcolor (NORMAL_COLOR);
  833. widget_erase ((Widget*) tree);
  834. if (tree->is_panel) {
  835. draw_double_box (h, tree->widget.y, tree->widget.x, tree->widget.lines,
  836. tree->widget.cols);
  837. if (show_mini_info)
  838. tty_draw_hline (tree->widget.y + tlines (tree) + 1,
  839. tree->widget.x + 1,
  840. ACS_HLINE, tree->widget.cols - 2);
  841. }
  842. }
  843. static void
  844. tree_rescan_command (void *data)
  845. {
  846. WTree *tree = data;
  847. tree_rescan_cmd (tree);
  848. }
  849. static void
  850. tree_rmdir_command (void *data)
  851. {
  852. WTree *tree = data;
  853. tree_rmdir_cmd (tree);
  854. }
  855. static cb_ret_t
  856. tree_callback (Widget *w, widget_msg_t msg, int parm)
  857. {
  858. WTree *tree = (WTree *) w;
  859. Dlg_head *h = tree->widget.parent;
  860. switch (msg) {
  861. case WIDGET_DRAW:
  862. tree_frame (h, tree);
  863. show_tree (tree);
  864. return MSG_HANDLED;
  865. case WIDGET_KEY:
  866. return tree_key (tree, parm);
  867. case WIDGET_FOCUS:
  868. tree->active = 1;
  869. buttonbar_set_label (h, 1, _("Help"), tree_help_cmd);
  870. buttonbar_set_label_data (h, 2, _("Rescan"),
  871. tree_rescan_command, tree);
  872. buttonbar_set_label_data (h, 3, _("Forget"), tree_forget_cmd, tree);
  873. buttonbar_set_label_data (h, 5, _("Copy"), tree_copy_cmd, tree);
  874. buttonbar_set_label_data (h, 6, _("RenMov"), tree_move_cmd, tree);
  875. #if 0
  876. /* FIXME: mkdir is currently defunct */
  877. buttonbar_set_label_data (h, 7, _("Mkdir"), tree_mkdir_cmd, tree);
  878. #else
  879. buttonbar_clear_label (h, 7);
  880. #endif
  881. buttonbar_set_label_data (h, 8, _("Rmdir"), tree_rmdir_command, tree);
  882. set_navig_label (tree);
  883. buttonbar_redraw (h);
  884. /* FIXME: Should find a better way of only displaying the
  885. currently selected item */
  886. show_tree (tree);
  887. return MSG_HANDLED;
  888. /* FIXME: Should find a better way of changing the color of the
  889. selected item */
  890. case WIDGET_UNFOCUS:
  891. tree->active = 0;
  892. show_tree (tree);
  893. return MSG_HANDLED;
  894. case WIDGET_DESTROY:
  895. tree_destroy (tree);
  896. return MSG_HANDLED;
  897. default:
  898. return default_proc (msg, parm);
  899. }
  900. }
  901. WTree *
  902. tree_new (int is_panel, int y, int x, int lines, int cols)
  903. {
  904. WTree *tree = g_new (WTree, 1);
  905. init_widget (&tree->widget, y, x, lines, cols,
  906. tree_callback, event_callback);
  907. tree->is_panel = is_panel;
  908. tree->selected_ptr = 0;
  909. tree->store = tree_store_get ();
  910. tree_store_add_entry_remove_hook (remove_callback, tree);
  911. tree->tree_shown = 0;
  912. tree->search_buffer[0] = 0;
  913. tree->topdiff = tree->widget.lines / 2;
  914. tree->searching = 0;
  915. tree->active = 0;
  916. /* We do not want to keep the cursor */
  917. widget_want_cursor (tree->widget, 0);
  918. load_tree (tree);
  919. return tree;
  920. }
  921. /* Return name of the currently selected entry */
  922. char *
  923. tree_selected_name (WTree *tree)
  924. {
  925. return tree->selected_ptr->name;
  926. }