]> git.mxchange.org Git - flightgear.git/blob - src/Cockpit/panel.cxx
initialize marker_xs.
[flightgear.git] / src / Cockpit / panel.cxx
1 //  panel.cxx - default, 2D single-engine prop instrument panel
2 //
3 //  Written by David Megginson, started January 2000.
4 //
5 //  This program is free software; you can redistribute it and/or
6 //  modify it under the terms of the GNU General Public License as
7 //  published by the Free Software Foundation; either version 2 of the
8 //  License, or (at your option) any later version.
9 // 
10 //  This program is distributed in the hope that it will be useful, but
11 //  WITHOUT ANY WARRANTY; without even the implied warranty of
12 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 //  General Public License for more details.
14 // 
15 //  You should have received a copy of the GNU General Public License
16 //  along with this program; if not, write to the Free Software
17 //  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 //
19 //  $Id$
20
21 #ifdef HAVE_CONFIG_H
22 #  include <config.h>
23 #endif
24
25 #ifdef HAVE_WINDOWS_H          
26 #  include <windows.h>
27 #endif
28
29 #include <string.h>
30
31 #include <plib/ssg.h>
32 #include <plib/fnt.h>
33
34 #include <simgear/debug/logstream.hxx>
35 #include <simgear/misc/sg_path.hxx>
36
37 #include <Main/globals.hxx>
38 #include <Main/fg_props.hxx>
39 #include <Main/viewmgr.hxx>
40 #include <Objects/texload.h>
41 #include <Time/light.hxx>
42
43 #include "hud.hxx"
44 #include "panel.hxx"
45
46 #define WIN_X 0
47 #define WIN_Y 0
48 #define WIN_W 1024
49 #define WIN_H 768
50
51
52 \f
53 ////////////////////////////////////////////////////////////////////////
54 // Local functions.
55 ////////////////////////////////////////////////////////////////////////
56
57
58 /**
59  * Calculate the aspect adjustment for the panel.
60  */
61 static float
62 get_aspect_adjust (int xsize, int ysize)
63 {
64   float ideal_aspect = float(WIN_W) / float(WIN_H);
65   float real_aspect = float(xsize) / float(ysize);
66   return (real_aspect / ideal_aspect);
67 }
68
69
70 \f
71 ////////////////////////////////////////////////////////////////////////
72 // Global functions.
73 ////////////////////////////////////////////////////////////////////////
74
75 bool
76 fgPanelVisible ()
77 {
78   return ((current_panel != 0) &&
79           (current_panel->getVisibility()) &&
80           (globals->get_viewmgr()->get_current() == 0) &&
81           (globals->get_current_view()->get_view_offset() == 0.0));
82 }
83
84
85 \f
86 ////////////////////////////////////////////////////////////////////////
87 // Implementation of FGTextureManager.
88 ////////////////////////////////////////////////////////////////////////
89
90 map<string,ssgTexture *> FGTextureManager::_textureMap;
91
92 ssgTexture *
93 FGTextureManager::createTexture (const string &relativePath)
94 {
95   ssgTexture * texture = _textureMap[relativePath];
96   if (texture == 0) {
97     SG_LOG( SG_COCKPIT, SG_DEBUG,
98             "Texture " << relativePath << " does not yet exist" );
99     SGPath tpath(globals->get_fg_root());
100     tpath.append(relativePath);
101     texture = new ssgTexture((char *)tpath.c_str(), false, false);
102     _textureMap[relativePath] = texture;
103     if (_textureMap[relativePath] == 0) 
104       SG_LOG( SG_COCKPIT, SG_ALERT, "Texture *still* doesn't exist" );
105     SG_LOG( SG_COCKPIT, SG_DEBUG, "Created texture " << relativePath
106             << " handle=" << texture->getHandle() );
107   }
108
109   return texture;
110 }
111
112
113
114 \f
115 ////////////////////////////////////////////////////////////////////////
116 // Implementation of FGCropped Texture.
117 ////////////////////////////////////////////////////////////////////////
118
119
120 FGCroppedTexture::FGCroppedTexture ()
121   : _path(""), _texture(0),
122     _minX(0.0), _minY(0.0), _maxX(1.0), _maxY(1.0)
123 {
124 }
125
126
127 FGCroppedTexture::FGCroppedTexture (const string &path,
128                                     float minX, float minY,
129                                     float maxX, float maxY)
130   : _path(path), _texture(0),
131     _minX(minX), _minY(minY), _maxX(maxX), _maxY(maxY)
132 {
133 }
134
135
136 FGCroppedTexture::~FGCroppedTexture ()
137 {
138 }
139
140
141 ssgTexture *
142 FGCroppedTexture::getTexture ()
143 {
144   if (_texture == 0) {
145     _texture = FGTextureManager::createTexture(_path);
146   }
147   return _texture;
148 }
149
150
151 \f
152 ////////////////////////////////////////////////////////////////////////
153 // Implementation of FGPanel.
154 ////////////////////////////////////////////////////////////////////////
155
156 FGPanel * current_panel = NULL;
157 static fntRenderer text_renderer;
158
159
160 /**
161  * Constructor.
162  */
163 FGPanel::FGPanel ()
164   : _mouseDown(false),
165     _mouseInstrument(0),
166     _width(WIN_W), _height(int(WIN_H * 0.5768 + 1)),
167     _x_offset(0), _y_offset(0), _view_height(int(WIN_H * 0.4232)),
168     _bound(false),
169     _xsize_node(fgGetNode("/sim/startup/xsize", true)),
170     _ysize_node(fgGetNode("/sim/startup/ysize", true))
171 {
172   setVisibility(fgPanelVisible());
173 }
174
175
176 /**
177  * Destructor.
178  */
179 FGPanel::~FGPanel ()
180 {
181   if (_bound)
182     unbind();
183   for (instrument_list_type::iterator it = _instruments.begin();
184        it != _instruments.end();
185        it++) {
186     delete *it;
187     *it = 0;
188   }
189 }
190
191
192 /**
193  * Add an instrument to the panel.
194  */
195 void
196 FGPanel::addInstrument (FGPanelInstrument * instrument)
197 {
198   _instruments.push_back(instrument);
199 }
200
201
202 /**
203  * Initialize the panel.
204  */
205 void
206 FGPanel::init ()
207 {
208   // NO-OP
209 }
210
211
212 /**
213  * Bind panel properties.
214  */
215 void
216 FGPanel::bind ()
217 {
218   fgTie("/sim/panel/visibility", &_visibility);
219   fgSetArchivable("/sim/panel/visibility");
220   fgTie("/sim/panel/x-offset", &_x_offset);
221   fgSetArchivable("/sim/panel/x-offset");
222   fgTie("/sim/panel/y-offset", &_y_offset);
223   fgSetArchivable("/sim/panel/y-offset");
224   _bound = true;
225 }
226
227
228 /**
229  * Unbind panel properties.
230  */
231 void
232 FGPanel::unbind ()
233 {
234   fgUntie("/sim/panel/visibility");
235   fgUntie("/sim/panel/x-offset");
236   fgUntie("/sim/panel/y-offset");
237   _bound = false;
238 }
239
240
241 /**
242  * Update the panel.
243  */
244 void
245 FGPanel::update ()
246 {
247                                 // Do nothing if the panel isn't visible.
248     if (!fgPanelVisible())
249         return;
250
251                                 // If the mouse is down, do something
252     if (_mouseDown) {
253         _mouseDelay--;
254         if (_mouseDelay < 0) {
255             _mouseInstrument->doMouseAction(_mouseButton, _mouseX, _mouseY);
256             _mouseDelay = 2;
257         }
258     }
259
260                                 // Now, draw the panel
261     float aspect_adjust = get_aspect_adjust(_xsize_node->getIntValue(),
262                                             _ysize_node->getIntValue());
263     if (aspect_adjust <1.0)
264         update(WIN_X, int(WIN_W * aspect_adjust), WIN_Y, WIN_H);
265     else
266         update(WIN_X, WIN_W, WIN_Y, int(WIN_H / aspect_adjust));
267 }
268
269
270 void
271 FGPanel::update (GLfloat winx, GLfloat winw, GLfloat winy, GLfloat winh)
272 {
273   glMatrixMode(GL_PROJECTION);
274   glPushMatrix();
275   glLoadIdentity();
276   gluOrtho2D(winx, winx + winw, winy, winy + winh); /* right side up */
277   // gluOrtho2D(winx + winw, winx, winy + winh, winy); /* up side down */
278
279   glMatrixMode(GL_MODELVIEW);
280   glPushMatrix();
281   glLoadIdentity();
282
283   glTranslated(_x_offset, _y_offset, 0);
284
285                                 // Draw the background
286   glEnable(GL_TEXTURE_2D);
287   glDisable(GL_LIGHTING);
288   glEnable(GL_BLEND);
289   glEnable(GL_ALPHA_TEST);
290   glEnable(GL_COLOR_MATERIAL);
291   // glColor4f(1.0, 1.0, 1.0, 1.0);
292   if ( cur_light_params.sun_angle * SGD_RADIANS_TO_DEGREES < 95.0 ) {
293       glColor4fv( cur_light_params.scene_diffuse );
294   } else {
295       glColor4f(0.7, 0.2, 0.2, 1.0);
296   }
297   glBindTexture(GL_TEXTURE_2D, _bg->getHandle());
298   // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
299   // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
300   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
301   glBegin(GL_POLYGON);
302   glTexCoord2f(0.0, 0.0); glVertex3f(WIN_X, WIN_Y, 0);
303   glTexCoord2f(1.0, 0.0); glVertex3f(WIN_X + _width, WIN_Y, 0);
304   glTexCoord2f(1.0, 1.0); glVertex3f(WIN_X + _width, WIN_Y + _height, 0);
305   glTexCoord2f(0.0, 1.0); glVertex3f(WIN_X, WIN_Y + _height, 0);
306   glEnd();
307
308                                 // Draw the instruments.
309   instrument_list_type::const_iterator current = _instruments.begin();
310   instrument_list_type::const_iterator end = _instruments.end();
311
312   for ( ; current != end; current++) {
313     FGPanelInstrument * instr = *current;
314     glLoadIdentity();
315     glTranslated(_x_offset, _y_offset, 0);
316     glTranslated(instr->getXPos(), instr->getYPos(), 0);
317     instr->draw();
318   }
319
320   glMatrixMode(GL_PROJECTION);
321   glPopMatrix();
322   glMatrixMode(GL_MODELVIEW);
323   glPopMatrix();
324   ssgForceBasicState();
325   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
326 }
327
328
329 /**
330  * Set the panel's visibility.
331  */
332 void
333 FGPanel::setVisibility (bool visibility)
334 {
335   _visibility = visibility;
336 }
337
338
339 /**
340  * Return true if the panel is visible.
341  */
342 bool
343 FGPanel::getVisibility () const
344 {
345   return _visibility;
346 }
347
348
349 /**
350  * Set the panel's background texture.
351  */
352 void
353 FGPanel::setBackground (ssgTexture * texture)
354 {
355   _bg = texture;
356 }
357
358
359 /**
360  * Set the panel's x-offset.
361  */
362 void
363 FGPanel::setXOffset (int offset)
364 {
365   if (offset <= 0 && offset >= -_width + WIN_W)
366     _x_offset = offset;
367 }
368
369
370 /**
371  * Set the panel's y-offset.
372  */
373 void
374 FGPanel::setYOffset (int offset)
375 {
376   if (offset <= 0 && offset >= -_height)
377     _y_offset = offset;
378 }
379
380
381 /**
382  * Perform a mouse action.
383  */
384 bool
385 FGPanel::doMouseAction (int button, int updown, int x, int y)
386 {
387                                 // FIXME: this same code appears in update()
388   int xsize = _xsize_node->getIntValue();
389   int ysize = _ysize_node->getIntValue();
390   float aspect_adjust = get_aspect_adjust(xsize, ysize);
391
392                                 // Note a released button and return
393   // cerr << "Doing mouse action\n";
394   if (updown == 1) {
395     _mouseDown = false;
396     _mouseInstrument = 0;
397     return true;
398   }
399
400                                 // Scale for the real window size.
401   if (aspect_adjust < 1.0) {
402     x = int(((float)x / xsize) * WIN_W * aspect_adjust);
403     y = int(WIN_H - ((float(y) / ysize) * WIN_H));
404   } else {
405     x = int(((float)x / xsize) * WIN_W);
406     y = int((WIN_H - ((float(y) / ysize) * WIN_H)) / aspect_adjust);
407   }
408
409                                 // Adjust for offsets.
410   x -= _x_offset;
411   y -= _y_offset;
412
413                                 // Search for a matching instrument.
414   for (int i = 0; i < (int)_instruments.size(); i++) {
415     FGPanelInstrument *inst = _instruments[i];
416     int ix = inst->getXPos();
417     int iy = inst->getYPos();
418     int iw = inst->getWidth() / 2;
419     int ih = inst->getHeight() / 2;
420     if (x >= ix - iw && x < ix + iw && y >= iy - ih && y < iy + ih) {
421       _mouseDown = true;
422       _mouseDelay = 20;
423       _mouseInstrument = inst;
424       _mouseButton = button;
425       _mouseX = x - ix;
426       _mouseY = y - iy;
427                                 // Always do the action once.
428       _mouseInstrument->doMouseAction(_mouseButton, _mouseX, _mouseY);
429       return true;
430     }
431   }
432   return false;
433 }
434
435
436 \f
437 ////////////////////////////////////////////////////////////////////////.
438 // Implementation of FGPanelAction.
439 ////////////////////////////////////////////////////////////////////////
440
441 FGPanelAction::FGPanelAction ()
442 {
443 }
444
445 FGPanelAction::FGPanelAction (int button, int x, int y, int w, int h)
446   : _button(button), _x(x), _y(y), _w(w), _h(h)
447 {
448 }
449
450 FGPanelAction::~FGPanelAction ()
451 {
452 }
453
454 void
455 FGPanelAction::addBinding (const FGBinding &binding)
456 {
457   _bindings.push_back(binding);
458 }
459
460 void
461 FGPanelAction::doAction ()
462 {
463   int nBindings = _bindings.size();
464   for (int i = 0; i < nBindings; i++) {
465     _bindings[i].fire();
466   }
467 }
468
469
470 \f
471 ////////////////////////////////////////////////////////////////////////
472 // Implementation of FGPanelTransformation.
473 ////////////////////////////////////////////////////////////////////////
474
475 FGPanelTransformation::FGPanelTransformation ()
476   : table(0)
477 {
478 }
479
480 FGPanelTransformation::~FGPanelTransformation ()
481 {
482 }
483
484
485 \f
486 ////////////////////////////////////////////////////////////////////////
487 // Implementation of FGPanelInstrument.
488 ////////////////////////////////////////////////////////////////////////
489
490
491 FGPanelInstrument::FGPanelInstrument ()
492 {
493   setPosition(0, 0);
494   setSize(0, 0);
495 }
496
497 FGPanelInstrument::FGPanelInstrument (int x, int y, int w, int h)
498 {
499   setPosition(x, y);
500   setSize(w, h);
501 }
502
503 FGPanelInstrument::~FGPanelInstrument ()
504 {
505   for (action_list_type::iterator it = _actions.begin();
506        it != _actions.end();
507        it++) {
508     delete *it;
509     *it = 0;
510   }
511 }
512
513 void
514 FGPanelInstrument::setPosition (int x, int y)
515 {
516   _x = x;
517   _y = y;
518 }
519
520 void
521 FGPanelInstrument::setSize (int w, int h)
522 {
523   _w = w;
524   _h = h;
525 }
526
527 int
528 FGPanelInstrument::getXPos () const
529 {
530   return _x;
531 }
532
533 int
534 FGPanelInstrument::getYPos () const
535 {
536   return _y;
537 }
538
539 int
540 FGPanelInstrument::getWidth () const
541 {
542   return _w;
543 }
544
545 int
546 FGPanelInstrument::getHeight () const
547 {
548   return _h;
549 }
550
551 void
552 FGPanelInstrument::addAction (FGPanelAction * action)
553 {
554   _actions.push_back(action);
555 }
556
557                                 // Coordinates relative to centre.
558 bool
559 FGPanelInstrument::doMouseAction (int button, int x, int y)
560 {
561   action_list_type::iterator it = _actions.begin();
562   action_list_type::iterator last = _actions.end();
563   for ( ; it != last; it++) {
564     if ((*it)->inArea(button, x, y)) {
565       (*it)->doAction();
566       return true;
567     }
568   }
569   return false;
570 }
571
572
573 \f
574 ////////////////////////////////////////////////////////////////////////
575 // Implementation of FGLayeredInstrument.
576 ////////////////////////////////////////////////////////////////////////
577
578 FGLayeredInstrument::FGLayeredInstrument (int x, int y, int w, int h)
579   : FGPanelInstrument(x, y, w, h)
580 {
581 }
582
583 FGLayeredInstrument::~FGLayeredInstrument ()
584 {
585   for (layer_list::iterator it = _layers.begin(); it != _layers.end(); it++) {
586     delete *it;
587     *it = 0;
588   }
589 }
590
591 void
592 FGLayeredInstrument::draw ()
593 {
594   for (int i = 0; i < (int)_layers.size(); i++) {
595     glPushMatrix();
596     glTranslatef(0.0, 0.0, (i / 100.0) + 0.1);
597     _layers[i]->draw();
598     glPopMatrix();
599   }
600 }
601
602 int
603 FGLayeredInstrument::addLayer (FGInstrumentLayer *layer)
604 {
605   int n = _layers.size();
606   if (layer->getWidth() == -1) {
607     layer->setWidth(getWidth());
608   }
609   if (layer->getHeight() == -1) {
610     layer->setHeight(getHeight());
611   }
612   _layers.push_back(layer);
613   return n;
614 }
615
616 int
617 FGLayeredInstrument::addLayer (FGCroppedTexture &texture,
618                                int w, int h)
619 {
620   return addLayer(new FGTexturedLayer(texture, w, h));
621 }
622
623 void
624 FGLayeredInstrument::addTransformation (FGPanelTransformation * transformation)
625 {
626   int layer = _layers.size() - 1;
627   _layers[layer]->addTransformation(transformation);
628 }
629
630
631 \f
632 ////////////////////////////////////////////////////////////////////////
633 // Implementation of FGInstrumentLayer.
634 ////////////////////////////////////////////////////////////////////////
635
636 FGInstrumentLayer::FGInstrumentLayer (int w, int h)
637   : _w(w),
638     _h(h)
639 {
640 }
641
642 FGInstrumentLayer::~FGInstrumentLayer ()
643 {
644   for (transformation_list::iterator it = _transformations.begin();
645        it != _transformations.end();
646        it++) {
647     delete *it;
648     *it = 0;
649   }
650 }
651
652 void
653 FGInstrumentLayer::transform () const
654 {
655   transformation_list::const_iterator it = _transformations.begin();
656   transformation_list::const_iterator last = _transformations.end();
657   while (it != last) {
658     FGPanelTransformation *t = *it;
659     float val = (t->node == 0 ? 0.0 : t->node->getFloatValue());
660     if (val < t->min) {
661       val = t->min;
662     } else if (val > t->max) {
663       val = t->max;
664     }
665     if(t->table==0) {
666         val = val * t->factor + t->offset;
667     } else {
668         val = t->table->interpolate(val) * t->factor + t->offset;
669     }
670
671     switch (t->type) {
672     case FGPanelTransformation::XSHIFT:
673       glTranslatef(val, 0.0, 0.0);
674       break;
675     case FGPanelTransformation::YSHIFT:
676       glTranslatef(0.0, val, 0.0);
677       break;
678     case FGPanelTransformation::ROTATION:
679       glRotatef(-val, 0.0, 0.0, 1.0);
680       break;
681     }
682     it++;
683   }
684 }
685
686 void
687 FGInstrumentLayer::addTransformation (FGPanelTransformation * transformation)
688 {
689   _transformations.push_back(transformation);
690 }
691
692
693 \f
694 ////////////////////////////////////////////////////////////////////////
695 // Implementation of FGTexturedLayer.
696 ////////////////////////////////////////////////////////////////////////
697
698
699 FGTexturedLayer::FGTexturedLayer (const FGCroppedTexture &texture, int w, int h)
700   : FGInstrumentLayer(w, h)
701 {
702   setTexture(texture);
703 }
704
705
706 FGTexturedLayer::~FGTexturedLayer ()
707 {
708 }
709
710
711 void
712 FGTexturedLayer::draw ()
713 {
714   int w2 = _w / 2;
715   int h2 = _h / 2;
716
717   transform();
718   glBindTexture(GL_TEXTURE_2D, _texture.getTexture()->getHandle());
719   glBegin(GL_POLYGON);
720
721                                 // From Curt: turn on the panel
722                                 // lights after sundown.
723   if ( cur_light_params.sun_angle * SGD_RADIANS_TO_DEGREES < 95.0 ) {
724       glColor4fv( cur_light_params.scene_diffuse );
725   } else {
726       glColor4f(0.7, 0.2, 0.2, 1.0);
727   }
728
729
730   glTexCoord2f(_texture.getMinX(), _texture.getMinY()); glVertex2f(-w2, -h2);
731   glTexCoord2f(_texture.getMaxX(), _texture.getMinY()); glVertex2f(w2, -h2);
732   glTexCoord2f(_texture.getMaxX(), _texture.getMaxY()); glVertex2f(w2, h2);
733   glTexCoord2f(_texture.getMinX(), _texture.getMaxY()); glVertex2f(-w2, h2);
734   glEnd();
735 }
736
737
738 \f
739 ////////////////////////////////////////////////////////////////////////
740 // Implementation of FGTextLayer.
741 ////////////////////////////////////////////////////////////////////////
742
743 FGTextLayer::FGTextLayer (int w, int h)
744   : FGInstrumentLayer(w, h), _pointSize(14.0)
745 {
746   _then.stamp();
747   _color[0] = _color[1] = _color[2] = 0.0;
748   _color[3] = 1.0;
749 }
750
751 FGTextLayer::~FGTextLayer ()
752 {
753   chunk_list::iterator it = _chunks.begin();
754   chunk_list::iterator last = _chunks.end();
755   for ( ; it != last; it++) {
756     delete *it;
757   }
758 }
759
760 void
761 FGTextLayer::draw ()
762 {
763   glPushMatrix();
764   glColor4fv(_color);
765   transform();
766   text_renderer.setFont(guiFntHandle);
767   text_renderer.setPointSize(_pointSize);
768   text_renderer.begin();
769   text_renderer.start3f(0, 0, 0);
770
771   _now.stamp();
772   if (_now - _then > 100000) {
773     recalc_value();
774     _then = _now;
775   }
776   text_renderer.puts((char *)(_value.c_str()));
777
778   text_renderer.end();
779   glColor4f(1.0, 1.0, 1.0, 1.0);        // FIXME
780   glPopMatrix();
781 }
782
783 void
784 FGTextLayer::addChunk (FGTextLayer::Chunk * chunk)
785 {
786   _chunks.push_back(chunk);
787 }
788
789 void
790 FGTextLayer::setColor (float r, float g, float b)
791 {
792   _color[0] = r;
793   _color[1] = g;
794   _color[2] = b;
795   _color[3] = 1.0;
796 }
797
798 void
799 FGTextLayer::setPointSize (float size)
800 {
801   _pointSize = size;
802 }
803
804 void
805 FGTextLayer::setFont(fntFont * font)
806 {
807   text_renderer.setFont(font);
808 }
809
810
811 void
812 FGTextLayer::recalc_value () const
813 {
814   _value = "";
815   chunk_list::const_iterator it = _chunks.begin();
816   chunk_list::const_iterator last = _chunks.end();
817   for ( ; it != last; it++) {
818     _value += (*it)->getValue();
819   }
820 }
821
822
823 \f
824 ////////////////////////////////////////////////////////////////////////
825 // Implementation of FGTextLayer::Chunk.
826 ////////////////////////////////////////////////////////////////////////
827
828 FGTextLayer::Chunk::Chunk (const string &text, const string &fmt)
829   : _type(FGTextLayer::TEXT), _fmt(fmt)
830 {
831   _text = text;
832   if (_fmt == "") 
833     _fmt = "%s";
834 }
835
836 FGTextLayer::Chunk::Chunk (ChunkType type, const SGPropertyNode * node,
837                            const string &fmt, float mult)
838   : _type(type), _fmt(fmt), _mult(mult)
839 {
840   if (_fmt == "") {
841     if (type == TEXT_VALUE)
842       _fmt = "%s";
843     else
844       _fmt = "%.2f";
845   }
846   _node = node;
847 }
848
849 const char *
850 FGTextLayer::Chunk::getValue () const
851 {
852   switch (_type) {
853   case TEXT:
854     sprintf(_buf, _fmt.c_str(), _text.c_str());
855     return _buf;
856   case TEXT_VALUE:
857     sprintf(_buf, _fmt.c_str(), _node->getStringValue().c_str());
858     break;
859   case DOUBLE_VALUE:
860     sprintf(_buf, _fmt.c_str(), _node->getFloatValue() * _mult);
861     break;
862   }
863   return _buf;
864 }
865
866
867 \f
868 ////////////////////////////////////////////////////////////////////////
869 // Implementation of FGSwitchLayer.
870 ////////////////////////////////////////////////////////////////////////
871
872 FGSwitchLayer::FGSwitchLayer (int w, int h, const SGPropertyNode * node,
873                               FGInstrumentLayer * layer1,
874                               FGInstrumentLayer * layer2)
875   : FGInstrumentLayer(w, h), _node(node), _layer1(layer1), _layer2(layer2)
876 {
877 }
878
879 FGSwitchLayer::~FGSwitchLayer ()
880 {
881   delete _layer1;
882   delete _layer2;
883 }
884
885 void
886 FGSwitchLayer::draw ()
887 {
888   transform();
889   if (_node->getBoolValue()) {
890     _layer1->draw();
891   } else {
892     _layer2->draw();
893   }
894 }
895
896 \f
897 // end of panel.cxx