]> git.mxchange.org Git - flightgear.git/blob - src/Cockpit/panel.cxx
dca6cfdff95b3382478ba7ffdfe1f5d3cc909b23
[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);
277
278   glMatrixMode(GL_MODELVIEW);
279   glPushMatrix();
280   glLoadIdentity();
281
282   glTranslated(_x_offset, _y_offset, 0);
283
284                                 // Draw the background
285   glEnable(GL_TEXTURE_2D);
286   glDisable(GL_LIGHTING);
287   glEnable(GL_BLEND);
288   glEnable(GL_ALPHA_TEST);
289   glEnable(GL_COLOR_MATERIAL);
290   // glColor4f(1.0, 1.0, 1.0, 1.0);
291   if ( cur_light_params.sun_angle * SGD_RADIANS_TO_DEGREES < 95.0 ) {
292       glColor4fv( cur_light_params.scene_diffuse );
293   } else {
294       glColor4f(0.7, 0.2, 0.2, 1.0);
295   }
296   glBindTexture(GL_TEXTURE_2D, _bg->getHandle());
297   // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
298   // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
299   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
300   glBegin(GL_POLYGON);
301   glTexCoord2f(0.0, 0.0); glVertex3f(WIN_X, WIN_Y, 0);
302   glTexCoord2f(1.0, 0.0); glVertex3f(WIN_X + _width, WIN_Y, 0);
303   glTexCoord2f(1.0, 1.0); glVertex3f(WIN_X + _width, WIN_Y + _height, 0);
304   glTexCoord2f(0.0, 1.0); glVertex3f(WIN_X, WIN_Y + _height, 0);
305   glEnd();
306
307                                 // Draw the instruments.
308   instrument_list_type::const_iterator current = _instruments.begin();
309   instrument_list_type::const_iterator end = _instruments.end();
310
311   for ( ; current != end; current++) {
312     FGPanelInstrument * instr = *current;
313     glLoadIdentity();
314     glTranslated(_x_offset, _y_offset, 0);
315     glTranslated(instr->getXPos(), instr->getYPos(), 0);
316     instr->draw();
317   }
318
319   glMatrixMode(GL_PROJECTION);
320   glPopMatrix();
321   glMatrixMode(GL_MODELVIEW);
322   glPopMatrix();
323   ssgForceBasicState();
324   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
325 }
326
327
328 /**
329  * Set the panel's visibility.
330  */
331 void
332 FGPanel::setVisibility (bool visibility)
333 {
334   _visibility = visibility;
335 }
336
337
338 /**
339  * Return true if the panel is visible.
340  */
341 bool
342 FGPanel::getVisibility () const
343 {
344   return _visibility;
345 }
346
347
348 /**
349  * Set the panel's background texture.
350  */
351 void
352 FGPanel::setBackground (ssgTexture * texture)
353 {
354   _bg = texture;
355 }
356
357
358 /**
359  * Set the panel's x-offset.
360  */
361 void
362 FGPanel::setXOffset (int offset)
363 {
364   if (offset <= 0 && offset >= -_width + WIN_W)
365     _x_offset = offset;
366 }
367
368
369 /**
370  * Set the panel's y-offset.
371  */
372 void
373 FGPanel::setYOffset (int offset)
374 {
375   if (offset <= 0 && offset >= -_height)
376     _y_offset = offset;
377 }
378
379
380 /**
381  * Perform a mouse action.
382  */
383 bool
384 FGPanel::doMouseAction (int button, int updown, int x, int y)
385 {
386                                 // FIXME: this same code appears in update()
387   int xsize = _xsize_node->getIntValue();
388   int ysize = _ysize_node->getIntValue();
389   float aspect_adjust = get_aspect_adjust(xsize, ysize);
390
391                                 // Note a released button and return
392   // cerr << "Doing mouse action\n";
393   if (updown == 1) {
394     _mouseDown = false;
395     _mouseInstrument = 0;
396     return true;
397   }
398
399                                 // Scale for the real window size.
400   if (aspect_adjust < 1.0) {
401     x = int(((float)x / xsize) * WIN_W * aspect_adjust);
402     y = int(WIN_H - ((float(y) / ysize) * WIN_H));
403   } else {
404     x = int(((float)x / xsize) * WIN_W);
405     y = int((WIN_H - ((float(y) / ysize) * WIN_H)) / aspect_adjust);
406   }
407
408                                 // Adjust for offsets.
409   x -= _x_offset;
410   y -= _y_offset;
411
412                                 // Search for a matching instrument.
413   for (int i = 0; i < (int)_instruments.size(); i++) {
414     FGPanelInstrument *inst = _instruments[i];
415     int ix = inst->getXPos();
416     int iy = inst->getYPos();
417     int iw = inst->getWidth() / 2;
418     int ih = inst->getHeight() / 2;
419     if (x >= ix - iw && x < ix + iw && y >= iy - ih && y < iy + ih) {
420       _mouseDown = true;
421       _mouseDelay = 20;
422       _mouseInstrument = inst;
423       _mouseButton = button;
424       _mouseX = x - ix;
425       _mouseY = y - iy;
426                                 // Always do the action once.
427       _mouseInstrument->doMouseAction(_mouseButton, _mouseX, _mouseY);
428       return true;
429     }
430   }
431   return false;
432 }
433
434
435 \f
436 ////////////////////////////////////////////////////////////////////////.
437 // Implementation of FGPanelAction.
438 ////////////////////////////////////////////////////////////////////////
439
440 FGPanelAction::FGPanelAction ()
441 {
442 }
443
444 FGPanelAction::FGPanelAction (int button, int x, int y, int w, int h)
445   : _button(button), _x(x), _y(y), _w(w), _h(h)
446 {
447 }
448
449 FGPanelAction::~FGPanelAction ()
450 {
451 }
452
453
454 \f
455 ////////////////////////////////////////////////////////////////////////
456 // Implementation of FGAdjustAction.
457 ////////////////////////////////////////////////////////////////////////
458
459 FGAdjustAction::FGAdjustAction (int button, int x, int y, int w, int h,
460                                 SGPropertyNode * node, float increment, 
461                                 float min, float max, bool wrap)
462   : FGPanelAction(button, x, y, w, h),
463     _node(node), _increment(increment), _min(min), _max(max), _wrap(wrap)
464 {
465 }
466
467 FGAdjustAction::~FGAdjustAction ()
468 {
469 }
470
471 void
472 FGAdjustAction::doAction ()
473 {
474   float val = _node->getFloatValue();
475   val += _increment;
476   if (val < _min) {
477     val = (_wrap ? _max : _min);
478   } else if (val > _max) {
479     val = (_wrap ? _min : _max);
480   }
481   _node->setDoubleValue(val);
482 }
483
484
485 \f
486 ////////////////////////////////////////////////////////////////////////
487 // Implementation of FGSwapAction.
488 ////////////////////////////////////////////////////////////////////////
489
490 FGSwapAction::FGSwapAction (int button, int x, int y, int w, int h,
491                             SGPropertyNode * node1, SGPropertyNode * node2)
492   : FGPanelAction(button, x, y, w, h), _node1(node1), _node2(node2)
493 {
494 }
495
496 FGSwapAction::~FGSwapAction ()
497 {
498 }
499
500 void
501 FGSwapAction::doAction ()
502 {
503   float val = _node1->getFloatValue();
504   _node1->setDoubleValue(_node2->getFloatValue());
505   _node2->setDoubleValue(val);
506 }
507
508
509 \f
510 ////////////////////////////////////////////////////////////////////////
511 // Implementation of FGToggleAction.
512 ////////////////////////////////////////////////////////////////////////
513
514 FGToggleAction::FGToggleAction (int button, int x, int y, int w, int h,
515                                 SGPropertyNode * node)
516   : FGPanelAction(button, x, y, w, h), _node(node)
517 {
518 }
519
520 FGToggleAction::~FGToggleAction ()
521 {
522 }
523
524 void
525 FGToggleAction::doAction ()
526 {
527   _node->setBoolValue(!(_node->getBoolValue()));
528 }
529
530
531 \f
532 ////////////////////////////////////////////////////////////////////////
533 // Implementation of FGPanelTransformation.
534 ////////////////////////////////////////////////////////////////////////
535
536 FGPanelTransformation::FGPanelTransformation ()
537   : table(0)
538 {
539 }
540
541 FGPanelTransformation::~FGPanelTransformation ()
542 {
543 }
544
545
546 \f
547 ////////////////////////////////////////////////////////////////////////
548 // Implementation of FGPanelInstrument.
549 ////////////////////////////////////////////////////////////////////////
550
551
552 FGPanelInstrument::FGPanelInstrument ()
553 {
554   setPosition(0, 0);
555   setSize(0, 0);
556 }
557
558 FGPanelInstrument::FGPanelInstrument (int x, int y, int w, int h)
559 {
560   setPosition(x, y);
561   setSize(w, h);
562 }
563
564 FGPanelInstrument::~FGPanelInstrument ()
565 {
566   for (action_list_type::iterator it = _actions.begin();
567        it != _actions.end();
568        it++) {
569     delete *it;
570     *it = 0;
571   }
572 }
573
574 void
575 FGPanelInstrument::setPosition (int x, int y)
576 {
577   _x = x;
578   _y = y;
579 }
580
581 void
582 FGPanelInstrument::setSize (int w, int h)
583 {
584   _w = w;
585   _h = h;
586 }
587
588 int
589 FGPanelInstrument::getXPos () const
590 {
591   return _x;
592 }
593
594 int
595 FGPanelInstrument::getYPos () const
596 {
597   return _y;
598 }
599
600 int
601 FGPanelInstrument::getWidth () const
602 {
603   return _w;
604 }
605
606 int
607 FGPanelInstrument::getHeight () const
608 {
609   return _h;
610 }
611
612 void
613 FGPanelInstrument::addAction (FGPanelAction * action)
614 {
615   _actions.push_back(action);
616 }
617
618                                 // Coordinates relative to centre.
619 bool
620 FGPanelInstrument::doMouseAction (int button, int x, int y)
621 {
622   action_list_type::iterator it = _actions.begin();
623   action_list_type::iterator last = _actions.end();
624   for ( ; it != last; it++) {
625     if ((*it)->inArea(button, x, y)) {
626       (*it)->doAction();
627       return true;
628     }
629   }
630   return false;
631 }
632
633
634 \f
635 ////////////////////////////////////////////////////////////////////////
636 // Implementation of FGLayeredInstrument.
637 ////////////////////////////////////////////////////////////////////////
638
639 FGLayeredInstrument::FGLayeredInstrument (int x, int y, int w, int h)
640   : FGPanelInstrument(x, y, w, h)
641 {
642 }
643
644 FGLayeredInstrument::~FGLayeredInstrument ()
645 {
646   for (layer_list::iterator it = _layers.begin(); it != _layers.end(); it++) {
647     delete *it;
648     *it = 0;
649   }
650 }
651
652 void
653 FGLayeredInstrument::draw ()
654 {
655   for (int i = 0; i < (int)_layers.size(); i++) {
656     glPushMatrix();
657     glTranslatef(0.0, 0.0, (i / 100.0) + 0.1);
658     _layers[i]->draw();
659     glPopMatrix();
660   }
661 }
662
663 int
664 FGLayeredInstrument::addLayer (FGInstrumentLayer *layer)
665 {
666   int n = _layers.size();
667   if (layer->getWidth() == -1) {
668     layer->setWidth(getWidth());
669   }
670   if (layer->getHeight() == -1) {
671     layer->setHeight(getHeight());
672   }
673   _layers.push_back(layer);
674   return n;
675 }
676
677 int
678 FGLayeredInstrument::addLayer (FGCroppedTexture &texture,
679                                int w, int h)
680 {
681   return addLayer(new FGTexturedLayer(texture, w, h));
682 }
683
684 void
685 FGLayeredInstrument::addTransformation (FGPanelTransformation * transformation)
686 {
687   int layer = _layers.size() - 1;
688   _layers[layer]->addTransformation(transformation);
689 }
690
691
692 \f
693 ////////////////////////////////////////////////////////////////////////
694 // Implementation of FGInstrumentLayer.
695 ////////////////////////////////////////////////////////////////////////
696
697 FGInstrumentLayer::FGInstrumentLayer (int w, int h)
698   : _w(w),
699     _h(h)
700 {
701 }
702
703 FGInstrumentLayer::~FGInstrumentLayer ()
704 {
705   for (transformation_list::iterator it = _transformations.begin();
706        it != _transformations.end();
707        it++) {
708     delete *it;
709     *it = 0;
710   }
711 }
712
713 void
714 FGInstrumentLayer::transform () const
715 {
716   transformation_list::const_iterator it = _transformations.begin();
717   transformation_list::const_iterator last = _transformations.end();
718   while (it != last) {
719     FGPanelTransformation *t = *it;
720     float val = (t->node == 0 ? 0.0 : t->node->getFloatValue());
721     if (val < t->min) {
722       val = t->min;
723     } else if (val > t->max) {
724       val = t->max;
725     }
726     if(t->table==0) {
727         val = val * t->factor + t->offset;
728     } else {
729         val = t->table->interpolate(val) * t->factor + t->offset;
730     }
731
732     switch (t->type) {
733     case FGPanelTransformation::XSHIFT:
734       glTranslatef(val, 0.0, 0.0);
735       break;
736     case FGPanelTransformation::YSHIFT:
737       glTranslatef(0.0, val, 0.0);
738       break;
739     case FGPanelTransformation::ROTATION:
740       glRotatef(-val, 0.0, 0.0, 1.0);
741       break;
742     }
743     it++;
744   }
745 }
746
747 void
748 FGInstrumentLayer::addTransformation (FGPanelTransformation * transformation)
749 {
750   _transformations.push_back(transformation);
751 }
752
753
754 \f
755 ////////////////////////////////////////////////////////////////////////
756 // Implementation of FGTexturedLayer.
757 ////////////////////////////////////////////////////////////////////////
758
759
760 FGTexturedLayer::FGTexturedLayer (const FGCroppedTexture &texture, int w, int h)
761   : FGInstrumentLayer(w, h)
762 {
763   setTexture(texture);
764 }
765
766
767 FGTexturedLayer::~FGTexturedLayer ()
768 {
769 }
770
771
772 void
773 FGTexturedLayer::draw ()
774 {
775   int w2 = _w / 2;
776   int h2 = _h / 2;
777
778   transform();
779   glBindTexture(GL_TEXTURE_2D, _texture.getTexture()->getHandle());
780   glBegin(GL_POLYGON);
781
782                                 // From Curt: turn on the panel
783                                 // lights after sundown.
784   if ( cur_light_params.sun_angle * SGD_RADIANS_TO_DEGREES < 95.0 ) {
785       glColor4fv( cur_light_params.scene_diffuse );
786   } else {
787       glColor4f(0.7, 0.2, 0.2, 1.0);
788   }
789
790
791   glTexCoord2f(_texture.getMinX(), _texture.getMinY()); glVertex2f(-w2, -h2);
792   glTexCoord2f(_texture.getMaxX(), _texture.getMinY()); glVertex2f(w2, -h2);
793   glTexCoord2f(_texture.getMaxX(), _texture.getMaxY()); glVertex2f(w2, h2);
794   glTexCoord2f(_texture.getMinX(), _texture.getMaxY()); glVertex2f(-w2, h2);
795   glEnd();
796 }
797
798
799 \f
800 ////////////////////////////////////////////////////////////////////////
801 // Implementation of FGTextLayer.
802 ////////////////////////////////////////////////////////////////////////
803
804 FGTextLayer::FGTextLayer (int w, int h)
805   : FGInstrumentLayer(w, h), _pointSize(14.0)
806 {
807   _then.stamp();
808   _color[0] = _color[1] = _color[2] = 0.0;
809   _color[3] = 1.0;
810 }
811
812 FGTextLayer::~FGTextLayer ()
813 {
814   chunk_list::iterator it = _chunks.begin();
815   chunk_list::iterator last = _chunks.end();
816   for ( ; it != last; it++) {
817     delete *it;
818   }
819 }
820
821 void
822 FGTextLayer::draw ()
823 {
824   glPushMatrix();
825   glColor4fv(_color);
826   transform();
827   text_renderer.setFont(guiFntHandle);
828   text_renderer.setPointSize(_pointSize);
829   text_renderer.begin();
830   text_renderer.start3f(0, 0, 0);
831
832   _now.stamp();
833   if (_now - _then > 100000) {
834     recalc_value();
835     _then = _now;
836   }
837   text_renderer.puts((char *)(_value.c_str()));
838
839   text_renderer.end();
840   glColor4f(1.0, 1.0, 1.0, 1.0);        // FIXME
841   glPopMatrix();
842 }
843
844 void
845 FGTextLayer::addChunk (FGTextLayer::Chunk * chunk)
846 {
847   _chunks.push_back(chunk);
848 }
849
850 void
851 FGTextLayer::setColor (float r, float g, float b)
852 {
853   _color[0] = r;
854   _color[1] = g;
855   _color[2] = b;
856   _color[3] = 1.0;
857 }
858
859 void
860 FGTextLayer::setPointSize (float size)
861 {
862   _pointSize = size;
863 }
864
865 void
866 FGTextLayer::setFont(fntFont * font)
867 {
868   text_renderer.setFont(font);
869 }
870
871
872 void
873 FGTextLayer::recalc_value () const
874 {
875   _value = "";
876   chunk_list::const_iterator it = _chunks.begin();
877   chunk_list::const_iterator last = _chunks.end();
878   for ( ; it != last; it++) {
879     _value += (*it)->getValue();
880   }
881 }
882
883
884 \f
885 ////////////////////////////////////////////////////////////////////////
886 // Implementation of FGTextLayer::Chunk.
887 ////////////////////////////////////////////////////////////////////////
888
889 FGTextLayer::Chunk::Chunk (const string &text, const string &fmt)
890   : _type(FGTextLayer::TEXT), _fmt(fmt)
891 {
892   _text = text;
893   if (_fmt == "") 
894     _fmt = "%s";
895 }
896
897 FGTextLayer::Chunk::Chunk (ChunkType type, const SGPropertyNode * node,
898                            const string &fmt, float mult)
899   : _type(type), _fmt(fmt), _mult(mult)
900 {
901   if (_fmt == "") {
902     if (type == TEXT_VALUE)
903       _fmt = "%s";
904     else
905       _fmt = "%.2f";
906   }
907   _node = node;
908 }
909
910 const char *
911 FGTextLayer::Chunk::getValue () const
912 {
913   switch (_type) {
914   case TEXT:
915     sprintf(_buf, _fmt.c_str(), _text.c_str());
916     return _buf;
917   case TEXT_VALUE:
918     sprintf(_buf, _fmt.c_str(), _node->getStringValue().c_str());
919     break;
920   case DOUBLE_VALUE:
921     sprintf(_buf, _fmt.c_str(), _node->getFloatValue() * _mult);
922     break;
923   }
924   return _buf;
925 }
926
927
928 \f
929 ////////////////////////////////////////////////////////////////////////
930 // Implementation of FGSwitchLayer.
931 ////////////////////////////////////////////////////////////////////////
932
933 FGSwitchLayer::FGSwitchLayer (int w, int h, const SGPropertyNode * node,
934                               FGInstrumentLayer * layer1,
935                               FGInstrumentLayer * layer2)
936   : FGInstrumentLayer(w, h), _node(node), _layer1(layer1), _layer2(layer2)
937 {
938 }
939
940 FGSwitchLayer::~FGSwitchLayer ()
941 {
942   delete _layer1;
943   delete _layer2;
944 }
945
946 void
947 FGSwitchLayer::draw ()
948 {
949   transform();
950   if (_node->getBoolValue()) {
951     _layer1->draw();
952   } else {
953     _layer2->draw();
954   }
955 }
956
957 \f
958 // end of panel.cxx