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 General Public License
24 along with this program; if not, write to the Free Software
25 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
27 For further information visit http://plib.sourceforge.net
38 #include <simgear/compiler.h>
39 #include <simgear/props/props.hxx>
44 #include <Main/fg_os.hxx>
45 #include <Main/globals.hxx>
46 #include "new_gui.hxx"
47 #include "prop_picker.hxx"
50 static puObject *PP_widget = 0;
52 #define PROPPICK_X 100
53 #define PROPPICK_Y 200
54 #define PROPPICK_W 500
55 #define PROPPICK_H 300
57 static puObject *PE_widget = 0;
61 // entry point: called from src/GUI/menubar.cxx -- do_properties_dialog() =====
63 void prop_pickerView( puObject * )
68 fgPropPicker *me = (fgPropPicker *)PP_widget -> getUserData();
71 FG_PUSH_PUI_DIALOG( me );
77 // ============================================================================
79 void prop_pickerInit()
82 fgPropPicker *PP = new fgPropPicker ( PROPPICK_X, PROPPICK_Y, PROPPICK_W, PROPPICK_H,
83 1, globals->get_props(), "FG Properties");
89 void prop_pickerRefresh()
94 fgPropPicker *me = (fgPropPicker *)PP_widget -> getUserData();
95 me -> find_props( true );
100 void prop_editOpen( SGPropertyNode *node )
104 PE_widget = new fgPropEdit(node);
106 fgPropEdit *me = (fgPropEdit *)PE_widget -> getUserData();
107 me -> namestring = node->getDisplayName();
108 me -> propname -> setLabel (me->namestring.c_str());
109 me -> propinput -> setValue (node->getStringValue());
110 me -> propinput -> acceptInput ();
111 me -> setEditNode(node);
112 FG_PUSH_PUI_DIALOG( me );
116 static string getValueTypeString( const SGPropertyNode_ptr node ) {
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 // Like strcmp, but for sorting property nodes into a suitable display order.
145 static int nodeNameCompare(const void *ppNode1, const void *ppNode2)
147 const SGPropertyNode_ptr pNode1 = *(const SGPropertyNode_ptr *)ppNode1;
148 const SGPropertyNode_ptr pNode2 = *(const SGPropertyNode_ptr *)ppNode2;
150 // Compare name first, and then index.
151 int diff = strcmp(pNode1->getName(), pNode2->getName());
155 return pNode1->getIndex() - pNode2->getIndex();
161 // property picker class ======================================================
163 void fgPropPicker::fgPropPickerHandleSlider ( puObject * slider )
165 puListBox* list_box = (puListBox*) slider -> getUserData ();
168 slider -> getValue ( &val );
170 int scroll_range = list_box->getNumItems () - list_box->getNumVisible();
172 if ( scroll_range > 0 ) {
173 int index = int ( scroll_range * val + 0.5 );
174 list_box -> setTopItem ( index );
179 void fgPropPicker::fgPropPickerHandleArrow ( puObject *arrow )
181 puSlider *slider = (puSlider *) arrow->getUserData ();
182 puListBox* list_box = (puListBox*) slider -> getUserData ();
184 int type = ((puArrowButton *)arrow)->getArrowType();
185 int inc = ( type == PUARROW_DOWN ) ? 1 :
186 ( type == PUARROW_UP ) ? -1 :
187 ( type == PUARROW_FASTDOWN ) ? 10 :
188 ( type == PUARROW_FASTUP ) ? -10 : 0;
191 slider -> getValue ( &val );
193 int scroll_range = list_box->getNumItems () - list_box->getNumVisible();
195 if ( scroll_range > 0 ) {
196 int index = int ( scroll_range * val + 0.5 );
198 // if ( index > scroll_range ) index = scroll_range;
199 // Allow buttons to scroll further than the slider does
200 if ( index > ( list_box->getNumItems () - 1 ) )
201 index = ( list_box->getNumItems () - 1 );
205 slider -> setValue ( 1.0f - (float)index / scroll_range );
206 list_box -> setTopItem ( index );
211 void fgPropPicker::handle_select ( puObject* list_box )
213 fgPropPicker* prop_picker = (fgPropPicker*) list_box -> getUserData ();
215 list_box -> getValue ( &selected );
217 if ( selected >= 0 && selected < prop_picker -> num_files ) {
218 const char *src = prop_picker -> files [ selected ];
220 if (prop_picker->dotFiles && (selected < 2)) {
221 if ( strcmp ( src, "." ) == 0 ) {
222 /* Do nothing - but better refresh anyway. */
223 prop_picker -> find_props ();
226 } else if ( strcmp ( src, ".." ) == 0 ) {
227 /* Do back up one level - so refresh. */
229 SGPropertyNode *parent = prop_picker->getCurrent()->getParent();
231 prop_picker->setCurrent(parent);
232 prop_picker -> find_props ();
238 // we know we're dealing with a regular entry, so convert
239 // it to an index into children[]
240 if (prop_picker->dotFiles)
242 SGPropertyNode_ptr child = prop_picker->children[selected];
243 assert(child != NULL);
245 // check if it's a directory (had children)
246 if ( child->nChildren() ) {
247 prop_picker->setCurrent(child);
248 prop_picker -> find_props ();
252 // it is a regular property
253 if (child->getType() == SGPropertyNode::BOOL && (fgGetKeyModifiers() & KEYMOD_CTRL)) {
254 child->setBoolValue(!child->getBoolValue());
255 prop_pickerRefresh();
257 prop_editOpen(child);
260 // The user clicked on blank screen - maybe we should
261 // refresh just in case some other process created the file.
262 // Should be obsolete once we observe child add/remove on our top node
263 prop_picker -> find_props ();
268 void fgPropPicker::fgPropPickerHandleOk ( puObject* b )
270 fgPropPicker* prop_picker = (fgPropPicker*) b -> getUserData ();
272 // nothing to do, just hide
273 FG_POP_PUI_DIALOG( prop_picker );
277 void fgPropPicker::delete_arrays ()
280 for ( int i=0; i<num_files; i++ )
283 for (int C=0; C<num_children; ++C) {
284 if (children[C]->nChildren() == 0)
285 children[C]->removeChangeListener(this);
295 fgPropPicker::~fgPropPicker ()
299 if ( this == puActiveWidget () )
300 puDeactivateWidget ();
306 fgPropPicker::fgPropPicker ( int x, int y, int w, int h, int arrows,
307 SGPropertyNode *start, const char *title ) :
310 flags(fgGetNode("/sim/gui/dialogs/property-browser/show-flags", true)),
311 _gui((NewGUI *)globals->get_subsystem("gui"))
313 puFont LegendFont, LabelFont;
314 puGetDefaultFonts ( &LegendFont, &LabelFont );
315 FGColor txtcol(_gui->getColor("label"));
316 txtcol.merge(_gui->getColor("text"));
317 txtcol.merge(_gui->getColor("text-label"));
326 arrow_count = arrows;
328 frame = new puFrame ( 0, 0, w, h );
332 proppath = new puText (10, h-30);
333 proppath -> setLabel (curr->getPath(true));
334 proppath -> setColor(PUCOL_LABEL, txtcol.red(), txtcol.green(),
335 txtcol.blue(), txtcol.alpha());
337 slider = new puSlider (w-30,40+20*arrows,h-100-40*arrows,TRUE,20);
338 slider->setValue(1.0f);
340 list_box = new puListBox ( 10, 40, w-40, h-60 );
341 list_box -> setLabel ( title );
342 list_box -> setLabelPlace ( PUPLACE_ABOVE );
343 list_box -> setStyle ( -PUSTYLE_SMALL_SHADED );
344 list_box -> setUserData ( this );
345 list_box -> setCallback ( handle_select );
346 list_box -> setValue ( 0 );
347 list_box -> setColor(PUCOL_LABEL, txtcol.red(), txtcol.green(),
348 txtcol.blue(), txtcol.alpha());
350 ok_button = new puOneShot ( 10, 10, (w<170)?(w/2-5):80, 30 );
351 ok_button -> setLegend ( "Ok" );
352 ok_button -> setUserData ( this );
353 ok_button -> setCallback ( fgPropPickerHandleOk );
356 down_arrow = new puArrowButton ( w-30, 20+20*arrows, w-10, 40+20*arrows, PUARROW_DOWN );
357 down_arrow->setUserData ( slider );
358 down_arrow->setCallback ( fgPropPickerHandleArrow );
360 up_arrow = new puArrowButton ( w-30, h-60-20*arrows, w-10, h-40-20*arrows, PUARROW_UP );
361 up_arrow->setUserData ( slider );
362 up_arrow->setCallback ( fgPropPickerHandleArrow );
365 // after picker is built, load the list box with data...
368 // printf("after Props files[1]=%s\n",files[1]);
369 // printf("num items %i", list_box -> getNumItems ());
371 slider -> setUserData ( list_box );
372 slider -> setCallback ( fgPropPickerHandleSlider );
374 FG_FINALIZE_PUI_DIALOG( this );
378 // Replace list with children of current
379 void fgPropPicker::find_props ( bool restore_pos )
385 num_files = (int)curr->nChildren();
387 // instantiate string objects and add [.] and [..] for subdirs
388 if (!curr->getParent()) {
389 files = new char* [ num_files+1 ];
394 // add two for the .. and .
396 // make room for .. and .
397 files = new char* [ num_files+1 ];
399 stdString line = ".";
400 files [ 0 ] = new char[line.size() + 1];
401 strcpy ( files [ 0 ], line.c_str() );
404 files [ 1 ] = new char[line.size() + 1];
405 strcpy ( files [ 1 ], line.c_str() );
411 num_children = curr->nChildren();
412 children = new SGPropertyNode_ptr[num_children];
413 for (i = 0; i < num_children; i++)
414 children[i] = curr->getChild(i);
416 qsort(children, num_children, sizeof(children[0]), nodeNameCompare);
418 // Make lists of the children's names, values, etc.
419 for (i = 0; i < num_children; i++) {
420 SGPropertyNode * child = children[i];
422 if ( child->nChildren() > 0 ) {
423 stdString name = stdString(child->getDisplayName(true)) + '/';
424 files[ pi ] = new char[ name.size() + 1 ];
425 strcpy ( files [ pi ], name.c_str() );
428 files[pi] = NULL; // ensure it's NULL before setting intial value
429 updateTextForEntry(i);
430 child->addChangeListener(this);
436 files [ num_files ] = NULL;
438 proppath -> setLabel(curr->getPath(true));
440 int top = list_box->getTopItem();
441 list_box -> newList ( files );
443 list_box->setTopItem(top);
445 // adjust the size of the slider...
446 if (num_files > list_box->getNumVisible()) {
447 slider->setSliderFraction((float)list_box->getNumVisible() / num_files);
449 slider->setValue(1.0f);
453 down_arrow->reveal();
463 void fgPropPicker::updateTextForEntry(int index)
465 assert((index >= 0) && (index < num_children));
466 SGPropertyNode_ptr node = children[index];
468 stdString name = node->getDisplayName(true);
469 stdString type = getValueTypeString(node);
470 stdString value = node->getStringValue();
472 stdString line = name + " = '" + value + "' (" + type;
474 if (flags->getBoolValue()) {
476 if (!node->getAttribute(SGPropertyNode::READ))
478 if (!node->getAttribute(SGPropertyNode::WRITE))
480 if (node->getAttribute(SGPropertyNode::TRACE_READ))
482 if (node->getAttribute(SGPropertyNode::TRACE_WRITE))
484 if (node->getAttribute(SGPropertyNode::ARCHIVE))
486 if (node->getAttribute(SGPropertyNode::USERARCHIVE))
496 // truncate entries to plib pui limit
497 if (line.length() >= PUSTRING_MAX)
498 line[PUSTRING_MAX-1] = '\0';
503 // don't leak everywhere if we're updating
504 delete[] files[index];
506 files[index] = new char[ line.size() + 1 ];
507 strcpy ( files [ index ], line.c_str() );
511 void fgPropPicker::valueChanged(SGPropertyNode *nd)
513 for (int i = 0; i < num_children; i++)
514 if (children[i] == nd) {
515 updateTextForEntry(i);
523 // property editor class ======================================================
525 void fgPropEdit::fgPropEditHandleCancel ( puObject* b )
527 fgPropEdit* prop_edit = (fgPropEdit*) b -> getUserData ();
528 prop_pickerRefresh();
529 FG_POP_PUI_DIALOG( prop_edit );
533 void fgPropEdit::fgPropEditHandleOK ( puObject* b )
535 fgPropEdit* prop_edit = (fgPropEdit*) b -> getUserData ();
538 prop_edit -> propinput -> getValue( &tvalue );
539 prop_edit->getEditNode()->setStringValue(tvalue);
541 // update the picker display so it shows new value
542 prop_pickerRefresh();
544 FG_POP_PUI_DIALOG( prop_edit );
548 fgPropEdit::fgPropEdit ( SGPropertyNode *n ) :
551 _gui((NewGUI *)globals->get_subsystem("gui"))
555 puFont LegendFont, LabelFont;
556 puGetDefaultFonts ( &LegendFont, &LabelFont );
557 FGColor txtcol(_gui->getColor("label"));
558 txtcol.merge(_gui->getColor("text"));
559 txtcol.merge(_gui->getColor("text-label"));
561 // locate in relation to picker widget...
563 int fy = PROPPICK_Y + PROPPICK_H;
564 frame = new puFrame (fx,fy, fx+500, fy+120);
568 namestring = node->getDisplayName();
569 propname = new puText (fx+10, fy+90);
570 propname -> setLabel(namestring.c_str());
571 propname -> setColor(PUCOL_LABEL, txtcol.red(), txtcol.green(),
572 txtcol.blue(), txtcol.alpha());
574 propinput = new puInput (fx+10, fy+50, fx+480, fy+80);
575 propinput -> setValue (node->getStringValue());
576 propinput -> acceptInput();
578 ok_button = new puOneShot (fx+10, fy+10, fx+80, fy+30);
579 ok_button -> setUserData (this);
580 ok_button -> setLegend (gui_msg_OK);
581 ok_button -> setCallback (fgPropEditHandleOK);
582 ok_button -> makeReturnDefault (TRUE);
584 cancel_button = new puOneShot (fx+100, fy+10, fx+180, fy+30);
585 cancel_button -> setUserData (this);
586 cancel_button -> setLegend (gui_msg_CANCEL);
587 cancel_button -> setCallback (fgPropEditHandleCancel);
589 FG_FINALIZE_PUI_DIALOG( this );