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 <simgear/compiler.h>
39 #include <simgear/props/props.hxx>
43 #include <Main/globals.hxx>
44 #include "new_gui.hxx"
45 #include "prop_picker.hxx"
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;
52 #define DOTDOTSLASH "../"
55 static puObject *PP_widget = 0;
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
63 static puObject *PE_widget = 0;
65 void prop_pickerInit()
67 if( PP_widget == 0 ) {
68 fgPropPicker *PP = new fgPropPicker ( PROPPICK_X, PROPPICK_Y, PROPPICK_W, PROPPICK_H, 1, "/", "FG Properties");
73 void prop_pickerView( puObject * )
75 if( PP_widget == 0 ) {
78 fgPropPicker *me = (fgPropPicker *)PP_widget -> getUserData();
81 FG_PUSH_PUI_DIALOG( me );
84 void prop_pickerRefresh()
86 if( PP_widget == 0 ) {
89 fgPropPicker *me = (fgPropPicker *)PP_widget -> getUserData();
93 void prop_editInit( const char * name, const char * value, char * proppath )
95 if( PE_widget == 0 ) {
96 fgPropEdit *PP = new fgPropEdit ( name, value, proppath );
101 void prop_editOpen( const char * name, const char * value, char * proppath )
103 if( PE_widget == 0 ) {
104 prop_editInit( name, value, proppath );
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 );
114 // return a human readable form of the value "type"
115 static string getValueTypeString( const SGPropertyNode_ptr node ) {
118 if ( node == NULL ) {
122 SGPropertyNode::Type type = node->getType();
123 if ( type == SGPropertyNode::UNSPECIFIED ) {
124 result = "unspecified";
125 } else if ( type == SGPropertyNode::NONE ) {
127 } else if ( type == SGPropertyNode::BOOL ) {
129 } else if ( type == SGPropertyNode::INT ) {
131 } else if ( type == SGPropertyNode::LONG ) {
133 } else if ( type == SGPropertyNode::FLOAT ) {
135 } else if ( type == SGPropertyNode::DOUBLE ) {
137 } else if ( type == SGPropertyNode::STRING ) {
144 void fgPropPicker::fgPropPickerHandleSlider ( puObject * slider )
146 puListBox* list_box = (puListBox*) slider -> getUserData () ;
149 slider -> getValue ( &val ) ;
151 int scroll_range = list_box->getNumItems () - list_box->getNumVisible() ;
152 if ( scroll_range > 0 )
154 int index = int ( scroll_range * val + 0.5 ) ;
155 list_box -> setTopItem ( index ) ;
159 void fgPropPicker::fgPropPickerHandleArrow ( puObject *arrow )
161 puSlider *slider = (puSlider *) arrow->getUserData () ;
162 puListBox* list_box = (puListBox*) slider -> getUserData () ;
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 ;
171 slider -> getValue ( &val ) ;
173 int scroll_range = list_box->getNumItems () - list_box->getNumVisible() ;
174 if ( scroll_range > 0 )
176 int index = int ( scroll_range * val + 0.5 ) ;
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 ;
184 slider -> setValue ( 1.0f - (float)index / scroll_range ) ;
185 list_box -> setTopItem ( index ) ;
190 void fgPropPicker::chop_file ( char *fname )
192 /* removes everything back to the last '/' */
194 for ( int i = strlen(fname)-1 ; fname[i] != SLASH[0] && i >= 0 ; i-- )
199 void fgPropPicker::go_up_one_directory ( char *fname )
201 /* removes everything back to the last but one '/' */
203 chop_file ( fname ) ;
205 if ( strlen ( fname ) == 0 )
207 /* Empty string! The only way to go up is to append a "../" */
209 strcpy ( fname, DOTDOTSLASH ) ;
213 /* If the last path element is a "../" then we'll have to add another "../" */
215 if ( strcmp ( & fname [ strlen(fname)-3 ], DOTDOTSLASH ) == 0 )
217 if ( strlen ( fname ) + 4 >= PUSTRING_MAX )
219 ulSetError ( UL_WARNING, "PUI: fgPropPicker - path is too long, max is %d.",
224 strcat ( fname, DOTDOTSLASH ) ;
228 /* Otherwise, just delete the last element of the path. */
230 /* Remove the trailing slash - then remove the rest as if it was a file name */
232 fname [ strlen(fname)-1 ] = '\0' ;
233 chop_file ( fname ) ;
237 void fgPropPicker::handle_select ( puObject* list_box )
239 fgPropPicker* prop_picker = (fgPropPicker*) list_box -> getUserData () ;
241 list_box -> getValue ( &selected ) ;
243 if ( selected >= 0 && selected < prop_picker -> num_files )
245 char *dst = prop_picker -> startDir ;
246 char *src = prop_picker -> files [ selected ] ;
248 if (prop_picker->dotFiles && (selected < 2)) {
249 if ( strcmp ( src, "." ) == 0 ) {
250 /* Do nothing - but better refresh anyway. */
252 prop_picker -> find_props () ;
254 } else if ( strcmp ( src, ".." ) == 0 ) {
255 /* Do back up one level - so refresh. */
257 go_up_one_directory ( dst ) ;
258 prop_picker -> find_props () ;
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);
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 */
273 if ( strlen ( dst ) + strlen ( src ) + 2 >= PUSTRING_MAX )
275 ulSetError ( UL_WARNING,
276 "PUI: fgPropPicker - path is too long, max is %d.", PUSTRING_MAX ) ;
280 strcat ( dst, src ) ; /* add path to descend to */
281 prop_picker -> find_props () ;
285 /* If this is a regular file - then just append it to the string */
287 if ( strlen ( dst ) + strlen ( src ) + 2 >= PUSTRING_MAX )
289 ulSetError ( UL_WARNING,
290 "PUI: fgPropPicker - path is too long, max is %d.", PUSTRING_MAX ) ;
294 prop_editOpen(child->getName(), child->getStringValue(), dst);
299 The user clicked on blank screen - maybe we should
300 refresh just in case some other process created the
303 // should be obsolete once we observe child add/remove on our top node
304 prop_picker -> find_props () ;
309 void fgPropPicker::fgPropPickerHandleOk ( puObject* b )
311 fgPropPicker* prop_picker = (fgPropPicker*) b -> getUserData () ;
313 /* nothing to do, just hide */
314 FG_POP_PUI_DIALOG( prop_picker );
318 void fgPropPicker::delete_arrays ()
322 for ( int i=0; i<num_files; i++ ) {
326 for (int C=0; C<num_children; ++C) {
327 if (children[C]->nChildren() == 0)
328 children[C]->removeChangeListener(this);
338 fgPropPicker::~fgPropPicker ()
342 if ( this == puActiveWidget () )
343 puDeactivateWidget () ;
348 fgPropPicker::fgPropPicker ( int x, int y, int w, int h, int arrows,
349 const char *dir, const char *title ) :
351 _gui((NewGUI *)globals->get_subsystem("gui"))
353 puFont LegendFont, LabelFont;
354 puGetDefaultFonts ( &LegendFont, &LabelFont );
355 FGColor txtcol(_gui->getColor("label"));
356 txtcol.merge(_gui->getColor("text"));
357 txtcol.merge(_gui->getColor("text-label"));
362 strcpy ( startDir, dir ) ;
363 // printf ( "StartDirLEN=%i", strlen(startDir));
364 if ( arrows > 2 ) arrows = 2 ;
365 if ( arrows < 0 ) arrows = 0 ;
366 arrow_count = arrows ;
368 frame = new puFrame ( 0, 0, w, h );
372 proppath = new puText (10, h-30);
373 proppath -> setLabel (startDir);
374 proppath -> setColor(PUCOL_LABEL, txtcol.red(), txtcol.green(),
375 txtcol.blue(), txtcol.alpha());
377 slider = new puSlider (w-30,40+20*arrows,h-100-40*arrows,TRUE,20);
378 slider->setValue(1.0f);
380 list_box = new puListBox ( 10, 40, w-40, h-60 ) ;
381 list_box -> setLabel ( title );
382 list_box -> setLabelPlace ( PUPLACE_ABOVE ) ;
383 list_box -> setStyle ( -PUSTYLE_SMALL_SHADED ) ;
384 list_box -> setUserData ( this ) ;
385 list_box -> setCallback ( handle_select ) ;
386 list_box -> setValue ( 0 ) ;
387 list_box -> setColor(PUCOL_LABEL, txtcol.red(), txtcol.green(),
388 txtcol.blue(), txtcol.alpha());
390 ok_button = new puOneShot ( 10, 10, (w<170)?(w/2-5):80, 30 ) ;
391 ok_button -> setLegend ( "Ok" ) ;
392 ok_button -> setUserData ( this ) ;
393 ok_button -> setCallback ( fgPropPickerHandleOk ) ;
397 down_arrow = new puArrowButton ( w-30, 20+20*arrows, w-10, 40+20*arrows, PUARROW_DOWN ) ;
398 down_arrow->setUserData ( slider ) ;
399 down_arrow->setCallback ( fgPropPickerHandleArrow ) ;
401 up_arrow = new puArrowButton ( w-30, h-60-20*arrows, w-10, h-40-20*arrows, PUARROW_UP ) ;
402 up_arrow->setUserData ( slider ) ;
403 up_arrow->setCallback ( fgPropPickerHandleArrow ) ;
406 // after picker is built, load the list box with data...
409 // printf("after Props files[1]=%s\n",files[1]);
410 // printf("num items %i", list_box -> getNumItems ());
412 slider -> setUserData ( list_box ) ;
413 slider -> setCallback ( fgPropPickerHandleSlider ) ;
415 FG_FINALIZE_PUI_DIALOG( this );
419 // Like strcmp, but for sorting property nodes into a suitable display order.
420 static int nodeNameCompare(const void *ppNode1, const void *ppNode2)
422 const SGPropertyNode_ptr pNode1 = *(const SGPropertyNode_ptr *)ppNode1;
423 const SGPropertyNode_ptr pNode2 = *(const SGPropertyNode_ptr *)ppNode2;
425 // Compare name first, and then index.
426 int diff = strcmp(pNode1->getName(), pNode2->getName());
427 if (diff) return diff;
428 return pNode1->getIndex() - pNode2->getIndex();
432 // Replace the current list of properties with the children of node "startDir".
433 void fgPropPicker::find_props ()
441 // printf("dir begin of find_props=%s\n",startDir);
442 // printf("len of dir=%i",strlen(startDir));
443 SGPropertyNode * node = globals->get_props()->getNode(startDir);
445 num_files = (node) ? (int)node->nChildren() : 0;
447 // instantiate string objects and add [.] and [..] for subdirs
448 if (strcmp(startDir,"/") == 0) {
449 files = new char* [ num_files+1 ] ;
453 // add two for the .. and .
454 num_files = num_files + 2;
455 // make room for .. and .
456 files = new char* [ num_files+1 ] ;
458 stdString line = ".";
459 files [ 0 ] = new char[line.size() + 1];
460 strcpy ( files [ 0 ], line.c_str() );
463 files [ 1 ] = new char[line.size() + 1];
464 strcpy ( files [ 1 ], line.c_str() );
472 // Get the list of children
473 num_children = node->nChildren();
474 children = new SGPropertyNode_ptr[num_children];
475 for (i = 0; i < num_children; i++) {
476 children[i] = node->getChild(i);
479 // Sort the children into display order
480 qsort(children, num_children, sizeof(children[0]), nodeNameCompare);
482 // Make lists of the children's names, values, etc.
483 for (i = 0; i < num_children; i++) {
484 SGPropertyNode * child = children[i];
485 stdString name = child->getDisplayName(true);
487 if ( child->nChildren() > 0 ) {
488 files[ pi ] = new char[ strlen(name.c_str())+2 ] ;
489 strcpy ( files [ pi ], name.c_str() ) ;
490 strcat ( files [ pi ], "/" ) ;
492 files[pi] = NULL; // ensure it's NULL before setting intial value
493 updateTextForEntry(i);
495 child->addChangeListener(this);
502 files [ num_files ] = NULL ;
504 proppath -> setLabel (startDir);
506 list_box -> newList ( files ) ;
508 // adjust the size of the slider...
509 if (num_files > list_box->getNumVisible()) {
510 slider->setSliderFraction((float)list_box->getNumVisible() / num_files);
511 slider->setValue(1.0f);
514 down_arrow->reveal();
522 void fgPropPicker::updateTextForEntry(int index)
524 assert((index >= 0) && (index < num_children));
525 SGPropertyNode_ptr node = children[index];
527 // take a copy of the value
528 stdString value = node->getStringValue();
530 stdString line = node->getDisplayName() + stdString(" = '")
531 + value + "' " + "(";
532 line += getValueTypeString( node );
535 // truncate entries to plib pui limit
536 if (line.length() >= PUSTRING_MAX)
537 line[PUSTRING_MAX-1] = '\0';
539 if (dotFiles) index +=2;
541 // don't leak everywhere if we're updating
542 delete[] files[index];
544 files[index] = new char[ strlen(line.c_str())+2 ] ;
545 strcpy ( files [ index ], line.c_str() ) ;
548 void fgPropPicker::valueChanged(SGPropertyNode *nd)
550 for (int C=0; C<num_children; ++C)
551 if (children[C] == nd) {
552 updateTextForEntry(C);
557 void fgPropEdit::fgPropEditHandleCancel ( puObject* b )
559 fgPropEdit* prop_edit = (fgPropEdit*) b -> getUserData () ;
560 FG_POP_PUI_DIALOG( prop_edit );
563 void fgPropEdit::fgPropEditHandleOK ( puObject* b )
565 fgPropEdit* prop_edit = (fgPropEdit*) b -> getUserData () ;
569 // use label text for property node to be updated
570 tname = prop_edit -> propname -> getLabel();
571 prop_edit -> propinput -> getValue( &tvalue );
573 SGPropertyNode * node = globals->get_props()->getNode(prop_edit -> propPath);
574 node->getNode( prop_edit -> propname -> getLabel(), true)->setStringValue(tvalue);
576 // update the picker display so it shows new value
577 prop_pickerRefresh();
579 FG_POP_PUI_DIALOG( prop_edit );
582 fgPropEdit::fgPropEdit ( const char *name, const char *value, char *proppath ) :
584 _gui((NewGUI *)globals->get_subsystem("gui"))
586 puFont LegendFont, LabelFont;
587 puGetDefaultFonts ( &LegendFont, &LabelFont );
588 FGColor txtcol(_gui->getColor("label"));
589 txtcol.merge(_gui->getColor("text"));
590 txtcol.merge(_gui->getColor("text-label"));
592 // locate in relation to picker widget...
594 int fy = PROPPICK_Y + PROPPICK_H;
595 frame = new puFrame (fx,fy, fx+500, fy+120);
597 strcpy (propPath, proppath);
601 propname = new puText (fx+10, fy+90);
602 propname -> setLabel (name);
603 propname -> setColor(PUCOL_LABEL, txtcol.red(), txtcol.green(),
604 txtcol.blue(), txtcol.alpha());
606 propinput = new puInput (fx+10, fy+50, fx+480, fy+80);
607 propinput -> setValue (value);
608 propinput -> acceptInput();
611 ok_button = new puOneShot (fx+10, fy+10, fx+80, fy+30);
612 ok_button -> setUserData (this);
613 ok_button -> setLegend (gui_msg_OK);
614 ok_button -> setCallback (fgPropEditHandleOK);
615 ok_button -> makeReturnDefault(TRUE);
617 cancel_button = new puOneShot (fx+100, fy+10, fx+180, fy+30);
618 cancel_button -> setUserData (this);
619 cancel_button -> setLegend (gui_msg_CANCEL);
620 cancel_button -> setCallback (fgPropEditHandleCancel);
622 FG_FINALIZE_PUI_DIALOG( this );