1 /*===========================================================================
2 *
3 *                            PUBLIC DOMAIN NOTICE
4 *               National Center for Biotechnology Information
5 *
6 *  This software/database is a "United States Government Work" under the
7 *  terms of the United States Copyright Act.  It was written as part of
8 *  the author's official duties as a United States Government employee and
9 *  thus cannot be copyrighted.  This software/database is freely available
10 *  to the public for use. The National Library of Medicine and the U.S.
11 *  Government have not placed any restriction on its use or reproduction.
12 *
13 *  Although all reasonable efforts have been taken to ensure the accuracy
14 *  and reliability of the software and data, the NLM and the U.S.
15 *  Government do not and cannot warrant the performance or results that
16 *  may be obtained by using this software or data. The NLM and the U.S.
17 *  Government disclaim all warranties, express or implied, including
18 *  warranties of performance, merchantability or fitness for any particular
19 *  purpose.
20 *
21 *  Please cite the author in any work or product based on this material.
22 *
23 * ===========================================================================
24 *
25 */
26 
27 #include <tui/extern.h>
28 
29 #include <klib/rc.h>
30 #include <klib/text.h>
31 #include <klib/printf.h>
32 #include <kfs/directory.h>
33 
34 #include <vfs/manager.h>
35 #include <vfs/path.h>
36 
37 #include <tui/tui_dlg.h>
38 #include "tui-priv.h"
39 
40 #include <sysalloc.h>
41 #include <stdio.h>
42 #include <string.h>
43 #include <stdlib.h>
44 
45 
46 typedef struct dir_dlg_data
47 {
48     uint32_t dlg_w, dlg_h;
49 
50     /* for the inputline */
51     char current_dir_internal[ 4096 ];
52     char parent_dir_internal[ 4096 ];
53 
54     /* a VFS-Manager to do a path-conversion */
55     VFSManager * vfs_mgr;
56 
57 	/* a native KDirectory to fill the path-list */
58 	KDirectory * dir;
59 
60     KTUI_color bg1;
61     KTUI_color bg2;
62 
63     bool in_root;
64     bool ok_pressed;
65     bool done;
66     bool allow_dir_create;
67 
68 } dir_dlg_data;
69 
70 
71 enum
72 {
73     ID_CURR = 100,
74     ID_LABEL1,
75     ID_DIRS,
76     ID_B_OK,
77     ID_B_CANCEL,
78     ID_B_CREATE,
79     ID_B_GOTO
80 };
81 
82 
init_dlg_data(dir_dlg_data * data,uint32_t dlg_w,uint32_t dlg_h,char * path,KTUI_color bg1,KTUI_color bg2,bool allow_dir_create)83 static void init_dlg_data( dir_dlg_data * data, uint32_t dlg_w, uint32_t dlg_h,
84                            char * path, KTUI_color bg1, KTUI_color bg2, bool allow_dir_create )
85 {
86     size_t written;
87 
88     data->dlg_w = dlg_w;
89     data->dlg_h = dlg_h;
90 
91     VFSManagerMake ( &data->vfs_mgr );
92 	KDirectoryNativeDir ( &data->dir );
93 
94     native_to_internal( data->vfs_mgr, path, data->current_dir_internal, sizeof data->current_dir_internal, &written );
95 
96 	data->in_root = ( ( data->current_dir_internal[ 0 ] == '/' ) && ( data->current_dir_internal[ 1 ] == 0 ) );
97 	data->parent_dir_internal[ 0 ] = 0;
98     data->done = false;
99     data->ok_pressed = false;
100     data->bg1 = bg1;
101     data->bg2 = bg2;
102     data->allow_dir_create = allow_dir_create;
103 }
104 
105 
destroy_dlg_data(dir_dlg_data * data)106 static void destroy_dlg_data( dir_dlg_data * data )
107 {
108     if ( data->vfs_mgr != NULL )
109     {
110         VFSManagerRelease ( data->vfs_mgr );
111         data->vfs_mgr = NULL;
112     }
113 	if ( data->dir != NULL )
114 	{
115 		KDirectoryRelease ( data->dir );
116 		data->dir = NULL;
117 	}
118 }
119 
120 
create_allowed(dir_dlg_data * data)121 static bool create_allowed( dir_dlg_data * data )
122 {
123 #ifdef WINDOWS
124     return !data->in_root;
125 #else
126     return true;
127 #endif
128 }
129 
PopulateDirDlg(struct KTUIDlg * dlg,dir_dlg_data * data)130 static rc_t PopulateDirDlg ( struct KTUIDlg * dlg, dir_dlg_data * data )
131 {
132     uint32_t x;
133     rc_t rc = KTUIDlgAddLabel2( dlg, ID_CURR, 1, 2, data->dlg_w - 2, "" );
134 
135     if ( rc == 0 )
136         rc = KTUIDlgAddLabel2( dlg, ID_LABEL1, 1, 4, data->dlg_w - 2, "directories:" );
137 
138     if ( rc == 0 )
139     {
140         tui_rect r;
141         set_rect( &r, 1, 5, data->dlg_w - 2, data->dlg_h - 8 );
142         rc = KTUIDlgAddList ( dlg, ID_DIRS, &r );
143     }
144 
145     x = 1;
146     if ( rc == 0 )
147         rc = KTUIDlgAddBtn2( dlg, ID_B_OK, x, data->dlg_h - 2, 12, "OK" );
148     x += ( 12 + 1 );
149 
150     if ( rc == 0 )
151         rc = KTUIDlgAddBtn2( dlg, ID_B_CANCEL, x, data->dlg_h - 2, 22, "Cancel (ESC-ESC)" );
152     x += ( 22 + 1 );
153 
154     if ( rc == 0 )
155         rc = KTUIDlgAddBtn2( dlg, ID_B_GOTO, x, data->dlg_h - 2, 12, "Goto" );
156     x += ( 12 + 1 );
157 
158     if ( rc == 0 && data->allow_dir_create )
159     {
160         rc = KTUIDlgAddBtn2( dlg, ID_B_CREATE, x, data->dlg_h - 2, 18, "Create Dir" );
161         if ( rc == 0 )
162             rc = KTUIDlgSetWidgetVisible ( dlg, ID_B_CREATE, create_allowed( data ) );
163     }
164 
165 	if ( rc == 0 )
166 		rc = set_native_caption( dlg, data->vfs_mgr, ID_CURR, data->current_dir_internal );
167 
168     if ( rc == 0 )
169         rc = fill_widget_with_dirs( dlg, data->dir, ID_DIRS, data->current_dir_internal, data->parent_dir_internal, true );
170 
171     if ( rc == 0 )
172         rc = KTUIDlgSetFocus( dlg, ID_DIRS );
173     return rc;
174 }
175 
176 
DirDlg_Goto_Parent(dir_dlg_data * data)177 static void DirDlg_Goto_Parent ( dir_dlg_data * data )
178 {
179 	char * right_slash = string_rchr ( data->current_dir_internal, string_size ( data->current_dir_internal ), '/' );
180 	if ( right_slash != NULL )
181 	{
182 		size_t written;
183 
184 		data->in_root = ( right_slash == data->current_dir_internal );
185 		if ( data->in_root )
186 			data->current_dir_internal[ 1 ] = 0;
187 		else
188 			*right_slash = 0;
189 
190 		string_printf( data->parent_dir_internal, sizeof data->parent_dir_internal, &written, "%s", right_slash + 1 );
191 	}
192 }
193 
194 
DirDlg_Goto_Child(struct KTUIDlg * dlg,dir_dlg_data * data,uint32_t selection)195 static rc_t DirDlg_Goto_Child ( struct KTUIDlg * dlg, dir_dlg_data * data, uint32_t selection )
196 {
197 	rc_t rc = 0;
198 	/* we are goint one directory forward in the tree: */
199 	const char * s = KTUIDlgGetWidgetStringByIdx ( dlg, ID_DIRS, selection );
200 	if ( s != NULL )
201 	{
202 		/* add path segment from selection */
203 		char temp[ 4096 ];
204 		size_t written;
205 
206 		if ( data->in_root )
207 #ifdef WINDOWS
208 			rc = string_printf ( temp, sizeof temp, &written, "/%c", s[ 0 ] );
209 #else
210 			rc = string_printf ( temp, sizeof temp, &written, "/%s", s );
211 #endif
212 		else
213 			rc = string_printf ( temp, sizeof temp, &written, "%s/%s", data->current_dir_internal, s );
214 
215 		if ( rc == 0 )
216 			string_copy_measure ( data->current_dir_internal, sizeof data->current_dir_internal, temp );
217 
218 		data->parent_dir_internal[ 0 ] = 0;
219 		data->in_root = false;
220 	}
221 	return rc;
222 }
223 
224 
DirDlgDirectoryChanged(struct KTUIDlg * dlg,dir_dlg_data * data,uint32_t selection)225 static rc_t DirDlgDirectoryChanged ( struct KTUIDlg * dlg, dir_dlg_data * data, uint32_t selection )
226 {
227     rc_t rc = 0;
228 
229     if ( selection == 0 && !data->in_root )
230         DirDlg_Goto_Parent ( data );
231     else
232         rc = DirDlg_Goto_Child ( dlg, data, selection );
233 
234     if ( rc == 0 )
235         rc = set_native_caption( dlg, data->vfs_mgr, ID_CURR, data->current_dir_internal );
236 
237     if ( rc == 0 )
238         rc = fill_widget_with_dirs( dlg, data->dir, ID_DIRS, data->current_dir_internal, data->parent_dir_internal, true );
239 
240     if ( rc == 0 )
241         rc = KTUIDlgSetWidgetVisible ( dlg, ID_B_CREATE, create_allowed( data ) );
242 
243     if ( rc == 0 )
244         rc = KTUIDlgDraw( dlg, false );
245 
246     return rc;
247 }
248 
249 
Present_Input(struct KTUIDlg * dlg,dir_dlg_data * data,const char * caption,char * buffer,size_t buffer_size,bool * selected)250 static rc_t Present_Input ( struct KTUIDlg * dlg, dir_dlg_data * data, const char * caption,
251                             char * buffer, size_t buffer_size, bool * selected )
252 {
253     tui_rect r;
254     rc_t rc = KTUIDlgGetRect ( dlg, &r );
255     *selected = false;
256     if ( rc == 0 )
257     {
258         uint32_t w = 40, h = 6;
259         uint32_t x = ( r.w - w ) / 2;
260         uint32_t y = ( r.h - h ) / 2;
261         struct KTUI * tui = KTUIDlgGetTui( dlg );
262 
263         rc = TUI_EditBuffer( tui, dlg, caption, buffer, buffer_size, x, y, w, selected, data->bg1, data->bg2 );
264 
265         /* redraw in any case, even creating a directory failed, because we have to erase the sub-dialog from the screen */
266         KTUIDlgDraw( dlg, false );
267     }
268     return rc;
269 }
270 
DirDlgCreate(struct KTUIDlg * dlg,dir_dlg_data * data)271 static rc_t DirDlgCreate ( struct KTUIDlg * dlg, dir_dlg_data * data )
272 {
273     rc_t rc = 0;
274     if ( create_allowed( data ) )
275     {
276         char leafname[ 1024 ];
277         bool selected;
278 
279         leafname[ 0 ] = 0;
280         rc = Present_Input ( dlg, data, "new sub-directory", leafname, sizeof leafname, &selected );
281         if ( rc == 0 )
282         {
283             if ( rc == 0 && selected && leafname[ 0 ] != 0 )
284             {
285                 rc = KDirectoryCreateDir ( data->dir, 0775, kcmCreate | kcmParents,
286                                            "%s/%s", data->current_dir_internal, leafname );
287                 if ( rc == 0 )
288                     rc = fill_widget_with_dirs( dlg, data->dir, ID_DIRS, data->current_dir_internal, leafname, true );
289                 if ( rc == 0 )
290                     rc = KTUIDlgDraw( dlg, false );
291             }
292             /* set the focus to the directory-list */
293             KTUIDlgSetFocus( dlg, ID_DIRS );
294         }
295     }
296     return rc;
297 }
298 
299 
DirDlgGotoDir(struct KTUIDlg * dlg,dir_dlg_data * data)300 static rc_t DirDlgGotoDir ( struct KTUIDlg * dlg, dir_dlg_data * data )
301 {
302     char new_path[ 1024 ];
303     size_t written;
304     rc_t rc = internal_to_native( data->vfs_mgr, data->current_dir_internal, new_path, sizeof new_path, &written );
305     if ( rc == 0 )
306     {
307         bool selected;
308         rc = Present_Input ( dlg, data, "goto path", new_path, sizeof new_path, &selected );
309         if ( rc == 0 && selected )
310         {
311             rc = native_to_internal( data->vfs_mgr, new_path, data->current_dir_internal,
312                                      sizeof data->current_dir_internal, &written );
313             if ( rc == 0 )
314                 rc = fill_widget_with_dirs( dlg, data->dir, ID_DIRS, data->current_dir_internal, NULL, true );
315             if ( rc == 0 )
316                 rc = set_native_caption( dlg, data->vfs_mgr, ID_CURR, data->current_dir_internal );
317 
318             KTUIDlgSetFocus( dlg, ID_DIRS );
319         }
320     }
321     return rc;
322 }
323 
324 
DirDlgEvent(struct KTUIDlg * dlg,tuidlg_event * dev,dir_dlg_data * data)325 static rc_t DirDlgEvent ( struct KTUIDlg * dlg, tuidlg_event * dev, dir_dlg_data * data )
326 {
327     rc_t rc = 0;
328     if ( dev->event_type == ktuidlg_event_select )
329     {
330         switch ( dev->widget_id )
331         {
332             case ID_DIRS     : rc = DirDlgDirectoryChanged( dlg, data, ( uint32_t )dev->value_1 ); break;
333             case ID_B_OK     : data->ok_pressed = data->done = true; break;
334             case ID_B_CANCEL : data->done = true; break;
335             case ID_B_CREATE : rc = DirDlgCreate( dlg, data ); break;
336             case ID_B_GOTO   : rc = DirDlgGotoDir( dlg, data ); break;
337         }
338     }
339     return rc;
340 }
341 
342 
DirDlgTuiEvent(struct KTUIDlg * dlg,dir_dlg_data * data,tui_event * ev,bool * handled)343 static rc_t DirDlgTuiEvent( struct KTUIDlg * dlg, dir_dlg_data * data, tui_event * ev, bool * handled )
344 {
345     rc_t rc = 0;
346     *handled = false;
347     if ( ev->event_type == ktui_event_kb )
348     {
349         switch( ev->data.kb_data.code )
350         {
351             case ktui_F7 : rc = DirDlgCreate ( dlg, data ); *handled = true; break;
352             case ktui_F3 : rc = DirDlgGotoDir ( dlg, data ); *handled = true; break;
353             case ktui_F2 : data->ok_pressed = data->done = true; *handled = true; break;
354             case ktui_alpha : switch ( ev->data.kb_data.key )
355                                {
356                                     case 'c' :
357                                     case 'C' : rc = DirDlgCreate ( dlg, data ); *handled = true; break;
358                                     case 'g' :
359                                     case 'G' : rc = DirDlgGotoDir ( dlg, data ); *handled = true; break;
360                                }
361                                break;
362         }
363     }
364     return rc;
365 }
366 
367 
DirDlgLoop(struct KTUIDlg * dlg,dir_dlg_data * data)368 static rc_t DirDlgLoop ( struct KTUIDlg * dlg, dir_dlg_data * data )
369 {
370     rc_t rc;
371     tui_event event;
372     struct KTUI * tui = KTUIDlgGetTui( dlg );
373 
374     KTUIDlgDraw( dlg, false );  /* draw this dialog */
375     do
376     {
377         rc = KTUIGet ( tui, &event );
378         if ( rc == 0 )
379         {
380             bool handled = false;
381             rc = DirDlgTuiEvent( dlg, data, &event, &handled );
382             if ( rc == 0 && !handled )
383             {
384                 rc = KTUIDlgHandleEvent( dlg, &event );
385                 if ( rc == 0 )
386                 {
387                     tuidlg_event dev;
388                     do
389                     {
390                         rc = KTUIDlgGet ( dlg, &dev );
391                         if ( rc == 0 && dev.event_type != ktuidlg_event_none )
392                             rc = DirDlgEvent( dlg, &dev, data );
393                     } while ( rc == 0 && dev.event_type != ktuidlg_event_none );
394                 }
395             }
396         }
397     } while ( rc == 0 && !is_alpha_key( &event, 27 ) && !data->done );
398     return rc;
399 }
400 
401 
make_dlg_with_bg(struct KTUIDlg ** dlg,struct KTUIPalette ** pal,struct KTUI * tui_,struct KTUIDlg * parent,tui_rect * r,KTUI_color bg1,KTUI_color bg2)402 static rc_t make_dlg_with_bg( struct KTUIDlg ** dlg,
403                               struct KTUIPalette ** pal,
404                               struct KTUI * tui_,
405                               struct KTUIDlg * parent,
406                               tui_rect * r,
407                               KTUI_color bg1,
408                               KTUI_color bg2 )
409 {
410     rc_t rc;
411     struct KTUI * tui = tui_;
412     if ( tui == NULL )
413         tui = KTUIDlgGetTui( parent );
414     if ( tui == NULL )
415         rc = RC( rcApp, rcAttr, rcCreating, rcParam, rcNull );
416     else
417     {
418         rc = KTUIPaletteMake ( pal );
419         if ( rc == 0 )
420         {
421             KTUIPaletteSet_bg ( *pal, ktuipa_dlg, bg1 );
422             KTUIPaletteSet_bg ( *pal, ktuipa_dlg_caption, bg2 );
423             rc = KTUIDlgMake ( tui, dlg, parent, *pal, r );
424         }
425     }
426     return rc;
427 }
428 
429 
DirDlg(struct KTUI * tui,struct KTUIDlg * parent,char * buffer,uint32_t buffer_size,bool * done,tui_rect * r,KTUI_color bg1,KTUI_color bg2,bool allow_dir_create)430 LIB_EXPORT rc_t CC DirDlg ( struct KTUI * tui,
431                             struct KTUIDlg * parent,
432                             char * buffer,             /* if empty ... local path */
433                             uint32_t buffer_size,
434                             bool * done,               /* user has selected a directory */
435                             tui_rect * r,              /* rectangle for dialog */
436                             KTUI_color bg1,
437                             KTUI_color bg2,
438                             bool allow_dir_create )
439 {
440     struct KTUIPalette * pal;
441     struct KTUIDlg * dlg;
442     rc_t rc = make_dlg_with_bg( &dlg, &pal, tui, parent, r, bg1, bg2 );
443     if ( rc == 0 )
444     {
445         rc = KTUIDlgSetCaption ( dlg, "select directory" );
446         if ( rc == 0 )
447         {
448             dir_dlg_data data;
449 
450             init_dlg_data( &data, r->w, r->h, buffer, bg1, bg2, allow_dir_create );
451             rc = PopulateDirDlg ( dlg, &data );
452             if ( rc == 0 )
453                 DirDlgLoop( dlg, &data );
454 
455             if ( done != NULL )
456                 *done = data.ok_pressed;
457 
458             if ( data.ok_pressed )
459             {
460                 size_t written;
461                 internal_to_native( data.vfs_mgr, data.current_dir_internal, buffer, buffer_size, &written );
462             }
463 
464             destroy_dlg_data( &data );
465         }
466         KTUIDlgRelease ( dlg );
467         KTUIPaletteRelease ( pal );
468     }
469     return rc;
470 }
471