]> git.mxchange.org Git - flightgear.git/blob - src/GUI/prop_picker.cxx
dbb99615b97d6eba237682496d91aed848114035
[flightgear.git] / src / GUI / prop_picker.cxx
1 /*
2
3      Adapted by Jim Wilson, beginning Sept 2001 (FG v 0.79)
4
5 ****     Insert FlightGear GPL here.
6
7      Based on puFilePicker from:
8
9      ********
10      PLIB - A Suite of Portable Game Libraries
11      Copyright (C) 2001  Steve Baker
12  
13      This library is free software; you can redistribute it and/or
14      modify it under the terms of the GNU Library General Public
15      License as published by the Free Software Foundation; either
16      version 2 of the License, or (at your option) any later version.
17  
18      This library is distributed in the hope that it will be useful,
19      but WITHOUT ANY WARRANTY; without even the implied warranty of
20      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21      Library General Public License for more details.
22  
23      You should have received a copy of the GNU Library General Public
24      License along with this library; if not, write to the Free
25      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
26  
27      For further information visit http://plib.sourceforge.net
28
29      $Id$
30      ********
31 */
32
33
34 #ifdef HAVE_CONFIG_H
35 #  include <config.h>
36 #endif
37
38 #include "prop_picker.hxx"
39 #include <Main/globals.hxx>
40
41 #include <simgear/compiler.h>
42 #include <simgear/misc/props.hxx>
43
44 #define DOTDOTSLASH "../"
45 #define SLASH       "/"
46
47 string name, line, value;
48
49 static puObject *PP_widget = 0;
50
51 // widget location and size...
52 #define PROPPICK_X 100
53 #define PROPPICK_Y 200
54 #define PROPPICK_W 500
55 #define PROPPICK_H 300
56
57 static puObject *PE_widget = 0;
58
59 void prop_pickerInit()
60 {
61         if( PP_widget == 0 ) {
62             fgPropPicker *PP = new fgPropPicker ( PROPPICK_X, PROPPICK_Y, PROPPICK_W, PROPPICK_H, 1, "/", "FG Properties");
63             PP_widget = PP;
64         }
65 }
66
67 void prop_pickerView( puObject * )
68 {
69         if( PP_widget == 0 ) {
70                 prop_pickerInit();
71         }
72         fgPropPicker *me = (fgPropPicker *)PP_widget -> getUserData();
73         // refresh
74         me -> find_props();
75         FG_PUSH_PUI_DIALOG( me );
76 }
77
78 void prop_pickerRefresh()
79 {
80         if( PP_widget == 0 ) {
81                 prop_pickerInit();
82         }
83         fgPropPicker *me = (fgPropPicker *)PP_widget -> getUserData();
84         me -> find_props();
85 }
86
87 void prop_editInit(char * name, char * value, char * proppath)
88 {
89         if( PE_widget == 0 ) {
90             fgPropEdit *PP = new fgPropEdit ( name, value, proppath );
91             PE_widget = PP;
92         }
93 }
94
95 void prop_editOpen( char * name, char * value, char * proppath )
96 {
97         if( PE_widget == 0 ) {
98                 prop_editInit( name, value, proppath );
99         }
100         fgPropEdit *me = (fgPropEdit *)PE_widget -> getUserData();
101         me -> propname ->    setLabel          (name);
102         me -> propinput ->   setValue          (value);
103         strcpy(me -> propPath, proppath);
104         me -> propinput -> acceptInput ();
105         FG_PUSH_PUI_DIALOG( me );
106 }
107
108 // return a human readable form of the value "type"
109 static string getValueTypeString( const SGPropertyNode *node ) {
110     string result;
111
112     if ( node == NULL ) {
113         return "unknown";
114     }
115
116     SGPropertyNode::Type type = node->getType();
117     if ( type == SGPropertyNode::UNSPECIFIED ) {
118         result = "unspecified";
119     } else if ( type == SGPropertyNode::NONE ) {
120         result = "none";
121     } else if ( type == SGPropertyNode::BOOL ) {
122         result = "bool";
123     } else if ( type == SGPropertyNode::INT ) {
124         result = "int";
125     } else if ( type == SGPropertyNode::LONG ) {
126         result = "long";
127     } else if ( type == SGPropertyNode::FLOAT ) {
128         result = "float";
129     } else if ( type == SGPropertyNode::DOUBLE ) {
130         result = "double";
131     } else if ( type == SGPropertyNode::STRING ) {
132         result = "string";
133     }
134
135     return result;
136 }
137
138 void fgPropPicker::fgPropPickerHandleSlider ( puObject * slider )
139 {
140   float val ;
141   slider -> getValue ( &val ) ;
142   val = 1.0f - val ;
143
144   puListBox* list_box = (puListBox*) slider -> getUserData () ;
145   int index = int ( list_box -> getNumItems () * val ) ;
146   list_box -> setTopItem ( index ) ;
147 }
148
149 void fgPropPicker::fgPropPickerHandleArrow ( puObject *arrow )
150 {
151   puSlider *slider = (puSlider *) arrow->getUserData () ;
152   puListBox* list_box = (puListBox*) slider -> getUserData () ;
153
154   int type = ((puArrowButton *)arrow)->getArrowType() ;
155   int inc = ( type == PUARROW_DOWN     ) ?   1 :
156             ( type == PUARROW_UP       ) ?  -1 :
157             ( type == PUARROW_FASTDOWN ) ?  10 :
158             ( type == PUARROW_FASTUP   ) ? -10 : 0 ;
159
160   float val ;
161   slider -> getValue ( &val ) ;
162   val = 1.0f - val ;
163   int num_items = list_box->getNumItems () - 1 ;
164   if ( num_items > 0 )
165   {
166     int index = int ( num_items * val + 0.5 ) + inc ;
167     if ( index > num_items ) index = num_items ;
168     if ( index < 0 ) index = 0 ;
169
170     slider -> setValue ( 1.0f - (float)index / num_items ) ;
171     list_box -> setTopItem ( index ) ;
172   }
173 }
174
175
176 void fgPropPicker::chop_file ( char *fname )
177 {
178   /* removes everything back to the last '/' */
179
180   for ( int i = strlen(fname)-1 ; fname[i] != SLASH[0] && i >= 0 ; i-- )
181     fname[i] = '\0' ;
182 }
183
184
185 void fgPropPicker::go_up_one_directory ( char *fname )
186 {
187   /* removes everything back to the last but one '/' */
188
189   chop_file ( fname ) ;
190
191   if ( strlen ( fname ) == 0 )
192   {
193     /* Empty string!  The only way to go up is to append a "../" */
194
195     strcpy ( fname, DOTDOTSLASH ) ;
196     return ;
197   }
198
199   /* If the last path element is a "../" then we'll have to add another "../" */
200
201   if ( strcmp ( & fname [ strlen(fname)-3 ], DOTDOTSLASH ) == 0 )
202   {
203    if ( strlen ( fname ) + 4 >= PUSTRING_MAX )
204     {
205       ulSetError ( UL_WARNING, "PUI: fgPropPicker - path is too long, max is %d.",
206                           PUSTRING_MAX ) ;
207       return ;
208     }
209
210     strcat ( fname, DOTDOTSLASH ) ;
211     return ;
212   }
213
214   /* Otherwise, just delete the last element of the path. */
215
216   /* Remove the trailing slash - then remove the rest as if it was a file name */
217
218   fname [ strlen(fname)-1 ] = '\0' ;
219   chop_file ( fname ) ;
220 }
221
222
223 void fgPropPicker::handle_select ( puObject* list_box )
224 {
225   fgPropPicker* prop_picker = (fgPropPicker*) list_box -> getUserData () ;
226
227   int selected ;
228   list_box -> getValue ( &selected ) ;
229
230   if ( selected >= 0 && selected < prop_picker -> num_files )
231   {
232     char *dst = prop_picker -> startDir ;
233     char *src = prop_picker -> files [ selected ] ;
234
235     if ( strcmp ( src, "." ) == 0 )
236     {
237       /* Do nothing - but better refresh anyway. */
238
239       prop_picker -> find_props () ;
240       return ;
241     } 
242
243     if ( strcmp ( src, ".." ) == 0 )
244     {
245       /* Do back up one level - so refresh. */
246
247       go_up_one_directory ( dst ) ;
248       prop_picker -> find_props () ;
249       return ;
250     } 
251
252     if ( prop_picker -> dflag [ selected ] )
253     {
254       /* If this is a directory - then descend into it and refresh */
255
256       if ( strlen ( dst ) + strlen ( src ) + 2 >= PUSTRING_MAX )
257       {
258         ulSetError ( UL_WARNING,
259           "PUI: fgPropPicker - path is too long, max is %d.", PUSTRING_MAX ) ;
260         return ;
261       }
262
263       strcat ( dst, src ) ; /* add path to descend to */
264       prop_picker -> find_props () ;
265       return ;
266     }
267
268     /* If this is a regular file - then just append it to the string */
269
270     if ( strlen ( dst ) + strlen ( src ) + 2 >= PUSTRING_MAX )
271     {
272       ulSetError ( UL_WARNING, 
273         "PUI: fgPropPicker - path is too long, max is %d.", PUSTRING_MAX ) ;
274       return ;
275     }
276     prop_editOpen( prop_picker -> names [ selected ], prop_picker -> values [ selected ], dst);
277   }
278   else
279   {
280     /*
281       The user clicked on blank screen - maybe we should
282       refresh just in case some other process created the
283       file.
284     */
285
286     prop_picker -> find_props () ;
287   }
288 }
289
290
291 void fgPropPicker::fgPropPickerHandleOk ( puObject* b )
292 {
293   fgPropPicker* prop_picker = (fgPropPicker*) b -> getUserData () ;
294
295   /* nothing to do, just hide */
296   FG_POP_PUI_DIALOG( prop_picker );
297 }
298
299 /*
300
301 fgPropPicker::~fgPropPicker ()
302 {
303   if ( files )
304   {
305     for ( int i=0; i<num_files; i++ ) {
306       delete files[i];
307       delete names[i];
308       delete values[i];
309     }
310
311     delete[] files;
312     delete[] names;
313     delete[] values;
314     delete[] dflag;
315   }
316
317   if ( this == puActiveWidget () )
318     puDeactivateWidget () ;
319 }
320
321 */
322
323 fgPropPicker::fgPropPicker ( int x, int y, int w, int h, int arrows,
324                                       const char *dir, const char *title ) : puDialogBox ( x,y )
325 {
326   puFont LegendFont, LabelFont;
327   puGetDefaultFonts ( &LegendFont, &LabelFont );
328
329   files = NULL ;
330   dflag = NULL ;
331   names = NULL ;
332   values = NULL ;
333   num_files = 0 ;
334
335   strcpy ( startDir, dir ) ;
336   // printf ( "StartDirLEN=%i", strlen(startDir));
337   if ( arrows > 2 ) arrows = 2 ;
338   if ( arrows < 0 ) arrows = 0 ;
339   arrow_count = arrows ;
340
341   frame = new puFrame ( 0, 0, w, h );
342
343   setUserData( this );
344
345   proppath = new puText            (10, h-30);
346   proppath ->    setLabel          (startDir);
347
348   slider = new puSlider (w-30,40+20*arrows,h-100-40*arrows,TRUE,20);
349   slider->setDelta(0.1f);
350   slider->setValue(1.0f);
351   slider->setSliderFraction (0.2f) ;
352   slider->setCBMode( PUSLIDER_DELTA );
353   
354   list_box = new puListBox ( 10, 40, w-40, h-60 ) ;
355   list_box -> setLabel ( title );
356   list_box -> setLabelPlace ( PUPLACE_ABOVE ) ;
357   list_box -> setStyle ( -PUSTYLE_SMALL_SHADED ) ;
358   list_box -> setUserData ( this ) ;
359   list_box -> setCallback ( handle_select ) ;
360   list_box -> setValue ( 0 ) ;
361
362   ok_button = new puOneShot ( 10, 10, (w<170)?(w/2-5):80, 30 ) ;
363   ok_button -> setLegend ( "Ok" ) ;
364   ok_button -> setUserData ( this ) ;
365   ok_button -> setCallback ( fgPropPickerHandleOk ) ;
366
367   if ( arrows > 0 )
368   {
369     down_arrow = new puArrowButton ( w-30, 20+20*arrows, w-10, 40+20*arrows, PUARROW_DOWN ) ;
370     down_arrow->setUserData ( slider ) ;
371     down_arrow->setCallback ( fgPropPickerHandleArrow ) ;
372
373     up_arrow = new puArrowButton ( w-30, h-60-20*arrows, w-10, h-40-20*arrows, PUARROW_UP ) ;
374     up_arrow->setUserData ( slider ) ;
375     up_arrow->setCallback ( fgPropPickerHandleArrow ) ;
376   }
377
378   if ( arrows == 2 )
379   {
380     down_arrow = new puArrowButton ( w-30, 40, w-10, 60, PUARROW_FASTDOWN ) ;
381     down_arrow->setUserData ( slider ) ;
382     down_arrow->setCallback ( fgPropPickerHandleArrow ) ;
383
384     up_arrow = new puArrowButton ( w-30, h-80, w-10, h-60, PUARROW_FASTUP ) ;
385     up_arrow->setUserData ( slider ) ;
386     up_arrow->setCallback ( fgPropPickerHandleArrow ) ;
387   }
388
389   // after picker is built, load the list box with data...
390   find_props () ;
391
392 //  printf("after Props files[1]=%s\n",files[1]);
393 //  printf("num items %i", list_box -> getNumItems ());
394
395   slider -> setUserData ( list_box ) ;
396   slider -> setCallback ( fgPropPickerHandleSlider ) ;
397
398   FG_FINALIZE_PUI_DIALOG( this );
399   printf("fgPropPicker - End of Init\n");
400 }
401
402
403 void fgPropPicker::find_props ()
404 {
405
406   int pi;
407
408   if ( files != NULL )
409   {
410     for ( int i = 0 ; i < num_files ; i++ ) {
411       delete files[i] ;
412       delete names[i] ;
413       delete values[i] ;
414     }
415
416     delete [] files ;
417     delete [] names ;
418     delete [] values ;
419     delete [] dflag ;
420   }
421
422   num_files = 0 ;
423
424   char dir [ PUSTRING_MAX * 2 ] ;
425
426   int  iindex = 0;
427   char sindex [ 20 ];
428
429   strcpy ( dir, startDir ) ;
430
431   int i = 0 ;
432 //  printf("dir begin of find_props=%s\n",dir);
433 //  printf("len of dir=%i",strlen(dir));
434   SGPropertyNode * node = globals->get_props()->getNode(dir);
435
436   num_files = (int)node->nChildren();
437
438   // instantiate string objects and add [.] and [..] for subdirs
439   if (strcmp(dir,"/") == 0) {
440     files = new char* [ num_files+1 ] ;
441     names = new char* [ num_files+1 ] ;
442     values = new char* [ num_files+1 ] ;
443     dflag = new char  [ num_files+1 ] ;
444     pi = 0;
445   } else {
446     // add two for the .. and .
447     num_files = num_files + 2;
448     // make room for .. and .
449     files = new char* [ num_files+1 ] ;
450     names = new char* [ num_files+1 ] ;
451     values = new char* [ num_files+1 ] ;
452     dflag = new char  [ num_files+1 ] ;
453     line = ".";
454     files [ 0 ] = new char[ strlen(line.c_str())+2 ];
455     strcpy ( files [ 0 ], line.c_str() );
456     names [ 0 ] = new char[ 2 ];
457     values[ 0 ] = new char[ 2 ] ;
458     line = "..";
459     dflag[ 0 ] = 1;
460     files [ 1 ] = new char[ strlen(line.c_str())+2 ];
461     strcpy ( files [ 1 ], line.c_str() );
462     names [ 1 ] = new char[ strlen(line.c_str())+2 ];
463     values[ 1 ] = new char[ 2 ] ;
464     strcpy ( names [ 1 ], line.c_str() );
465
466     dflag[ 1 ] = 1;
467     pi = 2;
468   };
469
470
471   for (i = 0; i < (int)node->nChildren(); i++) {
472             SGPropertyNode * child = node->getChild(i);
473             name = child->getName();
474             if ( node->getChild(name.c_str(), 1) != 0 ) {
475                 iindex = child->getIndex();
476                 sprintf(sindex, "[%d]", iindex);
477                 name += sindex;
478             }
479             line = name;
480             names[ pi ] = new char[ strlen(line.c_str())+2 ] ;
481             strcpy ( names [ pi ], line.c_str() ) ;
482             if ( child->nChildren() > 0 ) {
483                 dflag[ pi ] = 1 ;
484                 files[ pi ] = new char[ strlen(line.c_str())+strlen(sindex)+4 ] ;
485                 strcpy ( files [ pi ], line.c_str() ) ;
486                 strcat ( files [ pi ], "/" ) ;
487                 values[ pi ] = new char[ 2 ] ;
488             } else {
489                 dflag[ pi ] = 0 ;
490                 value = node->getStringValue ( name.c_str(), "" );
491                 values[ pi ] = new char[ strlen(value.c_str())+2 ] ;
492                 strcpy ( values [pi], value.c_str() );
493                 line += " = '" + value + "' " + "(";
494                 line += getValueTypeString( node->getNode( name.c_str() ) );
495                 line += ")";
496                 files[ pi ] = new char[ strlen(line.c_str())+2 ] ;
497                 strcpy ( files [ pi ], line.c_str() ) ;
498             }
499             // printf("files->%i of %i %s\n",pi, node->nChildren(), files [pi]);
500             ++pi;
501   }
502
503   files [ num_files ] = NULL ;
504
505   // leave the . and .. alone...
506   int ii = ( strcmp(files [0], "." ) == 0 ) ? 2 : 0;
507
508   // Sort the entries.  This is a simple N^2 extraction sort.  More
509   // elaborate algorithms aren't necessary for the few dozen
510   // properties we're going to sort.
511   for(i=ii; i<num_files; i++) {
512     int j, min = i;
513     char df, *tmp;
514     for(j=i+1; j<num_files; j++)
515       if(strcmp(names[j], names[min]) < 0)
516         min = j;
517     if(i != min) {
518       tmp =  names[min];  names[min] =  names[i];  names[i] = tmp;
519       tmp =  files[min];  files[min] =  files[i];  files[i] = tmp;
520       tmp = values[min]; values[min] = values[i]; values[i] = tmp;
521       df  =  dflag[min];  dflag[min] =  dflag[i];  dflag[i] = df;
522     }
523   }
524
525   // printf("files pointer=%i/%i\n", files, num_files);
526
527   proppath ->    setLabel          (startDir);
528
529   list_box -> newList ( files ) ;
530
531   // if non-empty list, adjust the size of the slider...
532   if (num_files > 1) {
533     if ((11.0f/(num_files)) < 1) {
534       slider->setSliderFraction (11.0f/(num_files)) ;
535       slider->reveal();
536       up_arrow->reveal();
537       down_arrow->reveal();
538       }
539     else {
540       slider->setSliderFraction (0.9999f) ;
541       slider->hide();
542       up_arrow->hide();
543       down_arrow->hide();      
544       }
545   }
546   
547 }
548
549 void fgPropEdit::fgPropEditHandleCancel ( puObject* b )
550 {
551   fgPropEdit* prop_edit = (fgPropEdit*) b -> getUserData () ;
552   FG_POP_PUI_DIALOG( prop_edit );
553 }
554
555 void fgPropEdit::fgPropEditHandleOK ( puObject* b )
556 {
557   fgPropEdit* prop_edit = (fgPropEdit*) b -> getUserData () ;
558   const char* tname;
559   char* tvalue;
560
561   // use label text for property node to be updated
562   tname = prop_edit -> propname -> getLabel();
563   prop_edit -> propinput -> getValue( &tvalue );
564
565   SGPropertyNode * node = globals->get_props()->getNode(prop_edit -> propPath);
566   node->getNode( prop_edit -> propname -> getLabel(), true)->setStringValue(tvalue);
567
568   // update the picker display so it shows new value
569   prop_pickerRefresh();
570
571   FG_POP_PUI_DIALOG( prop_edit );
572 }
573
574 fgPropEdit::fgPropEdit ( char *name, char *value, char *proppath ) : puDialogBox ( 0, 0 )
575
576 {
577     puFont LegendFont, LabelFont;
578     puGetDefaultFonts ( &LegendFont, &LabelFont );
579
580     // locate in relation to picker widget...
581     int fx = PROPPICK_X;
582     int fy = PROPPICK_Y + PROPPICK_H;
583     frame   = new puFrame           (fx,fy, fx+500, fy+120);
584
585     strcpy (propPath, proppath);
586
587     setUserData( this );
588
589     propname = new puText            (fx+10, fy+90);
590     propname ->    setLabel          (name);
591         
592     propinput   = new puInput           (fx+10, fy+50, fx+480, fy+80);
593     propinput   ->    setValue          (value);
594     propinput   ->    acceptInput();
595         
596
597     ok_button     =  new puOneShot   (fx+10, fy+10, fx+80, fy+30);
598     ok_button     ->     setUserData (this);
599     ok_button     ->     setLegend   (gui_msg_OK);
600     ok_button     ->     setCallback (fgPropEditHandleOK);
601     ok_button     ->     makeReturnDefault(TRUE);
602         
603     cancel_button =  new puOneShot   (fx+100, fy+10, fx+180, fy+30);
604     cancel_button     ->     setUserData (this);
605     cancel_button ->     setLegend   (gui_msg_CANCEL);
606     cancel_button ->     setCallback (fgPropEditHandleCancel);
607         
608     FG_FINALIZE_PUI_DIALOG( this );
609 }