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