]> git.mxchange.org Git - flightgear.git/blob - src/Cockpit/panel.cxx
use global fontcache. This hast the nice side effect that panels can now
[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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18 //
19 //  $Id$
20
21 //JVK
22 // On 2D panels all instruments include light sources were in night displayed
23 // with a red mask (instrument light). It is not correct for light sources
24 // (bulbs). There is added new layer property "emissive" (boolean) (only for
25 // textured layers).
26 // If a layer has to shine set it in the "instrument_def_file.xml" inside the
27 // <layer> tag by adding <emissive>true</emissive> tag. When omitted the default
28 // value is for backward compatibility set to false.
29
30 #ifdef HAVE_CONFIG_H
31 #  include <config.h>
32 #endif
33
34 #ifdef HAVE_WINDOWS_H          
35 #  include <windows.h>
36 #endif
37
38 #include <stdio.h>      // sprintf
39 #include <string.h>
40
41 #include <simgear/compiler.h>
42
43 #include SG_GLU_H
44
45 #include <plib/ssg.h>
46 #include <plib/fnt.h>
47
48 #include <simgear/debug/logstream.hxx>
49 #include <simgear/misc/sg_path.hxx>
50
51 #include <Main/globals.hxx>
52 #include <Main/fg_props.hxx>
53 #include <Main/viewmgr.hxx>
54 #include <Time/light.hxx>
55 #include <GUI/new_gui.hxx>      // FGFontCache
56
57 #include "hud.hxx"
58 #include "panel.hxx"
59
60 #define WIN_X 0
61 #define WIN_Y 0
62 #define WIN_W 1024
63 #define WIN_H 768
64
65 // The number of polygon-offset "units" to place between layers.  In
66 // principle, one is supposed to be enough.  In practice, I find that
67 // my hardware/driver requires many more.
68 #define POFF_UNITS 4
69
70 ////////////////////////////////////////////////////////////////////////
71 // Local functions.
72 ////////////////////////////////////////////////////////////////////////
73
74
75 /**
76  * Calculate the aspect adjustment for the panel.
77  */
78 static float
79 get_aspect_adjust (int xsize, int ysize)
80 {
81   float ideal_aspect = float(WIN_W) / float(WIN_H);
82   float real_aspect = float(xsize) / float(ysize);
83   return (real_aspect / ideal_aspect);
84 }
85
86
87 \f
88 ////////////////////////////////////////////////////////////////////////
89 // Global functions.
90 ////////////////////////////////////////////////////////////////////////
91
92 bool
93 fgPanelVisible ()
94 {
95      if(globals->get_current_panel() == 0)
96         return false;
97      if(globals->get_current_panel()->getVisibility() == 0)
98         return false;
99      if(globals->get_viewmgr()->get_current() != 0)
100         return false;
101      if(globals->get_current_view()->getHeadingOffset_deg() * SGD_DEGREES_TO_RADIANS != 0)
102         return false;
103      return true;
104 }
105
106
107 \f
108 ////////////////////////////////////////////////////////////////////////
109 // Implementation of FGTextureManager.
110 ////////////////////////////////////////////////////////////////////////
111
112 map<string,ssgTexture *> FGTextureManager::_textureMap;
113
114 ssgTexture *
115 FGTextureManager::createTexture (const string &relativePath)
116 {
117   ssgTexture * texture = _textureMap[relativePath];
118   if (texture == 0) {
119     SG_LOG( SG_COCKPIT, SG_DEBUG,
120             "Texture " << relativePath << " does not yet exist" );
121     SGPath tpath(globals->get_fg_root());
122     tpath.append(relativePath);
123     texture = new ssgTexture((char *)tpath.c_str(), false, false);
124     _textureMap[relativePath] = texture;
125     if (_textureMap[relativePath] == 0) 
126       SG_LOG( SG_COCKPIT, SG_ALERT, "Texture *still* doesn't exist" );
127     SG_LOG( SG_COCKPIT, SG_DEBUG, "Created texture " << relativePath
128             << " handle=" << texture->getHandle() );
129   }
130
131   return texture;
132 }
133
134
135
136 \f
137 ////////////////////////////////////////////////////////////////////////
138 // Implementation of FGCropped Texture.
139 ////////////////////////////////////////////////////////////////////////
140
141
142 FGCroppedTexture::FGCroppedTexture ()
143   : _path(""), _texture(0),
144     _minX(0.0), _minY(0.0), _maxX(1.0), _maxY(1.0)
145 {
146 }
147
148
149 FGCroppedTexture::FGCroppedTexture (const string &path,
150                                     float minX, float minY,
151                                     float maxX, float maxY)
152   : _path(path), _texture(0),
153     _minX(minX), _minY(minY), _maxX(maxX), _maxY(maxY)
154 {
155 }
156
157
158 FGCroppedTexture::~FGCroppedTexture ()
159 {
160 }
161
162
163 ssgTexture *
164 FGCroppedTexture::getTexture ()
165 {
166   if (_texture == 0) {
167     _texture = FGTextureManager::createTexture(_path);
168   }
169   return _texture;
170 }
171
172
173 \f
174 ////////////////////////////////////////////////////////////////////////
175 // Implementation of FGPanel.
176 ////////////////////////////////////////////////////////////////////////
177
178 static fntRenderer text_renderer;
179 static sgVec4 panel_color;
180 static sgVec4 emissive_panel_color = {1,1,1,1};
181
182 /**
183  * Constructor.
184  */
185 FGPanel::FGPanel ()
186   : _mouseDown(false),
187     _mouseInstrument(0),
188     _width(WIN_W), _height(int(WIN_H * 0.5768 + 1)),
189     _view_height(int(WIN_H * 0.4232)),
190     _visibility(fgGetNode("/sim/panel/visibility", true)),
191     _x_offset(fgGetNode("/sim/panel/x-offset", true)),
192     _y_offset(fgGetNode("/sim/panel/y-offset", true)),
193     _jitter(fgGetNode("/sim/panel/jitter", true)),
194     _flipx(fgGetNode("/sim/panel/flip-x", true)),
195     _xsize_node(fgGetNode("/sim/startup/xsize", true)),
196     _ysize_node(fgGetNode("/sim/startup/ysize", true)),
197     _enable_depth_test(false)
198 {
199 }
200
201
202 /**
203  * Destructor.
204  */
205 FGPanel::~FGPanel ()
206 {
207   for (instrument_list_type::iterator it = _instruments.begin();
208        it != _instruments.end();
209        it++) {
210     delete *it;
211     *it = 0;
212   }
213 }
214
215
216 /**
217  * Add an instrument to the panel.
218  */
219 void
220 FGPanel::addInstrument (FGPanelInstrument * instrument)
221 {
222   _instruments.push_back(instrument);
223 }
224
225
226 /**
227  * Initialize the panel.
228  */
229 void
230 FGPanel::init ()
231 {
232 }
233
234
235 /**
236  * Bind panel properties.
237  */
238 void
239 FGPanel::bind ()
240 {
241   fgSetArchivable("/sim/panel/visibility");
242   fgSetArchivable("/sim/panel/x-offset");
243   fgSetArchivable("/sim/panel/y-offset");
244   fgSetArchivable("/sim/panel/jitter");
245 }
246
247
248 /**
249  * Unbind panel properties.
250  */
251 void
252 FGPanel::unbind ()
253 {
254 }
255
256
257 /**
258  * Update the panel.
259  */
260 void
261 FGPanel::update (double dt)
262 {
263                                 // Do nothing if the panel isn't visible.
264     if ( !fgPanelVisible() ) {
265         return;
266     }
267
268     updateMouseDelay();
269
270                                 // Now, draw the panel
271     float aspect_adjust = get_aspect_adjust(_xsize_node->getIntValue(),
272                                             _ysize_node->getIntValue());
273     if (aspect_adjust <1.0)
274         update(WIN_X, int(WIN_W * aspect_adjust), WIN_Y, WIN_H);
275     else
276         update(WIN_X, WIN_W, WIN_Y, int(WIN_H / aspect_adjust));
277 }
278
279 /**
280  * Handle repeatable mouse events.  Called from update() and from
281  * fgUpdate3DPanels().  This functionality needs to move into the
282  * input subsystem.  Counting a tick every two frames is clumsy...
283  */
284 void FGPanel::updateMouseDelay()
285 {
286     if (_mouseDown) {
287         _mouseDelay--;
288         if (_mouseDelay < 0) {
289             _mouseInstrument->doMouseAction(_mouseButton, 0, _mouseX, _mouseY);
290             _mouseDelay = 2;
291         }
292     }
293 }
294
295
296 void
297 FGPanel::update (GLfloat winx, GLfloat winw, GLfloat winy, GLfloat winh)
298 {
299                                 // Calculate accelerations
300                                 // and jiggle the panel accordingly
301                                 // The factors and bounds are just
302                                 // initial guesses; using sqrt smooths
303                                 // out the spikes.
304   double x_offset = _x_offset->getIntValue();
305   double y_offset = _y_offset->getIntValue();
306
307 #if 0
308   if (_jitter->getFloatValue() != 0.0) {
309     double a_x_pilot = current_aircraft.fdm_state->get_A_X_pilot();
310     double a_y_pilot = current_aircraft.fdm_state->get_A_Y_pilot();
311     double a_z_pilot = current_aircraft.fdm_state->get_A_Z_pilot();
312
313     double a_zx_pilot = a_z_pilot - a_x_pilot;
314     
315     int x_adjust = int(sqrt(fabs(a_y_pilot) * _jitter->getFloatValue())) *
316                    (a_y_pilot < 0 ? -1 : 1);
317     int y_adjust = int(sqrt(fabs(a_zx_pilot) * _jitter->getFloatValue())) *
318                    (a_zx_pilot < 0 ? -1 : 1);
319
320                                 // adjustments in screen coordinates
321     x_offset += x_adjust;
322     y_offset += y_adjust;
323   }
324 #endif
325
326   glMatrixMode(GL_PROJECTION);
327   glPushMatrix();
328   glLoadIdentity();
329   if ( _flipx->getBoolValue() ) {
330     gluOrtho2D(winx + winw, winx, winy + winh, winy); /* up side down */
331   } else {
332     gluOrtho2D(winx, winx + winw, winy, winy + winh); /* right side up */
333   }
334   
335   glMatrixMode(GL_MODELVIEW);
336   glPushMatrix();
337   glLoadIdentity();
338   
339   glTranslated(x_offset, y_offset, 0);
340   
341   draw();
342
343   glMatrixMode(GL_PROJECTION);
344   glPopMatrix();
345   glMatrixMode(GL_MODELVIEW);
346   glPopMatrix();
347
348   ssgForceBasicState();
349   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
350 }
351
352 void
353 FGPanel::draw()
354 {
355   // In 3D mode, it's possible that we are being drawn exactly on top
356   // of an existing polygon.  Use an offset to prevent z-fighting.  In
357   // 2D mode, this is a no-op.
358   glEnable(GL_POLYGON_OFFSET_FILL);
359   glPolygonOffset(-1, -POFF_UNITS);
360
361   // save some state
362   glPushAttrib( GL_COLOR_BUFFER_BIT | GL_ENABLE_BIT | GL_LIGHTING_BIT
363                 | GL_TEXTURE_BIT | GL_PIXEL_MODE_BIT | GL_CULL_FACE 
364                 | GL_DEPTH_BUFFER_BIT );
365
366   // Draw the background
367   glEnable(GL_TEXTURE_2D);
368   glDisable(GL_LIGHTING);
369   glEnable(GL_BLEND);
370   glEnable(GL_ALPHA_TEST);
371   glEnable(GL_COLOR_MATERIAL);
372   glEnable(GL_CULL_FACE);
373   glCullFace(GL_BACK);
374   if( _enable_depth_test )
375       glDepthFunc(GL_ALWAYS);
376   else
377     glDisable(GL_DEPTH_TEST);
378
379   FGLight *l = (FGLight *)(globals->get_subsystem("lighting"));
380   sgCopyVec4( panel_color, l->scene_diffuse());
381   if ( fgGetDouble("/systems/electrical/outputs/instrument-lights") > 1.0 ) {
382       if ( panel_color[0] < 0.7 ) panel_color[0] = 0.7;
383       if ( panel_color[1] < 0.2 ) panel_color[1] = 0.2;
384       if ( panel_color[2] < 0.2 ) panel_color[2] = 0.2;
385   }
386   glColor4fv( panel_color );
387   if (_bg != 0) {
388     glBindTexture(GL_TEXTURE_2D, _bg->getHandle());
389     // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
390     // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
391     glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
392     glBegin(GL_POLYGON);
393     glTexCoord2f(0.0, 0.0); glVertex2f(WIN_X, WIN_Y);
394     glTexCoord2f(1.0, 0.0); glVertex2f(WIN_X + _width, WIN_Y);
395     glTexCoord2f(1.0, 1.0); glVertex2f(WIN_X + _width, WIN_Y + _height);
396     glTexCoord2f(0.0, 1.0); glVertex2f(WIN_X, WIN_Y + _height);
397     glEnd();
398   } else {
399     for (int i = 0; i < 4; i ++) {
400       // top row of textures...(1,3,5,7)
401       glBindTexture(GL_TEXTURE_2D, _mbg[i*2]->getHandle());
402       glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
403       glBegin(GL_POLYGON);
404       glTexCoord2f(0.0, 0.0); glVertex2f(WIN_X + (_width/4) * i, WIN_Y + (_height/2));
405       glTexCoord2f(1.0, 0.0); glVertex2f(WIN_X + (_width/4) * (i+1), WIN_Y + (_height/2));
406       glTexCoord2f(1.0, 1.0); glVertex2f(WIN_X + (_width/4) * (i+1), WIN_Y + _height);
407       glTexCoord2f(0.0, 1.0); glVertex2f(WIN_X + (_width/4) * i, WIN_Y + _height);
408       glEnd();
409       // bottom row of textures...(2,4,6,8)
410       glBindTexture(GL_TEXTURE_2D, _mbg[(i*2)+1]->getHandle());
411       glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
412       glBegin(GL_POLYGON);
413       glTexCoord2f(0.0, 0.0); glVertex2f(WIN_X + (_width/4) * i, WIN_Y);
414       glTexCoord2f(1.0, 0.0); glVertex2f(WIN_X + (_width/4) * (i+1), WIN_Y);
415       glTexCoord2f(1.0, 1.0); glVertex2f(WIN_X + (_width/4) * (i+1), WIN_Y + (_height/2));
416       glTexCoord2f(0.0, 1.0); glVertex2f(WIN_X + (_width/4) * i, WIN_Y + (_height/2));
417       glEnd();
418     }
419   }
420
421   // Draw the instruments.
422   instrument_list_type::const_iterator current = _instruments.begin();
423   instrument_list_type::const_iterator end = _instruments.end();
424
425   for ( ; current != end; current++) {
426     FGPanelInstrument * instr = *current;
427     glPushMatrix();
428     glTranslated(instr->getXPos(), instr->getYPos(), 0);
429     instr->draw();
430     glPopMatrix();
431   }
432
433   // Draw yellow "hotspots" if directed to.  This is a panel authoring
434   // feature; not intended to be high performance or to look good.
435   if ( fgGetBool("/sim/panel-hotspots") ) {
436     glDisable(GL_TEXTURE_2D);
437     glColor3f(1, 1, 0);
438     
439     for ( unsigned int i = 0; i < _instruments.size(); i++ )
440       _instruments[i]->drawHotspots();
441   }
442
443
444   // restore some original state
445   if( _enable_depth_test )
446     glDepthFunc(GL_LESS);
447   glPopAttrib();
448   glPolygonOffset(0, 0);
449   glDisable(GL_POLYGON_OFFSET_FILL);
450 }
451
452 /**
453  * Set the panel's visibility.
454  */
455 void
456 FGPanel::setVisibility (bool visibility)
457 {
458   _visibility->setBoolValue( visibility );
459 }
460
461
462 /**
463  * Return true if the panel is visible.
464  */
465 bool
466 FGPanel::getVisibility () const
467 {
468   return _visibility->getBoolValue();
469 }
470
471
472 /**
473  * Set the panel's background texture.
474  */
475 void
476 FGPanel::setBackground (ssgTexture * texture)
477 {
478   _bg = texture;
479 }
480
481 /**
482  * Set the panel's multiple background textures.
483  */
484 void
485 FGPanel::setMultiBackground (ssgTexture * texture, int idx)
486 {
487   _bg = 0;
488   _mbg[idx] = texture;
489 }
490
491 /**
492  * Set the panel's x-offset.
493  */
494 void
495 FGPanel::setXOffset (int offset)
496 {
497   if (offset <= 0 && offset >= -_width + WIN_W)
498     _x_offset->setIntValue( offset );
499 }
500
501
502 /**
503  * Set the panel's y-offset.
504  */
505 void
506 FGPanel::setYOffset (int offset)
507 {
508   if (offset <= 0 && offset >= -_height)
509     _y_offset->setIntValue( offset );
510 }
511
512 /**
513  * Handle a mouse action in panel-local (not screen) coordinates.
514  * Used by the 3D panel code in Model/panelnode.cxx, in situations
515  * where the panel doesn't control its own screen location.
516  */
517 bool
518 FGPanel::doLocalMouseAction(int button, int updown, int x, int y)
519 {
520   // Note a released button and return
521   if (updown == 1) {
522     if (_mouseInstrument != 0)
523         _mouseInstrument->doMouseAction(_mouseButton, 1, _mouseX, _mouseY);
524     _mouseDown = false;
525     _mouseInstrument = 0;
526     return false;
527   }
528
529   // Search for a matching instrument.
530   for (int i = 0; i < (int)_instruments.size(); i++) {
531     FGPanelInstrument *inst = _instruments[i];
532     int ix = inst->getXPos();
533     int iy = inst->getYPos();
534     int iw = inst->getWidth() / 2;
535     int ih = inst->getHeight() / 2;
536     if (x >= ix - iw && x < ix + iw && y >= iy - ih && y < iy + ih) {
537       _mouseDown = true;
538       _mouseDelay = 20;
539       _mouseInstrument = inst;
540       _mouseButton = button;
541       _mouseX = x - ix;
542       _mouseY = y - iy;
543       // Always do the action once.
544       return _mouseInstrument->doMouseAction(_mouseButton, 0,
545                                              _mouseX, _mouseY);
546     }
547   }
548   return false;
549 }
550
551 /**
552  * Perform a mouse action.
553  */
554 bool
555 FGPanel::doMouseAction (int button, int updown, int x, int y)
556 {
557                                 // FIXME: this same code appears in update()
558   int xsize = _xsize_node->getIntValue();
559   int ysize = _ysize_node->getIntValue();
560   float aspect_adjust = get_aspect_adjust(xsize, ysize);
561
562                                 // Scale for the real window size.
563   if (aspect_adjust < 1.0) {
564     x = int(((float)x / xsize) * WIN_W * aspect_adjust);
565     y = int(WIN_H - ((float(y) / ysize) * WIN_H));
566   } else {
567     x = int(((float)x / xsize) * WIN_W);
568     y = int((WIN_H - ((float(y) / ysize) * WIN_H)) / aspect_adjust);
569   }
570
571                                 // Adjust for offsets.
572   x -= _x_offset->getIntValue();
573   y -= _y_offset->getIntValue();
574
575   // Having fixed up the coordinates, fall through to the local
576   // coordinate handler.
577   return doLocalMouseAction(button, updown, x, y);
578
579
580 void FGPanel::setDepthTest (bool enable) {
581     _enable_depth_test = enable;
582 }
583
584
585 \f
586 ////////////////////////////////////////////////////////////////////////.
587 // Implementation of FGPanelAction.
588 ////////////////////////////////////////////////////////////////////////
589
590 FGPanelAction::FGPanelAction ()
591 {
592 }
593
594 FGPanelAction::FGPanelAction (int button, int x, int y, int w, int h,
595                               bool repeatable)
596     : _button(button), _x(x), _y(y), _w(w), _h(h), _repeatable(repeatable)
597 {
598 }
599
600 FGPanelAction::~FGPanelAction ()
601 {
602   for (unsigned int i = 0; i < 2; i++) {
603       for (unsigned int j = 0; j < _bindings[i].size(); j++)
604           delete _bindings[i][j];
605   }
606 }
607
608 void
609 FGPanelAction::addBinding (FGBinding * binding, int updown)
610 {
611   _bindings[updown].push_back(binding);
612 }
613
614 bool
615 FGPanelAction::doAction (int updown)
616 {
617   if (test()) {
618     if ((updown != _last_state) || (updown == 0 && _repeatable)) {
619         int nBindings = _bindings[updown].size();
620         for (int i = 0; i < nBindings; i++)
621             _bindings[updown][i]->fire();
622     }
623     _last_state = updown;
624     return true;
625   } else {
626     return false;
627   }
628 }
629
630
631 \f
632 ////////////////////////////////////////////////////////////////////////
633 // Implementation of FGPanelTransformation.
634 ////////////////////////////////////////////////////////////////////////
635
636 FGPanelTransformation::FGPanelTransformation ()
637   : table(0)
638 {
639 }
640
641 FGPanelTransformation::~FGPanelTransformation ()
642 {
643   delete table;
644 }
645
646
647 \f
648 ////////////////////////////////////////////////////////////////////////
649 // Implementation of FGPanelInstrument.
650 ////////////////////////////////////////////////////////////////////////
651
652
653 FGPanelInstrument::FGPanelInstrument ()
654 {
655   setPosition(0, 0);
656   setSize(0, 0);
657 }
658
659 FGPanelInstrument::FGPanelInstrument (int x, int y, int w, int h)
660 {
661   setPosition(x, y);
662   setSize(w, h);
663 }
664
665 FGPanelInstrument::~FGPanelInstrument ()
666 {
667   for (action_list_type::iterator it = _actions.begin();
668        it != _actions.end();
669        it++) {
670     delete *it;
671     *it = 0;
672   }
673 }
674
675 void
676 FGPanelInstrument::drawHotspots()
677 {
678   for ( unsigned int i = 0; i < _actions.size(); i++ ) {
679     FGPanelAction* a = _actions[i];
680     float x1 = getXPos() + a->getX();
681     float x2 = x1 + a->getWidth();
682     float y1 = getYPos() + a->getY();
683     float y2 = y1 + a->getHeight();
684
685     glBegin(GL_LINE_LOOP);
686     glVertex2f(x1, y1);
687     glVertex2f(x1, y2);
688     glVertex2f(x2, y2);
689     glVertex2f(x2, y1);
690     glEnd();
691   }
692 }
693
694 void
695 FGPanelInstrument::setPosition (int x, int y)
696 {
697   _x = x;
698   _y = y;
699 }
700
701 void
702 FGPanelInstrument::setSize (int w, int h)
703 {
704   _w = w;
705   _h = h;
706 }
707
708 int
709 FGPanelInstrument::getXPos () const
710 {
711   return _x;
712 }
713
714 int
715 FGPanelInstrument::getYPos () const
716 {
717   return _y;
718 }
719
720 int
721 FGPanelInstrument::getWidth () const
722 {
723   return _w;
724 }
725
726 int
727 FGPanelInstrument::getHeight () const
728 {
729   return _h;
730 }
731
732 void
733 FGPanelInstrument::addAction (FGPanelAction * action)
734 {
735   _actions.push_back(action);
736 }
737
738                                 // Coordinates relative to centre.
739 bool
740 FGPanelInstrument::doMouseAction (int button, int updown, int x, int y)
741 {
742   if (test()) {
743     action_list_type::iterator it = _actions.begin();
744     action_list_type::iterator last = _actions.end();
745     for ( ; it != last; it++) {
746       if ((*it)->inArea(button, x, y) &&
747           (*it)->doAction(updown))
748         return true;
749     }
750   }
751   return false;
752 }
753
754
755 \f
756 ////////////////////////////////////////////////////////////////////////
757 // Implementation of FGLayeredInstrument.
758 ////////////////////////////////////////////////////////////////////////
759
760 FGLayeredInstrument::FGLayeredInstrument (int x, int y, int w, int h)
761   : FGPanelInstrument(x, y, w, h)
762 {
763 }
764
765 FGLayeredInstrument::~FGLayeredInstrument ()
766 {
767   for (layer_list::iterator it = _layers.begin(); it != _layers.end(); it++) {
768     delete *it;
769     *it = 0;
770   }
771 }
772
773 void
774 FGLayeredInstrument::draw ()
775 {
776   if (!test())
777     return;
778   
779   for (int i = 0; i < (int)_layers.size(); i++) {
780     glPushMatrix();
781     _layers[i]->draw();
782     glPopMatrix();
783   }
784 }
785
786 int
787 FGLayeredInstrument::addLayer (FGInstrumentLayer *layer)
788 {
789   int n = _layers.size();
790   if (layer->getWidth() == -1) {
791     layer->setWidth(getWidth());
792   }
793   if (layer->getHeight() == -1) {
794     layer->setHeight(getHeight());
795   }
796   _layers.push_back(layer);
797   return n;
798 }
799
800 int
801 FGLayeredInstrument::addLayer (const FGCroppedTexture &texture,
802                                int w, int h)
803 {
804   return addLayer(new FGTexturedLayer(texture, w, h));
805 }
806
807 void
808 FGLayeredInstrument::addTransformation (FGPanelTransformation * transformation)
809 {
810   int layer = _layers.size() - 1;
811   _layers[layer]->addTransformation(transformation);
812 }
813
814
815 \f
816 ////////////////////////////////////////////////////////////////////////
817 // Implementation of FGSpecialInstrument.
818 ////////////////////////////////////////////////////////////////////////
819
820 FGSpecialInstrument::FGSpecialInstrument (DCLGPS* sb)
821   : FGPanelInstrument()
822 {
823   complex = sb;
824 }
825
826 FGSpecialInstrument::~FGSpecialInstrument ()
827 {
828 }
829
830 void
831 FGSpecialInstrument::draw ()
832 {
833   complex->draw();
834 }
835
836
837 \f
838 ////////////////////////////////////////////////////////////////////////
839 // Implementation of FGInstrumentLayer.
840 ////////////////////////////////////////////////////////////////////////
841
842 FGInstrumentLayer::FGInstrumentLayer (int w, int h)
843   : _w(w),
844     _h(h)
845 {
846 }
847
848 FGInstrumentLayer::~FGInstrumentLayer ()
849 {
850   for (transformation_list::iterator it = _transformations.begin();
851        it != _transformations.end();
852        it++) {
853     delete *it;
854     *it = 0;
855   }
856 }
857
858 void
859 FGInstrumentLayer::transform () const
860 {
861   transformation_list::const_iterator it = _transformations.begin();
862   transformation_list::const_iterator last = _transformations.end();
863   while (it != last) {
864     FGPanelTransformation *t = *it;
865     if (t->test()) {
866       float val = (t->node == 0 ? 0.0 : t->node->getFloatValue());
867
868       if (t->has_mod)
869           val = fmod(val, t->mod);
870       if (val < t->min) {
871         val = t->min;
872       } else if (val > t->max) {
873         val = t->max;
874       }
875
876       if(t->table==0) {
877         val = val * t->factor + t->offset;
878       } else {
879         val = t->table->interpolate(val) * t->factor + t->offset;
880       }
881       
882       switch (t->type) {
883       case FGPanelTransformation::XSHIFT:
884         glTranslatef(val, 0.0, 0.0);
885         break;
886       case FGPanelTransformation::YSHIFT:
887         glTranslatef(0.0, val, 0.0);
888         break;
889       case FGPanelTransformation::ROTATION:
890         glRotatef(-val, 0.0, 0.0, 1.0);
891         break;
892       }
893     }
894     it++;
895   }
896 }
897
898 void
899 FGInstrumentLayer::addTransformation (FGPanelTransformation * transformation)
900 {
901   _transformations.push_back(transformation);
902 }
903
904
905 \f
906 ////////////////////////////////////////////////////////////////////////
907 // Implementation of FGGroupLayer.
908 ////////////////////////////////////////////////////////////////////////
909
910 FGGroupLayer::FGGroupLayer ()
911 {
912 }
913
914 FGGroupLayer::~FGGroupLayer ()
915 {
916   for (unsigned int i = 0; i < _layers.size(); i++)
917     delete _layers[i];
918 }
919
920 void
921 FGGroupLayer::draw ()
922 {
923   if (test()) {
924     transform();
925     int nLayers = _layers.size();
926     for (int i = 0; i < nLayers; i++)
927       _layers[i]->draw();
928   }
929 }
930
931 void
932 FGGroupLayer::addLayer (FGInstrumentLayer * layer)
933 {
934   _layers.push_back(layer);
935 }
936
937
938 \f
939 ////////////////////////////////////////////////////////////////////////
940 // Implementation of FGTexturedLayer.
941 ////////////////////////////////////////////////////////////////////////
942
943
944 FGTexturedLayer::FGTexturedLayer (const FGCroppedTexture &texture, int w, int h)
945   : FGInstrumentLayer(w, h),
946     _emissive(false)
947 {
948   setTexture(texture);
949 }
950
951
952 FGTexturedLayer::~FGTexturedLayer ()
953 {
954 }
955
956
957 void
958 FGTexturedLayer::draw ()
959 {
960   if (test()) {
961     int w2 = _w / 2;
962     int h2 = _h / 2;
963     
964     transform();
965     glBindTexture(GL_TEXTURE_2D, _texture.getTexture()->getHandle());
966     glBegin(GL_POLYGON);
967
968     if (_emissive) {
969       glColor4fv( emissive_panel_color );
970     } else {
971                                 // From Curt: turn on the panel
972                                 // lights after sundown.
973       glColor4fv( panel_color );
974     }
975
976     glTexCoord2f(_texture.getMinX(), _texture.getMinY()); glVertex2f(-w2, -h2);
977     glTexCoord2f(_texture.getMaxX(), _texture.getMinY()); glVertex2f(w2, -h2);
978     glTexCoord2f(_texture.getMaxX(), _texture.getMaxY()); glVertex2f(w2, h2);
979     glTexCoord2f(_texture.getMinX(), _texture.getMaxY()); glVertex2f(-w2, h2);
980     glEnd();
981   }
982 }
983
984
985 \f
986 ////////////////////////////////////////////////////////////////////////
987 // Implementation of FGTextLayer.
988 ////////////////////////////////////////////////////////////////////////
989
990 FGTextLayer::FGTextLayer (int w, int h)
991   : FGInstrumentLayer(w, h), _pointSize(14.0), _font_name("default.txf")
992 {
993   _then.stamp();
994   _color[0] = _color[1] = _color[2] = 0.0;
995   _color[3] = 1.0;
996 }
997
998 FGTextLayer::~FGTextLayer ()
999 {
1000   chunk_list::iterator it = _chunks.begin();
1001   chunk_list::iterator last = _chunks.end();
1002   for ( ; it != last; it++) {
1003     delete *it;
1004   }
1005 }
1006
1007 void
1008 FGTextLayer::draw ()
1009 {
1010   if (test()) {
1011     glColor4fv(_color);
1012     transform();
1013
1014     FGFontCache *fc = globals->get_fontcache();
1015     text_renderer.setFont(fc->getTexFont(_font_name.c_str()));
1016
1017     text_renderer.setPointSize(_pointSize);
1018     text_renderer.begin();
1019     text_renderer.start3f(0, 0, 0);
1020
1021     _now.stamp();
1022     long diff = _now - _then;
1023
1024     if (diff > 100000 || diff < 0 ) {
1025       // ( diff < 0 ) is a sanity check and indicates our time stamp
1026       // difference math probably overflowed.  We can handle a max
1027       // difference of 35.8 minutes since the returned value is in
1028       // usec.  So if the panel is left off longer than that we can
1029       // over flow the math with it is turned back on.  This (diff <
1030       // 0) catches that situation, get's us out of trouble, and
1031       // back on track.
1032       recalc_value();
1033       _then = _now;
1034     }
1035
1036     // Something is goofy.  The code in this file renders only CCW
1037     // polygons, and I have verified that the font code in plib
1038     // renders only CCW trianbles.  Yet they come out backwards.
1039     // Something around here or in plib is either changing the winding
1040     // order or (more likely) pushing a left-handed matrix onto the
1041     // stack.  But I can't find it; get out the chainsaw...
1042     glFrontFace(GL_CW);
1043     text_renderer.puts((char *)(_value.c_str()));
1044     glFrontFace(GL_CCW);
1045
1046     text_renderer.end();
1047     glColor4f(1.0, 1.0, 1.0, 1.0);      // FIXME
1048   }
1049 }
1050
1051 void
1052 FGTextLayer::addChunk (FGTextLayer::Chunk * chunk)
1053 {
1054   _chunks.push_back(chunk);
1055 }
1056
1057 void
1058 FGTextLayer::setColor (float r, float g, float b)
1059 {
1060   _color[0] = r;
1061   _color[1] = g;
1062   _color[2] = b;
1063   _color[3] = 1.0;
1064 }
1065
1066 void
1067 FGTextLayer::setPointSize (float size)
1068 {
1069   _pointSize = size;
1070 }
1071
1072 void
1073 FGTextLayer::setFontName(const string &name)
1074 {
1075   _font_name = name + ".txf";
1076 }
1077
1078
1079 void
1080 FGTextLayer::setFont(fntFont * font)
1081 {
1082   text_renderer.setFont(font);
1083 }
1084
1085
1086 void
1087 FGTextLayer::recalc_value () const
1088 {
1089   _value = "";
1090   chunk_list::const_iterator it = _chunks.begin();
1091   chunk_list::const_iterator last = _chunks.end();
1092   for ( ; it != last; it++) {
1093     _value += (*it)->getValue();
1094   }
1095 }
1096
1097
1098 \f
1099 ////////////////////////////////////////////////////////////////////////
1100 // Implementation of FGTextLayer::Chunk.
1101 ////////////////////////////////////////////////////////////////////////
1102
1103 FGTextLayer::Chunk::Chunk (const string &text, const string &fmt)
1104   : _type(FGTextLayer::TEXT), _fmt(fmt)
1105 {
1106   _text = text;
1107   if (_fmt.empty()) 
1108     _fmt = "%s";
1109 }
1110
1111 FGTextLayer::Chunk::Chunk (ChunkType type, const SGPropertyNode * node,
1112                            const string &fmt, float mult, float offs,
1113                            bool truncation)
1114   : _type(type), _fmt(fmt), _mult(mult), _offs(offs), _trunc(truncation)
1115 {
1116   if (_fmt.empty()) {
1117     if (type == TEXT_VALUE)
1118       _fmt = "%s";
1119     else
1120       _fmt = "%.2f";
1121   }
1122   _node = node;
1123 }
1124
1125 const char *
1126 FGTextLayer::Chunk::getValue () const
1127 {
1128   if (test()) {
1129     _buf[0] = '\0';
1130     switch (_type) {
1131     case TEXT:
1132       sprintf(_buf, _fmt.c_str(), _text.c_str());
1133       return _buf;
1134     case TEXT_VALUE:
1135       sprintf(_buf, _fmt.c_str(), _node->getStringValue());
1136       break;
1137     case DOUBLE_VALUE:
1138       double d = _offs + _node->getFloatValue() * _mult;
1139       if (_trunc)  d = (d < 0) ? -floor(-d) : floor(d);
1140       sprintf(_buf, _fmt.c_str(), d);
1141       break;
1142     }
1143     return _buf;
1144   } else {
1145     return "";
1146   }
1147 }
1148
1149
1150 \f
1151 ////////////////////////////////////////////////////////////////////////
1152 // Implementation of FGSwitchLayer.
1153 ////////////////////////////////////////////////////////////////////////
1154
1155 FGSwitchLayer::FGSwitchLayer ()
1156   : FGGroupLayer()
1157 {
1158 }
1159
1160 void
1161 FGSwitchLayer::draw ()
1162 {
1163   if (test()) {
1164     transform();
1165     int nLayers = _layers.size();
1166     for (int i = 0; i < nLayers; i++) {
1167       if (_layers[i]->test()) {
1168           _layers[i]->draw();
1169           return;
1170       }
1171     }
1172   }
1173 }
1174
1175 \f
1176 // end of panel.cxx
1177
1178
1179