]> git.mxchange.org Git - flightgear.git/blob - src/GUI/prop_picker.cxx
make FGFontCache independent of NewGUI and allow early construction in
[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 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.
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 SG_USING_STD(string);
43
44 #include <Main/fg_os.hxx>
45 #include <Main/globals.hxx>
46 #include "new_gui.hxx"
47 #include "prop_picker.hxx"
48
49
50 static puObject *PP_widget = 0;
51
52 #define PROPPICK_X 100
53 #define PROPPICK_Y 200
54 #define PROPPICK_W 500
55 #define PROPPICK_H 300
56
57 static puObject *PE_widget = 0;
58
59
60
61 // entry point: called from src/GUI/menubar.cxx -- do_properties_dialog() =====
62
63 void prop_pickerView( puObject * )
64 {
65     if( PP_widget == 0 )
66         prop_pickerInit();
67
68     fgPropPicker *me = (fgPropPicker *)PP_widget -> getUserData();
69     // refresh
70     me -> find_props();
71     FG_PUSH_PUI_DIALOG( me );
72 }
73
74
75
76
77 // ============================================================================
78
79 void prop_pickerInit()
80 {
81     if (!PP_widget) {
82         fgPropPicker *PP = new fgPropPicker ( PROPPICK_X, PROPPICK_Y, PROPPICK_W, PROPPICK_H,
83                 1, globals->get_props(), "FG Properties");
84         PP_widget = PP;
85     }
86 }
87
88
89 void prop_pickerRefresh()
90 {
91     if (!PP_widget)
92         prop_pickerInit();
93
94     fgPropPicker *me = (fgPropPicker *)PP_widget -> getUserData();
95     me -> find_props( true );
96     me -> clrValue();
97 }
98
99
100 void prop_editOpen( SGPropertyNode *node )
101 {
102     assert(node);
103     if (!PE_widget)
104         PE_widget = new fgPropEdit(node);
105
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 );
113 }
114
115
116 static string getValueTypeString( const SGPropertyNode_ptr node ) {
117     string result;
118
119     if ( node == NULL )
120         return "unknown";
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     return result;
141 }
142
143
144 // Like strcmp, but for sorting property nodes into a suitable display order.
145 static int nodeNameCompare(const void *ppNode1, const void *ppNode2)
146 {
147     const SGPropertyNode_ptr pNode1 = *(const SGPropertyNode_ptr *)ppNode1;
148     const SGPropertyNode_ptr pNode2 = *(const SGPropertyNode_ptr *)ppNode2;
149
150     // Compare name first, and then index.
151     int diff = strcmp(pNode1->getName(), pNode2->getName());
152     if (diff)
153         return diff;
154
155     return pNode1->getIndex() - pNode2->getIndex();
156 }
157
158
159
160
161 // property picker class ======================================================
162
163 void fgPropPicker::fgPropPickerHandleSlider ( puObject * slider )
164 {
165     puListBox* list_box = (puListBox*) slider -> getUserData ();
166
167     float val;
168     slider -> getValue ( &val );
169     val = 1.0f - val;
170     int scroll_range = list_box->getNumItems () - list_box->getNumVisible();
171
172     if ( scroll_range > 0 ) {
173         int index = int ( scroll_range * val + 0.5 );
174         list_box -> setTopItem ( index );
175     }
176 }
177
178
179 void fgPropPicker::fgPropPickerHandleArrow ( puObject *arrow )
180 {
181     puSlider *slider = (puSlider *) arrow->getUserData ();
182     puListBox* list_box = (puListBox*) slider -> getUserData ();
183
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;
189
190     float val;
191     slider -> getValue ( &val );
192     val = 1.0f - val;
193     int scroll_range = list_box->getNumItems () - list_box->getNumVisible();
194
195     if ( scroll_range > 0 ) {
196         int index = int ( scroll_range * val + 0.5 );
197         index += inc;
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 );
202         if ( index < 0 )
203             index = 0;
204
205         slider -> setValue ( 1.0f - (float)index / scroll_range );
206         list_box -> setTopItem ( index );
207     }
208 }
209
210
211 void fgPropPicker::handle_select ( puObject* list_box )
212 {
213     fgPropPicker* prop_picker = (fgPropPicker*) list_box -> getUserData ();
214     int selected;
215     list_box -> getValue ( &selected );
216
217     if ( selected >= 0 && selected < prop_picker -> num_files ) {
218         const char *src = prop_picker -> files [ selected ];
219
220         if (prop_picker->dotFiles && (selected < 2)) {
221             if ( strcmp ( src, "." ) == 0 ) {
222                 /* Do nothing - but better refresh anyway. */
223                 prop_picker -> find_props ();
224                 return;
225
226             } else if ( strcmp ( src, ".." ) == 0 ) {
227                 /* Do back up one level - so refresh. */
228
229                 SGPropertyNode *parent = prop_picker->getCurrent()->getParent();
230                 if (parent) {
231                     prop_picker->setCurrent(parent);
232                     prop_picker -> find_props ();
233                 }
234                 return;
235             }
236         }
237
238         // we know we're dealing with a regular entry, so convert
239         // it to an index into children[]
240         if (prop_picker->dotFiles)
241             selected -= 2;
242         SGPropertyNode_ptr child = prop_picker->children[selected];
243         assert(child != NULL);
244
245         // check if it's a directory (had children)
246         if ( child->nChildren() ) {
247             prop_picker->setCurrent(child);
248             prop_picker -> find_props ();
249             return;
250         }
251
252         // it is a regular property
253         if (child->getType() == SGPropertyNode::BOOL && (fgGetKeyModifiers() & KEYMOD_CTRL)) {
254             child->setBoolValue(!child->getBoolValue());
255             prop_pickerRefresh();
256         } else
257             prop_editOpen(child);
258
259     } else {
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 ();
264     }
265 }
266
267
268 void fgPropPicker::fgPropPickerHandleOk ( puObject* b )
269 {
270     fgPropPicker* prop_picker = (fgPropPicker*) b -> getUserData ();
271
272     // nothing to do, just hide
273     FG_POP_PUI_DIALOG( prop_picker );
274 }
275
276
277 void fgPropPicker::delete_arrays ()
278 {
279     if ( files ) {
280         for ( int i=0; i<num_files; i++ )
281             delete[] files[i];
282
283         for (int C=0; C<num_children; ++C) {
284             if (children[C]->nChildren() == 0)
285                 children[C]->removeChangeListener(this);
286         }
287
288         delete[] files;
289         delete[] children;
290     }
291 }
292
293 /*
294
295 fgPropPicker::~fgPropPicker ()
296 {
297   delete_arrays();
298
299   if ( this == puActiveWidget () )
300     puDeactivateWidget ();
301 }
302
303 */
304
305
306 fgPropPicker::fgPropPicker ( int x, int y, int w, int h, int arrows,
307                              SGPropertyNode *start, const char *title ) :
308     fgPopup ( x,y ),
309     curr(start),
310     flags(fgGetNode("/sim/gui/dialogs/property-browser/show-flags", true)),
311     _gui((NewGUI *)globals->get_subsystem("gui"))
312 {
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"));
318
319     files = NULL;
320     num_files = 0;
321
322     if ( arrows > 2 )
323         arrows = 2;
324     if ( arrows < 0 )
325         arrows = 0;
326     arrow_count = arrows;
327
328     frame = new puFrame ( 0, 0, w, h );
329
330     setUserData( this );
331
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());
336
337     slider = new puSlider (w-30,40+20*arrows,h-100-40*arrows,TRUE,20);
338     slider->setValue(1.0f);
339
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());
349
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 );
354
355     if ( arrows > 0 ) {
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 );
359
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 );
363     }
364
365     // after picker is built, load the list box with data...
366     find_props ();
367
368     // printf("after Props files[1]=%s\n",files[1]);
369     // printf("num items %i", list_box -> getNumItems ());
370
371     slider -> setUserData ( list_box );
372     slider -> setCallback ( fgPropPickerHandleSlider );
373
374     FG_FINALIZE_PUI_DIALOG( this );
375 }
376
377
378 // Replace list with children of current
379 void fgPropPicker::find_props ( bool restore_pos )
380 {
381     int pi;
382     int i;
383
384     delete_arrays();
385     num_files = (int)curr->nChildren();
386
387     // instantiate string objects and add [.] and [..] for subdirs
388     if (!curr->getParent()) {
389         files = new char* [ num_files+1 ];
390         pi = 0;
391         dotFiles = false;
392
393     } else {
394         // add two for the .. and .
395         num_files += 2;
396         // make room for .. and .
397         files = new char* [ num_files+1 ];
398
399         stdString line = ".";
400         files [ 0 ] = new char[line.size() + 1];
401         strcpy ( files [ 0 ], line.c_str() );
402
403         line = "..";
404         files [ 1 ] = new char[line.size() + 1];
405         strcpy ( files [ 1 ], line.c_str() );
406
407         pi = 2;
408         dotFiles = true;
409     }
410
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);
415
416     qsort(children, num_children, sizeof(children[0]), nodeNameCompare);
417
418     // Make lists of the children's names, values, etc.
419     for (i = 0; i < num_children; i++) {
420         SGPropertyNode * child = children[i];
421
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() );
426
427         } else {
428             files[pi] = NULL; // ensure it's NULL before setting intial value
429             updateTextForEntry(i);
430             child->addChangeListener(this);
431         }
432
433         ++pi;
434     }
435
436     files [ num_files ] = NULL;
437
438     proppath -> setLabel(curr->getPath(true));
439
440     int top = list_box->getTopItem();
441     list_box -> newList ( files );
442     if (restore_pos)
443         list_box->setTopItem(top);
444
445     // adjust the size of the slider...
446     if (num_files > list_box->getNumVisible()) {
447         slider->setSliderFraction((float)list_box->getNumVisible() / num_files);
448         if (!restore_pos)
449             slider->setValue(1.0f);
450
451         slider->reveal();
452         up_arrow->reveal();
453         down_arrow->reveal();
454
455     } else {
456         slider->hide();
457         up_arrow->hide();
458         down_arrow->hide();
459     }
460 }
461
462
463 void fgPropPicker::updateTextForEntry(int index)
464 {
465     assert((index >= 0) && (index < num_children));
466     SGPropertyNode_ptr node = children[index];
467
468     stdString name = node->getDisplayName(true);
469     stdString type = getValueTypeString(node);
470     stdString value = node->getStringValue();
471
472     stdString line = name + " = '" + value + "' (" + type;
473
474     if (flags->getBoolValue()) {
475         stdString ext;
476         if (!node->getAttribute(SGPropertyNode::READ))
477             ext += 'r';
478         if (!node->getAttribute(SGPropertyNode::WRITE))
479             ext += 'w';
480         if (node->getAttribute(SGPropertyNode::TRACE_READ))
481             ext += 'R';
482         if (node->getAttribute(SGPropertyNode::TRACE_WRITE))
483             ext += 'W';
484         if (node->getAttribute(SGPropertyNode::ARCHIVE))
485             ext += 'A';
486         if (node->getAttribute(SGPropertyNode::USERARCHIVE))
487             ext += 'U';
488         if (node->isTied())
489             ext += 'T';
490         if (ext.size())
491             line += ", " + ext;
492     }
493
494     line += ')';
495
496     // truncate entries to plib pui limit
497     if (line.length() >= PUSTRING_MAX)
498         line[PUSTRING_MAX-1] = '\0';
499
500     if (dotFiles)
501         index += 2;
502
503     // don't leak everywhere if we're updating
504     delete[] files[index];
505
506     files[index] = new char[ line.size() + 1 ];
507     strcpy ( files [ index ], line.c_str() );
508 }
509
510
511 void fgPropPicker::valueChanged(SGPropertyNode *nd)
512 {
513     for (int i = 0; i < num_children; i++)
514         if (children[i] == nd) {
515             updateTextForEntry(i);
516             return;
517         }
518 }
519
520
521
522
523 // property editor class ======================================================
524
525 void fgPropEdit::fgPropEditHandleCancel ( puObject* b )
526 {
527     fgPropEdit* prop_edit = (fgPropEdit*) b -> getUserData ();
528     prop_pickerRefresh();
529     FG_POP_PUI_DIALOG( prop_edit );
530 }
531
532
533 void fgPropEdit::fgPropEditHandleOK ( puObject* b )
534 {
535     fgPropEdit* prop_edit = (fgPropEdit*) b -> getUserData ();
536     char* tvalue;
537
538     prop_edit -> propinput -> getValue( &tvalue );
539     prop_edit->getEditNode()->setStringValue(tvalue);
540
541     // update the picker display so it shows new value
542     prop_pickerRefresh();
543
544     FG_POP_PUI_DIALOG( prop_edit );
545 }
546
547
548 fgPropEdit::fgPropEdit ( SGPropertyNode *n ) :
549     fgPopup ( 0, 0 ),
550     node(n),
551     _gui((NewGUI *)globals->get_subsystem("gui"))
552 {
553     assert(n);
554
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"));
560
561     // locate in relation to picker widget...
562     int fx = PROPPICK_X;
563     int fy = PROPPICK_Y + PROPPICK_H;
564     frame   = new puFrame (fx,fy, fx+500, fy+120);
565
566     setUserData( this );
567
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());
573
574     propinput = new puInput (fx+10, fy+50, fx+480, fy+80);
575     propinput -> setValue (node->getStringValue());
576     propinput -> acceptInput();
577
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);
583
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);
588
589     FG_FINALIZE_PUI_DIALOG( this );
590 }
591
592