3 Adapted by Jim Wilson, beginning Sept 2001 (FG v 0.79)
5 **** Insert FlightGear GPL here.
7 Based on puFilePicker from:
10 PLIB - A Suite of Portable Game Libraries
11 Copyright (C) 2001 Steve Baker
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.
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.
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
27 For further information visit http://plib.sourceforge.net
38 #include "prop_picker.hxx"
39 #include <Main/globals.hxx>
41 #include <simgear/compiler.h>
42 #include <simgear/misc/props.hxx>
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;
50 #define DOTDOTSLASH "../"
53 static puObject *PP_widget = 0;
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
61 static puObject *PE_widget = 0;
63 void prop_pickerInit()
65 if( PP_widget == 0 ) {
66 fgPropPicker *PP = new fgPropPicker ( PROPPICK_X, PROPPICK_Y, PROPPICK_W, PROPPICK_H, 1, "/", "FG Properties");
71 void prop_pickerView( puObject * )
73 if( PP_widget == 0 ) {
76 fgPropPicker *me = (fgPropPicker *)PP_widget -> getUserData();
79 FG_PUSH_PUI_DIALOG( me );
82 void prop_pickerRefresh()
84 if( PP_widget == 0 ) {
87 fgPropPicker *me = (fgPropPicker *)PP_widget -> getUserData();
91 void prop_editInit(char * name, char * value, char * proppath)
93 if( PE_widget == 0 ) {
94 fgPropEdit *PP = new fgPropEdit ( name, value, proppath );
99 void prop_editOpen( char * name, char * value, char * proppath )
101 if( PE_widget == 0 ) {
102 prop_editInit( name, value, proppath );
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 );
112 // return a human readable form of the value "type"
113 static string getValueTypeString( const SGPropertyNode *node ) {
116 if ( node == NULL ) {
120 SGPropertyNode::Type type = node->getType();
121 if ( type == SGPropertyNode::UNSPECIFIED ) {
122 result = "unspecified";
123 } else if ( type == SGPropertyNode::NONE ) {
125 } else if ( type == SGPropertyNode::BOOL ) {
127 } else if ( type == SGPropertyNode::INT ) {
129 } else if ( type == SGPropertyNode::LONG ) {
131 } else if ( type == SGPropertyNode::FLOAT ) {
133 } else if ( type == SGPropertyNode::DOUBLE ) {
135 } else if ( type == SGPropertyNode::STRING ) {
142 void fgPropPicker::fgPropPickerHandleSlider ( puObject * slider )
144 puListBox* list_box = (puListBox*) slider -> getUserData () ;
147 slider -> getValue ( &val ) ;
149 int scroll_range = list_box->getNumItems () - list_box->getNumVisible() ;
150 if ( scroll_range > 0 )
152 int index = int ( scroll_range * val + 0.5 ) ;
153 list_box -> setTopItem ( index ) ;
157 void fgPropPicker::fgPropPickerHandleArrow ( puObject *arrow )
159 puSlider *slider = (puSlider *) arrow->getUserData () ;
160 puListBox* list_box = (puListBox*) slider -> getUserData () ;
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 ;
169 slider -> getValue ( &val ) ;
171 int scroll_range = list_box->getNumItems () - list_box->getNumVisible() ;
172 if ( scroll_range > 0 )
174 int index = int ( scroll_range * val + 0.5 ) ;
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 ;
182 slider -> setValue ( 1.0f - (float)index / scroll_range ) ;
183 list_box -> setTopItem ( index ) ;
188 void fgPropPicker::chop_file ( char *fname )
190 /* removes everything back to the last '/' */
192 for ( int i = strlen(fname)-1 ; fname[i] != SLASH[0] && i >= 0 ; i-- )
197 void fgPropPicker::go_up_one_directory ( char *fname )
199 /* removes everything back to the last but one '/' */
201 chop_file ( fname ) ;
203 if ( strlen ( fname ) == 0 )
205 /* Empty string! The only way to go up is to append a "../" */
207 strcpy ( fname, DOTDOTSLASH ) ;
211 /* If the last path element is a "../" then we'll have to add another "../" */
213 if ( strcmp ( & fname [ strlen(fname)-3 ], DOTDOTSLASH ) == 0 )
215 if ( strlen ( fname ) + 4 >= PUSTRING_MAX )
217 ulSetError ( UL_WARNING, "PUI: fgPropPicker - path is too long, max is %d.",
222 strcat ( fname, DOTDOTSLASH ) ;
226 /* Otherwise, just delete the last element of the path. */
228 /* Remove the trailing slash - then remove the rest as if it was a file name */
230 fname [ strlen(fname)-1 ] = '\0' ;
231 chop_file ( fname ) ;
235 void fgPropPicker::handle_select ( puObject* list_box )
237 fgPropPicker* prop_picker = (fgPropPicker*) list_box -> getUserData () ;
240 list_box -> getValue ( &selected ) ;
242 if ( selected >= 0 && selected < prop_picker -> num_files )
244 char *dst = prop_picker -> startDir ;
245 char *src = prop_picker -> files [ selected ] ;
247 if ( strcmp ( src, "." ) == 0 )
249 /* Do nothing - but better refresh anyway. */
251 prop_picker -> find_props () ;
255 if ( strcmp ( src, ".." ) == 0 )
257 /* Do back up one level - so refresh. */
259 go_up_one_directory ( dst ) ;
260 prop_picker -> find_props () ;
264 if ( prop_picker -> dflag [ selected ] )
266 /* If this is a directory - then descend into it and refresh */
268 if ( strlen ( dst ) + strlen ( src ) + 2 >= PUSTRING_MAX )
270 ulSetError ( UL_WARNING,
271 "PUI: fgPropPicker - path is too long, max is %d.", PUSTRING_MAX ) ;
275 strcat ( dst, src ) ; /* add path to descend to */
276 prop_picker -> find_props () ;
280 /* If this is a regular file - then just append it to the string */
282 if ( strlen ( dst ) + strlen ( src ) + 2 >= PUSTRING_MAX )
284 ulSetError ( UL_WARNING,
285 "PUI: fgPropPicker - path is too long, max is %d.", PUSTRING_MAX ) ;
288 prop_editOpen( prop_picker -> names [ selected ], prop_picker -> values [ selected ], dst);
293 The user clicked on blank screen - maybe we should
294 refresh just in case some other process created the
298 prop_picker -> find_props () ;
303 void fgPropPicker::fgPropPickerHandleOk ( puObject* b )
305 fgPropPicker* prop_picker = (fgPropPicker*) b -> getUserData () ;
307 /* nothing to do, just hide */
308 FG_POP_PUI_DIALOG( prop_picker );
312 void fgPropPicker::delete_arrays ()
316 for ( int i=0; i<num_files; i++ ) {
331 fgPropPicker::~fgPropPicker ()
335 if ( this == puActiveWidget () )
336 puDeactivateWidget () ;
341 fgPropPicker::fgPropPicker ( int x, int y, int w, int h, int arrows,
342 const char *dir, const char *title ) : puDialogBox ( x,y )
344 puFont LegendFont, LabelFont;
345 puGetDefaultFonts ( &LegendFont, &LabelFont );
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 ;
359 frame = new puFrame ( 0, 0, w, h );
363 proppath = new puText (10, h-30);
364 proppath -> setLabel (startDir);
366 slider = new puSlider (w-30,40+20*arrows,h-100-40*arrows,TRUE,20);
367 slider->setValue(1.0f);
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 ) ;
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 ) ;
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 ) ;
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 ) ;
393 // after picker is built, load the list box with data...
396 // printf("after Props files[1]=%s\n",files[1]);
397 // printf("num items %i", list_box -> getNumItems ());
399 slider -> setUserData ( list_box ) ;
400 slider -> setCallback ( fgPropPickerHandleSlider ) ;
402 FG_FINALIZE_PUI_DIALOG( this );
403 printf("fgPropPicker - End of Init\n");
407 // Like strcmp, but for sorting property nodes into a suitable display order.
408 static int nodeNameCompare(const void *ppNode1, const void *ppNode2)
410 const SGPropertyNode_ptr pNode1 = *(const SGPropertyNode_ptr *)ppNode1;
411 const SGPropertyNode_ptr pNode2 = *(const SGPropertyNode_ptr *)ppNode2;
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();
420 // Replace the current list of properties with the children of node "startDir".
421 void fgPropPicker::find_props ()
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);
433 num_files = (node) ? (int)node->nChildren() : 0;
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 ] ;
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 ] ;
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() );
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);
476 // Sort the children into display order
477 qsort(children, nChildren, sizeof(children[0]), nodeNameCompare);
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 ) {
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 ] ;
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 );
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() ) ;
504 // printf("files->%i of %i %s\n",pi, node->nChildren(), files [pi]);
511 files [ num_files ] = NULL ;
513 // printf("files pointer=%i/%i\n", files, num_files);
515 proppath -> setLabel (startDir);
517 list_box -> newList ( files ) ;
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);
525 down_arrow->reveal();
533 void fgPropEdit::fgPropEditHandleCancel ( puObject* b )
535 fgPropEdit* prop_edit = (fgPropEdit*) b -> getUserData () ;
536 FG_POP_PUI_DIALOG( prop_edit );
539 void fgPropEdit::fgPropEditHandleOK ( puObject* b )
541 fgPropEdit* prop_edit = (fgPropEdit*) b -> getUserData () ;
545 // use label text for property node to be updated
546 tname = prop_edit -> propname -> getLabel();
547 prop_edit -> propinput -> getValue( &tvalue );
549 SGPropertyNode * node = globals->get_props()->getNode(prop_edit -> propPath);
550 node->getNode( prop_edit -> propname -> getLabel(), true)->setStringValue(tvalue);
552 // update the picker display so it shows new value
553 prop_pickerRefresh();
555 FG_POP_PUI_DIALOG( prop_edit );
558 fgPropEdit::fgPropEdit ( char *name, char *value, char *proppath ) : puDialogBox ( 0, 0 )
561 puFont LegendFont, LabelFont;
562 puGetDefaultFonts ( &LegendFont, &LabelFont );
564 // locate in relation to picker widget...
566 int fy = PROPPICK_Y + PROPPICK_H;
567 frame = new puFrame (fx,fy, fx+500, fy+120);
569 strcpy (propPath, proppath);
573 propname = new puText (fx+10, fy+90);
574 propname -> setLabel (name);
576 propinput = new puInput (fx+10, fy+50, fx+480, fy+80);
577 propinput -> setValue (value);
578 propinput -> acceptInput();
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);
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);
592 FG_FINALIZE_PUI_DIALOG( this );