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