1 /* Directory tree browser for the Midnight Commander
2 Copyright (C) 1994, 1995, 1996, 1997 The Free Software Foundation
4 Written: 1994, 1996 Janne Kukonlehto
6 1996, 1999 Miguel de Icaza
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 This module has been converted to be a widget.
24 The program load and saves the tree each time the tree widget is
25 created and destroyed. This is required for the future vfs layer,
26 it will be possible to have tree views over virtual file systems.
38 #include "wtools.h" /* message() */
45 #include "file.h" /* For copy_dir_dir(), move_dir_dir(), erase_dir() */
47 #include "key.h" /* For mi_getch() */
49 #include "treestore.h"
52 #define tlines(t) (t->is_panel ? t->widget.lines-2 - (show_mini_info ? 2 : 0) : t->widget.lines)
54 extern int command_prompt
;
56 /* Use the color of the parent widget for the unselected entries */
57 #define TREE_NORMALC(h) (DLG_NORMALC (h))
59 /* Specifies the display mode: 1d or 2d */
60 static int tree_navigation_flag
;
64 struct TreeStore
*store
;
65 tree_entry
*selected_ptr
; /* The selected directory */
66 char search_buffer
[256]; /* Current search string */
67 tree_entry
**tree_shown
; /* Entries currently on screen */
68 int is_panel
; /* panel or plain widget flag */
69 int active
; /* if it's currently selected */
70 int searching
; /* Are we on searching mode? */
71 int topdiff
; /* The difference between the topmost
72 shown and the selected */
76 static void save_tree (WTree
*tree
);
77 static void tree_rescan_cmd (WTree
*);
79 static tree_entry
*back_ptr (tree_entry
*ptr
, int *count
)
83 while (ptr
&& ptr
->prev
&& i
< *count
){
91 static tree_entry
*forw_ptr (tree_entry
*ptr
, int *count
)
95 while (ptr
&& ptr
->next
&& i
< *count
){
104 remove_callback (tree_entry
*entry
, void *data
)
108 if (tree
->selected_ptr
== entry
){
109 if (tree
->selected_ptr
->next
)
110 tree
->selected_ptr
= tree
->selected_ptr
->next
;
112 tree
->selected_ptr
= tree
->selected_ptr
->prev
;
116 static void tree_remove_entry (WTree
*tree
, char *name
)
119 tree_store_remove_entry (name
);
122 static void tree_destroy (WTree
*tree
)
124 tree_store_remove_entry_remove_hook (remove_callback
);
127 g_free (tree
->tree_shown
);
128 tree
->tree_shown
= 0;
129 tree
->selected_ptr
= NULL
;
132 /* Loads the .mc.tree file */
133 static void load_tree (WTree
*tree
)
137 tree
->selected_ptr
= tree
->store
->tree_first
;
138 tree_chdir (tree
, home_dir
);
141 /* Save the .mc.tree file */
142 static void save_tree (WTree
*tree
)
147 error
= tree_store_save ();
150 fprintf (stderr
, _("Cannot open the %s file for writing:\n%s\n"), MC_TREE
,
151 unix_error_string (error
));
155 static void tree_show_mini_info (WTree
*tree
, int tree_lines
, int tree_cols
)
157 Dlg_head
*h
= tree
->widget
.parent
;
168 widget_move (&tree
->widget
, line
, 1);
169 hline (' ', tree_cols
);
170 widget_move (&tree
->widget
, line
, 1);
172 if (tree
->searching
){
173 /* Show search string */
174 attrset (TREE_NORMALC (h
));
175 attrset (DLG_FOCUSC (h
));
178 addstr ((char *) name_trunc (tree
->search_buffer
, tree_cols
-2));
180 attrset (DLG_FOCUSC (h
));
182 /* Show full name of selected directory */
183 addstr ((char *) name_trunc (tree
->selected_ptr
->name
, tree_cols
));
187 static void show_tree (WTree
*tree
)
189 Dlg_head
*h
= tree
->widget
.parent
;
191 int i
, j
, topsublevel
;
193 int tree_lines
, tree_cols
;
197 tree_lines
= tlines (tree
);
198 tree_cols
= tree
->widget
.cols
;
200 attrset (TREE_NORMALC (h
));
201 widget_move ((Widget
*)tree
, y
, x
);
207 g_free (tree
->tree_shown
);
208 tree
->tree_shown
= g_new (tree_entry
*, tree_lines
);
210 for (i
= 0; i
< tree_lines
; i
++)
211 tree
->tree_shown
[i
] = NULL
;
212 if (tree
->store
->tree_first
)
213 topsublevel
= tree
->store
->tree_first
->sublevel
;
216 if (!tree
->selected_ptr
){
217 tree
->selected_ptr
= tree
->store
->tree_first
;
220 current
= tree
->selected_ptr
;
222 /* Calculate the directory which is to be shown on the topmost line */
223 if (tree_navigation_flag
){
225 while (current
->prev
&& i
< tree
->topdiff
){
226 current
= current
->prev
;
227 if (current
->sublevel
< tree
->selected_ptr
->sublevel
){
228 if (strncmp (current
->name
, tree
->selected_ptr
->name
,
229 strlen (current
->name
)) == 0)
231 } else if (current
->sublevel
== tree
->selected_ptr
->sublevel
){
232 for (j
= strlen (current
->name
) - 1; current
->name
[j
] != PATH_SEP
; j
--);
233 if (strncmp (current
->name
, tree
->selected_ptr
->name
, j
) == 0)
235 } else if (current
->sublevel
== tree
->selected_ptr
->sublevel
+ 1
236 && strlen (tree
->selected_ptr
->name
) > 1){
237 if (strncmp (current
->name
, tree
->selected_ptr
->name
,
238 strlen (tree
->selected_ptr
->name
)) == 0)
244 current
= back_ptr (current
, &tree
->topdiff
);
246 /* Loop for every line */
247 for (i
= 0; i
< tree_lines
; i
++){
248 /* Move to the beginning of the line */
249 widget_move (&tree
->widget
, y
+i
, x
);
251 hline (' ', tree_cols
);
252 widget_move (&tree
->widget
, y
+i
, x
);
257 tree
->tree_shown
[i
] = current
;
258 if (current
->sublevel
== topsublevel
){
260 /* Top level directory */
261 if (tree
->active
&& current
== tree
->selected_ptr
) {
262 if (!use_colors
&& !tree
->is_panel
)
263 attrset (MARKED_COLOR
);
265 attrset (SELECTED_COLOR
);
269 addstr ((char *) name_trunc (current
->name
, tree_cols
- 6));
271 /* Sub level directory */
274 /* Output branch parts */
275 for (j
= 0; j
< current
->sublevel
- topsublevel
- 1; j
++){
276 if (tree_cols
- 8 - 3 * j
< 9)
279 if (current
->submask
& (1 << (j
+ topsublevel
+ 1)))
286 if (!current
->next
|| !(current
->next
->submask
& (1 << current
->sublevel
)))
287 addch (ACS_LLCORNER
);
293 if (tree
->active
&& current
== tree
->selected_ptr
) {
294 /* Selected directory -> change color */
295 if (!use_colors
&& !tree
->is_panel
)
296 attrset (MARKED_COLOR
);
298 attrset (SELECTED_COLOR
);
303 addstr ((char *) name_trunc (current
->subname
,
304 tree_cols
- 2 - 4 - 3 * j
));
308 /* Return to normal color */
309 attrset (TREE_NORMALC (h
));
311 /* Calculate the next value for current */
312 current
= current
->next
;
313 if (tree_navigation_flag
){
315 if (current
->sublevel
< tree
->selected_ptr
->sublevel
){
316 if (strncmp (current
->name
, tree
->selected_ptr
->name
,
317 strlen (current
->name
)) == 0)
319 } else if (current
->sublevel
== tree
->selected_ptr
->sublevel
){
320 for (j
= strlen (current
->name
) - 1; current
->name
[j
] != PATH_SEP
; j
--);
321 if (strncmp (current
->name
,tree
->selected_ptr
->name
,j
)== 0)
323 } else if (current
->sublevel
== tree
->selected_ptr
->sublevel
+1
324 && strlen (tree
->selected_ptr
->name
) > 1){
325 if (strncmp (current
->name
, tree
->selected_ptr
->name
,
326 strlen (tree
->selected_ptr
->name
)) == 0)
329 current
= current
->next
;
333 tree_show_mini_info (tree
, tree_lines
, tree_cols
);
336 static void check_focus (WTree
*tree
)
338 if (tree
->topdiff
< 3)
340 else if (tree
->topdiff
>= tlines (tree
) - 3)
341 tree
->topdiff
= tlines (tree
) - 3 - 1;
344 static void tree_move_backward (WTree
*tree
, int i
)
349 if (tree_navigation_flag
){
350 current
= tree
->selected_ptr
;
351 while (j
< i
&& current
->prev
352 && current
->prev
->sublevel
>= tree
->selected_ptr
->sublevel
){
353 current
= current
->prev
;
354 if (current
->sublevel
== tree
->selected_ptr
->sublevel
){
355 tree
->selected_ptr
= current
;
361 tree
->selected_ptr
= back_ptr (tree
->selected_ptr
, &i
);
366 static void tree_move_forward (WTree
*tree
, int i
)
371 if (tree_navigation_flag
){
372 current
= tree
->selected_ptr
;
373 while (j
< i
&& current
->next
374 && current
->next
->sublevel
>= tree
->selected_ptr
->sublevel
){
375 current
= current
->next
;
376 if (current
->sublevel
== tree
->selected_ptr
->sublevel
){
377 tree
->selected_ptr
= current
;
383 tree
->selected_ptr
= forw_ptr (tree
->selected_ptr
, &i
);
388 static void tree_move_to_child (WTree
*tree
)
392 /* Do we have a starting point? */
393 if (!tree
->selected_ptr
)
395 /* Take the next entry */
396 current
= tree
->selected_ptr
->next
;
397 /* Is it the child of the selected entry */
398 if (current
&& current
->sublevel
> tree
->selected_ptr
->sublevel
){
399 /* Yes -> select this entry */
400 tree
->selected_ptr
= current
;
404 /* No -> rescan and try again */
405 tree_rescan_cmd (tree
);
406 current
= tree
->selected_ptr
->next
;
407 if (current
&& current
->sublevel
> tree
->selected_ptr
->sublevel
){
408 tree
->selected_ptr
= current
;
415 static int tree_move_to_parent (WTree
*tree
)
420 if (!tree
->selected_ptr
)
422 old
= tree
->selected_ptr
;
423 current
= tree
->selected_ptr
->prev
;
424 while (current
&& current
->sublevel
>= tree
->selected_ptr
->sublevel
){
425 current
= current
->prev
;
429 current
= tree
->store
->tree_first
;
430 tree
->selected_ptr
= current
;
432 return tree
->selected_ptr
!= old
;
435 static void tree_move_to_top (WTree
*tree
)
437 tree
->selected_ptr
= tree
->store
->tree_first
;
441 static void tree_move_to_bottom (WTree
*tree
)
443 tree
->selected_ptr
= tree
->store
->tree_last
;
444 tree
->topdiff
= tlines (tree
) - 3 - 1;
447 void tree_chdir (WTree
*tree
, const char *dir
)
451 current
= tree_store_whereis (dir
);
453 tree
->selected_ptr
= current
;
459 sync_tree (const char *path
)
461 tree_chdir (the_tree
, path
);
464 /* Handle mouse click */
466 tree_event (WTree
*tree
, int y
)
468 if (tree
->tree_shown
[y
]){
469 tree
->selected_ptr
= tree
->tree_shown
[y
];
475 static void chdir_sel (WTree
*tree
);
477 static void maybe_chdir (WTree
*tree
)
479 if (!(xtree_mode
&& tree
->is_panel
))
487 event_callback (Gpm_Event
*event
, void *data
)
491 if (!(event
->type
& GPM_UP
))
503 tree_move_backward (tree
, tlines (tree
) - 1);
506 else if (event
->y
>= tlines (tree
)){
507 tree_move_forward (tree
, tlines (tree
) - 1);
510 tree_event (tree
, event
->y
);
511 if ((event
->type
& (GPM_UP
|GPM_DOUBLE
)) == (GPM_UP
|GPM_DOUBLE
)){
518 /* Search tree for text */
519 static int search_tree (WTree
*tree
, char *text
)
527 current
= tree
->selected_ptr
;
529 while (!wrapped
|| current
!= tree
->selected_ptr
){
530 if (strncmp (current
->subname
, text
, len
) == 0){
531 tree
->selected_ptr
= current
;
535 current
= current
->next
;
537 current
= tree
->store
->tree_first
;
546 static void tree_do_search (WTree
*tree
, int key
)
550 l
= strlen (tree
->search_buffer
);
551 if (l
&& (key
== KEY_BACKSPACE
))
552 tree
->search_buffer
[--l
] = 0;
554 if (key
&& l
< sizeof (tree
->search_buffer
)){
555 tree
->search_buffer
[l
] = key
;
556 tree
->search_buffer
[l
+1] = 0;
561 if (!search_tree (tree
, tree
->search_buffer
))
562 tree
->search_buffer
[--l
] = 0;
569 tree_rescan_cmd (WTree
*tree
)
571 char old_dir
[MC_MAXPATHLEN
];
573 if (!tree
->selected_ptr
|| !mc_get_current_wd (old_dir
, MC_MAXPATHLEN
) ||
574 mc_chdir (tree
->selected_ptr
->name
))
577 tree_store_rescan (tree
->selected_ptr
->name
);
582 tree_forget_cmd (void *data
)
585 if (tree
->selected_ptr
)
586 tree_remove_entry (tree
, tree
->selected_ptr
->name
);
589 static void tree_copy (WTree
*tree
, const char *default_dest
)
596 if (!tree
->selected_ptr
)
598 g_snprintf (cmd_buf
, sizeof(cmd_buf
), _("Copy \"%s\" directory to:"),
599 name_trunc (tree
->selected_ptr
->name
, 50));
600 dest
= input_expand_dialog (_(" Copy "), cmd_buf
, default_dest
);
610 ctx
= file_op_context_new (OP_COPY
);
611 file_op_context_create_ui (ctx
, FALSE
);
612 copy_dir_dir (ctx
, tree
->selected_ptr
->name
, dest
, 1, 0, 0, 0, &count
, &bytes
);
613 file_op_context_destroy (ctx
);
621 interactive_display (NULL
, "[Directory Tree]");
625 tree_copy_cmd (void *data
)
628 tree_copy (tree
, "");
631 static void tree_move (WTree
*tree
, const char *default_dest
)
639 if (!tree
->selected_ptr
)
641 g_snprintf (cmd_buf
, sizeof (cmd_buf
), _("Move \"%s\" directory to:"),
642 name_trunc (tree
->selected_ptr
->name
, 50));
643 dest
= input_expand_dialog (_(" Move "), cmd_buf
, default_dest
);
650 if (stat (dest
, &buf
)){
651 message (1, MSG_ERROR
, _(" Cannot stat the destination \n %s "),
652 unix_error_string (errno
));
656 if (!S_ISDIR (buf
.st_mode
)){
657 file_error (_(" Destination \"%s\" must be a directory \n %s "),
663 ctx
= file_op_context_new (OP_MOVE
);
664 file_op_context_create_ui (ctx
, FALSE
);
665 move_dir_dir (ctx
, tree
->selected_ptr
->name
, dest
, &count
, &bytes
);
666 file_op_context_destroy (ctx
);
672 tree_move_cmd (void *data
)
675 tree_move (tree
, "");
680 tree_mkdir_cmd (WTree
*tree
)
682 char old_dir
[MC_MAXPATHLEN
];
684 if (!tree
->selected_ptr
)
686 if (!mc_get_current_wd (old_dir
, MC_MAXPATHLEN
))
688 if (chdir (tree
->selected_ptr
->name
))
693 tree_rescan_cmd (tree
);
699 tree_rmdir_cmd (WTree
*tree
)
705 if (!tree
->selected_ptr
)
708 if (confirm_delete
) {
713 g_strdup_printf (_(" Delete %s? "),
714 tree
->selected_ptr
->name
);
716 query_dialog (_(" Delete "), buf
, 3, 2, _("&Yes"), _("&No"));
722 ctx
= file_op_context_new (OP_DELETE
);
723 file_op_context_create_ui (ctx
, FALSE
);
724 if (erase_dir (ctx
, tree
->selected_ptr
->name
, &count
, &bytes
) ==
726 tree_forget_cmd (tree
);
727 file_op_context_destroy (ctx
);
730 static void set_navig_label (WTree
*tree
);
733 tree_toggle_navig (void *data
)
736 /* FIXME: invalid use of boolean variable */
737 tree_navigation_flag
= 1 - tree_navigation_flag
;
738 set_navig_label (tree
);
742 set_navig_label (WTree
*tree
)
744 buttonbar_set_label_data (tree
->widget
.parent
, 4,
745 tree_navigation_flag
? _("Static") : _("Dynamc"),
746 tree_toggle_navig
, tree
);
750 move_down (WTree
*tree
)
752 tree_move_forward (tree
, 1);
758 move_up (WTree
*tree
)
760 tree_move_backward (tree
, 1);
766 move_home (WTree
*tree
)
768 tree_move_to_top (tree
);
774 move_end (WTree
*tree
)
776 tree_move_to_bottom (tree
);
782 move_left (WTree
*tree
)
786 if (tree_navigation_flag
){
787 v
= tree_move_to_parent (tree
);
796 move_right (WTree
*tree
)
798 if (tree_navigation_flag
){
799 tree_move_to_child (tree
);
808 move_prevp (WTree
*tree
)
810 tree_move_backward (tree
, tlines (tree
) - 1);
816 move_nextp (WTree
*tree
)
818 tree_move_forward (tree
, tlines (tree
) - 1);
824 chdir_sel (WTree
*tree
)
826 if (!tree
->is_panel
) {
830 if (do_cd (tree
->selected_ptr
->name
, cd_exact
)) {
831 select_item (current_panel
);
833 message (1, MSG_ERROR
, _(" Cannot chdir to \"%s\" \n %s "),
834 tree
->selected_ptr
->name
, unix_error_string (errno
));
842 tree_start_search (WTree
*tree
)
846 if (tree
->searching
){
848 if (tree
->selected_ptr
== tree
->store
->tree_last
)
849 tree_move_to_top(tree
);
851 /* set navigation mode temporarily to 'Static' because in
852 * dynamic navigation mode tree_move_forward will not move
853 * to a lower sublevel if necessary (sequent searches must
854 * start with the directory followed the last found directory)
856 i
= tree_navigation_flag
;
857 tree_navigation_flag
= 0;
858 tree_move_forward (tree
, 1);
859 tree_navigation_flag
= i
;
861 tree_do_search (tree
, 0);
865 tree
->search_buffer
[0] = 0;
869 typedef void (*tree_key_action
) (WTree
*);
875 static const tree_key_map tree_keymap
[] = {
876 { XCTRL('n'), move_down
},
877 { XCTRL('p'), move_up
},
878 { KEY_DOWN
, move_down
},
881 { KEY_ENTER
, chdir_sel
},
882 { KEY_HOME
, move_home
},
883 { KEY_A1
, move_home
},
884 { ALT ('<'), move_home
},
885 { KEY_END
, move_end
},
886 { KEY_C1
, move_end
},
887 { ALT ('>'), move_end
},
888 { KEY_NPAGE
, move_nextp
},
889 { KEY_PPAGE
, move_prevp
},
890 { XCTRL('v'), move_nextp
},
891 { ALT('v'), move_prevp
},
892 { XCTRL('p'), move_up
},
893 { XCTRL('p'), move_down
},
894 { XCTRL('s'), tree_start_search
},
895 { ALT('s'), tree_start_search
},
896 { XCTRL('r'), tree_rescan_cmd
},
897 { KEY_DC
, tree_rmdir_cmd
},
901 static inline cb_ret_t
902 tree_key (WTree
*tree
, int key
)
906 for (i
= 0; tree_keymap
[i
].key_code
; i
++){
907 if (key
== tree_keymap
[i
].key_code
){
908 if (tree_keymap
[i
].fn
!= tree_start_search
)
910 (*tree_keymap
[i
].fn
)(tree
);
916 /* We do not want to use them if we do not need to */
917 /* Input line may want to take the motion key event */
919 return move_left (tree
) ? MSG_HANDLED
: MSG_NOT_HANDLED
;
921 if (key
== KEY_RIGHT
)
922 return move_right (tree
) ? MSG_HANDLED
: MSG_NOT_HANDLED
;
924 if (is_abort_char (key
)) {
925 if (tree
->is_panel
) {
928 return MSG_HANDLED
; /* eat abort char */
930 /* modal tree dialog: let upper layer see the
931 abort character and close the dialog */
932 return MSG_NOT_HANDLED
;
935 /* Do not eat characters not meant for the tree below ' ' (e.g. C-l). */
936 if ((key
>= ' ' && key
<= 255) || key
== KEY_BACKSPACE
) {
937 if (tree
->searching
){
938 tree_do_search (tree
, key
);
943 if (!command_prompt
) {
944 tree_start_search (tree
);
945 tree_do_search (tree
, key
);
948 return tree
->is_panel
? MSG_HANDLED
: MSG_NOT_HANDLED
;
951 return MSG_NOT_HANDLED
;
955 tree_frame (Dlg_head
*h
, WTree
*tree
)
957 attrset (NORMAL_COLOR
);
958 widget_erase ((Widget
*) tree
);
960 draw_double_box (h
, tree
->widget
.y
, tree
->widget
.x
, tree
->widget
.lines
,
963 if (show_mini_info
&& tree
->is_panel
){
964 widget_move (tree
, tlines (tree
) + 1, 1);
965 hline (ACS_HLINE
, tree
->widget
.cols
- 2);
970 tree_rescan_command (void *data
)
973 tree_rescan_cmd (tree
);
977 tree_rmdir_command (void *data
)
980 tree_rmdir_cmd (tree
);
984 tree_callback (Widget
*w
, widget_msg_t msg
, int parm
)
986 WTree
*tree
= (WTree
*) w
;
987 Dlg_head
*h
= tree
->widget
.parent
;
991 tree_frame (h
, tree
);
996 return tree_key (tree
, parm
);
1000 buttonbar_set_label (h
, 1, _("Help"), tree_help_cmd
);
1001 buttonbar_set_label_data (h
, 2, _("Rescan"),
1002 tree_rescan_command
, tree
);
1003 buttonbar_set_label_data (h
, 3, _("Forget"), tree_forget_cmd
, tree
);
1004 buttonbar_set_label_data (h
, 5, _("Copy"), tree_copy_cmd
, tree
);
1005 buttonbar_set_label_data (h
, 6, _("RenMov"), tree_move_cmd
, tree
);
1007 /* FIXME: mkdir is currently defunct */
1008 buttonbar_set_label_data (h
, 7, _("Mkdir"), tree_mkdir_cmd
, tree
);
1010 buttonbar_clear_label (h
, 7);
1012 buttonbar_set_label_data (h
, 8, _("Rmdir"), tree_rmdir_command
, tree
);
1013 set_navig_label (tree
);
1014 buttonbar_redraw (h
);
1017 /* FIXME: Should find a better way of only displaying the
1018 currently selected item */
1022 /* FIXME: Should find a better way of changing the color of the
1025 case WIDGET_UNFOCUS
:
1030 case WIDGET_DESTROY
:
1031 tree_destroy (tree
);
1035 return default_proc (msg
, parm
);
1040 tree_new (int is_panel
, int y
, int x
, int lines
, int cols
)
1042 WTree
*tree
= g_new (WTree
, 1);
1044 init_widget (&tree
->widget
, y
, x
, lines
, cols
,
1045 tree_callback
, event_callback
);
1046 tree
->is_panel
= is_panel
;
1047 tree
->selected_ptr
= 0;
1049 tree
->store
= tree_store_get ();
1050 tree_store_add_entry_remove_hook (remove_callback
, tree
);
1051 tree
->tree_shown
= 0;
1052 tree
->search_buffer
[0] = 0;
1053 tree
->topdiff
= tree
->widget
.lines
/ 2;
1054 tree
->searching
= 0;
1057 /* We do not want to keep the cursor */
1058 widget_want_cursor (tree
->widget
, 0);
1063 /* Return name of the currently selected entry */
1065 tree_selected_name (WTree
*tree
)
1067 return tree
->selected_ptr
->name
;