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