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