]> git.mxchange.org Git - flightgear.git/blob - src/Main/fg_commands.cxx
FlightGear can handle network connections even without multiplayer support
[flightgear.git] / src / Main / fg_commands.cxx
1 // fg_commands.cxx - internal FGFS commands.
2
3 #include <string.h>             // strcmp()
4
5 #include <simgear/compiler.h>
6 #include <simgear/misc/exception.hxx>
7
8 #include STL_STRING
9 #include STL_FSTREAM
10
11 #include <simgear/sg_inlines.h>
12 #include <simgear/debug/logstream.hxx>
13 #include <simgear/math/sg_random.h>
14 #include <simgear/misc/commands.hxx>
15 #include <simgear/props/props.hxx>
16
17 #include <Cockpit/panel.hxx>
18 #include <Cockpit/panel_io.hxx>
19 #include <FDM/flight.hxx>
20 #include <GUI/gui.h>
21 #include <GUI/new_gui.hxx>
22 #include <GUI/dialog.hxx>
23 #include <Scenery/tilemgr.hxx>
24 #if defined(HAVE_PLIB_PSL)
25 #  include <Scripting/scriptmgr.hxx>
26 #endif
27 #include <Time/tmp.hxx>
28
29 #include "fg_init.hxx"
30 #include "fg_commands.hxx"
31
32 SG_USING_STD(string);
33 SG_USING_STD(ifstream);
34 SG_USING_STD(ofstream);
35
36 #include "fg_props.hxx"
37 #include "fg_io.hxx"
38 #include "globals.hxx"
39 #include "util.hxx"
40 #include "viewmgr.hxx"
41
42
43 \f
44 ////////////////////////////////////////////////////////////////////////
45 // Static helper functions.
46 ////////////////////////////////////////////////////////////////////////
47
48
49 static inline SGPropertyNode *
50 get_prop (const SGPropertyNode * arg)
51 {
52     return fgGetNode(arg->getStringValue("property[0]", "/null"), true);
53 }
54
55 static inline SGPropertyNode *
56 get_prop2 (const SGPropertyNode * arg)
57 {
58     return fgGetNode(arg->getStringValue("property[1]", "/null"), true);
59 }
60
61
62 /**
63  * Get a double value and split it as required.
64  */
65 static void
66 split_value (double full_value, const char * mask,
67              double * unmodifiable, double * modifiable)
68 {
69     if (!strcmp("integer", mask)) {
70         *modifiable = (full_value < 0 ? ceil(full_value) : floor (full_value));
71         *unmodifiable = full_value - *modifiable;
72     } else if (!strcmp("decimal", mask)) {
73         *unmodifiable = (full_value < 0 ? ceil(full_value) : floor(full_value));
74         *modifiable = full_value - *unmodifiable;
75     } else {
76         if (strcmp("all", mask))
77             SG_LOG(SG_GENERAL, SG_ALERT, "Bad value " << mask << " for mask;"
78                    << " assuming 'all'");
79         *unmodifiable = 0;
80         *modifiable = full_value;
81     }
82 }
83
84
85 /**
86  * Clamp or wrap a value as specified.
87  */
88 static void
89 limit_value (double * value, const SGPropertyNode * arg)
90 {
91     const SGPropertyNode * min_node = arg->getChild("min");
92     const SGPropertyNode * max_node = arg->getChild("max");
93
94     bool wrap = arg->getBoolValue("wrap");
95
96     if (min_node == 0 || max_node == 0)
97         wrap = false;
98   
99     if (wrap) {                 // wrap such that min <= x < max
100         double min_val = min_node->getDoubleValue();
101         double max_val = max_node->getDoubleValue();
102         double resolution = arg->getDoubleValue("resolution");
103         if (resolution > 0.0) {
104             // snap to (min + N*resolution), taking special care to handle imprecision
105             int n = (int)floor((*value - min_val) / resolution + 0.5);
106             int steps = (int)floor((max_val - min_val) / resolution + 0.5);
107             SG_NORMALIZE_RANGE(n, 0, steps);
108             *value = min_val + resolution * n;
109         } else {
110             // plain circular wrapping
111             SG_NORMALIZE_RANGE(*value, min_val, max_val);
112         }
113     } else {                    // clamp such that min <= x <= max
114         if ((min_node != 0) && (*value < min_node->getDoubleValue()))
115             *value = min_node->getDoubleValue();
116         else if ((max_node != 0) && (*value > max_node->getDoubleValue()))
117             *value = max_node->getDoubleValue();
118     }
119 }
120
121 static bool
122 compare_values (SGPropertyNode * value1, SGPropertyNode * value2)
123 {
124     switch (value1->getType()) {
125     case SGPropertyNode::BOOL:
126         return (value1->getBoolValue() == value2->getBoolValue());
127     case SGPropertyNode::INT:
128         return (value1->getIntValue() == value2->getIntValue());
129     case SGPropertyNode::LONG:
130         return (value1->getLongValue() == value2->getLongValue());
131     case SGPropertyNode::FLOAT:
132         return (value1->getFloatValue() == value2->getFloatValue());
133     case SGPropertyNode::DOUBLE:
134         return (value1->getDoubleValue() == value2->getDoubleValue());
135     default:
136         return !strcmp(value1->getStringValue(), value2->getStringValue());
137     }
138 }
139
140
141 \f
142 ////////////////////////////////////////////////////////////////////////
143 // Command implementations.
144 ////////////////////////////////////////////////////////////////////////
145
146
147 /**
148  * Built-in command: do nothing.
149  */
150 static bool
151 do_null (const SGPropertyNode * arg)
152 {
153   return true;
154 }
155
156 #if defined(HAVE_PLIB_PSL)
157 /**
158  * Built-in command: run a PSL script.
159  *
160  * script: the PSL script to execute
161  */
162 static bool
163 do_script (const SGPropertyNode * arg)
164 {
165     FGScriptMgr * mgr = (FGScriptMgr *)globals->get_subsystem("scripting");
166     return mgr->run(arg->getStringValue("script"));
167 }
168 #endif // HAVE_PLIB_PSL
169
170
171 /**
172  * Built-in command: exit FlightGear.
173  *
174  * status: the exit status to return to the operating system (defaults to 0)
175  */
176 static bool
177 do_exit (const SGPropertyNode * arg)
178 {
179   SG_LOG(SG_INPUT, SG_INFO, "Program exit requested.");
180   fgExit(arg->getIntValue("status", 0));
181   return true;
182 }
183
184
185 /**
186  * Built-in command: reinitialize one or more subsystems.
187  *
188  * subsystem[*]: the name(s) of the subsystem(s) to reinitialize; if
189  * none is specified, reinitialize all of them.
190  */
191 static bool
192 do_reinit (const SGPropertyNode * arg)
193 {
194     bool result = true;
195
196     vector<SGPropertyNode_ptr> subsystems = arg->getChildren("subsystem");
197     if (subsystems.size() == 0) {
198         globals->get_subsystem_mgr()->reinit();
199     } else {
200         for ( unsigned int i = 0; i < subsystems.size(); i++ ) {
201             const char * name = subsystems[i]->getStringValue();
202             FGSubsystem * subsystem = globals->get_subsystem(name);
203             if (subsystem == 0) {
204                 result = false;
205                 SG_LOG( SG_GENERAL, SG_ALERT,
206                         "Subsystem " << name << "not found" );
207             } else {
208                 subsystem->reinit();
209             }
210         }
211     }
212
213     return result;
214 }
215
216 /**
217  * Built-in command: suspend one or more subsystems.
218  *
219  * subsystem[*] - the name(s) of the subsystem(s) to suspend.
220  */
221 static bool
222 do_suspend (const SGPropertyNode * arg)
223 {
224     bool result = true;
225
226     vector<SGPropertyNode_ptr> subsystems = arg->getChildren("subsystem");
227     for ( unsigned int i = 0; i < subsystems.size(); i++ ) {
228         const char * name = subsystems[i]->getStringValue();
229         FGSubsystem * subsystem = globals->get_subsystem(name);
230         if (subsystem == 0) {
231             result = false;
232             SG_LOG(SG_GENERAL, SG_ALERT, "Subsystem " << name << "not found");
233         } else {
234             subsystem->suspend();
235         }
236     }
237     return result;
238 }
239
240 /**
241  * Built-in command: suspend one or more subsystems.
242  *
243  * subsystem[*] - the name(s) of the subsystem(s) to suspend.
244  */
245 static bool
246 do_resume (const SGPropertyNode * arg)
247 {
248     bool result = true;
249
250     vector<SGPropertyNode_ptr> subsystems = arg->getChildren("subsystem");
251     for ( unsigned int i = 0; i < subsystems.size(); i++ ) {
252         const char * name = subsystems[i]->getStringValue();
253         FGSubsystem * subsystem = globals->get_subsystem(name);
254         if (subsystem == 0) {
255             result = false;
256             SG_LOG(SG_GENERAL, SG_ALERT, "Subsystem " << name << "not found");
257         } else {
258             subsystem->resume();
259         }
260     }
261     return result;
262 }
263
264
265 /**
266  * Built-in command: load flight.
267  *
268  * file (optional): the name of the file to load (relative to current
269  *   directory).  Defaults to "fgfs.sav"
270  */
271 static bool
272 do_load (const SGPropertyNode * arg)
273 {
274   const string &file = arg->getStringValue("file", "fgfs.sav");
275   ifstream input(file.c_str());
276   if (input.good() && fgLoadFlight(input)) {
277     input.close();
278     SG_LOG(SG_INPUT, SG_INFO, "Restored flight from " << file);
279     return true;
280   } else {
281     SG_LOG(SG_INPUT, SG_ALERT, "Cannot load flight from " << file);
282     return false;
283   }
284 }
285
286
287 /**
288  * Built-in command: save flight.
289  *
290  * file (optional): the name of the file to save (relative to the
291  * current directory).  Defaults to "fgfs.sav".
292  */
293 static bool
294 do_save (const SGPropertyNode * arg)
295 {
296   const string &file = arg->getStringValue("file", "fgfs.sav");
297   bool write_all = arg->getBoolValue("write-all", false);
298   SG_LOG(SG_INPUT, SG_INFO, "Saving flight");
299   ofstream output(file.c_str());
300   if (output.good() && fgSaveFlight(output, write_all)) {
301     output.close();
302     SG_LOG(SG_INPUT, SG_INFO, "Saved flight to " << file);
303     return true;
304   } else {
305     SG_LOG(SG_INPUT, SG_ALERT, "Cannot save flight to " << file);
306     return false;
307   }
308 }
309
310
311 /**
312  * Built-in command: (re)load the panel.
313  *
314  * path (optional): the file name to load the panel from 
315  * (relative to FG_ROOT).  Defaults to the value of /sim/panel/path,
316  * and if that's unspecified, to "Panels/Default/default.xml".
317  */
318 static bool
319 do_panel_load (const SGPropertyNode * arg)
320 {
321   string panel_path =
322     arg->getStringValue("path",
323                         fgGetString("/sim/panel/path",
324                                     "Panels/Default/default.xml"));
325   FGPanel * new_panel = fgReadPanel(panel_path);
326   if (new_panel == 0) {
327     SG_LOG(SG_INPUT, SG_ALERT,
328            "Error reading new panel from " << panel_path);
329     return false;
330   }
331   SG_LOG(SG_INPUT, SG_INFO, "Loaded new panel from " << panel_path);
332   globals->get_current_panel()->unbind();
333   delete globals->get_current_panel();
334   globals->set_current_panel( new_panel );
335   globals->get_current_panel()->bind();
336   return true;
337 }
338
339
340 /**
341  * Built-in command: pass a mouse click to the panel.
342  *
343  * button: the mouse button number, zero-based.
344  * is-down: true if the button is down, false if it is up.
345  * x-pos: the x position of the mouse click.
346  * y-pos: the y position of the mouse click.
347  */
348 static bool
349 do_panel_mouse_click (const SGPropertyNode * arg)
350 {
351   if (globals->get_current_panel() != 0)
352     return globals->get_current_panel()
353       ->doMouseAction(arg->getIntValue("button"),
354                       arg->getBoolValue("is-down") ? PU_DOWN : PU_UP,
355                       arg->getIntValue("x-pos"),
356                       arg->getIntValue("y-pos"));
357   else
358     return false;
359 }
360
361
362 /**
363  * Built-in command: (re)load preferences.
364  *
365  * path (optional): the file name to load the panel from (relative
366  * to FG_ROOT). Defaults to "preferences.xml".
367  */
368 static bool
369 do_preferences_load (const SGPropertyNode * arg)
370 {
371   try {
372     fgLoadProps(arg->getStringValue("path", "preferences.xml"),
373                 globals->get_props());
374   } catch (const sg_exception &e) {
375     guiErrorMessage("Error reading global preferences: ", e);
376     return false;
377   }
378   SG_LOG(SG_INPUT, SG_INFO, "Successfully read global preferences.");
379   return true;
380 }
381
382
383 static void
384 fix_hud_visibility()
385 {
386   if ( !strcmp(fgGetString("/sim/flight-model"), "ada") ) {
387       globals->get_props()->setBoolValue( "/sim/hud/visibility", true );
388       if ( globals->get_viewmgr()->get_current() == 1 ) {
389           globals->get_props()->setBoolValue( "/sim/hud/visibility", false );
390       }
391   }
392 }
393
394 static void
395 do_view_next( bool )
396 {
397     globals->get_current_view()->setHeadingOffset_deg(0.0);
398     globals->get_viewmgr()->next_view();
399     fix_hud_visibility();
400     globals->get_tile_mgr()->refresh_view_timestamps();
401 }
402
403 static void
404 do_view_prev( bool )
405 {
406     globals->get_current_view()->setHeadingOffset_deg(0.0);
407     globals->get_viewmgr()->prev_view();
408     fix_hud_visibility();
409     globals->get_tile_mgr()->refresh_view_timestamps();
410 }
411
412 /**
413  * Built-in command: cycle view.
414  */
415 static bool
416 do_view_cycle (const SGPropertyNode * arg)
417 {
418   globals->get_current_view()->setHeadingOffset_deg(0.0);
419   globals->get_viewmgr()->next_view();
420   fix_hud_visibility();
421   globals->get_tile_mgr()->refresh_view_timestamps();
422 //   fgReshape(fgGetInt("/sim/startup/xsize"), fgGetInt("/sim/startup/ysize"));
423   return true;
424 }
425
426 /**
427  * Built-in command: capture screen.
428  */
429 static bool
430 do_screen_capture (const SGPropertyNode * arg)
431 {
432   fgDumpSnapShot();
433   return true;
434 }
435
436
437 /**
438  * Reload the tile cache.
439  */
440 static bool
441 do_tile_cache_reload (const SGPropertyNode * arg)
442 {
443     static const SGPropertyNode *master_freeze
444         = fgGetNode("/sim/freeze/master");
445     bool freeze = master_freeze->getBoolValue();
446     SG_LOG(SG_INPUT, SG_INFO, "ReIniting TileCache");
447     if ( !freeze ) {
448         fgSetBool("/sim/freeze/master", true);
449     }
450     // BusyCursor(0);
451     if ( globals->get_tile_mgr()->init() ) {
452         // Load the local scenery data
453         double visibility_meters = fgGetDouble("/environment/visibility-m");
454         globals->get_tile_mgr()->update( visibility_meters );
455     } else {
456         SG_LOG( SG_GENERAL, SG_ALERT, 
457                 "Error in Tile Manager initialization!" );
458         exit(-1);
459     }
460     // BusyCursor(1);
461     if ( !freeze ) {
462         fgSetBool("/sim/freeze/master", false);
463     }
464     return true;
465 }
466
467
468 /**
469  * Update the lighting manually.
470  */
471 static bool
472 do_lighting_update (const SGPropertyNode * arg)
473 {
474   fgUpdateSkyAndLightingParams();
475   return true;
476 }
477
478
479 /**
480  * Built-in command: toggle a bool property value.
481  *
482  * property: The name of the property to toggle.
483  */
484 static bool
485 do_property_toggle (const SGPropertyNode * arg)
486 {
487   SGPropertyNode * prop = get_prop(arg);
488   return prop->setBoolValue(!prop->getBoolValue());
489 }
490
491
492 /**
493  * Built-in command: assign a value to a property.
494  *
495  * property: the name of the property to assign.
496  * value: the value to assign; or
497  * property[1]: the property to copy from.
498  */
499 static bool
500 do_property_assign (const SGPropertyNode * arg)
501 {
502   SGPropertyNode * prop = get_prop(arg);
503   const SGPropertyNode * prop2 = get_prop2(arg);
504   const SGPropertyNode * value = arg->getNode("value");
505
506   if (value != 0)
507       return prop->setUnspecifiedValue(value->getStringValue());
508   else if (prop2)
509       return prop->setUnspecifiedValue(prop2->getStringValue());
510   else
511       return false;
512 }
513
514
515 /**
516  * Built-in command: increment or decrement a property value.
517  *
518  * If the 'step' argument is present, it will be used; otherwise,
519  * the command uses 'offset' and 'factor', usually from the mouse.
520  *
521  * property: the name of the property to increment or decrement.
522  * step: the amount of the increment or decrement (default: 0).
523  * offset: offset from the current setting (used for the mouse; multiplied 
524  *         by factor)
525  * factor: scaling amount for the offset (defaults to 1).
526  * min: the minimum allowed value (default: no minimum).
527  * max: the maximum allowed value (default: no maximum).
528  * mask: 'integer' to apply only to the left of the decimal point, 
529  *       'decimal' to apply only to the right of the decimal point,
530  *       or 'all' to apply to the whole number (the default).
531  * wrap: true if the value should be wrapped when it passes min or max;
532  *       both min and max must be present for this to work (default:
533  *       false).
534  */
535 static bool
536 do_property_adjust (const SGPropertyNode * arg)
537 {
538   SGPropertyNode * prop = get_prop(arg);
539
540   double amount = 0;
541   if (arg->hasValue("step"))
542       amount = arg->getDoubleValue("step");
543   else
544       amount = (arg->getDoubleValue("factor", 1)
545                 * arg->getDoubleValue("offset"));
546           
547   double unmodifiable, modifiable;
548   split_value(prop->getDoubleValue(), arg->getStringValue("mask", "all"),
549               &unmodifiable, &modifiable);
550   modifiable += amount;
551   limit_value(&modifiable, arg);
552
553   prop->setDoubleValue(unmodifiable + modifiable);
554
555   return true;
556 }
557
558
559 /**
560  * Built-in command: multiply a property value.
561  *
562  * property: the name of the property to multiply.
563  * factor: the amount by which to multiply.
564  * min: the minimum allowed value (default: no minimum).
565  * max: the maximum allowed value (default: no maximum).
566  * mask: 'integer' to apply only to the left of the decimal point, 
567  *       'decimal' to apply only to the right of the decimal point,
568  *       or 'all' to apply to the whole number (the default).
569  * wrap: true if the value should be wrapped when it passes min or max;
570  *       both min and max must be present for this to work (default:
571  *       false).
572  */
573 static bool
574 do_property_multiply (const SGPropertyNode * arg)
575 {
576   SGPropertyNode * prop = get_prop(arg);
577   double factor = arg->getDoubleValue("factor", 1);
578
579   double unmodifiable, modifiable;
580   split_value(prop->getDoubleValue(), arg->getStringValue("mask", "all"),
581               &unmodifiable, &modifiable);
582   modifiable *= factor;
583   limit_value(&modifiable, arg);
584
585   prop->setDoubleValue(unmodifiable + modifiable);
586
587   return true;
588 }
589
590
591 /**
592  * Built-in command: swap two property values.
593  *
594  * property[0]: the name of the first property.
595  * property[1]: the name of the second property.
596  */
597 static bool
598 do_property_swap (const SGPropertyNode * arg)
599 {
600   SGPropertyNode * prop1 = get_prop(arg);
601   SGPropertyNode * prop2 = get_prop2(arg);
602
603                                 // FIXME: inefficient
604   const string & tmp = prop1->getStringValue();
605   return (prop1->setUnspecifiedValue(prop2->getStringValue()) &&
606           prop2->setUnspecifiedValue(tmp.c_str()));
607 }
608
609
610 /**
611  * Built-in command: Set a property to an axis or other moving input.
612  *
613  * property: the name of the property to set.
614  * setting: the current input setting, usually between -1.0 and 1.0.
615  * offset: the offset to shift by, before applying the factor.
616  * factor: the factor to multiply by (use negative to reverse).
617  */
618 static bool
619 do_property_scale (const SGPropertyNode * arg)
620 {
621   SGPropertyNode * prop = get_prop(arg);
622   double setting = arg->getDoubleValue("setting");
623   double offset = arg->getDoubleValue("offset", 0.0);
624   double factor = arg->getDoubleValue("factor", 1.0);
625   bool squared = arg->getBoolValue("squared", false);
626   int power = arg->getIntValue("power", (squared ? 2 : 1));
627
628   int sign = (setting < 0 ? -1 : 1);
629
630   switch (power) {
631   case 1:
632       break;
633   case 2:
634       setting = setting * setting * sign;
635       break;
636   case 3:
637       setting = setting * setting * setting;
638       break;
639   case 4:
640       setting = setting * setting * setting * setting * sign;
641       break;
642   default:
643       setting =  pow(setting, power);
644       if ((power % 2) == 0)
645           setting *= sign;
646       break;
647   }
648
649   return prop->setDoubleValue((setting + offset) * factor);
650 }
651
652
653 /**
654  * Built-in command: cycle a property through a set of values.
655  *
656  * If the current value isn't in the list, the cycle will
657  * (re)start from the beginning.
658  *
659  * property: the name of the property to cycle.
660  * value[*]: the list of values to cycle through.
661  */
662 static bool
663 do_property_cycle (const SGPropertyNode * arg)
664 {
665     SGPropertyNode * prop = get_prop(arg);
666     vector<SGPropertyNode_ptr> values = arg->getChildren("value");
667     int selection = -1;
668     int nSelections = values.size();
669
670     if (nSelections < 1) {
671         SG_LOG(SG_GENERAL, SG_ALERT, "No values for property-cycle");
672         return false;
673     }
674
675                                 // Try to find the current selection
676     for (int i = 0; i < nSelections; i++) {
677         if (compare_values(prop, values[i])) {
678             selection = i + 1;
679             break;
680         }
681     }
682
683                                 // Default or wrap to the first selection
684     if (selection < 0 || selection >= nSelections)
685         selection = 0;
686
687     prop->setUnspecifiedValue(values[selection]->getStringValue());
688     return true;
689 }
690
691
692 /**
693  * Built-in command: randomize a numeric property value.
694  *
695  * property: the name of the property value to randomize.
696  * min: the minimum allowed value.
697  * max: the maximum allowed value.
698  */
699 static bool
700 do_property_randomize (const SGPropertyNode * arg)
701 {
702     SGPropertyNode * prop = get_prop(arg);
703     double min = arg->getDoubleValue("min", DBL_MIN);
704     double max = arg->getDoubleValue("max", DBL_MAX);
705     prop->setDoubleValue(sg_random() * (max - min) + min);
706     return true;
707 }
708
709
710 /**
711  * Built-in command: Show an XML-configured dialog.
712  *
713  * dialog-name: the name of the GUI dialog to display.
714  */
715 static bool
716 do_dialog_show (const SGPropertyNode * arg)
717 {
718     NewGUI * gui = (NewGUI *)globals->get_subsystem("gui");
719     gui->showDialog(arg->getStringValue("dialog-name"));
720     return true;
721 }
722
723
724 /**
725  * Built-in Command: Hide the active XML-configured dialog.
726  */
727 static bool
728 do_dialog_close (const SGPropertyNode * arg)
729 {
730     NewGUI * gui = (NewGUI *)globals->get_subsystem("gui");
731     return gui->closeActiveDialog();
732 }
733
734
735 /**
736  * Update a value in the active XML-configured dialog.
737  *
738  * object-name: The name of the GUI object(s) (all GUI objects if omitted).
739  */
740 static bool
741 do_dialog_update (const SGPropertyNode * arg)
742 {
743     NewGUI * gui = (NewGUI *)globals->get_subsystem("gui");
744     FGDialog * dialog = gui->getActiveDialog();
745     if (dialog != 0) {
746         if (arg->hasValue("object-name")) {
747             dialog->updateValue(arg->getStringValue("object-name"));
748         } else {
749             dialog->updateValues();
750         }
751         return true;
752     } else {
753         return false;
754     }
755 }
756
757
758 /**
759  * Apply a value in the active XML-configured dialog.
760  *
761  * object-name: The name of the GUI object(s) (all GUI objects if omitted).
762  */
763 static bool
764 do_dialog_apply (const SGPropertyNode * arg)
765 {
766     NewGUI * gui = (NewGUI *)globals->get_subsystem("gui");
767     FGDialog * dialog = gui->getActiveDialog();
768     if (dialog != 0) {
769         if (arg->hasValue("object-name")) {
770             const char * name = arg->getStringValue("object-name");
771             dialog->applyValue(name);
772             dialog->updateValue(name);
773         } else {
774             dialog->applyValues();
775             dialog->updateValues();
776         }
777         return true;
778     } else {
779         return false;
780     }
781 }
782
783
784 /**
785  * Built-in command: commit presets (read from in /sim/presets/)
786  */
787 static bool
788 do_presets_commit (const SGPropertyNode * arg)
789 {
790     // unbind the current fdm state so property changes
791     // don't get lost when we subsequently delete this fdm
792     // and create a new one.
793     cur_fdm_state->unbind();
794         
795     // set position from presets
796     fgInitPosition();
797
798     // BusyCursor(0);
799     fgReInitSubsystems();
800
801     globals->get_tile_mgr()->update( fgGetDouble("/environment/visibility-m") );
802
803 #if 0
804     if ( ! fgGetBool("/sim/presets/onground") ) {
805         fgSetBool( "/sim/freeze/master", true );
806         fgSetBool( "/sim/freeze/clock", true );
807     }
808 #endif
809
810     return true;
811 }
812
813 /**
814  * Built-in command: set log level (0 ... 7)
815  */
816 static bool
817 do_log_level (const SGPropertyNode * arg)
818 {
819    sglog().setLogLevels( SG_ALL, (sgDebugPriority)arg->getIntValue() );
820
821    return true;
822 }
823
824
825
826 \f
827 ////////////////////////////////////////////////////////////////////////
828 // Command setup.
829 ////////////////////////////////////////////////////////////////////////
830
831
832 /**
833  * Table of built-in commands.
834  *
835  * New commands do not have to be added here; any module in the application
836  * can add a new command using globals->get_commands()->addCommand(...).
837  */
838 static struct {
839   const char * name;
840   SGCommandMgr::command_t command;
841 } built_ins [] = {
842     { "null", do_null },
843 #if defined(HAVE_PLIB_PSL)
844     { "script", do_script },
845 #endif // HAVE_PLIB_PSL
846     { "exit", do_exit },
847     { "reinit", do_reinit },
848     { "suspend", do_reinit },
849     { "resume", do_reinit },
850     { "load", do_load },
851     { "save", do_save },
852     { "panel-load", do_panel_load },
853     { "panel-mouse-click", do_panel_mouse_click },
854     { "preferences-load", do_preferences_load },
855     { "view-cycle", do_view_cycle },
856     { "screen-capture", do_screen_capture },
857     { "tile-cache-reload", do_tile_cache_reload },
858     { "lighting-update", do_lighting_update },
859     { "property-toggle", do_property_toggle },
860     { "property-assign", do_property_assign },
861     { "property-adjust", do_property_adjust },
862     { "property-multiply", do_property_multiply },
863     { "property-swap", do_property_swap },
864     { "property-scale", do_property_scale },
865     { "property-cycle", do_property_cycle },
866     { "property-randomize", do_property_randomize },
867     { "dialog-show", do_dialog_show },
868     { "dialog-close", do_dialog_close },
869     { "dialog-show", do_dialog_show },
870     { "dialog-update", do_dialog_update },
871     { "dialog-apply", do_dialog_apply },
872     { "presets-commit", do_presets_commit },
873     { "log-level", do_log_level },
874     { 0, 0 }                    // zero-terminated
875 };
876
877
878 /**
879  * Initialize the default built-in commands.
880  *
881  * Other commands may be added by other parts of the application.
882  */
883 void
884 fgInitCommands ()
885 {
886   SG_LOG(SG_GENERAL, SG_INFO, "Initializing basic built-in commands:");
887   for (int i = 0; built_ins[i].name != 0; i++) {
888     SG_LOG(SG_GENERAL, SG_INFO, "  " << built_ins[i].name);
889     globals->get_commands()->addCommand(built_ins[i].name,
890                                         built_ins[i].command);
891   }
892
893   typedef bool (*dummy)();
894   fgTie( "/command/view/next", dummy(0), do_view_next );
895   fgTie( "/command/view/prev", dummy(0), do_view_prev );
896 }
897
898 // end of fg_commands.cxx