]> git.mxchange.org Git - flightgear.git/commitdiff
kln89 user interface logical pages
authordaveluff <daveluff>
Wed, 30 Nov 2005 00:18:42 +0000 (00:18 +0000)
committerdaveluff <daveluff>
Wed, 30 Nov 2005 00:18:42 +0000 (00:18 +0000)
26 files changed:
src/Instrumentation/KLN89/kln89_page_act.cxx [new file with mode: 0644]
src/Instrumentation/KLN89/kln89_page_act.hxx [new file with mode: 0644]
src/Instrumentation/KLN89/kln89_page_apt.cxx [new file with mode: 0644]
src/Instrumentation/KLN89/kln89_page_apt.hxx [new file with mode: 0644]
src/Instrumentation/KLN89/kln89_page_cal.cxx [new file with mode: 0644]
src/Instrumentation/KLN89/kln89_page_cal.hxx [new file with mode: 0644]
src/Instrumentation/KLN89/kln89_page_dir.cxx [new file with mode: 0644]
src/Instrumentation/KLN89/kln89_page_dir.hxx [new file with mode: 0644]
src/Instrumentation/KLN89/kln89_page_fpl.cxx [new file with mode: 0644]
src/Instrumentation/KLN89/kln89_page_fpl.hxx [new file with mode: 0644]
src/Instrumentation/KLN89/kln89_page_int.cxx [new file with mode: 0644]
src/Instrumentation/KLN89/kln89_page_int.hxx [new file with mode: 0644]
src/Instrumentation/KLN89/kln89_page_nav.cxx [new file with mode: 0644]
src/Instrumentation/KLN89/kln89_page_nav.hxx [new file with mode: 0644]
src/Instrumentation/KLN89/kln89_page_ndb.cxx [new file with mode: 0644]
src/Instrumentation/KLN89/kln89_page_ndb.hxx [new file with mode: 0644]
src/Instrumentation/KLN89/kln89_page_nrst.cxx [new file with mode: 0644]
src/Instrumentation/KLN89/kln89_page_nrst.hxx [new file with mode: 0644]
src/Instrumentation/KLN89/kln89_page_oth.cxx [new file with mode: 0644]
src/Instrumentation/KLN89/kln89_page_oth.hxx [new file with mode: 0644]
src/Instrumentation/KLN89/kln89_page_set.cxx [new file with mode: 0644]
src/Instrumentation/KLN89/kln89_page_set.hxx [new file with mode: 0644]
src/Instrumentation/KLN89/kln89_page_usr.cxx [new file with mode: 0644]
src/Instrumentation/KLN89/kln89_page_usr.hxx [new file with mode: 0644]
src/Instrumentation/KLN89/kln89_page_vor.cxx [new file with mode: 0644]
src/Instrumentation/KLN89/kln89_page_vor.hxx [new file with mode: 0644]

diff --git a/src/Instrumentation/KLN89/kln89_page_act.cxx b/src/Instrumentation/KLN89/kln89_page_act.cxx
new file mode 100644 (file)
index 0000000..d121820
--- /dev/null
@@ -0,0 +1,125 @@
+// kln89_page_*.[ch]xx - this file is one of the "pages" that
+//                       are used in the KLN89 GPS unit simulation. 
+//
+// Written by David Luff, started 2005.
+//
+// Copyright (C) 2005 - David C Luff - david.luff@nottingham.ac.uk
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+// $Id$
+
+#include "kln89_page_act.hxx"
+#include "kln89_page_apt.hxx"
+#include "kln89_page_vor.hxx"
+#include "kln89_page_ndb.hxx"
+#include "kln89_page_int.hxx"
+#include "kln89_page_usr.hxx"
+
+KLN89ActPage::KLN89ActPage(KLN89* parent) 
+: KLN89Page(parent) {
+       _nSubPages = 1;
+       _subPage = 0;
+       _name = "ACT";
+       _actWp = NULL;
+       _actWpId = -1;
+       _actPage = NULL;
+       
+       _aptPage = new KLN89AptPage(parent);
+       _vorPage = new KLN89VorPage(parent);
+       _ndbPage = new KLN89NDBPage(parent);
+       _intPage = new KLN89IntPage(parent);
+       _usrPage = new KLN89UsrPage(parent);
+}
+
+KLN89ActPage::~KLN89ActPage() {
+       delete _aptPage;
+       delete _vorPage;
+       delete _ndbPage;
+       delete _intPage;
+       delete _usrPage;
+}
+
+void KLN89ActPage::Update(double dt) {
+       if(!_actWp) {
+               _actWp = _kln89->GetActiveWaypoint();
+               _actWpId = _kln89->GetActiveWaypointIndex();
+       }
+       if(_actWp) {
+               switch(_actWp->type) {
+               case GPS_WP_APT: _actPage = _aptPage; break;
+               case GPS_WP_VOR: _actPage = _vorPage; break;
+               case GPS_WP_NDB: _actPage = _ndbPage; break;
+               case GPS_WP_INT: _actPage = _intPage; break;
+               case GPS_WP_USR: _actPage = _usrPage; break;
+               default:
+                       _actPage = NULL;
+                       // ASSERT(0); // ie. we shouldn't ever get here.
+               }
+       }
+       
+       _id = _actWp->id; 
+       if(_actPage) {
+               _actPage->SetId(_actWp->id);
+               _actPage->Update(dt);
+       } else {
+               KLN89Page::Update(dt);
+       }
+}
+
+void KLN89ActPage::CrsrPressed() {
+       if(_actPage) {
+               _actPage->CrsrPressed();
+       } else {
+               KLN89Page::CrsrPressed();
+       }
+}
+
+void KLN89ActPage::EntPressed() {
+       if(_actPage) {
+               _actPage->EntPressed();
+       } else {
+               KLN89Page::EntPressed();
+       }
+}
+
+void KLN89ActPage::ClrPressed() {
+       if(_actPage) {
+               _actPage->ClrPressed();
+       } else {
+               KLN89Page::ClrPressed();
+       }
+}
+
+void KLN89ActPage::Knob2Left1() {
+       if((_kln89->_mode != KLN89_MODE_CRSR) && (_actPage)) {
+               _actPage->Knob2Left1();
+       }
+}
+
+void KLN89ActPage::Knob2Right1() {
+       if((_kln89->_mode != KLN89_MODE_CRSR) && (_actPage)) {
+               _actPage->Knob2Right1();
+       }
+}
+
+void KLN89ActPage::LooseFocus() {
+       // Setting to NULL and -1 is better than resetting to
+       // active waypoint and index since we can't guarantee that
+       // the fpl active waypoint won't change behind our backs
+       // when we don't have focus.
+       _actWp = NULL;
+       _actWpId = -1;
+}
diff --git a/src/Instrumentation/KLN89/kln89_page_act.hxx b/src/Instrumentation/KLN89/kln89_page_act.hxx
new file mode 100644 (file)
index 0000000..5e8d89c
--- /dev/null
@@ -0,0 +1,62 @@
+// kln89_page_*.[ch]xx - this file is one of the "pages" that
+//                       are used in the KLN89 GPS unit simulation. 
+//
+// Written by David Luff, started 2005.
+//
+// Copyright (C) 2005 - David C Luff - david.luff@nottingham.ac.uk
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+// $Id$
+
+#ifndef _KLN89_PAGE_ACT_HXX
+#define _KLN89_PAGE_ACT_HXX
+
+#include "kln89.hxx"
+
+class KLN89ActPage : public KLN89Page {
+
+public:
+       KLN89ActPage(KLN89* parent);
+       ~KLN89ActPage();
+
+       void Update(double dt);
+       
+       void CrsrPressed();
+       void EntPressed();
+       void ClrPressed();
+       void Knob2Left1();
+       void Knob2Right1();
+       
+       void LooseFocus();
+       
+private:
+       // Position of the currently displayed waypoint within the active flightplan.
+       // -1 indicates no active flightplan.
+       int _actWpId;
+       
+       GPSWaypoint* _actWp;
+       
+       // The actual ACT page that gets displayed...
+       GPSPage* _actPage;
+       // ...which points to one of the below.
+       GPSPage* _aptPage;
+       GPSPage* _vorPage;
+       GPSPage* _ndbPage;
+       GPSPage* _intPage;
+       GPSPage* _usrPage;
+};
+
+#endif // _KLN89_PAGE_ACT_HXX
diff --git a/src/Instrumentation/KLN89/kln89_page_apt.cxx b/src/Instrumentation/KLN89/kln89_page_apt.cxx
new file mode 100644 (file)
index 0000000..d1272e6
--- /dev/null
@@ -0,0 +1,854 @@
+// kln89_page_*.[ch]xx - this file is one of the "pages" that
+//                       are used in the KLN89 GPS unit simulation. 
+//
+// Written by David Luff, started 2005.
+//
+// Copyright (C) 2005 - David C Luff - david.luff@nottingham.ac.uk
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+// $Id$
+
+#include "kln89_page_apt.hxx"
+#include <ATC/commlist.hxx>
+
+// This function is copied from Airports/runways.cxx
+// TODO - Make the original properly available and remove this instance!!!!
+// Return reverse rwy number
+// eg 01 -> 19
+// 03L -> 21R
+static string GetReverseRunwayNo(string rwyno) {       
+    // cout << "Original rwyno = " << rwyNo << '\n';
+    
+    // standardize input number
+    string tmp = rwyno.substr(1, 1);
+    if (( tmp == "L" || tmp == "R" || tmp == "C" ) || (rwyno.size() == 1)) {
+               tmp = rwyno;
+               rwyno = "0" + tmp;
+               SG_LOG( SG_GENERAL, SG_INFO,
+                       "Standardising rwy number from " << tmp << " to " << rwyno );
+    }
+    
+    char buf[4];
+    int rn = atoi(rwyno.substr(0,2).c_str());
+    rn += 18;
+    while(rn > 36) {
+               rn -= 36;
+    }
+    sprintf(buf, "%02i", rn);
+    if(rwyno.size() == 3) {
+               if(rwyno.substr(2,1) == "L") {
+                       buf[2] = 'R';
+                       buf[3] = '\0';
+               } else if (rwyno.substr(2,1) == "R") {
+                       buf[2] = 'L';
+                       buf[3] = '\0';
+               } else if (rwyno.substr(2,1) == "C") {
+                       buf[2] = 'C';
+                       buf[3] = '\0';
+               } else if (rwyno.substr(2,1) == "T") {
+                       buf[2] = 'T';
+                       buf[3] = '\0';
+               } else {
+                       SG_LOG(SG_GENERAL, SG_ALERT, "Unknown runway code "
+                       << rwyno << " passed to GetReverseRunwayNo(...)");
+               }
+    }
+    return(buf);
+}
+
+KLN89AptPage::KLN89AptPage(KLN89* parent) 
+: KLN89Page(parent) {
+       _nSubPages = 8;
+       _subPage = 0;
+       _name = "APT";
+       _apt_id = "KHWD";
+       // Make sure that _last_apt_id doesn't match at startup to force airport data to be fetched on first update.
+       _last_apt_id = "XXXX";
+       _nRwyPages = 1;
+       _curRwyPage = 0;
+       _nFreqPages = 1;
+       _curFreqPage = 0;
+       ap = NULL;
+       _iapStart = 0;
+       _iafStart = 0;
+       _fStart = 0;
+       _iaps.clear();
+       _iafDialog = false;
+       _addDialog = false;
+       _replaceDialog = false;
+       _curIap = 0;
+       _curIaf = 0;
+}
+
+KLN89AptPage::~KLN89AptPage() {
+}
+
+void KLN89AptPage::Update(double dt) {
+       bool actPage = (_kln89->_activePage->GetName() == "ACT" ? true : false);
+       bool multi;  // Not set by FindFirst...
+       bool exact = false;
+       if(_apt_id.size() == 4) exact = true;
+       // TODO - move this search out to where the button is pressed, and cache the result!
+       if(_apt_id != _last_apt_id || ap == NULL) ap = _kln89->FindFirstAptById(_apt_id, multi, exact);
+       //if(np == NULL) cout << "NULL... ";
+       //if(b == false) cout << "false...\n";
+       /*
+       if(np && b) {
+               cout << "VOR FOUND!\n";
+       } else {
+               cout << ":-(\n";
+       }
+       */
+       
+       if(ap) {
+               //cout << "Valid airport found! id = " << ap->getId() << ", elev = " << ap->getElevation() << '\n';
+               if(_apt_id != _last_apt_id) {
+                       UpdateAirport(ap->getId());
+                       _last_apt_id = _apt_id;
+                       _curFreqPage = 0;
+                       _curRwyPage = 0;
+               }
+               _apt_id = ap->getId();
+               if(_kln89->GetActiveWaypoint()) {
+                       if(_apt_id == _kln89->GetActiveWaypoint()->id) {
+                               if(!(_kln89->_waypointAlert && _kln89->_blink)) {
+                                       // Active waypoint arrow
+                                       _kln89->DrawSpecialChar(4, 2, 0, 3);
+                               }
+                       }
+               }
+               if(_kln89->_mode != KLN89_MODE_CRSR) {
+                       if(!(_subPage == 7 && (_iafDialog || _addDialog || _replaceDialog))) {  // Don't draw the airport name when the IAP dialogs are active
+                               if(!_entInvert) {
+                                       if(!actPage) {
+                                               _kln89->DrawText(ap->getId(), 2, 1, 3);
+                                       } else {
+                                               // If it's the ACT page, The ID is shifted slightly right to make space for the waypoint index.
+                                               _kln89->DrawText(ap->getId(), 2, 4, 3);
+                                               char buf[3];
+                                               int n = snprintf(buf, 3, "%i", _kln89->GetActiveWaypointIndex() + 1);
+                                               _kln89->DrawText((string)buf, 2, 3 - n, 3);
+                                       }
+                               } else {
+                                       if(!_kln89->_blink) {
+                                               _kln89->DrawText(ap->getId(), 2, 1, 3, false, 99);
+                                               _kln89->DrawEnt();
+                                       }
+                               }
+                       }
+               }
+               if(_subPage == 0) {
+                       // Name
+                       _kln89->DrawText(ap->getName(), 2, 0, 2);
+                       // Elevation
+                       _kln89->DrawText(_kln89->_altUnits == GPS_ALT_UNITS_FT ? "ft" : "m", 2, 14, 3);
+                       char buf[6];
+                       int n = snprintf(buf, 5, "%i", (_kln89->_altUnits == GPS_ALT_UNITS_FT ? (int)(ap->getElevation()) : (int)((double)ap->getElevation() * SG_FEET_TO_METER)));
+                       _kln89->DrawText((string)buf, 2, 14 - n, 3);
+                       // Town
+                       airport_id_str_map_iterator itr = _kln89->_airportTowns.find(_apt_id);
+                       if(itr != _kln89->_airportTowns.end()) {
+                               _kln89->DrawText(itr->second, 2, 0, 1);
+                       }
+                       // State / Province / Country
+                       itr = _kln89->_airportStates.find(_apt_id);
+                       if(itr != _kln89->_airportStates.end()) {
+                               _kln89->DrawText(itr->second, 2, 0, 0);
+                       }
+               } else if(_subPage == 1) {
+                       _kln89->DrawLatitude(ap->getLatitude(), 2, 3, 2);
+                       _kln89->DrawLongitude(ap->getLongitude(), 2, 3, 1);
+                       _kln89->DrawDirDistField(ap->getLatitude() * SG_DEGREES_TO_RADIANS, ap->getLongitude() * SG_DEGREES_TO_RADIANS, 
+                                                2, 0, 0, _to_flag, (_kln89->_mode == KLN89_MODE_CRSR && _uLinePos == 5 ? true : false));
+               } else if(_subPage == 2) {
+                       // Try and calculate a realistic difference from UTC based on longitude
+                       float degLonPerHr = 360.0 / 24.0;       // 15 degrees per hour difference.
+                       // Since 0 longitude is the middle of UTC, the boundaries will be at 7.5, 22.5, 37.5 etc.
+                       int hrDiff = ((int)((fabs(ap->getLongitude())) + 7.5)) / 15;
+                       _kln89->DrawText("UTC", 2, 0, 2);
+                       if(hrDiff != 0) {
+                               _kln89->DrawText(ap->getLongitude() >= 0.0 ? "+" : "-", 2, 3, 2);
+                               char buf[3];
+                               snprintf(buf, 3, "%02i", hrDiff);
+                               _kln89->DrawText((string)buf, 2, 4, 2);
+                               _kln89->DrawText("(   DT)", 2, 6, 2);
+                               if(ap->getLongitude() >= 0.0) {
+                                       hrDiff++;
+                               } else {
+                                       hrDiff--;
+                               }
+                               _kln89->DrawText(ap->getLongitude() >= 0.0 ? "+" : "-", 2, 7, 2);
+                               snprintf(buf, 3, "%02i", hrDiff);
+                               _kln89->DrawText((string)buf, 2, 8, 2);
+                       }
+                       // I guess we can make a heuristic guess as to fuel availability from the runway sizes
+                       // For now assume that airports with asphalt or concrete runways will have at least 100L,
+                       // and that runways over 4000ft will have JET.
+                       if(_aptRwys[0]._surface_code <= 2) {
+                               if(_aptRwys[0]._length >= 4000) {
+                                       _kln89->DrawText("JET 100L", 2, 0, 1);
+                               } else {
+                                       _kln89->DrawText("100L", 2, 0, 1);
+                               }
+                       }
+                       if(_iaps.empty()) {
+                               _kln89->DrawText("NO APR", 2, 0, 0);
+                       } else {
+                               // TODO - output proper differentiation of ILS and NP APR and NP APR type eg GPS(R)
+                               _kln89->DrawText("NP APR", 2, 0, 0);
+                       }
+               } else if(_subPage == 3) {
+                       if(_nRwyPages > 1) {
+                               _kln89->DrawChar('+', 1, 3, 0);
+                       }
+                       unsigned int i = _curRwyPage * 2;
+                       string s;
+                       if(i < _aptRwys.size()) {
+                               // Rwy No.
+                               string s = _aptRwys[i]._rwy_no;
+                               _kln89->DrawText(s, 2, 9, 3);
+                               _kln89->DrawText("/", 2, 12, 3);
+                               _kln89->DrawText(GetReverseRunwayNo(s), 2, 13, 3);
+                               // Length
+                               s = GPSitoa(int(float(_aptRwys[i]._length) * (_kln89->_altUnits == GPS_ALT_UNITS_FT ? 1.0 : SG_FEET_TO_METER) + 0.5));
+                               _kln89->DrawText(s, 2, 5 - s.size(), 2);
+                               _kln89->DrawText((_kln89->_altUnits == GPS_ALT_UNITS_FT ? "ft" : "m"), 2, 5, 2);
+                               // Surface
+                               // TODO - why not store these strings as an array?
+                               switch(_aptRwys[i]._surface_code) {
+                               case 1:
+                                       // Asphalt - fall through
+                               case 2:
+                                       // Concrete
+                                       _kln89->DrawText("HRD", 2, 9, 2);
+                                       break;
+                               case 3:
+                               case 8:
+                                       // Turf / Turf helipad
+                                       _kln89->DrawText("TRF", 2, 9, 2);
+                                       break;
+                               case 4:
+                               case 9:
+                                       // Dirt / Dirt helipad
+                                       _kln89->DrawText("DRT", 2, 9, 2);
+                                       break;
+                               case 5:
+                                       // Gravel
+                                       _kln89->DrawText("GRV", 2, 9, 2);
+                                       break;
+                               case 6:
+                                       // Asphalt helipad - fall through
+                               case 7:
+                                       // Concrete helipad
+                                       _kln89->DrawText("HRD", 2, 9, 2);
+                                       break;
+                               case 12:
+                                       // Lakebed
+                                       _kln89->DrawText("CLY", 2, 9, 2);
+                               default:
+                                       // erm? ...
+                                       _kln89->DrawText("MAT", 2, 9, 2);
+                               }
+                       }
+                       i++;
+                       if(i < _aptRwys.size()) {
+                               // Rwy No.
+                               string s = _aptRwys[i]._rwy_no;
+                               _kln89->DrawText(s, 2, 9, 1);
+                               _kln89->DrawText("/", 2, 12, 1);
+                               _kln89->DrawText(GetReverseRunwayNo(s), 2, 13, 1);
+                               // Length
+                               s = GPSitoa(int(float(_aptRwys[i]._length) * (_kln89->_altUnits == GPS_ALT_UNITS_FT ? 1.0 : SG_FEET_TO_METER) + 0.5));
+                               _kln89->DrawText(s, 2, 5 - s.size(), 0);
+                               _kln89->DrawText((_kln89->_altUnits == GPS_ALT_UNITS_FT ? "ft" : "m"), 2, 5, 0);
+                               // Surface
+                               // TODO - why not store these strings as an array?
+                               switch(_aptRwys[i]._surface_code) {
+                               case 1:
+                                       // Asphalt - fall through
+                               case 2:
+                                       // Concrete
+                                       _kln89->DrawText("HRD", 2, 9, 0);
+                                       break;
+                               case 3:
+                               case 8:
+                                       // Turf / Turf helipad
+                                       _kln89->DrawText("TRF", 2, 9, 0);
+                                       break;
+                               case 4:
+                               case 9:
+                                       // Dirt / Dirt helipad
+                                       _kln89->DrawText("DRT", 2, 9, 0);
+                                       break;
+                               case 5:
+                                       // Gravel
+                                       _kln89->DrawText("GRV", 2, 9, 0);
+                                       break;
+                               case 6:
+                                       // Asphalt helipad - fall through
+                               case 7:
+                                       // Concrete helipad
+                                       _kln89->DrawText("HRD", 2, 9, 0);
+                                       break;
+                               case 12:
+                                       // Lakebed
+                                       _kln89->DrawText("CLY", 2, 9, 0);
+                               default:
+                                       // erm? ...
+                                       _kln89->DrawText("MAT", 2, 9, 0);
+                               }
+                       }
+               } else if(_subPage == 4) {
+                       if(_nFreqPages > 1) {
+                               _kln89->DrawChar('+', 1, 3, 0);
+                       }
+                       unsigned int i = _curFreqPage * 3;
+                       if(i < _aptFreqs.size()) {
+                               _kln89->DrawText(_aptFreqs[i].service, 2, 0, 2);
+                               _kln89->DrawFreq(_aptFreqs[i].freq, 2, 7, 2);
+                       }
+                       i++;
+                       if(i < _aptFreqs.size()) {
+                               _kln89->DrawText(_aptFreqs[i].service, 2, 0, 1);
+                               _kln89->DrawFreq(_aptFreqs[i].freq, 2, 7, 1);
+                       }
+                       i++;
+                       if(i < _aptFreqs.size()) {
+                               _kln89->DrawText(_aptFreqs[i].service, 2, 0, 0);
+                               _kln89->DrawFreq(_aptFreqs[i].freq, 2, 7, 0);
+                       }
+               } else if(_subPage == 5) {
+                       // TODO - user ought to be allowed to leave persistent remarks
+                       _kln89->DrawText("[Remarks]", 2, 2, 2);
+               } else if(_subPage == 6) {
+                       // We don't have SID/STAR database yet
+                       // TODO
+                       _kln89->DrawText("No SID/STAR", 2, 3, 2);
+                       _kln89->DrawText("In Data Base", 2, 2, 1);
+                       _kln89->DrawText("For This Airport", 2, 0, 0);
+               } else if(_subPage == 7) {
+                       if(_iaps.empty()) {
+                               _kln89->DrawText("IAP", 2, 11, 3);
+                               _kln89->DrawText("No Approach", 2, 3, 2);
+                               _kln89->DrawText("In Data Base", 2, 2, 1);
+                               _kln89->DrawText("For This Airport", 2, 0, 0);
+                       } else {
+                               if(_iafDialog) {
+                                       _kln89->DrawText(_iaps[_curIap]->_abbrev, 2, 1, 3);
+                                       _kln89->DrawText(_iaps[_curIap]->_rwyStr, 2, 7, 3);
+                                       _kln89->DrawText(_iaps[_curIap]->_id, 2, 12, 3);
+                                       _kln89->DrawText("IAF", 2, 2, 2);
+                                       int line = 0;
+                                       for(unsigned int i=_iafStart; i<_IAF.size(); ++i) {
+                                               if(line == 2) {
+                                                       i = _IAF.size() - 1;
+                                               }
+                                               // Assume that the IAF number is always single digit!
+                                               _kln89->DrawText(GPSitoa(i+1), 2, 6, 2-line);
+                                               if(!(_kln89->_mode == KLN89_MODE_CRSR && _kln89->_blink && _uLinePos == (line + 1))) {
+                                                       _kln89->DrawText(_IAF[i]->id, 2, 8, 2-line);
+                                               }
+                                               if(_kln89->_mode == KLN89_MODE_CRSR && _uLinePos == (line + 1) && !(_kln89->_blink )) {
+                                                       _kln89->Underline(2, 8, 2-line, 5);
+                                               }
+                                               ++line;
+                                       }
+                                       if(_uLinePos > 0 && !(_kln89->_blink)) {
+                                               _kln89->DrawEnt();
+                                       }
+                               } else if(_addDialog) {
+                                       _kln89->DrawText(_iaps[_curIap]->_abbrev, 2, 1, 3);
+                                       _kln89->DrawText(_iaps[_curIap]->_rwyStr, 2, 7, 3);
+                                       _kln89->DrawText(_iaps[_curIap]->_id, 2, 12, 3);
+                                       string s = GPSitoa(_fStart + 1);
+                                       _kln89->DrawText(s, 2, 2-s.size(), 2);
+                                       s = GPSitoa(_kln89->_approachFP->waypoints.size());
+                                       _kln89->DrawText(s, 2, 2-s.size(), 1);
+                                       if(!(_uLinePos == _fStart+1 && _kln89->_blink)) {
+                                               _kln89->DrawText(_kln89->_approachFP->waypoints[_fStart]->id, 2, 4, 2);
+                                               if(_uLinePos == _fStart+1) _kln89->Underline(2, 4, 2, 6);
+                                       }
+                                       if(!(_uLinePos == _maxULinePos-1 && _kln89->_blink)) {
+                                               _kln89->DrawText(_kln89->_approachFP->waypoints[_kln89->_approachFP->waypoints.size()-1]->id, 2, 4, 1);
+                                               if(_uLinePos == _maxULinePos-1) _kln89->Underline(2, 4, 1, 6);
+                                       }
+                                       if(!(_uLinePos > _kln89->_approachFP->waypoints.size() && _kln89->_blink)) {
+                                               _kln89->DrawText("ADD TO FPL 0?", 2, 2, 0);
+                                               if(_uLinePos > _kln89->_approachFP->waypoints.size()) {
+                                                       _kln89->Underline(2, 2, 0, 13);
+                                                       _kln89->DrawEnt();
+                                               }
+                                       }
+                               } else if(_replaceDialog) {
+                                       _kln89->DrawText(_iaps[_curIap]->_abbrev, 2, 1, 3);
+                                       _kln89->DrawText(_iaps[_curIap]->_rwyStr, 2, 7, 3);
+                                       _kln89->DrawText(_iaps[_curIap]->_id, 2, 12, 3);
+                                       _kln89->DrawText("Replace Existing", 2, 0, 2);
+                                       _kln89->DrawText("Approach", 2, 4, 1);
+                                       if(_uLinePos > 0 && !(_kln89->_blink)) {
+                                               _kln89->DrawText("APPROVE?", 2, 4, 0);
+                                               _kln89->Underline(2, 4, 0, 8);
+                                               _kln89->DrawEnt();
+                                       }
+                               } else {
+                                       _kln89->DrawText("IAP", 2, 11, 3);
+                                       int check = 0;
+                                       bool selApp = false;
+                                       if(_kln89->_mode == KLN89_MODE_CRSR && _uLinePos > 4) {
+                                               selApp = true;
+                                               if(!_kln89->_blink) _kln89->DrawEnt();
+                                       }
+                                       for(unsigned int i=0; i<_iaps.size(); ++i) {    // TODO - do this properly when > 3 IAPs
+                                               string s = GPSitoa(i+1);
+                                               _kln89->DrawText(s, 2, 2 - s.size(), 2-i);
+                                               if(!(selApp && _uLinePos == 5+i && _kln89->_blink)) {
+                                                       _kln89->DrawText(_iaps[i]->_abbrev, 2, 3, 2-i);
+                                                       _kln89->DrawText(_iaps[i]->_rwyStr, 2, 9, 2-i);
+                                               }
+                                               if(selApp && _uLinePos == 5+i && !_kln89->_blink) {
+                                                       _kln89->Underline(2, 3, 2-i, 9);
+                                               }
+                                               check++;
+                                               if(check > 2) break;
+                                       }
+                               }
+                       }
+               }
+       } else {
+               if(_kln89->_mode != KLN89_MODE_CRSR) _kln89->DrawText(_apt_id, 2, 1, 3);
+               if(_subPage == 0) {
+                       /*
+                       _kln89->DrawText("----.-", 2, 9, 3);
+                       _kln89->DrawText("--------------", 2, 0, 2);
+                       _kln89->DrawText("- -- --.--'", 2, 3, 1);
+                       _kln89->DrawText("---- --.--'", 2, 3, 0);
+                       _kln89->DrawSpecialChar(0, 2, 7, 1);
+                       _kln89->DrawSpecialChar(0, 2, 7, 0);
+                       */
+               }
+       }
+       
+       if(_kln89->_mode == KLN89_MODE_CRSR) {
+               if(!(_subPage == 7 && (_iafDialog || _addDialog || _replaceDialog))) {
+                       if(_uLinePos > 0 && _uLinePos < 5) {
+                               // TODO - blink as well
+                               _kln89->Underline(2, _uLinePos, 3, 1);
+                       }
+                       for(unsigned int i = 0; i < _apt_id.size(); ++i) {
+                               if(_uLinePos != (i + 1)) {
+                                       _kln89->DrawChar(_apt_id[i], 2, i + 1, 3);
+                               } else {
+                                       if(!_kln89->_blink) _kln89->DrawChar(_apt_id[i], 2, i + 1, 3);
+                               }
+                       }
+               }
+       }
+       
+       _id = _apt_id;
+
+       KLN89Page::Update(dt);
+}
+
+void KLN89AptPage::SetId(string s) {
+       _last_apt_id = _apt_id;
+       _save_apt_id = _apt_id;
+       _apt_id = s;
+       UpdateAirport(s);       // If we don't do this here we break things if s is the same as the current ID since the update wouldn't get called then.
+}
+
+// Update the cached airport details
+void KLN89AptPage::UpdateAirport(const string& id) {
+       // Frequencies
+       _aptFreqs.clear();
+       ATCData ad;
+       AptFreq aq;
+       //cout << "UpdateAirport called, id = " << id << '\n';
+       // TODO - the logic below only returns one service per type per airport - they can be on more than one freq though.
+       if(current_commlist->FindByCode(id, ad, ATIS)) {
+               //cout << "Found ATIS\n";
+               aq.service = "ATIS*";
+               aq.freq = ad.freq;
+               _aptFreqs.push_back(aq);
+       }
+       if(current_commlist->FindByCode(id, ad, GROUND)) {
+               aq.service = "GRND*";
+               aq.freq = ad.freq;
+               _aptFreqs.push_back(aq);
+       }
+       if(current_commlist->FindByCode(id, ad, TOWER)) {
+               aq.service = "TWR *";
+               aq.freq = ad.freq;
+               _aptFreqs.push_back(aq);
+       }
+       if(current_commlist->FindByCode(id, ad, APPROACH)) {
+               aq.service = "APR";
+               aq.freq = ad.freq;
+               _aptFreqs.push_back(aq);
+       }
+       _nFreqPages = (unsigned int)ceil((float(_aptFreqs.size())) / 3.0f);
+       
+       // Runways
+       _aptRwys.clear();
+       FGRunway r;
+       bool haveRwy = globals->get_runways()->search(id, &r);
+       while(haveRwy && r._id == id) {
+               // Insert the runway with longest at the start of the array
+               for(unsigned int i = 0; i <= _aptRwys.size(); ++i) {
+                       if(i == _aptRwys.size()) {
+                               _aptRwys.push_back(r);
+                               break;
+                       } else {
+                               if(r._length > _aptRwys[i]._length) {
+                                       _aptRwys.insert(_aptRwys.begin() + i, r);
+                                       break;
+                               }
+                       }
+               }
+               haveRwy = globals->get_runways()->next(&r);
+       }
+       _nRwyPages = (_aptRwys.size() + 1) / 2; // 2 runways per page.
+       if(_nFreqPages < 1) _nFreqPages = 1;
+       if(_nRwyPages < 1) _nRwyPages = 1;
+       
+       // Instrument approaches
+       // Only non-precision for now - TODO - handle precision approaches if necessary
+       _iaps.clear();
+       iap_map_iterator itr = _kln89->_np_iap.find(id);
+       if(itr != _kln89->_np_iap.end()) {
+               _iaps = itr->second;
+       }
+       if(_subPage == 7) _maxULinePos = 4 + _iaps.size();      // We shouldn't need to check the crsr for out-of-bounds here since we only update the airport details when the airport code is changed - ie. _uLinePos <= 4!
+}
+
+void KLN89AptPage::CrsrPressed() {
+       if(_kln89->_mode == KLN89_MODE_DISP) {
+               if(_subPage == 7) {
+                       // Pressing crsr jumps back to vanilla IAP page.
+                       _iafDialog = false;
+                       _addDialog = false;
+                       _replaceDialog = false;
+               }
+               return;
+       }
+       if(_kln89->_obsMode) {
+               _uLinePos = 0;
+       } else {
+               _uLinePos = 1;
+       }
+       if(_subPage == 0) {
+               _maxULinePos = 32;
+       } else if(_subPage == 7) {
+               // Don't *think* we need some of this since some of it we can only get to by pressing ENT, not CRSR.
+               if(_iafDialog) {
+                       _maxULinePos = _IAF.size();
+                       _uLinePos = 1;
+               } else if(_addDialog) {
+                       _maxULinePos = 1;
+                       _uLinePos = 1;
+               } else if(_replaceDialog) {
+                       _maxULinePos = 1;
+                       _uLinePos = 1;
+               } else {
+                       _maxULinePos = 4 + _iaps.size();
+                       if(_iaps.empty()) {
+                               _uLinePos = 1;
+                       } else {
+                               _uLinePos = 5;
+                       }
+               }
+       } else {
+               _maxULinePos = 5;
+       }
+}
+
+void KLN89AptPage::ClrPressed() {
+       if(_subPage == 1 && _uLinePos == 5) {
+               _to_flag = !_to_flag;
+       } else if(_subPage == 7) {
+               // Clear backs out IAP selection one step at a time
+               if(_iafDialog) {
+                       _iafDialog = false;
+                       _maxULinePos = 4 + _iaps.size();
+                       if(_iaps.empty()) {
+                               _uLinePos = 1;
+                       } else {
+                               _uLinePos = 5;
+                       }
+               } else if(_addDialog) {
+                       _addDialog = false;
+                       if(_IAF.size() > 1) {
+                               _iafDialog = true;
+                               _maxULinePos = 1;
+                               // Don't reset _curIaf since it is remembed.
+                               _uLinePos = 1 + _curIaf;        // TODO - make this robust to more than 3 IAF
+                       } else {
+                               _maxULinePos = 4 + _iaps.size();
+                               if(_iaps.empty()) {
+                                       _uLinePos = 1;
+                               } else {
+                                       _uLinePos = 5;
+                               }
+                       }                       
+               } else if(_replaceDialog) {
+                       _replaceDialog = false;
+                       _addDialog = true;
+                       _maxULinePos = 1;
+                       _uLinePos = 1;
+               }
+       }
+}
+
+void KLN89AptPage::EntPressed() {
+       //cout << "A\n"
+       if(_entInvert) {
+               _entInvert = false;
+               _last_apt_id = _apt_id;
+               _apt_id = _save_apt_id;
+       } else if(_subPage == 7 && _kln89->_mode == KLN89_MODE_CRSR && _uLinePos > 0) {
+               //cout << "B\n";
+               // We are selecting an approach
+               if(_iafDialog) {
+                       //cout << "C\n";
+                       if(_uLinePos > 0) {
+                               // Record the IAF that was picked
+                               if(_uLinePos == 3) {
+                                       _curIaf = _IAF.size() - 1;
+                               } else {
+                                       _curIaf = _uLinePos - 1 + _iafStart;
+                               }
+                               //cout << "_curIaf = " << _curIaf << '\n';
+                               // TODO - delete the waypoints inside _approachFP before clearing them!!!!!!!
+                               _kln89->_approachFP->waypoints.clear();
+                               GPSWaypoint* wp = new GPSWaypoint;
+                               *wp = *_IAF[_curIaf];   // Need to make copies here since we're going to alter ID and type sometimes
+                               string iafid = wp->id;
+                               //wp->id += 'i';
+                               _kln89->_approachFP->waypoints.push_back(wp);
+                               for(unsigned int i=0; i<_IAP.size(); ++i) {
+                                       if(_IAP[i]->id != iafid) {      // Don't duplicate waypoints that are part of the initial fix list and the approach procedure list.
+                        // FIXME - allow the same waypoint to be both the IAF and the FAF in some
+                        // approaches that have a procedure turn eg. KDLL
+                        // Also allow MAF to be the same as IAF!
+                                               wp = new GPSWaypoint;
+                                               *wp = *_IAP[i];
+                                               //cout << "Adding waypoint " << wp->id << ", type is " << wp->appType << '\n';
+                                               //if(wp->appType == GPS_FAF) wp->id += 'f';
+                                               //if(wp->appType == GPS_MAP) wp->id += 'm';
+                                               //cout << "New id = " << wp->id << '\n';
+                                               _kln89->_approachFP->waypoints.push_back(wp);
+                                       }
+                               }
+                               // Only add 1 missed approach procedure waypoint for now.  I think this might be standard always anyway.
+                               wp = new GPSWaypoint;
+                               *wp = *_MAP[0];
+                               //wp->id += 'h';
+                               _kln89->_approachFP->waypoints.push_back(wp);
+                               _iafDialog = false;
+                               _addDialog = true;
+                               _maxULinePos = _kln89->_approachFP->waypoints.size() + 1;
+                               _uLinePos = _maxULinePos;
+                       }
+               } else if(_addDialog) {
+                       if(_uLinePos == _maxULinePos) {
+                               _addDialog = false;
+                               if(_kln89->ApproachLoaded()) {
+                                       _replaceDialog = true;
+                                       _uLinePos = 1;
+                                       _maxULinePos = 1;
+                               } else {
+                    // Now load the approach into the active flightplan.
+                    // As far as I can tell, the rules are this:
+                    // If the airport of the approach is in the flightplan, insert it prior to this.  (Not sure what happens if airport has already been passed).
+                    // If the airport is not in the flightplan, append the approach to the flightplan, even if it is closer than the current active leg,
+                    // in which case reorientate to flightplan might put us on the approach, but unable to activate it.
+                    // However, it appears from the sim as if this can indeed happen if the user is not carefull.
+                    bool added = false;
+                    for(unsigned int i=0; i<_kln89->_activeFP->waypoints.size(); ++i) {
+                        if(_kln89->_activeFP->waypoints[i]->id == _apt_id) {
+                            _kln89->_activeFP->waypoints.insert(_kln89->_activeFP->waypoints.begin()+i, _kln89->_approachFP->waypoints.begin(), _kln89->_approachFP->waypoints.end());
+                            added = true;
+                            break;
+                        }
+                    }
+                    if(!added) {
+                        _kln89->_activeFP->waypoints.insert(_kln89->_activeFP->waypoints.end(), _kln89->_approachFP->waypoints.begin(), _kln89->_approachFP->waypoints.end());
+                    }
+                                       _kln89->_approachID = _apt_id;
+                                       _kln89->_approachAbbrev = _iaps[_curIap]->_abbrev;
+                                       _kln89->_approachRwyStr = _iaps[_curIap]->_rwyStr;
+                                       _kln89->_approachLoaded = true;
+                                       //_kln89->_messageStack.push_back("*Press ALT To Set Baro");
+                                       // Actually - this message is only sent when we go into appraoch-arm mode.
+                                       // TODO - check the flightplan for consistency
+                                       _kln89->OrientateToActiveFlightPlan();
+                                       _kln89->_mode = KLN89_MODE_DISP;
+                                       _kln89->_curPage = 7;
+                                       _kln89->_activePage = _kln89->_pages[7];        // Do we need to clean up here at all before jumping?
+                               }
+                       }
+               } else if(_replaceDialog) {
+                       // TODO - load the approach!
+               } else if(_uLinePos > 4) {
+                       _IAF.clear();
+                       _IAP.clear();
+                       _MAP.clear();
+                       _curIaf = 0;
+                       _IAF = ((FGNPIAP*)(_iaps[_uLinePos-5]))->_IAF;
+                       _IAP = ((FGNPIAP*)(_iaps[_uLinePos-5]))->_IAP;
+                       _MAP = ((FGNPIAP*)(_iaps[_uLinePos-5]))->_MAP;
+                       _curIap = _uLinePos - 5;        // TODO - handle the start of list ! no. 1, and the end of list not sequential!
+                       _uLinePos = 1;
+                       if(_IAF.size() > 1) {
+                               // More than 1 IAF - display the selection dialog
+                               _iafDialog = true;
+                               _maxULinePos = _IAF.size();
+                       } else {
+                               _addDialog = true;
+                               _maxULinePos = 1;
+                       }
+               }
+       }
+}
+
+void KLN89AptPage::Knob1Left1() {
+       if(_kln89->_mode == KLN89_MODE_CRSR && _subPage == 7 && _addDialog) {
+               if(_uLinePos == _maxULinePos) {
+                       _uLinePos--;
+                       if(_kln89->_approachFP->waypoints.size() > 1) _fStart = _kln89->_approachFP->waypoints.size() - 2;
+               } else if(_uLinePos == _maxULinePos - 1) {
+                       _uLinePos--;
+               } else if(_uLinePos > 0) {
+                       if(_fStart == 0) {
+                               _uLinePos--;
+                       } else {
+                               _uLinePos--;
+                               _fStart--;
+                       }
+               }
+       } else {
+               KLN89Page::Knob1Left1();
+       }
+}
+
+void KLN89AptPage::Knob1Right1() {
+       if(_kln89->_mode == KLN89_MODE_CRSR && _subPage == 7 && _addDialog) {
+               if(_uLinePos == _maxULinePos) {
+                       // no-op
+               } else if(_uLinePos == _maxULinePos - 1) {
+                       _uLinePos++;
+                       _fStart = 0;
+               } else if(_uLinePos > 0) {
+                       if(_fStart >= _kln89->_approachFP->waypoints.size() - 2) {
+                               _uLinePos++;
+                       } else {
+                               _uLinePos++;
+                               _fStart++;
+                       }
+               } else if(_uLinePos == 0) {
+                       _uLinePos++;
+                       _fStart = 0;
+               }
+       } else {
+               KLN89Page::Knob1Right1();
+       }
+}
+
+void KLN89AptPage::Knob2Left1() {
+       if(_kln89->_mode != KLN89_MODE_CRSR || _uLinePos == 0) {
+               if(_uLinePos == 0 && _kln89->_mode == KLN89_MODE_CRSR && _kln89->_obsMode) {
+                       KLN89Page::Knob2Left1();
+               } else if(_subPage == 5) {
+                       _subPage = 4;
+                       _curFreqPage = _nFreqPages - 1;
+               } else if(_subPage == 4) {
+                       // Freqency pages
+                       if(_curFreqPage == 0) {
+                               _subPage = 3;
+                               _curRwyPage = _nRwyPages - 1;
+                       } else {
+                               _curFreqPage--;
+                       }
+               } else if(_subPage == 3) {
+                       if(_curRwyPage == 0) {
+                               KLN89Page::Knob2Left1();
+                       } else {
+                               _curRwyPage--;
+                       }
+               } else {
+                       KLN89Page::Knob2Left1();
+               }
+       } else {
+               if(_uLinePos < 5 && !(_subPage == 7 && (_iafDialog || _addDialog || _replaceDialog))) {
+                       // Same logic for all pages - set the ID
+                       _apt_id = _apt_id.substr(0, _uLinePos);
+                       // ASSERT(_uLinePos > 0);
+                       if(_uLinePos == (_apt_id.size() + 1)) {
+                               _apt_id += '9';
+                       } else {
+                               _apt_id[_uLinePos - 1] = _kln89->DecChar(_apt_id[_uLinePos - 1], (_uLinePos == 1 ? false : true));
+                       }
+               } else {
+                       if(_subPage == 0) {
+                               // TODO - set by name
+                       } else {
+                               // NO-OP - to/fr is cycled by clr button
+                       }
+               }
+       }
+}
+
+void KLN89AptPage::Knob2Right1() {
+       if(_kln89->_mode != KLN89_MODE_CRSR || _uLinePos == 0) {
+               if(_uLinePos == 0 && _kln89->_mode == KLN89_MODE_CRSR && _kln89->_obsMode) {
+                       KLN89Page::Knob2Right1();
+               } else if(_subPage == 2) {
+                       _subPage = 3;
+                       _curRwyPage = 0;
+               } else if(_subPage == 3) {
+                       if(_curRwyPage == _nRwyPages - 1) {
+                               _subPage = 4;
+                               _curFreqPage = 0;
+                       } else {
+                               _curRwyPage++;
+                       }
+               } else if(_subPage == 4) {
+                       if(_curFreqPage == _nFreqPages - 1) {
+                               _subPage = 5;
+                       } else {
+                               _curFreqPage++;
+                       }
+               } else {
+                       KLN89Page::Knob2Right1();
+               }
+       } else {
+               if(_uLinePos < 5 && !(_subPage == 7 && (_iafDialog || _addDialog || _replaceDialog))) {
+                       // Same logic for all pages - set the ID
+                       _apt_id = _apt_id.substr(0, _uLinePos);
+                       // ASSERT(_uLinePos > 0);
+                       if(_uLinePos == (_apt_id.size() + 1)) {
+                               _apt_id += 'A';
+                       } else {
+                               _apt_id[_uLinePos - 1] = _kln89->IncChar(_apt_id[_uLinePos - 1], (_uLinePos == 1 ? false : true));
+                       }
+               } else {
+                       if(_subPage == 0) {
+                               // TODO - set by name
+                       } else {
+                               // NO-OP - to/fr is cycled by clr button
+                       }
+               }
+       }
+}
diff --git a/src/Instrumentation/KLN89/kln89_page_apt.hxx b/src/Instrumentation/KLN89/kln89_page_apt.hxx
new file mode 100644 (file)
index 0000000..d5814fd
--- /dev/null
@@ -0,0 +1,95 @@
+// kln89_page_*.[ch]xx - this file is one of the "pages" that
+//                       are used in the KLN89 GPS unit simulation. 
+//
+// Written by David Luff, started 2005.
+//
+// Copyright (C) 2005 - David C Luff - david.luff@nottingham.ac.uk
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+// $Id$
+
+#ifndef _KLN89_PAGE_APT
+#define _KLN89_PAGE_APT
+
+#include "kln89.hxx"
+
+#include <Airports/runways.hxx>
+
+struct AptFreq {
+       string service;
+       unsigned short int freq;
+};
+
+class KLN89AptPage : public KLN89Page {
+
+public:
+       KLN89AptPage(KLN89* parent);
+       ~KLN89AptPage();
+       
+       void Update(double dt);
+
+       void CrsrPressed();
+       void ClrPressed();
+       void EntPressed();
+       void Knob1Left1();
+       void Knob1Right1();
+       void Knob2Left1();
+       void Knob2Right1();
+       
+       void SetId(string s);
+       
+private:
+       // Update the cached airport details
+       void UpdateAirport(const string& id);
+
+       string _apt_id;
+       string _last_apt_id;
+       string _save_apt_id;
+       const FGAirport* ap;
+       
+       vector<FGRunway> _aptRwys;
+       vector<AptFreq> _aptFreqs;
+       
+       iap_list_type _iaps;
+       unsigned int _curIap;   // The index into _iaps of the IAP we are currently selecting
+       vector<GPSWaypoint*> _IAF;      // The initial approach fix(es)
+       vector<GPSWaypoint*> _IAP;      // The compulsory waypoints of the approach procedure (may duplicate one of the above).
+                                                               // _IAP includes the FAF and MAF.
+       vector<GPSWaypoint*> _MAP;      // The missed approach procedure (doesn't include the MAF).
+       unsigned int _curIaf;   // The index into _IAF of the IAF we are currently selecting, and then remembered as the one we selected
+       
+       // Position in rwy pages
+       unsigned int _curRwyPage;
+       unsigned int _nRwyPages;
+       
+       // Position in freq pages
+       unsigned int _curFreqPage;
+       unsigned int _nFreqPages;
+       
+       // Position in IAP list (0-based number of first IAP displayed)
+       unsigned int _iapStart;
+       // ditto for IAF list (can't test this since can't find an approach with > 3 IAF at the moment!)
+       unsigned int _iafStart;
+       // ditto for list of approach fixes when asking load confirmation
+       unsigned int _fStart;
+       
+       // Various IAP related dialog states that we might need to remember
+       bool _iafDialog;
+       bool _addDialog;
+       bool _replaceDialog;
+};
+
+#endif  // _KLN89_PAGE_APT
diff --git a/src/Instrumentation/KLN89/kln89_page_cal.cxx b/src/Instrumentation/KLN89/kln89_page_cal.cxx
new file mode 100644 (file)
index 0000000..42b9f3e
--- /dev/null
@@ -0,0 +1,110 @@
+// kln89_page_*.[ch]xx - this file is one of the "pages" that
+//                       are used in the KLN89 GPS unit simulation. 
+//
+// Written by David Luff, started 2005.
+//
+// Copyright (C) 2005 - David C Luff - david.luff@nottingham.ac.uk
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+// $Id$
+
+#include "kln89_page_cal.hxx"
+
+KLN89CalPage::KLN89CalPage(KLN89* parent)
+: KLN89Page(parent) {
+       _nSubPages = 8;
+       _subPage = 0;
+       _name = "CAL";
+       _nFp0 = 0;
+       _ground_speed_ms = 110 * 0.514444444444;
+}
+
+KLN89CalPage::~KLN89CalPage() {
+}
+
+void KLN89CalPage::Update(double dt) {
+       if(_subPage == 0) {
+               if(1) { // TODO - fix this hardwiring!
+                       // Flightplan calc
+                       _kln89->DrawText(">Fpl:", 2, 0, 3);
+                       _kln89->DrawText("0", 2, 6, 3);// TODO - fix this hardwiring!
+                       GPSFlightPlan* fp = _kln89->_flightPlans[_nFp0];
+                       if(fp) {
+                               unsigned int n = fp->waypoints.size();
+                               if(n < 2) {
+                                       // TODO - check that this is what really happens
+                                       _kln89->DrawText("----", 2, 9, 3);
+                                       _kln89->DrawText("----", 2, 9, 2);
+                               } else {
+                                       _kln89->DrawText(fp->waypoints[0]->id, 2, 9, 3);
+                                       _kln89->DrawText(fp->waypoints[n-1]->id, 2, 9, 2);
+                                       double cum_tot_m = 0.0;
+                                       for(unsigned int i = 1; i < fp->waypoints.size(); ++i) {
+                                               cum_tot_m += _kln89->GetGreatCircleDistance(fp->waypoints[i-1]->lat, fp->waypoints[i-1]->lon,
+                                                                                   fp->waypoints[i]->lat, fp->waypoints[i]->lon);
+                                       }
+                                       double ete = (cum_tot_m * SG_NM_TO_METER) / _ground_speed_ms;
+                                       _kln89->DrawDist(cum_tot_m, 2, 5, 1);
+                                       _kln89->DrawSpeed(_ground_speed_ms / 0.5144444444, 2, 5, 0);
+                                       _kln89->DrawTime(ete, 2, 14, 0);
+                               }
+                       } else {
+                               _kln89->DrawText("----", 2, 9, 3);
+                               _kln89->DrawText("----", 2, 9, 2);
+                       }
+               } else {
+                       _kln89->DrawText(">Wpt:", 2, 0, 3);
+               }
+               _kln89->DrawText("To", 2, 6, 2);
+               _kln89->DrawText("ESA ----'", 2, 7, 1); // TODO - implement an ESA calc
+               _kln89->DrawText("ETE", 2, 7, 0);
+       } else if(_subPage == 1) {
+               _kln89->DrawText(">Fpl: 0", 2, 0, 3);   // TODO - fix this hardwiring!
+               _kln89->DrawText("FF:", 2, 0, 2);
+               _kln89->DrawText("Res:", 2, 7, 1);
+               _kln89->DrawText("Fuel Req", 2, 0, 0);
+       } else if(_subPage == 2) {
+               _kln89->DrawText("Time:", 2, 0, 3);
+               _kln89->DrawText("Alarm at:", 2, 0, 2);
+               _kln89->DrawText("in:", 2, 6, 1);
+               _kln89->DrawText("Elapsed", 2, 0, 0);
+       } else if(_subPage == 3) {
+               _kln89->DrawText("PRESSURE ALT", 2, 1, 3);
+               _kln89->DrawText("Ind:", 2, 0, 2);
+               _kln89->DrawText("Baro:", 2, 0, 1);
+               _kln89->DrawText("Prs", 2, 0, 0);
+       } else if(_subPage == 4) {
+               _kln89->DrawText("DENSITY ALT", 2, 1, 3);
+               _kln89->DrawText("Prs:", 2, 0, 2);
+               _kln89->DrawText("Temp:", 2, 0, 1);
+               _kln89->DrawText("Den", 2, 0, 0);
+       } else if(_subPage == 5) {
+               _kln89->DrawText("CAS:", 2, 0, 3);
+               _kln89->DrawText("Prs:", 2, 0, 2);
+               _kln89->DrawText("Temp:", 2, 0, 1);
+               _kln89->DrawText("TAS", 2, 0, 0);
+       } else if(_subPage == 6) {
+               _kln89->DrawText("TAS:", 2, 0, 3);
+               _kln89->DrawText("Hdg:", 2, 0, 2);
+               _kln89->DrawText("Headwind:", 2, 0, 1);
+               _kln89->DrawText("True", 2, 4, 0);
+       } else {
+               _kln89->DrawText("SUNRISE", 2, 0, 1);
+               _kln89->DrawText("SUNSET", 2, 0, 0);
+       }
+
+       KLN89Page::Update(dt);
+}
diff --git a/src/Instrumentation/KLN89/kln89_page_cal.hxx b/src/Instrumentation/KLN89/kln89_page_cal.hxx
new file mode 100644 (file)
index 0000000..c8e832f
--- /dev/null
@@ -0,0 +1,42 @@
+// kln89_page_*.[ch]xx - this file is one of the "pages" that
+//                       are used in the KLN89 GPS unit simulation. 
+//
+// Written by David Luff, started 2005.
+//
+// Copyright (C) 2005 - David C Luff - david.luff@nottingham.ac.uk
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+// $Id$
+
+#ifndef _KLN89_PAGE_CAL
+#define _KLN89_PAGE_CAL
+
+#include "kln89.hxx"
+
+class KLN89CalPage : public KLN89Page {
+
+public:
+       KLN89CalPage(KLN89* parent);
+       ~KLN89CalPage();
+       
+       void Update(double dt);
+       
+private:
+       unsigned int _nFp0;     // flightplan no. displayed on page 1 (_subPage == 0).
+       double _ground_speed_ms;        // Assumed ground speed for ete calc on page 1 (user can alter this).
+};
+
+#endif // _KLN89_PAGE_CAL
diff --git a/src/Instrumentation/KLN89/kln89_page_dir.cxx b/src/Instrumentation/KLN89/kln89_page_dir.cxx
new file mode 100644 (file)
index 0000000..63c75ab
--- /dev/null
@@ -0,0 +1,103 @@
+// kln89_page_*.[ch]xx - this file is one of the "pages" that
+//                       are used in the KLN89 GPS unit simulation. 
+//
+// Written by David Luff, started 2005.
+//
+// Copyright (C) 2005 - David C Luff - david.luff@nottingham.ac.uk
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+// $Id$
+
+#include "kln89_page_dir.hxx"
+
+KLN89DirPage::KLN89DirPage(KLN89* parent)
+: KLN89Page(parent) {
+       _nSubPages = 1;
+       _subPage = 0;
+       _name = "DIR";
+       _DToWpDispMode = 2;
+}
+
+KLN89DirPage::~KLN89DirPage() {
+}
+
+void KLN89DirPage::Update(double dt) {
+       // TODO - this can apparently be "ACTIVATE:" under some circumstances
+       _kln89->DrawText("DIRECT TO:", 2, 2, 3);
+       
+       if(_kln89->_mode == KLN89_MODE_CRSR) {
+               if(_DToWpDispMode == 0) {
+                       string s = _id;
+                       while(s.size() < 5) s += ' ';
+                       if(!_kln89->_blink) {
+                               _kln89->DrawText(s, 2, 4, 1, false, 99);
+                               _kln89->DrawEnt(1, 0, 1);
+                       }
+               } else if(_DToWpDispMode == 1) {
+                       if(!_kln89->_blink) {
+                               // TODO
+                               _kln89->DrawEnt(1, 0, 1);
+                       }
+                       _kln89->Underline(2, 4, 1, 5);
+               } else {
+                       if(!_kln89->_blink) _kln89->DrawText("_____", 2, 4, 1);
+                       _kln89->Underline(2, 4, 1, 5);
+               }
+       } else {
+               _kln89->DrawText("_____", 2, 4, 1);
+       }
+       
+       KLN89Page::Update(dt);
+}
+
+void KLN89DirPage::SetId(string s) {
+       if(s.size()) {
+               _id = s;
+               // TODO - fill in lat, lon, type
+               // or just pass in waypoints (probably better!)
+               _DToWpDispMode = 0;
+               // TODO - this (above) should probably be dependent on whether s is a *valid* waypoint!
+       } else {
+               _DToWpDispMode = 2;
+       }
+       _saveMasterMode = _kln89->_mode;
+       _uLinePos = 1;  // Needed to stop Leg flashing
+}
+
+void KLN89DirPage::ClrPressed() {
+       if(_kln89->_mode == KLN89_MODE_CRSR) {
+               if(_DToWpDispMode <= 1) {
+                       _DToWpDispMode = 2;
+                       _id.clear();
+               } else {
+                       // Restore the original master mode
+                       _kln89->_mode = _saveMasterMode;
+                       // Stop displaying dir page
+                       _kln89->_activePage = _kln89->_pages[_kln89->_curPage];
+               }
+       } else {
+               // TODO
+       }
+}
+
+void KLN89DirPage::EntPressed() {
+       //cout << "DTO ENT Pressed()\n";
+       if(_id.empty()) {
+               _kln89->DtoCancel();
+       } else {
+               _kln89->DtoInitiate(_id);
+       }
+}
\ No newline at end of file
diff --git a/src/Instrumentation/KLN89/kln89_page_dir.hxx b/src/Instrumentation/KLN89/kln89_page_dir.hxx
new file mode 100644 (file)
index 0000000..ec31df4
--- /dev/null
@@ -0,0 +1,54 @@
+// kln89_page_*.[ch]xx - this file is one of the "pages" that
+//                       are used in the KLN89 GPS unit simulation. 
+//
+// Written by David Luff, started 2005.
+//
+// Copyright (C) 2005 - David C Luff - david.luff@nottingham.ac.uk
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+// $Id$
+
+#ifndef _KLN89_PAGE_DIR
+#define _KLN89_PAGE_DIR
+
+#include "kln89.hxx"
+
+class KLN89DirPage : public KLN89Page {
+
+public:
+       KLN89DirPage(KLN89* parent);
+       ~KLN89DirPage();
+       
+       void Update(double dt);
+       
+       void SetId(string s);
+       
+       void ClrPressed();
+       void EntPressed();
+       
+private:
+       // Waypoint display mode.
+       // There are a number of ways that the waypoint can be displayed in the DIR page:
+       // 0 => Whole candidate waypoint displayed, entirely inverted.  This is normally how the page is initially displayed, unless a candidate waypoint cannot be determined.
+       // 1 => Waypoint being entered, with a corresponding cursor position, and only the cursor position inverted.
+       // 2 => Blanks.  These can be displayed flashing when the cursor is active (eg. when CLR is pressed) and are always displayed if the cursor is turned off.
+       int _DToWpDispMode;
+       
+       // We need to save the mode when DTO gets pressed, since potentially this class handles page exit via. the CLR event handler
+       KLN89Mode _saveMasterMode;
+};
+
+#endif // _KLN89_PAGE_DIR
diff --git a/src/Instrumentation/KLN89/kln89_page_fpl.cxx b/src/Instrumentation/KLN89/kln89_page_fpl.cxx
new file mode 100644 (file)
index 0000000..99dfe98
--- /dev/null
@@ -0,0 +1,1029 @@
+// kln89_page_*.[ch]xx - this file is one of the "pages" that
+//                       are used in the KLN89 GPS unit simulation. 
+//
+// Written by David Luff, started 2005.
+//
+// Copyright (C) 2005 - David C Luff - david.luff@nottingham.ac.uk
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+// $Id$
+
+#include "kln89_page_fpl.hxx"
+#include <algorithm>
+
+using namespace std;
+
+KLN89FplPage::KLN89FplPage(KLN89* parent)
+: KLN89Page(parent) {
+       _nSubPages = 26;
+       _subPage = 0;
+       _name = "FPL";
+       _fpMode = 0;
+       _actFpMode = 0;
+       _wLinePos = 0;
+       _bEntWp = false;
+       _bEntExp = false;
+       _entWp = NULL;
+       _fplPos = 0;
+       _resetFplPos0 = true;
+       _delFP = false;
+       _delWp = false;
+       _delAppr = false;
+       _changeAppr = false;
+       _fp0SelWpId = "";
+}
+
+KLN89FplPage::~KLN89FplPage() {
+}
+
+void KLN89FplPage::Update(double dt) {
+       Calc();
+       
+       // NOTE - we need to draw the active leg arrow outside of this block to avoid the _delFP check.
+       // TODO - we really ought to merge the page 0 and other pages drawing code with a couple of lines of extra logic.
+       if(_subPage == 0 && !_delFP) {  // Note that in the _delFP case, the active flightplan gets a header, and hence the same geometry as the other fps, so we draw it there.
+               // active FlightPlan
+               // NOTE THAT FOR THE ACTIVE FLIGHT PLAN, TOP POSITION IS STILL 4 in the underline position scheme, to make 
+               // copy and paste easier!!!!
+
+               // ---------------------------------- Copy the active FlightPlan and insert approach header and fence if required ---------------
+               // For synatical convienience
+               //vector<GPSWaypoint*> waylist = _kln89->_flightPlans[_subPage]->waypoints;
+               // Copy every waypoint for now.
+        // This is inefficient, but allows us to insert dummy waypoints to represent the header and fence 
+               // in our *local copy* of the flightplan, if an approach is loaded.  There must be a better way to do this!!!!
+               //cout << "AA" << endl;
+               vector<GPSWaypoint> waylist;
+               for(unsigned int i=0; i<_kln89->_flightPlans[_subPage]->waypoints.size(); ++i) {
+                       waylist.push_back(*_kln89->_flightPlans[_subPage]->waypoints[i]);
+               }
+               //cout << "BB" << endl;
+               _hdrPos = -1;
+               _fencePos = -1;
+               if(_kln89->_approachLoaded) {
+                       GPSWaypoint wp;
+                       wp.id = "HHHH";
+                       wp.type = GPS_WP_VIRT;
+                       wp.appType = GPS_HDR;
+                       //cout << "CC" << endl;
+                       for(unsigned int i=0; i<waylist.size(); ++i) {
+                               // Insert the hdr immediately before the IAF
+                               if(waylist[i].appType == GPS_IAF) {
+                                       waylist.insert(waylist.begin()+i, wp);
+                                       // Need to insert empty string into the params to keep them in sync
+                                       _params.insert(_params.begin()+i-1, "");
+                                       _hdrPos = i;
+                                       break;
+                               }
+                       }
+                       //cout << "DD" << endl;
+                       wp.id = "FFFF";
+                       wp.type = GPS_WP_VIRT;
+                       wp.appType = GPS_FENCE;
+                       for(unsigned int i=0; i<waylist.size(); ++i) {
+                               // Insert the fence between the MAF and the MAP
+                               if(waylist[i].appType == GPS_MAHP) {
+                                       waylist.insert(waylist.begin()+i, wp);
+                                       // Need to insert empty string into the params to keep them in sync
+                                       _params.insert(_params.begin()+i-1, "");
+                                       _fencePos = i;
+                                       break;
+                               }
+                       }
+               }
+               /*
+               // Now make up a vector of waypoint numbers, since they aren't aligned with list position anymore
+               int num = 0;
+               vector<int> numlist;
+               numlist.clear();
+               for(unsigned int i=0; i<waylist.size(); ++i) {
+                       if(waylist[i].appType != GPS_HDR && waylist[i].appType != GPS_FENCE) {
+                               numlist.push_back(num);
+                               num++;
+                       } else {
+                               numlist.push_back(-1);
+                       }
+               }
+               */
+               int hfcount = 0;
+               for(unsigned int i=0; i<waylist.size(); ++i) {
+                       //cout << i + 1 - hfcount << ":  ID= " << waylist[i].id;
+                       if(waylist[i].appType == GPS_HDR) {
+                               hfcount++;
+                               //cout << " HDR!";
+                       }
+                       if(waylist[i].appType == GPS_FENCE) {
+                               hfcount++;
+                               //cout << " FENCE!";
+                       }
+                       //cout << '\n';
+               }
+               //----------------------------------------- end active FP copy ------------------------------------------------
+               
+               // Recalculate which waypoint is displayed at the top of the list if required (generally if this page has lost focus).
+               int idx = _parent->GetActiveWaypointIndex();
+               if(_resetFplPos0) {
+                       if(waylist.size() <= 1) {
+                               _fplPos = 0;
+                       } else if(waylist.size() <= 4) {
+                               _fplPos = 1;
+                       } else {
+                               // Make the active waypoint the second WP displayed
+                               _fplPos = idx;
+                               if(_fplPos != 0) {
+                                       _fplPos--;
+                               }
+                       }
+                       //cout << "HeaderPos = " << _hdrPos << ", fencePos = " << _fencePos << ", _fplPos = " << _fplPos << ", active waypoint index = " << _parent->GetActiveWaypointIndex() << '\n';
+                       if(_hdrPos >= 0 && idx >= _hdrPos) {
+                               _fplPos++;
+                               if(_fencePos >= 0 && (idx + 1) >= _fencePos) {
+                                       _fplPos++;
+                               }
+                       }
+                       _resetFplPos0 = false;
+               }
+               
+               // Increment the active waypoint position if required due hdr and fence here not above so it gets called every frame
+               if(_hdrPos >= 0 && idx >= _hdrPos) {
+                       idx++;
+                       if(_fencePos >= 0 && idx >= _fencePos) {
+                               idx++;
+                       }
+               }
+               
+               // Draw the leg arrow etc
+               int diff = idx - (int)_fplPos;
+               int drawPos = -1;
+               if(idx < 0) {
+                       // No active waypoint
+               } else if(diff < 0) {
+                       // Off screen to the top
+               } else if(diff > 2) {
+                       // TODO !
+               } else {
+                       drawPos = diff;
+               }
+               // Only the head is blinked during waypoint alerting
+               if(!(_kln89->_waypointAlert && _kln89->_blink)) {
+                       _kln89->DrawSpecialChar(4, 2, 0, 3-drawPos);
+               }
+               // If the active waypoint is immediately after an approach header then we need to do an extra-long tail leg
+               if(_hdrPos >= 0 && idx == _hdrPos + 1) {
+                       if(drawPos > 0 && !_kln89->_dto) _kln89->DrawLongLegTail(3-drawPos);
+               } else {
+                       if(drawPos > 0 && !_kln89->_dto) _kln89->DrawLegTail(3-drawPos);
+               }
+               
+               //cout << "Top pos is " << _fplPos0 << ' ';
+               
+               if(_kln89->_mode == KLN89_MODE_CRSR) {
+                       if(_uLinePos == 3) {
+                               _kln89->Underline(2, 13, 3, 3);
+                       } else if(_uLinePos >= 4) {
+                               if(_bEntWp) {
+                                       if(_wLinePos == 0) {
+                                               _kln89->Underline(2, 5, 3 - (_uLinePos - 4), 4);
+                                       } else if(_wLinePos == 4) {
+                                               _kln89->Underline(2, 4, 3 - (_uLinePos - 4), 4);
+                                       } else {
+                                               _kln89->Underline(2, 4, 3 - (_uLinePos - 4), _wLinePos);
+                                               _kln89->Underline(2, 5 + _wLinePos, 3 - (_uLinePos - 4), 4 - _wLinePos);
+                                       }
+                                       if(!_kln89->_blink) {
+                                               //_kln89->DrawText(_entWp->id, 2, 4, 2 - (_uLinePos - 4), false, _wLinePos);
+                                               _kln89->DrawEnt();
+                                       }
+                               } else {
+                                       _kln89->Underline(2, 4, 3 - (_uLinePos - 4), 5);
+                               }
+                       }
+               }
+               // ----------------------------------
+               //cout << "A1" << endl;
+                       
+               // Sanity check the top position - remember that we can have an extra blank one at the bottom even if CRSR is off if crsr is switched on then off
+               if((int)_fplPos > ((int)waylist.size()) - 3) _fplPos = (((int)waylist.size()) - 3 < 0 ? 0 : waylist.size() - 3);
+               unsigned int last_pos;
+               if(waylist.empty()) {
+                       last_pos = 0;
+               } else {
+                       last_pos = ((int)_fplPos == ((int)waylist.size()) - 3 ? waylist.size() : waylist.size() - 1);
+               }
+               //cout << "Initialising last_pos, last_pos = " << last_pos << '\n';
+               //cout << "B1" << endl;
+               if(waylist.size() < 4) last_pos = waylist.size();
+               
+               // Don't draw the cyclic field header if the top waypoint is the approach header
+               // Not sure if this also applies to the fence - don't think so but TODO - check!
+               if(waylist[_fplPos].appType != GPS_HDR) {
+                       _kln89->DrawChar('>', 2, 12, 3);
+                       if(!(_kln89->_mode == KLN89_MODE_CRSR && _uLinePos == 3 && _kln89->_blink)) {
+                               DrawFpMode(3);
+                       }
+               }
+               
+               // ACTIVE
+               for(unsigned int i=0; i<4; ++i) {
+                       //cout << "F1... " << i << endl;
+                       // Don't draw the waypoint number for the header or fence
+                       //cout << "_fplPos0 = " << _fplPos0 << ", waylist size is " << waylist.size() << '\n';
+                       //cout << "F1, i = " << i << ", way id = " << waylist[_fplPos0+i].id << '\n'; 
+                       if((waylist[_fplPos+i].appType == GPS_HDR || waylist[_fplPos+i].appType == GPS_FENCE) 
+                           && i != 3) {        // By definition, the header and fence lines can't be displayed on the last line hence the unconditional !i==3 is safe.
+                               // no-op
+                               //cout << "NOOP\n";
+                       } else {
+                               int n = (i < 3 ? _fplPos + i + 1 : last_pos + 1);
+                               if(_kln89->_approachLoaded) {
+                                       if(n > _hdrPos) --n;
+                                       if(n > _fencePos) --n;
+                               }
+                               string s = GPSitoa(n);
+                               // Don't draw the colon for waypoints that are part of the published approach
+                               if(waylist[_fplPos+i].appType == GPS_APP_NONE) {
+                                       s += ':';
+                               }
+                               if(!(_delWp && _uLinePos == i+4)) _kln89->DrawText(s, 2, 4 - (s[s.size()-1] == ':' ? s.size() : s.size()+1), 3 - i);
+                       }
+                       //cout << "F1 done!\n";
+                       bool drawID = true;
+                       if(_delWp && _uLinePos == i+4) {
+                               if(!_kln89->_blink) {
+                                       _kln89->DrawText("Del", 2, 0, 3-i);
+                                       _kln89->DrawChar('?', 2, 10, 3-i);
+                                       _kln89->Underline(2, 0, 3-i, 11);
+                                       _kln89->DrawEnt();
+                               }
+                       } else if(_kln89->_mode == KLN89_MODE_CRSR && _bEntWp && _uLinePos == i+4) {
+                               if(!_kln89->_blink) {
+                                       if(_wLinePos >= _entWp->id.size()) {
+                                               _kln89->DrawText(_entWp->id, 2, 4, 3-i);
+                                               _kln89->DrawChar(' ', 2, 4+_wLinePos, 3-i, false, true);
+                                       } else {
+                                               _kln89->DrawText(_entWp->id.substr(0, _wLinePos), 2, 4, 3-i);
+                                               _kln89->DrawChar(_entWp->id[_wLinePos], 2, 4+_wLinePos, 3-i, false, true);
+                                               _kln89->DrawText(_entWp->id.substr(_wLinePos+1, _entWp->id.size()-_wLinePos-1), 2, 5+_wLinePos, 3-i);
+                                       }
+                               }
+                               drawID = false;
+                       }
+                       //cout << "F2" << endl;
+                       if(drawID) {
+                               //cout << "F2a" << endl;
+                               if(i == 3 || _fplPos + i == waylist.size()) {
+                                       //cout << "F2a1" << endl;
+                                       //cout << "_uLinePos = " << _uLinePos << ", i = " << i << ", waylist.size() = " << waylist.size() << endl;
+                                       if(!(_kln89->_mode == KLN89_MODE_CRSR && _uLinePos == (i + 4) && _kln89->_blink)) {
+                                               //cout << "Drawing underline..." << endl;
+                                               _kln89->DrawText(last_pos < waylist.size() ? waylist[last_pos].GetAprId() : "_____", 2, 4, 3-i);
+                                               //cout << "2" << endl;
+                                       }
+                                       //cout << "3" << endl;
+                                       //cout << "last_pos = " << last_pos << endl;
+                                       if(last_pos > 0 && last_pos < waylist.size() && i > 0) {
+                                               //cout << "4" << endl; 
+                                               // Draw the param
+                                               if(_actFpMode == 0) {
+                                                       string s = _params[last_pos - 1];
+                                                       _kln89->DrawText(s, 2, 16-s.size(), 3-i);
+                                               } else if(_actFpMode == 3) {
+                                                       string s = _params[last_pos - 1];
+                                                       _kln89->DrawText(s, 2, 15-s.size(), 3-i);
+                                                       _kln89->DrawSpecialChar(0, 2, 15, 3-i);
+                                               }
+                                               //cout << "5" << endl;
+                                       }
+                                       //cout << "6" << endl;
+                                       break;
+                               } else {
+                                       //cout << "F2a2" << endl;
+                                       if(!(_kln89->_mode == KLN89_MODE_CRSR && _uLinePos == (i + 4) && _kln89->_blink)) {
+                                               if(waylist[_fplPos+i].appType == GPS_HDR) {
+                                                       if(_delAppr) {
+                                                               _kln89->DrawText("DELETE APPR?", 2, 1, 3-i);
+                                                       } else if(_changeAppr) {
+                                                               _kln89->DrawText("CHANGE APPR?", 2, 1, 3-i);
+                                                       } else {
+                                                               _kln89->DrawText(_kln89->_approachAbbrev, 2, 1, 3-i);
+                                                               _kln89->DrawText(_kln89->_approachRwyStr, 2, 7, 3-i);
+                                                               _kln89->DrawText(_kln89->_approachID, 2, 12, 3-i);
+                                                       }
+                                               } else if(waylist[_fplPos+i].appType == GPS_FENCE) {
+                                                       _kln89->DrawText("*NO WPT SEQ", 2, 0, 3-i);
+                                               } else {
+                                                       _kln89->DrawText(waylist[_fplPos+i].GetAprId(), 2, 4, 3-i);
+                                               }
+                                       }
+                               }
+                               //cout << "F2b" << endl;
+                               if(i > 0) {
+                                       // Draw the param
+                                       //cout << "i > 0 param draw...\n";
+                                       if(_actFpMode == 0) {
+                                               string s = _params[_fplPos + i - 1];
+                                               _kln89->DrawText(s, 2, 16-s.size(), 3-i);
+                                       } else if(_actFpMode == 3) {
+                                               string s = _params[_fplPos + i - 1];
+                                               _kln89->DrawText(s, 2, 15-s.size(), 3-i);
+                                               _kln89->DrawSpecialChar(0, 2, 15, 3-i);
+                                       }
+                               }
+                               //cout << "F2c" << endl;
+                       }
+                       //cout << "F3" << endl;
+               }
+               //cout << "GGGGGG" << endl;
+       } else {  // Not active flightplan
+               //cout << "Top pos is " << _fplPos << ' ';
+               // For synatical convienience
+               //int nWp = (_subPage == 0 && !_delFP ? 4 : 3); // number of waypoints to display
+               vector<GPSWaypoint*> waylist = _kln89->_flightPlans[_subPage]->waypoints;
+               if(waylist.empty()) {
+                       if(!(_kln89->_mode == KLN89_MODE_CRSR && _uLinePos == 1 && _kln89->_blink)) {
+                               _kln89->DrawText(_delFP ? "Delete FPL?" : "Copy FPL 0?", 2, 0, 3);
+                       }
+               } else {
+                       if(!(_kln89->_mode == KLN89_MODE_CRSR && (_uLinePos == 1 || _uLinePos == 2) && _kln89->_blink)) {
+                               _kln89->DrawText(_delFP ? "Delete FPL?" : "Use?", 2, 0, 3);
+                       }
+                       if(!(_kln89->_mode == KLN89_MODE_CRSR && _uLinePos == 2 && _kln89->_blink)) {
+                               if(!_delFP) _kln89->DrawText("Inverted?", 2, 5, 3);
+                       }
+               }
+               
+               // ----------------------------------
+               if(_kln89->_mode == KLN89_MODE_CRSR) {
+                       if(_uLinePos == 1) {
+                               if(!_kln89->_blink) {
+                                       _kln89->Underline(2, 0, 3, (waylist.empty() || _delFP ? 11 : 4));       // This underline is blinked
+                                       _kln89->DrawEnt();
+                               }
+                       } else if(_uLinePos == 2) {
+                               // assert(!waylist.empty());
+                               if(!_kln89->_blink) {
+                                       _kln89->Underline(2, 0, 3, 14); // This underline is blinked
+                                       _kln89->DrawEnt();
+                               }
+                       } else if(_uLinePos == 3) {
+                               _kln89->Underline(2, 13, 2, 3);
+                       } else if(_uLinePos >= 4) {
+                               if(_bEntWp) {
+                                       if(_wLinePos == 0) {
+                                               _kln89->Underline(2, 5, 2 - (_uLinePos - 4), 4);
+                                       } else if(_wLinePos == 4) {
+                                               _kln89->Underline(2, 4, 2 - (_uLinePos - 4), 4);
+                                       } else {
+                                               _kln89->Underline(2, 4, 2 - (_uLinePos - 4), _wLinePos);
+                                               _kln89->Underline(2, 5 + _wLinePos, 2 - (_uLinePos - 4), 4 - _wLinePos);
+                                       }
+                                       if(!_kln89->_blink) {
+                                               //_kln89->DrawText(_entWp->id, 2, 4, 2 - (_uLinePos - 4), false, _wLinePos);
+                                               _kln89->DrawEnt();
+                                       }
+                               } else {
+                                       if(!_delWp) _kln89->Underline(2, 4, 2 - (_uLinePos - 4), 5);
+                               }
+                       }
+               }
+               // ----------------------------------
+                       
+               _kln89->DrawChar('>', 2, 12, 2);
+               if(!(_kln89->_mode == KLN89_MODE_CRSR && _uLinePos == 3 && _kln89->_blink)) DrawFpMode(2);
+               // Sanity check the top position - remember that we can have an extra blank one at the bottom even if CRSR is off if crsr is switched on then off
+               if((int)_fplPos > ((int)waylist.size()) - 2) _fplPos = (((int)waylist.size()) - 2 < 0 ? 0 : waylist.size() - 2);
+               unsigned int last_pos;
+               if(waylist.empty()) {
+                       last_pos = 0;
+               } else {
+                       last_pos = ((int)_fplPos == ((int)waylist.size()) - 2 ? waylist.size() : waylist.size() - 1);
+               }
+               if(waylist.size() < 3) last_pos = waylist.size();
+               for(unsigned int i=0; i<3; ++i) {
+                       string s = GPSitoa(i < 2 ? _fplPos + i + 1 : last_pos + 1);
+                       s += ':';
+                       if(!(_delWp && _uLinePos == i+4)) _kln89->DrawText(s, 2, 4 - s.size(), 2 - i);
+                       bool drawID = true;
+                       if(_delWp && _uLinePos == i+4) {
+                               if(!_kln89->_blink) {
+                                       _kln89->DrawText("Del", 2, 0, 2-i);
+                                       _kln89->DrawChar('?', 2, 10, 2-i);
+                                       _kln89->Underline(2, 0, 2-i, 11);
+                                       _kln89->DrawEnt();
+                               }
+                       } else if(_kln89->_mode == KLN89_MODE_CRSR && _bEntWp && _uLinePos == i+4) {
+                               if(!_kln89->_blink) {
+                                       if(_wLinePos >= _entWp->id.size()) {
+                                               _kln89->DrawText(_entWp->id, 2, 4, 2-i);
+                                               _kln89->DrawChar(' ', 2, 4+_wLinePos, 2-i, false, true);
+                                       } else {
+                                               _kln89->DrawText(_entWp->id.substr(0, _wLinePos), 2, 4, 2-i);
+                                               _kln89->DrawChar(_entWp->id[_wLinePos], 2, 4+_wLinePos, 2-i, false, true);
+                                               _kln89->DrawText(_entWp->id.substr(_wLinePos+1, _entWp->id.size()-_wLinePos-1), 2, 5+_wLinePos, 2-i);
+                                       }
+                               }
+                               drawID = false;
+                       }
+                       if(drawID) {
+                               if(i == 2 || _fplPos + i == waylist.size()) {
+                                       if(!(_kln89->_mode == KLN89_MODE_CRSR && _uLinePos == (i + 4) && _kln89->_blink)) {
+                                               _kln89->DrawText(last_pos < waylist.size() ? waylist[last_pos]->id : "_____", 2, 4, 2-i);
+                                       }
+                                       if(last_pos > 0 && last_pos < waylist.size() && i > 0) {
+                                               // Draw the param
+                                               if(_fpMode == 0) {
+                                                       string s = _params[last_pos - 1];
+                                                       _kln89->DrawText(s, 2, 16-s.size(), 2-i);
+                                               }
+                                       }
+                                       break;
+                               } else {
+                                       if(!(_kln89->_mode == KLN89_MODE_CRSR && _uLinePos == (i + 4) && _kln89->_blink)) {
+                                               _kln89->DrawText(waylist[_fplPos+i]->id, 2, 4, 2-i);
+                                       }
+                                       if(i > 0) {
+                                               // Draw the param
+                                               if(_fpMode == 0) {
+                                                       string s = _params[_fplPos + i - 1];
+                                                       _kln89->DrawText(s, 2, 16-s.size(), 2-i);
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+       
+       KLN89Page::Update(dt);
+}
+
+void KLN89FplPage::DrawFpMode(int ypos) {
+       string s = "Dis";
+       if(0 == _subPage) {
+               if(_actFpMode == 1) {
+                       s = "ETE";
+               } else if(_actFpMode == 2) {
+                       s = "UTC";      // TODO - alter depending on chosen timezone
+               } else if(_actFpMode == 3) {
+                       s = (_kln89->_obsMode ? "OBS" : "Dtk");
+               }
+       } else {
+               if(_fpMode == 1) {
+                       s = "Dtk";
+               }
+       }
+       _kln89->DrawText(s, 2, 13, ypos);
+}
+
+// Bit of an ipsy-dipsy function this one - calc the required parameters for the displayed flightplan.
+void KLN89FplPage::Calc() {
+       _params.clear();
+       GPSFlightPlan* fp = _kln89->_flightPlans[_subPage];
+       vector<GPSWaypoint*> wv = fp->waypoints;
+       if(0 == _subPage) {
+               // Active FP - parameters are only displayed for the active waypoint onwards for the active plan,
+               // and distance is cumulative from the user position.
+               if(0 == _actFpMode) {
+                       // Dis
+                       double cum_tot = 0.0;
+                       if(wv.size() > 0) {
+                               //cum_tot += _kln89->GetHorizontalSeparation(_kln89->_gpsLat, _kln89->_gpsLon, wv[0]->lat, wv[0]->lon) * SG_METER_TO_NM;
+                               cum_tot += _kln89->GetGreatCircleDistance(_kln89->_gpsLat, _kln89->_gpsLon, wv[0]->lat, wv[0]->lon);
+                       }
+                       for(unsigned int i=1; i<wv.size(); ++i) {
+                               //cum_tot += _kln89->GetHorizontalSeparation(wv[i-1]->lat, wv[i-1]->lon, wv[i]->lat, wv[i]->lon) * SG_METER_TO_NM;      // TODO - add units switch!
+                               cum_tot += _kln89->GetGreatCircleDistance(wv[i-1]->lat, wv[i-1]->lon, wv[i]->lat, wv[i]->lon);  // TODO - add units switch!
+                               int n = (int)(cum_tot + 0.5);
+                               _params.push_back(GPSitoa(n));
+                       }
+               } else if(1 == _actFpMode) {
+               } else if(2 == _actFpMode) {
+               } else {
+                       // Dtk
+                       for(int i=1; i<wv.size(); ++i) {
+                               double dtk = _kln89->GetMagHeadingFromTo(wv[i-1]->lat, wv[i-1]->lon, wv[i]->lat, wv[i]->lon);
+                               int n = (int)(dtk + 0.5);
+                               _params.push_back(GPSitoa(n));
+                       }
+                       
+               }
+       } else {
+               // other FPs
+               if(0 == _fpMode) {
+                       double cum_tot = 0.0;
+                       for(int i=1; i<wv.size(); ++i) {
+                               //cum_tot += _kln89->GetHorizontalSeparation(wv[i-1]->lat, wv[i-1]->lon, wv[i]->lat, wv[i]->lon) * SG_METER_TO_NM;      // TODO - add units switch!
+                               cum_tot += _kln89->GetGreatCircleDistance(wv[i-1]->lat, wv[i-1]->lon, wv[i]->lat, wv[i]->lon);  // TODO - add units switch!
+                               int n = (int)(cum_tot + 0.5);
+                               _params.push_back(GPSitoa(n));
+                       }
+               } else {
+               }
+       }
+}
+
+void KLN89FplPage::CrsrPressed() {
+       if(_delFP) {
+               _delFP = false;
+               _kln89->_mode = KLN89_MODE_DISP;
+               return;
+       }
+
+       _wLinePos = 0;
+       if(_kln89->_mode == KLN89_MODE_DISP) {
+               _fp0SelWpId.clear();
+               if(_bEntWp) {
+                       for(unsigned int i = 0; i < _kln89->_flightPlans[_subPage]->waypoints.size(); ++i) {
+                               if(_kln89->_flightPlans[_subPage]->waypoints[i] == _entWp) {
+                                       _kln89->_flightPlans[_subPage]->waypoints.erase(_kln89->_flightPlans[_subPage]->waypoints.begin() + i);
+                               }
+                       }
+                       delete _entWp;
+                       _entWp = NULL;
+                       _bEntWp = false;
+                       _entWpStr.clear();
+               }
+       } else {
+               if(_kln89->_obsMode) {
+                       _uLinePos = 0;
+               } else {
+                       if(_kln89->_flightPlans[_subPage]->IsEmpty()) {
+                               _uLinePos = 4;
+                       } else {
+                               _uLinePos = (_subPage == 0 ? 3 : 1);
+                       }
+               }
+       }
+}
+
+void KLN89FplPage::ClrPressed() {
+       if(_delFP) {
+               _kln89->_mode = KLN89_MODE_DISP;
+               _delFP = false;
+       } else if(_delAppr) {
+               _kln89->_mode = KLN89_MODE_DISP;
+               _delAppr = false;
+       } else {
+               if(KLN89_MODE_CRSR == _kln89->_mode) {
+                       // TODO - see if we need to delete a waypoint
+                       if(_uLinePos >= 4) {
+                               if(_delWp) {
+                                       _kln89->_mode = KLN89_MODE_DISP;
+                                       _delWp = false;
+                               } else {
+                                       // First check that we're not trying to delete an approach waypoint.  Note that we can delete the approach by deleting the header though.
+                                       // Check for approach waypoints or header/fences in flightplan 0
+                                       int n = _fplPos + _uLinePos - 4;
+                                       bool hdrPos = false;
+                                       bool fencePos = false;
+                                       //cout << "_fplPos = " << _fplPos << ", _uLinePos = " << _uLinePos << ", n = " << n << ", _hdrPos = " << _hdrPos << ", _fencePos = " << _fencePos << '\n';
+                                       if(n == _hdrPos) {
+                                               //cout << "HEADER POS\n";
+                                               hdrPos = true;
+                                       }
+                                       if(n == _fencePos) {
+                                               //cout << "FENCE POS\n";
+                                               fencePos = true;
+                                       }
+                                       if(_hdrPos >= 0 && n > _hdrPos) --n;
+                                       if(_fencePos >= 0 && n >= _fencePos) --n;       // This one needs to be >= since n is already decremented by 1 in the line above!
+                                       //cout << "New n = " << n << '\n';
+                                       if(hdrPos) {
+                                               //cout << "HDRP\n";
+                                               _delAppr = true;
+                                       } else if(fencePos) {
+                                               //cout << "FENP\n";
+                                               // no-op
+                                       } else if(n >= _kln89->_flightPlans[_subPage]->waypoints.size()) {
+                                               // no-op - off the end of the list on the entry field
+                                       } else if(_kln89->_flightPlans[_subPage]->waypoints[n]->appType == GPS_APP_NONE) {
+                                               //cout << "DELFP\n";
+                                               _kln89->_mode = KLN89_MODE_CRSR;
+                                               _delWp = true;
+                                       } else {
+                                               ShowScratchpadMessage("Invald", " Del  ");
+                                       }
+                               }
+                       } else if(_uLinePos == 3) {
+                               if(_subPage == 0) {
+                                       _actFpMode++;
+                                       if(_actFpMode > 3) _actFpMode = 0;
+                               } else {
+                                       _fpMode++;
+                                       if(_fpMode > 1) _fpMode = 0;
+                               }
+                       }
+               } else {
+                       _delFP = true;
+                       _uLinePos = 1;
+                       _kln89->_mode = KLN89_MODE_CRSR;
+               }
+       }
+}
+
+void KLN89FplPage::CleanUp() {
+       // TODO - possibly need to clean up _delWp here as well, since it goes off if dto and then ent are pressed.
+       
+       _bEntWp = false;
+       for(unsigned int i = 0; i < _kln89->_flightPlans[_subPage]->waypoints.size(); ++i) {
+               if(_kln89->_flightPlans[_subPage]->waypoints[i] == _entWp) {
+                       _kln89->_flightPlans[_subPage]->waypoints.erase(_kln89->_flightPlans[_subPage]->waypoints.begin() + i);
+               }
+       }
+       delete _entWp;
+       _entWp = NULL;
+       _entWpStr.clear();
+       KLN89Page::CleanUp();
+}
+
+void KLN89FplPage::LooseFocus() {
+       _fplPos = 0;
+       _resetFplPos0 = true;
+       _wLinePos = 0;
+       _uLinePos = 0;
+       _fp0SelWpId.clear();
+       _scratchpadMsg = false;
+}
+
+void KLN89FplPage::EntPressed() {
+       if(_delFP) {
+               _parent->ClearFlightPlan(_subPage);
+               CrsrPressed();
+       } else if(_delWp) {
+               int pos = _uLinePos - 4 + _fplPos;
+               // Sanity check - the calculated wp position should never be off the end of the waypoint list.
+               if(pos > _kln89->_flightPlans[_subPage]->waypoints.size() - 1) {
+                       cout << "ERROR - _uLinePos too big in KLN89FplPage::EntPressed!\n";
+                       return;
+               }
+               _kln89->_flightPlans[_subPage]->waypoints.erase(_kln89->_flightPlans[_subPage]->waypoints.begin() + pos);
+               _delWp = false;
+               // Do we need to re-calc _fplPos here?
+       } else if(_bEntExp) {
+               _bEntWp = false;
+               _bEntExp = false;
+               _entWp = NULL;  // DON'T delete it! - it's been pushed onto the waypoint list at this point.
+               _entWpStr.clear();
+               _kln89->_cleanUpPage = -1;
+               _wLinePos = 0;
+               // TODO - in actual fact the previously underlined waypoint stays in the same position and underlined
+               // in some or possibly all circumstances - need to check this out and match it, but not too important
+               // for now.
+       } else if(_bEntWp) {
+               if(_entWp != NULL) {
+                       // TODO - should be able to get rid of this switch I think and use the enum values.
+                       switch(_entWp->type) {
+                       case GPS_WP_APT:
+                               _kln89->_activePage = _kln89->_pages[0];
+                               _kln89->_curPage = 0;
+                               ((KLN89Page*)_kln89->_pages[0])->SetEntInvert(true);
+                               break;
+                       case GPS_WP_VOR:
+                               _kln89->_activePage = _kln89->_pages[1];
+                               _kln89->_curPage = 1;
+                               ((KLN89Page*)_kln89->_pages[1])->SetEntInvert(true);
+                               break;
+                       case GPS_WP_NDB:
+                               _kln89->_activePage = _kln89->_pages[2];
+                               _kln89->_curPage = 2;
+                               ((KLN89Page*)_kln89->_pages[2])->SetEntInvert(true);
+                               break;
+                       case GPS_WP_INT:
+                               _kln89->_activePage = _kln89->_pages[3];
+                               _kln89->_curPage = 3;
+                               ((KLN89Page*)_kln89->_pages[3])->SetEntInvert(true);
+                               break;
+                       case GPS_WP_USR:
+                               _kln89->_activePage = _kln89->_pages[4];
+                               _kln89->_curPage = 4;
+                               ((KLN89Page*)_kln89->_pages[4])->SetEntInvert(true);
+                               break;
+                       default:
+                               cout << "Error - unknown waypoint type found in KLN89::FplPage::EntPressed()\n";
+                       }
+                       _kln89->_activePage->SetId(_entWp->id);
+                       _kln89->_entJump = 7;
+                       _kln89->_cleanUpPage = 7;
+                       _kln89->_entRestoreCrsr = true;
+                       _kln89->_mode = KLN89_MODE_DISP;
+               }
+               _bEntExp = true;
+       } else if(_uLinePos == 1) {
+               if(_kln89->_flightPlans[_subPage]->IsEmpty()) {
+                       // Copy fpl 0
+                       for(unsigned int i=0; i<_kln89->_flightPlans[0]->waypoints.size(); ++i) {
+                               GPSWaypoint* wp = new GPSWaypoint;
+                               *wp = *(_kln89->_flightPlans[0]->waypoints[i]);
+                               _kln89->_flightPlans[_subPage]->waypoints.push_back(wp);
+                       }
+               } else {
+                       // Use
+                       _parent->ClearFlightPlan(0);
+                       for(unsigned int i=0; i<_kln89->_flightPlans[_subPage]->waypoints.size(); ++i) {
+                               GPSWaypoint* wp = new GPSWaypoint;
+                               *wp = *(_kln89->_flightPlans[_subPage]->waypoints[i]);
+                               _kln89->_flightPlans[0]->waypoints.push_back(wp);
+                       }
+                       _kln89->OrientateToActiveFlightPlan();
+                       _subPage = 0;
+               }
+               _parent->CrsrPressed();
+       } else if(_uLinePos == 2) {
+               if(_kln89->_flightPlans[_subPage]->IsEmpty()) {
+                       // ERROR !!!
+               } else {
+                       // Use Invert
+                       _parent->ClearFlightPlan(0);
+                       for(unsigned int i=0; i<_kln89->_flightPlans[_subPage]->waypoints.size(); ++i) {
+                               GPSWaypoint* wp = new GPSWaypoint;
+                               *wp = *(_kln89->_flightPlans[_subPage]->waypoints[i]);
+                               // FIXME - very inefficient - use a reverse iterator on the source array and push_back instead!!!!!!!!
+                               _kln89->_flightPlans[0]->waypoints.insert(_kln89->_flightPlans[0]->waypoints.begin(), wp);
+                       }
+                       _kln89->OrientateToActiveFlightPlan();
+               }
+               _parent->CrsrPressed();
+               _subPage = 0;
+       }
+}
+
+void KLN89FplPage::Knob1Left1() {
+       if(_delFP) {
+               _delFP = false;
+               return;
+       }
+       _delWp = false;
+       _changeAppr = false;
+
+       if(_kln89->_mode == KLN89_MODE_CRSR) {
+               if(_bEntWp) {
+                       if(_wLinePos > 0) _wLinePos--;
+               } else {
+                       // _uLinePos with empty/not-empty plan: 1 = Copy FPL 0? / Use?, 2 = unused if empty / Invert?, 3 = >Dis/Dtk field, 4+ = Waypoint 1+
+                       if(_uLinePos == 0) {
+                               // No-op
+                       } else if(_uLinePos == 1 || _uLinePos == 2) {
+                               _uLinePos--;
+                       } else if(_uLinePos == 3) {
+                               _uLinePos = 4;
+                       } else if(_uLinePos == 4) {
+                               if(_kln89->_flightPlans[_subPage]->IsEmpty()) {
+                                       _uLinePos = (_subPage == 0 ? 0 : 1);
+                               } else if(_fplPos == 0) {
+                                       _uLinePos = (_subPage == 0 ? 0 : 2);
+                               } else {
+                                       _fplPos--;
+                               }
+                       } else if(_uLinePos == 5) {
+                               _uLinePos = 3;
+                       } else {
+                               _uLinePos--;
+                       }
+
+                       if(_subPage == 0 && _uLinePos > 3) {
+                               int ix = _fplPos + (_uLinePos - 4);
+                               if(_fencePos >= 0 && ix >= _fencePos) ix--;
+                               if(_hdrPos >= 0 && ix >= _hdrPos) ix--;
+                               if(ix >= _kln89->_activeFP->waypoints.size()) {
+                                       _fp0SelWpId.clear();
+                               } else {
+                                       _fp0SelWpId = _kln89->_activeFP->waypoints[ix]->id;
+                               }
+                       } else {
+                               _fp0SelWpId.clear();
+                               //cout << "Not page 0, or not in waypoints, clearing id!\n";
+                       }
+               }
+       }
+}
+
+void KLN89FplPage::Knob1Right1() {
+       if(_delFP) {
+               _delFP = false;
+               return;
+       }
+       _delWp = false;
+       _changeAppr = false;
+       
+       if(_kln89->_mode == KLN89_MODE_CRSR) {
+               if(_bEntWp) {
+                       if(_wLinePos < 4) _wLinePos++;
+               } else {
+                       // _uLinePos with empty/not-empty plan: 
+                       // 1 = Copy FPL 0? / Use?, 2 = unused if empty / Invert?, 3 = >Dis/Dtk field, 4+ = Waypoint 1+
+                       if(_uLinePos == 0) {
+                               _uLinePos = (_subPage == 0 ? 4 : 1);
+                       } else if(_uLinePos == 1) {
+                               _uLinePos = (_kln89->_flightPlans[_subPage]->IsEmpty() ? 4 : 2);
+                       } else if(_uLinePos == 2) {
+                               _uLinePos = 4;
+                       } else if(_uLinePos == 3) {
+                               if(!_kln89->_flightPlans[_subPage]->IsEmpty()) _uLinePos = 5;
+                       } else if(_uLinePos == 4) {
+                               _uLinePos = 3;
+                       } else if((_subPage == 0 && _uLinePos == 6) || (_subPage > 0 && _uLinePos == 5)) {
+                               // Urrggh - complicated!
+                               // 3 possibilities:
+                               // 1: We're on the entry field at the end of the list, and can't move any more.
+                               // 2: We're on the last or second-last field, and move to the last position
+                               // 3: We're on a field before the second-last one, and don't move, but change the list-head position
+                               // And 4: _subPage 0 can be complicated by the presence of header/fence lines in an approach.
+                               int hfcount = 0;
+                               if(_subPage == 0) {
+                                       if(_hdrPos >= 0) hfcount++;
+                                       if(_fencePos >= 0) hfcount++;
+                               }
+                               if(_kln89->_flightPlans[_subPage]->waypoints.size() == 1 || _fplPos == _kln89->_flightPlans[_subPage]->waypoints.size() + hfcount - 1) {
+                                       // 1: Don't move
+                               } else if(_fplPos >= _kln89->_flightPlans[_subPage]->waypoints.size() + hfcount - (_subPage == 0 ? 4 : 3)) {
+                                       _uLinePos++;
+                               } else {
+                                       _fplPos++;
+                               }
+                       } else if(_uLinePos == 5) {
+                               // Must be _subPage 0
+                               _uLinePos++;
+                       } else {
+                               // Must be the last line - either _uLinePos 6 or 7 depending on _subPage
+                               int thresh = (_subPage == 0 ? 3 : 2);
+                               if(_kln89->_flightPlans[_subPage]->waypoints.size() == thresh || _fplPos == _kln89->_flightPlans[_subPage]->waypoints.size() - thresh) {
+                                       // Don't move
+                               } else {
+                                       _fplPos++;
+                               }
+                       }
+                       
+                       if(_subPage == 0 && _uLinePos > 3) {
+                               int ix = _fplPos + (_uLinePos - 4);
+                               if(_fencePos >= 0 && ix >= _fencePos) ix--;
+                               if(_hdrPos >= 0 && ix >= _hdrPos) ix--;
+                               if(ix >= _kln89->_activeFP->waypoints.size()) {
+                                       _fp0SelWpId.clear();
+                               } else {
+                                       _fp0SelWpId = _kln89->_activeFP->waypoints[ix]->id;
+                               }
+                       } else {
+                               _fp0SelWpId.clear();
+                               //cout << "Not page 0, or not in waypoints, clearing id!\n";
+                       }
+               }
+       }
+}
+
+void KLN89FplPage::Knob2Left1() {
+       if(_delFP) {
+               _delFP = false;
+               return;
+       }
+       _delWp = false;
+
+       if(_kln89->_mode != KLN89_MODE_CRSR || _uLinePos == 0) {
+               if(_kln89->_mode != KLN89_MODE_CRSR) _resetFplPos0 = true;
+               KLN89Page::Knob2Left1();
+       } else {
+               if(_uLinePos > 3) {
+                       // Check for approach waypoints or header/fences in flightplan 0
+                       int n = _fplPos + _uLinePos - 4;
+                       bool hdrPos = false;
+                       bool fencePos = false;
+                       bool appWp = false;
+                       //cout << "_fplPos = " << _fplPos << ", _uLinePos = " << _uLinePos << ", n = " << n << ", _hdrPos = " << _hdrPos << ", _fencePos = " << _fencePos << '\n';
+                       if(n == _hdrPos) {
+                               //cout << "HEADER POS\n";
+                               hdrPos = true;
+                       }
+                       if(n == _fencePos) {
+                               //cout << "FENCE POS\n";
+                               fencePos = true;
+                       }
+                       if(_hdrPos >= 0 && n > _hdrPos) --n;
+                       if(_fencePos >= 0 && n >= _fencePos) --n;       // This one needs to be >= since n is already decremented by 1 in the line above!
+                       //cout << "New n = " << n << '\n';
+                       
+                       if(n < _kln89->_flightPlans[_subPage]->waypoints.size()) {
+                               if(_kln89->_flightPlans[_subPage]->waypoints[n]->appType != GPS_APP_NONE) {
+                                       appWp = true;
+                               }
+                       }
+                       
+                       if(hdrPos) {
+                               // TODO - not sure what we actually do in this condition
+                               _changeAppr = true;
+                       } else if(fencePos) {
+                               // no-op?
+                       } else if(appWp) {
+                               ShowScratchpadMessage("Invald", " Add  ");
+                       } else {
+                               if((_wLinePos + 1) > _entWpStr.size()) {
+                                       _entWpStr += '9';
+                               } else {
+                                       _entWpStr[_wLinePos] = _kln89->DecChar(_entWpStr[_wLinePos], (_wLinePos == 0 ? false : true));
+                               }
+                               _bEntWp = true;
+                               _fp0SelWpId.clear();    // Waypoints don't become the DTO default whilst being entered.
+                               
+                               bool multi;
+                               const GPSWaypoint* wp = _kln89->FindFirstById(_entWpStr.substr(0, _wLinePos+1), multi, false);
+                               if(NULL == wp) {
+                                       // no-op
+                               } else {
+                                       if(_entWp == NULL) {
+                                               _entWp = new GPSWaypoint;
+                                               if(_fplPos + (_uLinePos - 4) >= _kln89->_flightPlans[_subPage]->waypoints.size()) {
+                                                       _kln89->_flightPlans[_subPage]->waypoints.push_back(_entWp);
+                                               } else {
+                                                       _kln89->_flightPlans[_subPage]->waypoints.insert(_kln89->_flightPlans[_subPage]->waypoints.begin()+(_fplPos + (_uLinePos - 4)), _entWp);
+                                               }
+                                       }
+                                       // copy
+                                       *_entWp = *wp;
+                               }
+                       }
+               }
+       }
+}
+
+void KLN89FplPage::Knob2Right1() {
+       if(_delFP) {
+               _delFP = false;
+               return;
+       }
+       _delWp = false;
+
+       if(_kln89->_mode != KLN89_MODE_CRSR || _uLinePos == 0) {
+               if(_kln89->_mode != KLN89_MODE_CRSR) _resetFplPos0 = true;
+               KLN89Page::Knob2Right1();
+       } else {
+               if(_uLinePos > 3) {
+                       // Check for approach waypoints or header/fences in flightplan 0
+                       int n = _fplPos + _uLinePos - 4;
+                       bool hdrPos = false;
+                       bool fencePos = false;
+                       bool appWp = false;
+                       //cout << "_fplPos = " << _fplPos << ", _uLinePos = " << _uLinePos << ", n = " << n << ", _hdrPos = " << _hdrPos << ", _fencePos = " << _fencePos << '\n';
+                       if(n == _hdrPos) {
+                               //cout << "HEADER POS\n";
+                               hdrPos = true;
+                       }
+                       if(n == _fencePos) {
+                               //cout << "FENCE POS\n";
+                               fencePos = true;
+                       }
+                       if(_hdrPos >= 0 && n > _hdrPos) --n;
+                       if(_fencePos >= 0 && n >= _fencePos) --n;       // This one needs to be >= since n is already decremented by 1 in the line above!
+                       //cout << "New n = " << n << '\n';
+                       
+                       if(n < _kln89->_flightPlans[_subPage]->waypoints.size()) {
+                               if(_kln89->_flightPlans[_subPage]->waypoints[n]->appType != GPS_APP_NONE) {
+                                       appWp = true;
+                               }
+                       }
+                       
+                       if(hdrPos) {
+                               // TODO - not sure what we actually do in this condition
+                               _changeAppr = true;
+                       } else if(fencePos) {
+                               // no-op?
+                       } else if(appWp) {
+                               ShowScratchpadMessage("Invald", " Add  ");
+                       } else {
+                               if((_wLinePos + 1) > _entWpStr.size()) {
+                                       _entWpStr += '9';
+                               } else {
+                                       _entWpStr[_wLinePos] = _kln89->DecChar(_entWpStr[_wLinePos], (_wLinePos == 0 ? false : true));
+                               }
+                               _bEntWp = true;
+                               _fp0SelWpId.clear();    // Waypoints don't become the DTO default whilst being entered.
+                               
+                               bool multi;
+                               const GPSWaypoint* wp = _kln89->FindFirstById(_entWpStr.substr(0, _wLinePos+1), multi, false);
+                               if(NULL == wp) {
+                                       // no-op
+                               } else {
+                                       if(_entWp == NULL) {
+                                               _entWp = new GPSWaypoint;
+                                               if(_fplPos + (_uLinePos - 4) >= _kln89->_flightPlans[_subPage]->waypoints.size()) {
+                                                       _kln89->_flightPlans[_subPage]->waypoints.push_back(_entWp);
+                                               } else {
+                                                       _kln89->_flightPlans[_subPage]->waypoints.insert(_kln89->_flightPlans[_subPage]->waypoints.begin()+(_fplPos + (_uLinePos - 4)), _entWp);
+                                               }
+                                       }
+                                       // copy
+                                       *_entWp = *wp;
+                               }
+                       }
+               }
+       }
+}
diff --git a/src/Instrumentation/KLN89/kln89_page_fpl.hxx b/src/Instrumentation/KLN89/kln89_page_fpl.hxx
new file mode 100644 (file)
index 0000000..a571e16
--- /dev/null
@@ -0,0 +1,91 @@
+// kln89_page_*.[ch]xx - this file is one of the "pages" that
+//                       are used in the KLN89 GPS unit simulation. 
+//
+// Written by David Luff, started 2005.
+//
+// Copyright (C) 2005 - David C Luff - david.luff@nottingham.ac.uk
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+// $Id$
+
+#ifndef _KLN89_PAGE_FPL_HXX
+#define _KLN89_PAGE_FPL_HXX
+
+#include "kln89.hxx"
+
+class KLN89FplPage : public KLN89Page {
+
+public:
+       KLN89FplPage(KLN89* parent);
+       ~KLN89FplPage();
+       
+       void Update(double dt);
+       
+       void CrsrPressed();
+       void EntPressed();
+       void ClrPressed();
+       void Knob1Left1();
+       void Knob1Right1();
+       void Knob2Left1();
+       void Knob2Right1();
+       
+       void CleanUp();
+       void LooseFocus();
+       
+       // Override the base class GetId function to return the waypoint ID under the cursor
+       // on FPL0 page, if there is one and the cursor is on.
+       // Otherwise return an empty string.
+       inline string GetId() { return(_fp0SelWpId); } 
+       
+private:
+       int _fpMode;    // 0 = Dis, 1 = Dtk
+       int _actFpMode; // 0 = Dis, 1 = ETE, 2 = ETA, 3 = Dtk/OBS
+       
+       bool _bEntWp;   // set true when a waypoint is being entered
+       bool _bEntExp;  // Set true when ent is expected to set the currently entered waypoint as entered.
+       string _entWpStr;  // The currently entered wp ID (need not be valid) 
+       GPSWaypoint* _entWp;    // Waypoint being currently entered
+       
+       // The position of the cursor in a waypoint being entered
+       unsigned int _wLinePos;
+       
+       unsigned int _fplPos;   // The position of the start of the FP (NOT the active one) (zero-based).
+                                                       // since this is reset when subpage changes we only need 1, not an array of 25!
+       bool _resetFplPos0;             // Set true when a recalculation of _fplPos for the active flightplan page is required.
+       
+       int _hdrPos;
+       int _fencePos;
+
+    // Get the waypoint (returned thru the pointer) at the zero-based position (pos)
+    // in the waypoint display of the current page.
+    // Returns 0 if no waypoint, 1 if sucessfull, 2 if appr header, 3 if approach fence.
+    //int GetFPWaypoint(GPSWaypoint* wp, int pos);
+       
+       bool _delFP;    // Set true when the delete FP? dialogue is being displayed
+       bool _delWp;    // The position of the waypoint to delete is given by _uLinePos
+       bool _delAppr;  // Set true when the delete approach dialogue is being displayed
+       bool _changeAppr;
+       
+       void DrawFpMode(int ypos);
+       void Calc();
+       
+       // The ID of the waypoint under the cursor in fpl0, if those conditions exist!
+       string _fp0SelWpId;
+       
+       vector<string> _params;
+};
+
+#endif // _KLN89_PAGE_FPL_HXX
diff --git a/src/Instrumentation/KLN89/kln89_page_int.cxx b/src/Instrumentation/KLN89/kln89_page_int.cxx
new file mode 100644 (file)
index 0000000..e765cff
--- /dev/null
@@ -0,0 +1,242 @@
+// kln89_page_*.[ch]xx - this file is one of the "pages" that
+//                       are used in the KLN89 GPS unit simulation. 
+//
+// Written by David Luff, started 2005.
+//
+// Copyright (C) 2005 - David C Luff - david.luff@nottingham.ac.uk
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+// $Id$
+
+#include "kln89_page_int.hxx"
+
+KLN89IntPage::KLN89IntPage(KLN89* parent) 
+: KLN89Page(parent) {
+       _nSubPages = 2;
+       _subPage = 0;
+       _name = "INT";
+       _int_id = "PORTE";
+       _last_int_id = "";
+       _fp = NULL;
+       _nearestVor = NULL;
+       _refNav = NULL;
+}
+
+KLN89IntPage::~KLN89IntPage() {
+}
+
+void KLN89IntPage::Update(double dt) {
+       bool actPage = (_kln89->_activePage->GetName() == "ACT" ? true : false);
+       bool multi;  // Not set by FindFirst...
+       bool exact = false;
+       if(_int_id.size() == 5) exact = true;
+       if(_fp == NULL) {
+               _fp = _kln89->FindFirstIntById(_int_id, multi, exact);
+       } else if(_fp->get_ident() != _int_id) {
+               _fp = _kln89->FindFirstIntById(_int_id, multi, exact);
+       }
+       
+       if(_fp) {
+               _int_id = _fp->get_ident();
+               if(_kln89->GetActiveWaypoint()) {
+                       if(_int_id == _kln89->GetActiveWaypoint()->id) {
+                               if(!(_kln89->_waypointAlert && _kln89->_blink)) {
+                                       // Active waypoint arrow
+                                       _kln89->DrawSpecialChar(4, 2, 0, 3);
+                               }
+                       }
+               }
+               if(_int_id != _last_int_id) {
+                       _nearestVor = _kln89->FindClosestVor(_fp->get_lat() * SG_DEGREES_TO_RADIANS, _fp->get_lon() * SG_DEGREES_TO_RADIANS);
+                       if(_nearestVor) {
+                               _nvRadial = _kln89->GetMagHeadingFromTo(_nearestVor->get_lat() * SG_DEGREES_TO_RADIANS, _nearestVor->get_lon() * SG_DEGREES_TO_RADIANS,
+                                                                   _fp->get_lat() * SG_DEGREES_TO_RADIANS, _fp->get_lon() * SG_DEGREES_TO_RADIANS);
+                               _nvDist = _kln89->GetGreatCircleDistance(_nearestVor->get_lat() * SG_DEGREES_TO_RADIANS, _nearestVor->get_lon() * SG_DEGREES_TO_RADIANS,
+                                                                    _fp->get_lat() * SG_DEGREES_TO_RADIANS, _fp->get_lon() * SG_DEGREES_TO_RADIANS);
+                               _refNav = _nearestVor;  // TODO - check that this *always* holds - eg. when changing INT id after explicitly setting ref nav 
+                                                                       // but with no loss of focus.
+                       } else {
+                               _refNav = NULL;
+                       }
+                       _last_int_id = _int_id;
+               }
+               if(_kln89->_mode != KLN89_MODE_CRSR) {
+                       if(!_entInvert) {
+                               if(!actPage) {
+                                       _kln89->DrawText(_fp->get_ident(), 2, 1, 3);
+                               } else {
+                                       // If it's the ACT page, The ID is shifted slightly right to make space for the waypoint index.
+                                       _kln89->DrawText(_fp->get_ident(), 2, 4, 3);
+                                       char buf[3];
+                                       int n = snprintf(buf, 3, "%i", _kln89->GetActiveWaypointIndex() + 1);
+                                       _kln89->DrawText((string)buf, 2, 3 - n, 3);
+                                       // We also draw an I to differentiate INT from USR when it's the ACT page
+                                       _kln89->DrawText("I", 2, 11, 3);
+                               }
+                       } else {
+                               if(!_kln89->_blink) {
+                                       _kln89->DrawText(_fp->get_ident(), 2, 1, 3, false, 99);
+                                       _kln89->DrawEnt();
+                               }
+                       }
+               }
+               if(_subPage == 0) {
+                       _kln89->DrawLatitude(_fp->get_lat(), 2, 3, 2);
+                       _kln89->DrawLongitude(_fp->get_lon(), 2, 3, 1);
+                       _kln89->DrawDirDistField(_fp->get_lat() * SG_DEGREES_TO_RADIANS, _fp->get_lon() * SG_DEGREES_TO_RADIANS, 2, 0, 0, 
+                                                _to_flag, (_kln89->_mode == KLN89_MODE_CRSR && _uLinePos == 6 ? true : false));
+               } else {
+                       _kln89->DrawText("Ref:", 2, 1, 2);
+                       _kln89->DrawText("Rad:", 2, 1, 1);
+                       _kln89->DrawText("Dis:", 2, 1, 0);
+                       if(_refNav) {
+                               _kln89->DrawText(_refNav->get_ident(), 2, 9, 2);        // TODO - flash and allow to change if under cursor
+                               //_kln89->DrawHeading(_nvRadial, 2, 11, 1);
+                               //_kln89->DrawDist(_nvDist, 2, 11, 0);
+                               // Currently our draw heading and draw dist functions don't do as many decimal points as we want here,
+                               // so draw it ourselves!
+                               // Heading
+                               char buf[10];
+                               snprintf(buf, 6, "%.1f", _nvRadial);
+                               string s = buf;
+                               _kln89->DrawText(s, 2, 13 - s.size(), 1);
+                               _kln89->DrawSpecialChar(0, 2, 13, 1);   // Degrees symbol
+                               // Dist
+                               double d = _nvDist;
+                               d *= (_kln89->_distUnits == GPS_DIST_UNITS_NM ? 1.0 : SG_NM_TO_METER * 0.001);
+                               snprintf(buf, 9, "%.1f", d);
+                               s = buf;
+                               s += (_kln89->_distUnits == GPS_DIST_UNITS_NM ? "nm" : "Km");
+                               _kln89->DrawText(s, 2, 14 - s.size(), 0);
+                       }
+               }
+       } else {
+               // TODO - when we leave the page with invalid id and return it should
+               // revert to showing the last valid id.  Same for vor/ndb/probably apt etc.
+               if(_kln89->_mode != KLN89_MODE_CRSR) _kln89->DrawText(_int_id, 2, 1, 3);
+               if(_subPage == 0) {
+                       _kln89->DrawText("- -- --.--'", 2, 3, 2);
+                       _kln89->DrawText("---- --.--'", 2, 3, 1);
+                       _kln89->DrawSpecialChar(0, 2, 7, 2);
+                       _kln89->DrawSpecialChar(0, 2, 7, 1);
+                       _kln89->DrawText(">---    ----", 2, 0, 0);
+                       _kln89->DrawSpecialChar(0, 2, 4, 0);
+                       _kln89->DrawText(_to_flag ? "To" : "Fr", 2, 5, 0);
+                       _kln89->DrawText(_kln89->_distUnits == GPS_DIST_UNITS_NM ? "nm" : "km", 2, 12, 0);
+               }
+       }
+       
+       if(_kln89->_mode == KLN89_MODE_CRSR) {
+               if(_uLinePos > 0 && _uLinePos < 6) {
+                       // TODO - blink as well
+                       _kln89->Underline(2, _uLinePos, 3, 1);
+               }
+               for(unsigned int i = 0; i < _int_id.size(); ++i) {
+                       if(_uLinePos != (i + 1)) {
+                               _kln89->DrawChar(_int_id[i], 2, i + 1, 3);
+                       } else {
+                               if(!_kln89->_blink) _kln89->DrawChar(_int_id[i], 2, i + 1, 3);
+                       }
+               }
+       }
+       
+       // TODO - fix this duplication - use _id instead of _apt_id, _vor_id, _ndb_id, _int_id etc!
+       _id = _int_id;
+       
+       KLN89Page::Update(dt);
+}
+
+void KLN89IntPage::SetId(string s) {
+       _last_int_id = _int_id;
+       _save_int_id = _int_id;
+       _int_id = s;
+       _fp = NULL;
+}
+
+void KLN89IntPage::CrsrPressed() {
+       if(_kln89->_mode == KLN89_MODE_DISP) return;
+       if(_kln89->_obsMode) {
+               _uLinePos = 0;
+       } else {
+               _uLinePos = 1;
+       }
+       if(_subPage == 0) {
+               _maxULinePos = 6;
+       } else {
+               _maxULinePos = 6;
+       }
+}
+
+void KLN89IntPage::ClrPressed() {
+       if(_subPage == 0 && _uLinePos == 6) {
+               _to_flag = !_to_flag;
+       }
+}
+
+void KLN89IntPage::EntPressed() {
+       if(_entInvert) {
+               _entInvert = false;
+               _last_int_id = _int_id;
+               _int_id = _save_int_id;
+       }
+}
+
+void KLN89IntPage::Knob2Left1() {
+       if(_kln89->_mode != KLN89_MODE_CRSR || _uLinePos == 0) {
+               KLN89Page::Knob2Left1();
+       } else {
+               if(_uLinePos < 6) {
+                       // Same logic for both pages - set the ID
+                       _int_id = _int_id.substr(0, _uLinePos);
+                       // ASSERT(_uLinePos > 0);
+                       if(_uLinePos == (_int_id.size() + 1)) {
+                               _int_id += '9';
+                       } else {
+                               _int_id[_uLinePos - 1] = _kln89->DecChar(_int_id[_uLinePos - 1], (_uLinePos == 1 ? false : true));
+                       }
+               } else {
+                       if(_subPage == 0) {
+                               // NO-OP - from/to field is switched by clr button, not inner knob.
+                       } else {
+                               // TODO - LNR type field.
+                       }
+               }
+       }
+}
+
+void KLN89IntPage::Knob2Right1() {
+       if(_kln89->_mode != KLN89_MODE_CRSR || _uLinePos == 0) {
+               KLN89Page::Knob2Right1();
+       } else {
+               if(_uLinePos < 6) {
+                       // Same logic for both pages - set the ID
+                       _int_id = _int_id.substr(0, _uLinePos);
+                       // ASSERT(_uLinePos > 0);
+                       if(_uLinePos == (_int_id.size() + 1)) {
+                               _int_id += 'A';
+                       } else {
+                               _int_id[_uLinePos - 1] = _kln89->IncChar(_int_id[_uLinePos - 1], (_uLinePos == 1 ? false : true));
+                       }
+               } else {
+                       if(_subPage == 0) {
+                               // NO-OP - from/to field is switched by clr button, not inner knob.
+                       } else {
+                               // TODO - LNR type field.
+                       }
+               }
+       }
+}
+
diff --git a/src/Instrumentation/KLN89/kln89_page_int.hxx b/src/Instrumentation/KLN89/kln89_page_int.hxx
new file mode 100644 (file)
index 0000000..f8b8dc7
--- /dev/null
@@ -0,0 +1,56 @@
+// kln89_page_*.[ch]xx - this file is one of the "pages" that
+//                       are used in the KLN89 GPS unit simulation. 
+//
+// Written by David Luff, started 2005.
+//
+// Copyright (C) 2005 - David C Luff - david.luff@nottingham.ac.uk
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+// $Id$
+
+#ifndef _KLN89_PAGE_INT_HXX
+#define _KLN89_PAGE_INT_HXX
+
+#include "kln89.hxx"
+
+class KLN89IntPage : public KLN89Page {
+
+public:
+       KLN89IntPage(KLN89* parent);
+       ~KLN89IntPage();
+       
+       void Update(double dt);
+       
+       void CrsrPressed();
+       void ClrPressed();
+       void EntPressed();
+       void Knob2Left1();
+       void Knob2Right1();
+       
+       void SetId(string s);
+       
+private:
+       string _int_id;
+       string _last_int_id;
+       string _save_int_id;
+       const FGFix* _fp;
+       FGNavRecord* _nearestVor;
+       FGNavRecord* _refNav;   // Will usually be the same as _nearestVor, and gets reset to _nearestVor when page looses focus.
+       double _nvRadial;       // radial from nearest VOR
+       double _nvDist;         // distance to nearest VOR
+};
+
+#endif // _KLN89_PAGE_INT_HXX
diff --git a/src/Instrumentation/KLN89/kln89_page_nav.cxx b/src/Instrumentation/KLN89/kln89_page_nav.cxx
new file mode 100644 (file)
index 0000000..0cd1e57
--- /dev/null
@@ -0,0 +1,513 @@
+// kln89_page_*.[ch]xx - this file is one of the "pages" that
+//                       are used in the KLN89 GPS unit simulation. 
+//
+// Written by David Luff, started 2005.
+//
+// Copyright (C) 2005 - David C Luff - david.luff@nottingham.ac.uk
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+// $Id$
+
+#include "kln89_page_nav.hxx"
+
+KLN89NavPage::KLN89NavPage(KLN89* parent) 
+: KLN89Page(parent) {
+       _nSubPages = 4;
+       _subPage = 0;
+       _name = "NAV";
+       _posFormat = 0;         // Check - should this default to ref from waypoint?
+       _vnv = 0;
+       _nav4DataSnippet = 0;
+       _cdiFormat = 0;
+       _menuActive = false;
+       _menuPos = 0;
+       _suspendAVS = false;
+}
+
+KLN89NavPage::~KLN89NavPage() {
+}
+
+void KLN89NavPage::Update(double dt) {
+       GPSFlightPlan* fp = ((KLN89*)_parent)->_activeFP;
+       GPSWaypoint* awp = _parent->GetActiveWaypoint();
+       bool crsr = (_kln89->_mode == KLN89_MODE_CRSR);
+       bool blink = _kln89->_blink;
+       double lat = _kln89->_gpsLat * SG_RADIANS_TO_DEGREES;
+       double lon = _kln89->_gpsLon * SG_RADIANS_TO_DEGREES;
+       
+       if(0 == _subPage) {
+               if(_kln89->_navFlagged) {
+                       _kln89->DrawText(">    F L A G", 2, 0, 2);
+                       _kln89->DrawText("DTK ---  TK ---", 2, 0, 1);
+                       _kln89->DrawText(">--- To    --:--", 2, 0, 0);
+                       _kln89->DrawSpecialChar(0, 2, 7, 1);
+                       _kln89->DrawSpecialChar(0, 2, 15, 1);
+                       _kln89->DrawSpecialChar(0, 2, 4, 0);
+                       _kln89->DrawSpecialChar(1, 2, 3, 2);
+                       _kln89->DrawSpecialChar(1, 2, 4, 2);
+                       _kln89->DrawSpecialChar(1, 2, 6, 2);
+                       _kln89->DrawSpecialChar(1, 2, 10, 2);
+                       _kln89->DrawSpecialChar(1, 2, 12, 2);
+                       _kln89->DrawSpecialChar(1, 2, 13, 2);
+               } else {
+                       if(_kln89->_dto) {
+                               _kln89->DrawDTO(2, 7, 3);
+                       } else {
+                               if(!(_kln89->_waypointAlert && _kln89->_blink)) {
+                                       _kln89->DrawSpecialChar(3, 2, 8, 3);
+                               }
+                       }
+                       _kln89->DrawText(awp->id, 2, 10, 3);
+                       if(!_kln89->_dto && !_kln89->_obsMode && !_kln89->_fromWaypoint.id.empty()) {
+                               _kln89->DrawText(_kln89->_fromWaypoint.id, 2, 1, 3);
+                       }
+                       if(!(crsr && blink && _uLinePos == 1)) {
+                               if(_cdiFormat == 0) {
+                                       _kln89->DrawCDI();
+                               } else if(_cdiFormat == 1) {
+                                       _kln89->DrawText("Fly", 2, 2, 2);
+                                       double x = _kln89->CalcCrossTrackDeviation();
+                                       // TODO - check the R/L from sign of x below - I *think* it holds but not sure!
+                                       // Note also that we're setting Fly R or L based on the aircraft
+                                       // position only, not the heading.  Not sure if this is correct or not.
+                                       _kln89->DrawText(x < 0.0 ? "R" : "L", 2, 6, 2);
+                                       char buf[6];
+                                       int n;
+                                       x = fabs(x * (_kln89->_distUnits == GPS_DIST_UNITS_NM ? 1.0 : SG_NM_TO_METER * 0.001));
+                                       if(x < 1.0) {
+                                               n = snprintf(buf, 6, "%0.2f", x);
+                                       } else if(x < 100.0) {
+                                               n = snprintf(buf, 6, "%0.1f", x);
+                                       } else {
+                                               n = snprintf(buf, 6, "%i", (int)(x+0.5));
+                                       }
+                                       _kln89->DrawText((string)buf, 2, 13-n, 2);
+                                       _kln89->DrawText(_kln89->_distUnits == GPS_DIST_UNITS_NM ? "nm" : "km", 2, 13, 2);
+                               } else {
+                                       _kln89->DrawText("CDI Scale:", 2, 1, 2);
+                                       double d = _kln89->_cdiScales[_kln89->_currentCdiScaleIndex] * (_kln89->_distUnits == GPS_DIST_UNITS_NM ? 1.0 : SG_NM_TO_METER * 0.001);
+                                       char buf[5];
+                                       string s;
+                                       if(d >= 1.0) {
+                                               snprintf(buf, 4, "%2.1f", d);
+                                               s = buf;
+                                       } else {
+                                               snprintf(buf, 5, "%2.2f", d);
+                                               // trim the leading zero
+                                               s = buf;
+                                               s = s.substr(1, s.size() - 1);
+                                       }
+                                       _kln89->DrawText(s, 2, 11, 2);
+                                       _kln89->DrawText(_kln89->_distUnits == GPS_DIST_UNITS_NM ? "nm" : "km", 2, 14, 2);
+                               }
+                       }
+                       _kln89->DrawChar('>', 2, 0, 2);
+                       _kln89->DrawChar('>', 2, 0, 0);
+                       if(crsr) {
+                               if(_uLinePos == 1) _kln89->Underline(2, 1, 2, 15);
+                               else if(_uLinePos == 2) _kln89->Underline(2, 1, 0, 9);
+                       }
+                       // Desired and actual magnetic track
+                       if(!_kln89->_obsMode) {
+                               _kln89->DrawText("DTK", 2, 0, 1);
+                               _kln89->DrawHeading(_kln89->_dtkMag, 2, 7, 1);
+                       }
+                       _kln89->DrawText("TK", 2, 9, 1);
+                       if(_kln89->_groundSpeed_ms > 3) {       // about 6 knots, don't know exactly what value to disable track
+                               // The trouble with relying on FG gps's track value is we don't know when it's valid.
+                               _kln89->DrawHeading(_kln89->_magTrackDeg, 2, 15, 1);
+                       } else {
+                               _kln89->DrawText("---", 2, 12, 1);
+                               _kln89->DrawSpecialChar(0, 2, 15, 1);
+                       }
+                       
+                       // Radial to/from active waypoint.
+                       // TODO - Not sure if this either is or should be true or mag!!!!!!!
+                       if(!(crsr && blink && _uLinePos == 2)) {
+                               if(0 == _vnv) {
+                                       _kln89->DrawHeading((int)_kln89->GetHeadingToActiveWaypoint(), 2, 4, 0);
+                                       _kln89->DrawText("To", 2, 5, 0);
+                               } else if(1 == _vnv) {
+                                       _kln89->DrawHeading((int)_kln89->GetHeadingFromActiveWaypoint(), 2, 4, 0);
+                                       _kln89->DrawText("Fr", 2, 5, 0);
+                               } else {
+                                       _kln89->DrawText("Vnv Off", 2, 1, 0);
+                               }
+                       }
+                       // It seems that the floating point groundspeed must be at least 30kt
+                       // for an ETA to be calculated.  Note that this means that (integer) 30kt
+                       // can appear in the frame 1 display both with and without an ETA displayed.
+                       // TODO - need to switch off track (and heading bug change) based on instantaneous speed as well
+                       // since the long gps lag filter means we can still be displaying when stopped on ground.
+                       if(_kln89->_groundSpeed_kts > 30.0) {
+                               // Assuming eta display is always hh:mm
+                               // Does it ever switch to seconds when close?
+                               if(_kln89->_eta / 3600.0 > 100.0) {
+                                       // More that 100 hours ! - Doesn't fit.
+                                       _kln89->DrawText("--:--", 2, 11, 0);
+                               } else {
+                                       _kln89->DrawTime(_kln89->_eta, 2, 15, 0);
+                               }
+                       } else {
+                               _kln89->DrawText("--:--", 2, 11, 0);
+                       }
+               }
+       } else if(1 == _subPage) {
+               // Present position
+               _kln89->DrawChar('>', 2, 1, 3);
+               if(!(crsr && blink && _uLinePos == 1)) _kln89->DrawText("PRESENT POSN", 2, 2, 3);
+               if(crsr && _uLinePos == 1) _kln89->Underline(2, 2, 3, 12);
+               if(0 == _posFormat) {
+                       // Lat/lon
+                       _kln89->DrawLatitude(lat, 2, 3, 1);
+                       _kln89->DrawLongitude(lon, 2, 3, 0);
+               } else {
+                       // Ref from wp - defaults to nearest vor (and resets to default when page left and re-entered).
+               }
+       } else if(2 == _subPage) {
+               _kln89->DrawText("Time", 2, 0, 3);
+               // TODO - hardwired to UTC at the moment
+               _kln89->DrawText("UTC", 2, 6, 3);
+               string th = fgGetString("/instrumentation/clock/indicated-hour");
+               string tm = fgGetString("/instrumentation/clock/indicated-min");
+               if(th.size() == 1) th = "0" + th;
+               if(tm.size() == 1) tm = "0" + tm;
+               _kln89->DrawText(th + tm, 2, 11, 3);
+               _kln89->DrawText("Depart", 2, 0, 2);
+               _kln89->DrawText(_kln89->_departureTimeString, 2, 11, 2);
+               _kln89->DrawText("ETA", 2, 0, 1);
+               if(_kln89->_departed) {
+                       /* Rules of ETA waypoint are:
+                        If the active waypoint is part of the active flightplan, then display
+                        the ETA to the final (destination) waypoint of the active flightplan.
+                        If the active waypoint is not part of the active flightplan, then
+                        display the ETA to the active waypoint. */
+                       // TODO - implement the above properly - we haven't below!
+                       string wid = "";
+                       if(fp->waypoints.size()) {
+                               wid = fp->waypoints[fp->waypoints.size() - 1]->id;
+                       } else if(awp) {
+                               wid = awp->id;
+                       }
+                       if(!wid.empty()) {
+                               _kln89->DrawText(wid, 2, 4, 1);
+                               double tsec = _kln89->GetTimeToWaypoint(wid);
+                               if(tsec < 0.0) {
+                                       _kln89->DrawText("----", 2, 11, 1);
+                               } else {
+                                       int etah = (int)tsec / 3600;
+                                       int etam = ((int)tsec - etah * 3600) / 60;
+                                       etah += atoi(fgGetString("/instrumentation/clock/indicated-hour"));
+                                       etam += atoi(fgGetString("/instrumentation/clock/indicated-min"));
+                                       while(etam > 59) {
+                                               etam -= 60;
+                                               etah += 1;
+                                       }
+                                       while(etah > 23) {
+                                               etah -= 24;
+                                       }
+                                       char buf[6];
+                                       int n = snprintf(buf, 6, "%02i%02i", etah, etam);
+                                       _kln89->DrawText((string)buf, 2, 15-n, 1);
+                               }
+                       } else {
+                               _kln89->DrawText("----", 2, 11, 1);
+                       }
+               } else {
+                       _kln89->DrawText("----", 2, 11, 1);
+               }
+               _kln89->DrawText("Flight", 2, 0, 0);
+               if(_kln89->_departed) {
+                       int eh = (int)_kln89->_elapsedTime / 3600;
+                       int em = ((int)_kln89->_elapsedTime - eh * 3600) / 60;
+                       char buf[6];
+                       int n = snprintf(buf, 6, "%i:%02i", eh, em);
+                       _kln89->DrawText((string)buf, 2, 15-n, 0);
+               } else {
+                       _kln89->DrawText("-:--", 2, 11, 0);
+               }
+       } else {
+               // The moving map page the core KLN89 class draws this.
+               if(_kln89->_mapOrientation == 2 && _kln89->_groundSpeed_kts < 2) {
+                       // Don't draw it if in track up mode and groundspeed < 2kts, as per real-life unit.
+               } else {
+                       _kln89->DrawMap(!_suspendAVS);
+               }
+               // Now draw any annotation over it.
+               int scale = KLN89MapScales[_kln89->_mapScaleUnits][_kln89->_mapScaleIndex];
+               string scle_str = GPSitoa(scale);
+               if(crsr) {
+                       if(_menuActive) {
+                               // Draw a background quad to encompass on/off for the first three at 'off' length
+                               _kln89->DrawMapQuad(28, 9, 48, 36, true);
+                               _kln89->DrawMapText("SUA:", 1, 27, true);
+                               if(!(_menuPos == 0 && _kln89->_blink)) _kln89->DrawMapText((_kln89->_drawSUA ? "on" : "off"), 29, 27, true);
+                               if(_menuPos == 0) _kln89->DrawLine(28, 27, 48, 27);
+                               _kln89->DrawMapText("VOR:", 1, 18, true);
+                               if(!(_menuPos == 1 && _kln89->_blink)) _kln89->DrawMapText((_kln89->_drawVOR ? "on" : "off"), 29, 18, true);
+                               if(_menuPos == 1) _kln89->DrawLine(28, 18, 48, 18);
+                               _kln89->DrawMapText("APT:", 1, 9, true);
+                               if(!(_menuPos == 2 && _kln89->_blink)) _kln89->DrawMapText((_kln89->_drawApt ? "on" : "off"), 29, 9, true);
+                               if(_menuPos == 2) _kln89->DrawLine(28, 9, 48, 9);
+                               _kln89->DrawMapQuad(0, 0, 27, 8, true);
+                               if(!(_menuPos == 3 && _kln89->_blink)) {
+                                       if(_kln89->_mapOrientation == 0) {
+                                               _kln89->DrawMapText("N", 1, 0, true);
+                                               _kln89->DrawMapUpArrow(7, 1);
+                                       } else if(_kln89->_mapOrientation == 1) {
+                                               _kln89->DrawMapText("DTK", 1, 0, true);
+                                               _kln89->DrawMapUpArrow(21, 1);
+                                       } else {
+                                               // Don't bother with heading up for now!
+                                               _kln89->DrawMapText("TK", 1, 0, true);
+                                               _kln89->DrawMapUpArrow(14, 1);
+                                       }
+                               }
+                               if(_menuPos == 3) _kln89->DrawLine(0, 0, 27, 0);
+                       } else {
+                               if(_uLinePos == 2) {
+                                       if(!_kln89->_blink) {
+                                               _kln89->DrawMapText("Menu?", 1, 9, true);
+                                               _kln89->DrawEnt();
+                                               _kln89->DrawLine(0, 9, 34, 9);
+                                       } else {
+                                               _kln89->DrawMapQuad(0, 9, 34, 17, true);
+                                       }
+                               } else {
+                                       _kln89->DrawMapText("Menu?", 1, 9, true);
+                               }
+                               // right-justify the scale when _uLinePos == 3
+                               if(!(_uLinePos == 3 && _kln89->_blink)) _kln89->DrawMapText(scle_str, (_uLinePos == 3 ? 29 - (scle_str.size() * 7) : 1), 0, true);
+                               if(_uLinePos == 3) _kln89->DrawLine(0, 0, 27, 0);
+                       }
+               } else {
+                       // Just draw the scale
+                       _kln89->DrawMapText(scle_str, 1, 0, true);
+               }
+               // And do part of the field 1 update, since NAV 4 is a special case for the last line.
+               _kln89->DrawChar('>', 1, 0, 0);
+               if(crsr && _uLinePos == 1) _kln89->Underline(1, 1, 0, 5);
+               if(!(crsr && _uLinePos == 1 && _kln89->_blink)) {
+                       if(_kln89->_obsMode && _nav4DataSnippet == 0) _nav4DataSnippet = 1;
+                       double tsec;
+                       switch(_nav4DataSnippet) {
+                       case 0:
+                               // DTK
+                               _kln89->DrawLabel("DTK", -39, 6); 
+                               // TODO - check we have an active FP / dtk and draw dashes if not.
+                               char buf0[4];
+                               snprintf(buf0, 4, "%03i", (int)(_kln89->_dtkMag));
+                               _kln89->DrawText((string)buf0, 1, 3, 0);
+                               break;
+                       case 1:
+                               // groundspeed
+                               _kln89->DrawSpeed(_kln89->_groundSpeed_kts, 1, 5, 0);
+                               break;
+                       case 2:
+                               // ETE
+                               tsec = _kln89->GetETE();
+                               if(tsec < 0.0) {
+                                       _kln89->DrawText("--:--", 1, 1, 0);
+                               } else {
+                                       int eteh = (int)tsec / 3600;
+                                       int etem = ((int)tsec - eteh * 3600) / 60;
+                                       char buf[6];
+                                       int n = snprintf(buf, 6, "%02i:%02i", eteh, etem);
+                                       _kln89->DrawText((string)buf, 1, 6-n, 0);
+                               }
+                               break;
+                       case 3:
+                               // Cross-track correction
+                               double x = _kln89->CalcCrossTrackDeviation();
+                               if(x < 0.0) {
+                                       _kln89->DrawSpecialChar(3, 1, 5, 0);
+                               } else {
+                                       _kln89->DrawSpecialChar(7, 1, 5, 0);
+                               }
+                               char buf3[6];
+                               int n;
+                               x = fabs(x * (_kln89->_distUnits == GPS_DIST_UNITS_NM ? 1.0 : SG_NM_TO_METER * 0.001));
+                               if(x < 1.0) {
+                                       n = snprintf(buf3, 6, "%0.2f", x);
+                               } else if(x < 100.0) {
+                                       n = snprintf(buf3, 6, "%0.1f", x);
+                               } else {
+                                       n = snprintf(buf3, 6, "%i", (int)(x+0.5));
+                               }
+                               _kln89->DrawText((string)buf3, 1, 5-n, 0);
+                               break;
+                       }
+               }
+       }
+       
+       KLN89Page::Update(dt);
+}
+
+void KLN89NavPage::LooseFocus() {
+       _suspendAVS = false;
+}
+
+void KLN89NavPage::CrsrPressed() {
+       if(_kln89->_mode == KLN89_MODE_DISP) {
+               // Crsr just switched off
+               _menuActive = false;
+       } else {
+               // Crsr just switched on
+               if(_subPage < 3) {
+                       _uLinePos = 1;
+               } else {
+                       _uLinePos = 3;
+               }
+       }
+}
+
+void KLN89NavPage::EntPressed() {
+       if(_kln89->_mode == KLN89_MODE_CRSR) {
+               if(_subPage == 3 && _uLinePos == 2 && !_menuActive) {
+                       _menuActive = true;
+                       _menuPos = 0;
+                       _suspendAVS = false;
+               }
+       }
+}
+
+void KLN89NavPage::ClrPressed() {
+       if(_kln89->_mode == KLN89_MODE_CRSR) {
+               if(_subPage == 0) {
+                       if(_uLinePos == 1) {
+                               _cdiFormat++;
+                               if(_cdiFormat > 2) _cdiFormat = 0;
+                       } else if(_uLinePos == 2) {
+                               _vnv++;
+                               if(_vnv > 2) _vnv = 0;
+                       }
+               }
+               if(_subPage == 3) {
+                       if(_uLinePos > 1) {
+                               _suspendAVS = !_suspendAVS;
+                               _menuActive = false;
+                       } else if(_uLinePos == 1) {
+                               _nav4DataSnippet++;
+                               if(_nav4DataSnippet > 3) _nav4DataSnippet = 0;
+                       }
+               }
+       } else {
+               if(_subPage == 3) {
+                       _suspendAVS = !_suspendAVS;
+               }
+       }
+}
+
+void KLN89NavPage::Knob1Left1() {
+       if(_kln89->_mode == KLN89_MODE_CRSR) {
+               if(!(_subPage == 3 && _menuActive)) {
+                       if(_uLinePos > 0) _uLinePos--;
+               } else {
+                       if(_menuPos > 0) _menuPos--;
+               }
+       }
+}
+
+void KLN89NavPage::Knob1Right1() {
+       if(_kln89->_mode == KLN89_MODE_CRSR) {
+               if(_subPage < 2) {
+                       if(_uLinePos < 2) _uLinePos++;
+               } else if(_subPage == 2) {
+                       _uLinePos = 1;
+               } else {
+                       // NAV 4 - this is complicated by whether the menu is displayed or not.
+                       if(_menuActive) {
+                               if(_menuPos < 3) _menuPos++;
+                       } else {
+                               if(_uLinePos < 3) _uLinePos++;
+                       }
+               }
+       }
+}
+
+void KLN89NavPage::Knob2Left1() {
+       if(_kln89->_mode != KLN89_MODE_CRSR || _uLinePos == 0) {
+               KLN89Page::Knob2Left1();
+               return;
+       }
+       if(_subPage == 0) {
+               if(_uLinePos == 1 && _cdiFormat == 2) {
+                       _kln89->CDIFSDIncrease();
+               }
+       } else if(_subPage == 3) {
+               if(_menuActive) {
+                       if(_menuPos == 0) {
+                               _kln89->_drawSUA = !_kln89->_drawSUA;
+                       } else if(_menuPos == 1) {
+                               _kln89->_drawVOR = !_kln89->_drawVOR;
+                       } else if(_menuPos == 2) {
+                               _kln89->_drawApt = !_kln89->_drawApt;
+                       } else {
+                               if(_kln89->_mapOrientation == 0) {
+                                       // Don't allow heading up for now
+                                       _kln89->_mapOrientation = 2;
+                               } else {
+                                       _kln89->_mapOrientation--;
+                               }
+                               _kln89->UpdateMapHeading();
+                       }
+               } else if(_uLinePos == 3) {
+                       // TODO - add AUTO
+                       if(_kln89->_mapScaleIndex == 0) {
+                               _kln89->_mapScaleIndex = 20;
+                       } else {
+                               _kln89->_mapScaleIndex--;
+                       }
+               }
+       }
+}
+
+void KLN89NavPage::Knob2Right1() {
+       if(_kln89->_mode != KLN89_MODE_CRSR || _uLinePos == 0) {
+               KLN89Page::Knob2Right1();
+               return;
+       }
+       if(_subPage == 0) {
+               if(_uLinePos == 1 && _cdiFormat == 2) {
+                       _kln89->CDIFSDDecrease();
+               }
+       } else if(_subPage == 3) {
+               if(_menuActive) {
+                       if(_menuPos == 0) {
+                               _kln89->_drawSUA = !_kln89->_drawSUA;
+                       } else if(_menuPos == 1) {
+                               _kln89->_drawVOR = !_kln89->_drawVOR;
+                       } else if(_menuPos == 2) {
+                               _kln89->_drawApt = !_kln89->_drawApt;
+                       } else {
+                               if(_kln89->_mapOrientation >= 2) {
+                                       // Don't allow heading up for now
+                                       _kln89->_mapOrientation = 0;
+                               } else {
+                                       _kln89->_mapOrientation++;
+                               }
+                               _kln89->UpdateMapHeading();
+                       }
+               } else if(_uLinePos == 3) {
+                       // TODO - add AUTO
+                       if(_kln89->_mapScaleIndex == 20) {
+                               _kln89->_mapScaleIndex = 0;
+                       } else {
+                               _kln89->_mapScaleIndex++;
+                       }
+               }
+       }
+}
diff --git a/src/Instrumentation/KLN89/kln89_page_nav.hxx b/src/Instrumentation/KLN89/kln89_page_nav.hxx
new file mode 100644 (file)
index 0000000..7b722e3
--- /dev/null
@@ -0,0 +1,63 @@
+// kln89_page_*.[ch]xx - this file is one of the "pages" that
+//                       are used in the KLN89 GPS unit simulation. 
+//
+// Written by David Luff, started 2005.
+//
+// Copyright (C) 2005 - David C Luff - david.luff@nottingham.ac.uk
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+// $Id$
+
+#include "kln89.hxx"
+
+class KLN89NavPage : public KLN89Page {
+
+public:
+       KLN89NavPage(KLN89* parent);
+       ~KLN89NavPage();
+       
+       void Update(double dt);
+       
+       void CrsrPressed();
+       void EntPressed();
+       void ClrPressed();
+       void Knob1Left1();
+       void Knob1Right1();
+       void Knob2Left1();
+       void Knob2Right1();
+       
+       void LooseFocus();
+       
+private:
+       int _posFormat;         // 0 => lat,lon; 1 => ref to wp.
+       
+       int _vnv;       // 0 => To, 1 => Fr, 2 => off.
+       
+       // The data snippet to be displayed in field 1 when the moving map is active (NAV 4)
+       int _nav4DataSnippet;   // 0 => DTK, 1 => groundspeed, 2 => ETE, 3 => cross-track correction.
+       
+       // Format to draw in the CDI field.
+       // 0 => CDI, 1 => Cross track correction (eg " Fly R  2.15nm"), 2 => cdi scale (eg "CDI Scale:5.0nm")
+       int _cdiFormat;
+       
+       // Drawing of apt, vor and sua on the moving map can be temporarily suspended
+       // Note that this should be cleared when page focus is lost, or when the menu is displayed. 
+       bool _suspendAVS;
+       
+       // NAV 4 menu stuff
+       bool _menuActive;
+       int _menuPos;
+};
diff --git a/src/Instrumentation/KLN89/kln89_page_ndb.cxx b/src/Instrumentation/KLN89/kln89_page_ndb.cxx
new file mode 100644 (file)
index 0000000..4469895
--- /dev/null
@@ -0,0 +1,205 @@
+// kln89_page_*.[ch]xx - this file is one of the "pages" that
+//                       are used in the KLN89 GPS unit simulation. 
+//
+// Written by David Luff, started 2005.
+//
+// Copyright (C) 2005 - David C Luff - david.luff@nottingham.ac.uk
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+// $Id$
+
+#include "kln89_page_ndb.hxx"
+
+KLN89NDBPage::KLN89NDBPage(KLN89* parent) 
+: KLN89Page(parent) {
+       _nSubPages = 2;
+       _subPage = 0;
+       _name = "NDB";
+       _ndb_id = "SF";
+       np = NULL;
+}
+
+KLN89NDBPage::~KLN89NDBPage() {
+}
+
+void KLN89NDBPage::Update(double dt) {
+       bool actPage = (_kln89->_activePage->GetName() == "ACT" ? true : false);
+       bool multi;  // Not set by FindFirst...
+       bool exact = false;
+       if(_ndb_id.size() == 3) exact = true;
+       if(np == NULL) {
+               np = _kln89->FindFirstNDBById(_ndb_id, multi, exact);
+       } else if(np->get_ident() != _ndb_id) {
+               np = _kln89->FindFirstNDBById(_ndb_id, multi, exact);
+       }
+       //if(np == NULL) cout << "NULL... ";
+       //if(b == false) cout << "false...\n";
+       /*
+       if(np && b) {
+               cout << "VOR FOUND!\n";
+       } else {
+               cout << ":-(\n";
+       }
+       */
+       if(np) {
+               //cout << np->id << '\n';
+               _ndb_id = np->get_ident();
+               if(_kln89->GetActiveWaypoint()) {
+                       if(_ndb_id == _kln89->GetActiveWaypoint()->id) {
+                               if(!(_kln89->_waypointAlert && _kln89->_blink)) {
+                                       // Active waypoint arrow
+                                       _kln89->DrawSpecialChar(4, 2, 0, 3);
+                               }
+                       }
+               }
+               if(_kln89->_mode != KLN89_MODE_CRSR) {
+                       if(!_entInvert) {
+                               if(!actPage) {
+                                       _kln89->DrawText(np->get_ident(), 2, 1, 3);
+                               } else {
+                                       // If it's the ACT page, The ID is shifted slightly right to make space for the waypoint index.
+                                       _kln89->DrawText(np->get_ident(), 2, 4, 3);
+                                       char buf[3];
+                                       int n = snprintf(buf, 3, "%i", _kln89->GetActiveWaypointIndex() + 1);
+                                       _kln89->DrawText((string)buf, 2, 3 - n, 3);
+                               }
+                       } else {
+                               if(!_kln89->_blink) {
+                                       _kln89->DrawText(np->get_ident(), 2, 1, 3, false, 99);
+                                       _kln89->DrawEnt();
+                               }
+                       }
+               }
+               if(_subPage == 0) {
+                       // TODO - trim VOR-DME from the name, convert to uppercase, abbreviate, etc
+                       _kln89->DrawText(np->get_name(), 2, 0, 2);
+                       _kln89->DrawLatitude(np->get_lat(), 2, 3, 1);
+                       _kln89->DrawLongitude(np->get_lon(), 2, 3, 0);
+               } else {
+                       _kln89->DrawDirDistField(np->get_lat() * SG_DEGREES_TO_RADIANS, np->get_lon() * SG_DEGREES_TO_RADIANS,
+                                                2, 0, 0, _to_flag, (_kln89->_mode == KLN89_MODE_CRSR && _uLinePos == 4 ? true : false));
+               }
+       } else {
+               if(_kln89->_mode != KLN89_MODE_CRSR) _kln89->DrawText(_ndb_id, 2, 1, 3);
+               if(_subPage == 0) {
+                       _kln89->DrawText("----.-", 2, 9, 3);
+                       _kln89->DrawText("--------------", 2, 0, 2);
+                       _kln89->DrawText("- -- --.--'", 2, 3, 1);
+                       _kln89->DrawText("---- --.--'", 2, 3, 0);
+                       _kln89->DrawSpecialChar(0, 2, 7, 1);
+                       _kln89->DrawSpecialChar(0, 2, 7, 0);
+               }
+       }
+       
+       if(_kln89->_mode == KLN89_MODE_CRSR) {
+               if(_uLinePos > 0 && _uLinePos < 4) {
+                       // TODO - blink as well
+                       _kln89->Underline(2, _uLinePos, 3, 1);
+               }
+               for(unsigned int i = 0; i < _ndb_id.size(); ++i) {
+                       if(_uLinePos != (i + 1)) {
+                               _kln89->DrawChar(_ndb_id[i], 2, i + 1, 3);
+                       } else {
+                               if(!_kln89->_blink) _kln89->DrawChar(_ndb_id[i], 2, i + 1, 3);
+                       }
+               }
+       }
+       
+       _id = _ndb_id;
+
+       KLN89Page::Update(dt);
+}
+
+void KLN89NDBPage::SetId(string s) {
+       _last_ndb_id = _ndb_id;
+       _save_ndb_id = _ndb_id;
+       _ndb_id = s;
+       np = NULL;
+}
+
+void KLN89NDBPage::CrsrPressed() {
+       if(_kln89->_mode == KLN89_MODE_DISP) return;
+       if(_kln89->_obsMode) {
+               _uLinePos = 0;
+       } else {
+               _uLinePos = 1;
+       }
+       if(_subPage == 0) {
+               _maxULinePos = 17;
+       } else {
+               _maxULinePos = 4;
+       }
+}
+
+void KLN89NDBPage::ClrPressed() {
+       if(_subPage == 1 && _uLinePos == 4) {
+               _to_flag = !_to_flag;
+       }
+}
+
+void KLN89NDBPage::EntPressed() {
+       if(_entInvert) {
+               _entInvert = false;
+               _last_ndb_id = _ndb_id;
+               _ndb_id = _save_ndb_id;
+       }
+}
+
+void KLN89NDBPage::Knob2Left1() {
+       if(_kln89->_mode != KLN89_MODE_CRSR || _uLinePos == 0) {
+               KLN89Page::Knob2Left1();
+       } else {
+               if(_uLinePos < 4) {
+                       // Same logic for both pages - set the ID
+                       _ndb_id = _ndb_id.substr(0, _uLinePos);
+                       // ASSERT(_uLinePos > 0);
+                       if(_uLinePos == (_ndb_id.size() + 1)) {
+                               _ndb_id += '9';
+                       } else {
+                               _ndb_id[_uLinePos - 1] = _kln89->DecChar(_ndb_id[_uLinePos - 1], (_uLinePos == 1 ? false : true));
+                       }
+               } else {
+                       if(_subPage == 0) {
+                               // set by name
+                       } else {
+                               // NO-OP - from/to field is switched by clr button, not inner knob.
+                       }
+               }
+       }
+}
+
+void KLN89NDBPage::Knob2Right1() {
+       if(_kln89->_mode != KLN89_MODE_CRSR || _uLinePos == 0) {
+               KLN89Page::Knob2Right1();
+       } else {
+               if(_uLinePos < 4) {
+                       // Same logic for both pages - set the ID
+                       _ndb_id = _ndb_id.substr(0, _uLinePos);
+                       // ASSERT(_uLinePos > 0);
+                       if(_uLinePos == (_ndb_id.size() + 1)) {
+                               _ndb_id += 'A';
+                       } else {
+                               _ndb_id[_uLinePos - 1] = _kln89->IncChar(_ndb_id[_uLinePos - 1], (_uLinePos == 1 ? false : true));
+                       }
+               } else {
+                       if(_subPage == 0) {
+                               // set by name
+                       } else {
+                               // NO-OP - from/to field is switched by clr button, not inner knob.
+                       }
+               }
+       }
+}
diff --git a/src/Instrumentation/KLN89/kln89_page_ndb.hxx b/src/Instrumentation/KLN89/kln89_page_ndb.hxx
new file mode 100644 (file)
index 0000000..373844d
--- /dev/null
@@ -0,0 +1,52 @@
+// kln89_page_*.[ch]xx - this file is one of the "pages" that
+//                       are used in the KLN89 GPS unit simulation. 
+//
+// Written by David Luff, started 2005.
+//
+// Copyright (C) 2005 - David C Luff - david.luff@nottingham.ac.uk
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+// $Id$
+
+#ifndef _KLN89_PAGE_NDB_HXX
+#define _KLN89_PAGE_NDB_HXX
+
+#include "kln89.hxx"
+
+class KLN89NDBPage : public KLN89Page {
+
+public:
+       KLN89NDBPage(KLN89* parent);
+       ~KLN89NDBPage();
+       
+       void Update(double dt);
+
+       void CrsrPressed();
+       void ClrPressed();
+       void EntPressed();
+       void Knob2Left1();
+       void Knob2Right1();
+       
+       void SetId(string s);
+       
+private:
+       string _ndb_id; 
+       string _last_ndb_id;
+       string _save_ndb_id;
+       FGNavRecord* np;
+};
+
+#endif  // _KLN89_PAGE_NDB_HXX
diff --git a/src/Instrumentation/KLN89/kln89_page_nrst.cxx b/src/Instrumentation/KLN89/kln89_page_nrst.cxx
new file mode 100644 (file)
index 0000000..fad2170
--- /dev/null
@@ -0,0 +1,86 @@
+// kln89_page_*.[ch]xx - this file is one of the "pages" that
+//                       are used in the KLN89 GPS unit simulation. 
+//
+// Written by David Luff, started 2005.
+//
+// Copyright (C) 2005 - David C Luff - david.luff@nottingham.ac.uk
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+// $Id$
+
+#include "kln89_page_nrst.hxx"
+
+KLN89NrstPage::KLN89NrstPage(KLN89* parent) 
+: KLN89Page(parent) {
+       _nSubPages = 2;
+       _subPage = 0;
+       _name = "NRST";
+       _uLinePos = 1;
+       _maxULinePos = 8;
+}
+
+KLN89NrstPage::~KLN89NrstPage() {
+}
+
+void KLN89NrstPage::Update(double dt) {
+       // crsr is always on on nearest page
+       bool blink = _kln89->_blink;
+       
+       _kln89->DrawText("NEAREST", 2, 3, 3);
+       if(!(_uLinePos == 1 && blink)) _kln89->DrawText("APT?", 2, 0, 2);
+       if(!(_uLinePos == 2 && blink)) _kln89->DrawText("VOR?", 2, 5, 2);
+       if(!(_uLinePos == 3 && blink)) _kln89->DrawText("NDB?", 2, 10, 2);
+       if(!(_uLinePos == 4 && blink)) _kln89->DrawText("INT?", 2, 0, 1);
+       if(!(_uLinePos == 5 && blink)) _kln89->DrawText("USR?", 2, 5, 1);
+       if(!(_uLinePos == 6 && blink)) _kln89->DrawText("SUA?", 2, 10, 1);
+       if(!(_uLinePos == 7 && blink)) _kln89->DrawText("FSS?", 2, 0, 0);
+       if(!(_uLinePos == 8 && blink)) _kln89->DrawText("CTR?", 2, 5, 0);
+               
+       switch(_uLinePos) {
+       case 1: _kln89->Underline(2, 0, 2, 4); break;
+       case 2: _kln89->Underline(2, 5, 2, 4); break;
+       case 3: _kln89->Underline(2, 10, 2, 4); break;
+       case 4: _kln89->Underline(2, 0, 1, 4); break;
+       case 5: _kln89->Underline(2, 5, 1, 4); break;
+       case 6: _kln89->Underline(2, 10, 1, 4); break;
+       case 7: _kln89->Underline(2, 0, 0, 4); break;
+       case 8: _kln89->Underline(2, 5, 0, 4); break;
+       }
+       
+       // Actually, the kln89 sim from Bendix-King dosn't draw the 'ENT'
+       // for 'APT?' if it was on 'Leg' (pos 0) immediately previously, but does if
+       // it was not on 'Leg' immediately previously.  I think we can desist from
+       // reproducing this probable bug.
+       if(_uLinePos > 0) {
+               if(!blink) _kln89->DrawEnt();
+       }
+       
+       KLN89Page::Update(dt);
+}
+
+void KLN89NrstPage::CrsrPressed() {
+}
+
+void KLN89NrstPage::EntPressed() {
+       if(_uLinePos > 4) {
+               ShowScratchpadMessage("  No  ", " Nrst ");
+       }
+}
+
+void KLN89NrstPage::LooseFocus() {
+       _uLinePos = 1;
+       _scratchpadMsg = false;
+}
diff --git a/src/Instrumentation/KLN89/kln89_page_nrst.hxx b/src/Instrumentation/KLN89/kln89_page_nrst.hxx
new file mode 100644 (file)
index 0000000..1ad213c
--- /dev/null
@@ -0,0 +1,48 @@
+// kln89_page_*.[ch]xx - this file is one of the "pages" that
+//                       are used in the KLN89 GPS unit simulation. 
+//
+// Written by David Luff, started 2005.
+//
+// Copyright (C) 2005 - David C Luff - david.luff@nottingham.ac.uk
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+// $Id$
+
+#ifndef _KLN89_PAGE_NRST
+#define _KLN89_PAGE_NRST
+
+#include "kln89.hxx"
+
+class KLN89NrstPage : public KLN89Page {
+
+public:
+       KLN89NrstPage(KLN89* parent);
+       ~KLN89NrstPage();
+       
+       void Update(double dt);
+       
+       void CrsrPressed();
+       void EntPressed();
+       //void ClrPressed();
+       //void Knob1Left1();
+       //void Knob1Right1();
+       //void Knob2Left1();
+       //void Knob2Right1();
+       
+       void LooseFocus();
+};
+
+#endif // _KLN89_PAGE_NRST
diff --git a/src/Instrumentation/KLN89/kln89_page_oth.cxx b/src/Instrumentation/KLN89/kln89_page_oth.cxx
new file mode 100644 (file)
index 0000000..ae7ce92
--- /dev/null
@@ -0,0 +1,58 @@
+// kln89_page_*.[ch]xx - this file is one of the "pages" that
+//                       are used in the KLN89 GPS unit simulation. 
+//
+// Written by David Luff, started 2005.
+//
+// Copyright (C) 2005 - David C Luff - david.luff@nottingham.ac.uk
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+// $Id$
+
+#include "kln89_page_oth.hxx"
+
+KLN89OthPage::KLN89OthPage(KLN89* parent)
+: KLN89Page(parent) {
+       _nSubPages = 12;
+       _subPage = 0;
+       _name = "OTH";
+}
+
+KLN89OthPage::~KLN89OthPage() {
+}
+
+void KLN89OthPage::Update(double dt) {
+       // The OTH pages aren't terribly important at the moment, since we don't simulate
+       // error and failure, but lets hardwire some representitive output anyway.
+       if(_subPage == 0) {
+               _kln89->DrawText("State", 2, 0, 3);
+               _kln89->DrawText("GPS Alt", 2, 0, 2);
+               _kln89->DrawText("Estimated Posn", 2, 0, 1);
+               _kln89->DrawText("Error", 2, 1, 0);
+               
+               // FIXME - hardwired value.
+               _kln89->DrawText("NAV D", 2, 9, 3);
+               // TODO - add error physics to FG GPS where the alt value comes from.
+               char buf[6];
+               int n = snprintf(buf, 5, "%i", _kln89->_altUnits == GPS_ALT_UNITS_FT ? (int)_kln89->_alt : (int)(_kln89->_alt * SG_FEET_TO_METER));
+               _kln89->DrawText((string)buf, 2, 13-n, 2);
+               _kln89->DrawText(_kln89->_altUnits == GPS_ALT_UNITS_FT ? "ft" : "m", 2, 13, 2);
+               // FIXME - hardwired values.
+               // Note that a 5th digit if required is left padded one further at position 7.
+               _kln89->DrawText(_kln89->_distUnits == GPS_DIST_UNITS_NM ? "0.02nm" : "0.03km", 2, 8, 0);
+       }
+       
+       KLN89Page::Update(dt);
+}
diff --git a/src/Instrumentation/KLN89/kln89_page_oth.hxx b/src/Instrumentation/KLN89/kln89_page_oth.hxx
new file mode 100644 (file)
index 0000000..b648d75
--- /dev/null
@@ -0,0 +1,39 @@
+// kln89_page_*.[ch]xx - this file is one of the "pages" that
+//                       are used in the KLN89 GPS unit simulation. 
+//
+// Written by David Luff, started 2005.
+//
+// Copyright (C) 2005 - David C Luff - david.luff@nottingham.ac.uk
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+// $Id$
+
+#ifndef _KLN89_PAGE_OTH
+#define _KLN89_PAGE_OTH
+
+#include "kln89.hxx"
+
+class KLN89OthPage : public KLN89Page {
+
+public:
+       KLN89OthPage(KLN89* parent);
+       ~KLN89OthPage();
+
+       void Update(double dt);
+       
+};
+
+#endif  // _KLN89_PAGE_OTH
diff --git a/src/Instrumentation/KLN89/kln89_page_set.cxx b/src/Instrumentation/KLN89/kln89_page_set.cxx
new file mode 100644 (file)
index 0000000..fc09e81
--- /dev/null
@@ -0,0 +1,291 @@
+// kln89_page_*.[ch]xx - this file is one of the "pages" that
+//                       are used in the KLN89 GPS unit simulation. 
+//
+// Written by David Luff, started 2005.
+//
+// Copyright (C) 2005 - David C Luff - david.luff@nottingham.ac.uk
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+// $Id$
+
+#include "kln89_page_set.hxx"
+
+#include <iostream>
+using namespace std;
+
+KLN89SetPage::KLN89SetPage(KLN89* parent) 
+: KLN89Page(parent) {
+       _nSubPages = 11;
+       _subPage = 0;
+       _name = "SET";
+}
+
+KLN89SetPage::~KLN89SetPage() {
+}
+
+void KLN89SetPage::Update(double dt) {
+       string sBaro, sAlt, sVel;
+       switch(_subPage+1) {
+       case 1:
+               _kln89->DrawText("INIT POS:", 2, 0, 3);
+               break;
+       case 2:
+               _kln89->DrawText("DATE", 2, 0, 3);
+               _kln89->DrawText("TIME", 2, 0, 2);
+               _kln89->DrawText("Cord", 2, 0, 1);
+               _kln89->DrawText("Mag Var:", 2, 0, 0);
+               break;
+       case 3:
+               _kln89->DrawText("Update DB on", 2, 1, 3);
+               _kln89->DrawText("ground only", 2, 1, 2);
+               _kln89->DrawText("Key", 2, 0, 1);
+               _kln89->DrawText("Update pub DB?", 2, 0, 0);
+               break;
+       case 4:
+               //cout << "_uLinePos = " << _uLinePos << '\n';
+               _kln89->DrawText("TURN", 2, 5, 3);
+               _kln89->DrawText("ANTICIPATION", 2, 1, 2);
+               if(_kln89->_mode == KLN89_MODE_CRSR && _uLinePos == 1) {
+                       if(!_kln89->_blink) {
+                               _kln89->DrawText((_kln89->GetTurnAnticipation() ? "ENABLED" : "DISABLED"), 2, 3, 1);
+                       }
+                       _kln89->Underline(2, 3, 1, 8);
+               } else {
+                       _kln89->DrawText((_kln89->GetTurnAnticipation() ? "ENABLED" : "DISABLED"), 2, 3, 1);
+               }
+               break;
+       case 5:
+               _kln89->DrawText("Default First", 2, 0, 3);
+               _kln89->DrawText("Character of", 2, 1, 2);
+               _kln89->DrawText("Wpt identifier", 2, 0, 1);
+               _kln89->DrawText("Entry:", 2, 3, 0);
+               break;
+       case 6:
+               _kln89->DrawText("NEAREST APT", 2, 1, 3);
+               _kln89->DrawText("CRITERIA", 2, 3, 2);
+               _kln89->DrawText("Length:", 2, 0, 1);
+               _kln89->DrawText("Surface:", 2, 0, 0);
+               break;
+       case 7:
+               _kln89->DrawText("SUA ALERT", 2, 3, 3);
+               if(_kln89->_mode == KLN89_MODE_CRSR && _uLinePos == 1) {
+                       if(!_kln89->_blink) {
+                               _kln89->DrawText((_kln89->GetSuaAlertEnabled() ? "ENABLED" : "DISABLED"), 2, 4, 2);
+                       }
+                       _kln89->Underline(2, 4, 2, 8);
+               } else {
+                       _kln89->DrawText((_kln89->GetSuaAlertEnabled() ? "ENABLED" : "DISABLED"), 2, 4, 2);
+               }
+               if(_kln89->GetSuaAlertEnabled()) {
+                       _kln89->DrawText("Buffer:", 2, 0, 1);
+                       _kln89->DrawSpecialChar(5, 2, 7, 1);    // +- sign.
+                       if(_kln89->_mode == KLN89_MODE_CRSR && _uLinePos == 2) {
+                               if(!_kln89->_blink) {
+                                       _kln89->DrawText("00300", 2, 8, 1);             // TODO - fix this hardwiring!!!!
+                               }
+                               _kln89->Underline(2, 8, 1, 5);
+                       } else {
+                               _kln89->DrawText("00300", 2, 8, 1);             // TODO - fix this hardwiring!!!!
+                       }
+                       _kln89->DrawText("ft", 2, 13, 1);       // TODO - fix this hardwiring!!!!
+               }
+               break;
+       case 8:
+               _kln89->DrawText("SET UNITS:", 2, 3, 3);
+               _kln89->DrawText("Baro    :", 2, 0, 2);
+               _kln89->DrawText("Alt-APT :", 2, 0, 1);
+               _kln89->DrawText("Dist-Vel:", 2, 0, 0);
+               switch(_kln89->_baroUnits) {
+               case GPS_PRES_UNITS_IN:
+                       sBaro = "\"";
+                       break;
+               case GPS_PRES_UNITS_MB:
+                       sBaro = "mB";
+                       break;
+               case GPS_PRES_UNITS_HP:
+                       sBaro = "hP";
+                       break;
+               }
+               if(_kln89->_altUnits == GPS_ALT_UNITS_FT) sAlt = "ft";
+               else sAlt = "m";
+               if(_kln89->_distUnits == GPS_DIST_UNITS_NM) sVel = "nm-kt";
+               else sVel = "km-";
+                       
+               if(_kln89->_mode == KLN89_MODE_CRSR && _uLinePos == 1) {
+                       if(!_kln89->_blink) {
+                               _kln89->DrawText(sBaro, 2, 10, 2);
+                       }
+                       _kln89->Underline(2, 10, 2, 2);
+               } else {
+                       _kln89->DrawText(sBaro, 2, 10, 2);
+               }
+               if(_kln89->_mode == KLN89_MODE_CRSR && _uLinePos == 2) {
+                       if(!_kln89->_blink) {
+                               _kln89->DrawText(sAlt, 2, 10, 1);
+                       }
+                       _kln89->Underline(2, 10, 1, 2);
+               } else {
+                       _kln89->DrawText(sAlt, 2, 10, 1);
+               }
+               if(_kln89->_mode == KLN89_MODE_CRSR && _uLinePos == 3) {
+                       if(!_kln89->_blink) {
+                               _kln89->DrawText(sVel, 2, 10, 0);
+                               if(_kln89->_distUnits != GPS_DIST_UNITS_NM) _kln89->DrawKPH(2, 13, 0);
+                       }
+                       _kln89->Underline(2, 10, 0, 5);
+               } else {
+                       _kln89->DrawText(sVel, 2, 10, 0);
+                       if(_kln89->_distUnits != GPS_DIST_UNITS_NM) _kln89->DrawKPH(2, 13, 0);
+               }
+               break;
+       case 9:
+               _kln89->DrawText("Altitude", 2, 3, 3);
+               _kln89->DrawText("Alert:", 2, 1, 2);
+               break;
+       case 10:
+               _kln89->DrawText("BUS MONITOR", 2, 2, 3);
+               _kln89->DrawText("Bus Volt", 2, 0, 2);
+               _kln89->DrawText("Alert Volt", 2, 0, 1);
+               _kln89->DrawText("Alert Delay", 2, 0, 0);
+               break;
+       case 11:
+               _kln89->DrawText("MIN DISPLAY", 2, 2, 3);
+               _kln89->DrawText("BRIGHTNESS ADJ", 2, 1, 2);
+               break;
+       }
+       
+       KLN89Page::Update(dt);
+}
+
+void KLN89SetPage::CrsrPressed() {
+       if(_kln89->_mode == KLN89_MODE_DISP) return;
+       if(_kln89->_obsMode) {
+               _uLinePos = 0;
+       } else {
+               _uLinePos = 1;
+       }
+       switch(_subPage+1) {
+       case 1:
+               break;
+       case 2:
+               break;
+       case 3:
+               break;
+       case 4:
+               _maxULinePos = 1;
+               break;
+       case 5:
+               break;
+       case 6:
+               _maxULinePos = 2;
+               break;
+       case 7:
+               if(_kln89->GetSuaAlertEnabled()) _maxULinePos = 2;
+               else _maxULinePos = 1;
+               break;
+       case 8:
+               _maxULinePos = 3;
+               break;
+       case 9:
+               break;
+       case 10:
+               break;
+       case 11:
+               break;
+       }
+}
+
+void KLN89SetPage::Knob2Left1() {
+       if(_kln89->_mode != KLN89_MODE_CRSR || _uLinePos == 0) {
+               KLN89Page::Knob2Left1();
+       } else {
+               switch(_subPage+1) {
+               case 1:
+                       break;
+               case 2:
+                       break;
+               case 3:
+                       break;
+               case 4:
+                       if(_uLinePos == 1) {
+                               _kln89->SetTurnAnticipation(!_kln89->GetTurnAnticipation());
+                       }
+                       break;
+               case 5:
+                       break;
+               case 6:
+                       break;
+               case 7:
+                       if(_uLinePos == 1) {
+                               _kln89->SetSuaAlertEnabled(!_kln89->GetSuaAlertEnabled());
+                               _maxULinePos = (_kln89->GetSuaAlertEnabled() ? 2 : 1);
+                       } else if(_uLinePos == 2) {
+                               // TODO - implement variable sua alert buffer
+                       }
+                       break;
+               case 8:
+                       if(_uLinePos == 1) {  // baro units
+                               _kln89->SetBaroUnits(_kln89->GetBaroUnits() - 1, true);
+                       } else if(_uLinePos == 2) {
+                               _kln89->SetAltUnitsSI(!_kln89->GetAltUnitsSI());
+                       } else if(_uLinePos == 3) {
+                               _kln89->SetDistVelUnitsSI(!_kln89->GetDistVelUnitsSI());
+                       }
+                       break;
+               }
+       }
+}
+
+void KLN89SetPage::Knob2Right1() {
+       if(_kln89->_mode != KLN89_MODE_CRSR || _uLinePos == 0) {
+               KLN89Page::Knob2Right1();
+       } else {
+               switch(_subPage+1) {
+               case 1:
+                       break;
+               case 2:
+                       break;
+               case 3:
+                       break;
+               case 4:
+                       if(_uLinePos == 1) {   // Which it should be!
+                               _kln89->SetTurnAnticipation(!_kln89->GetTurnAnticipation());
+                       }
+                       break;
+               case 5:
+                       break;
+               case 6:
+                       break;
+               case 7:
+                       if(_uLinePos == 1) {
+                               _kln89->SetSuaAlertEnabled(!_kln89->GetSuaAlertEnabled());
+                               _maxULinePos = (_kln89->GetSuaAlertEnabled() ? 2 : 1);
+                       } else if(_uLinePos == 2) {
+                               // TODO - implement variable sua alert buffer
+                       }
+                       break;
+               case 8:
+                       if(_uLinePos == 1) {  // baro units
+                               _kln89->SetBaroUnits(_kln89->GetBaroUnits() + 1, true);
+                       } else if(_uLinePos == 2) {
+                               _kln89->SetAltUnitsSI(!_kln89->GetAltUnitsSI());
+                       } else if(_uLinePos == 3) {
+                               _kln89->SetDistVelUnitsSI(!_kln89->GetDistVelUnitsSI());
+                       }
+                       break;
+               }
+       }
+}
diff --git a/src/Instrumentation/KLN89/kln89_page_set.hxx b/src/Instrumentation/KLN89/kln89_page_set.hxx
new file mode 100644 (file)
index 0000000..3f2e774
--- /dev/null
@@ -0,0 +1,42 @@
+// kln89_page_*.[ch]xx - this file is one of the "pages" that
+//                       are used in the KLN89 GPS unit simulation. 
+//
+// Written by David Luff, started 2005.
+//
+// Copyright (C) 2005 - David C Luff - david.luff@nottingham.ac.uk
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+// $Id$
+
+#ifndef _KLN89_PAGE_SET_HXX
+#define _KLN89_PAGE_SET_HXX
+
+#include "kln89.hxx"
+
+class KLN89SetPage : public KLN89Page {
+
+public:
+       KLN89SetPage(KLN89* parent);
+       ~KLN89SetPage();
+       
+       void Update(double dt);
+       
+       void CrsrPressed();
+       void Knob2Left1();
+       void Knob2Right1();
+};
+
+#endif // _KLN89_PAGE_SET_HXX
diff --git a/src/Instrumentation/KLN89/kln89_page_usr.cxx b/src/Instrumentation/KLN89/kln89_page_usr.cxx
new file mode 100644 (file)
index 0000000..bf2cdce
--- /dev/null
@@ -0,0 +1,51 @@
+// kln89_page_*.[ch]xx - this file is one of the "pages" that
+//                       are used in the KLN89 GPS unit simulation. 
+//
+// Written by David Luff, started 2005.
+//
+// Copyright (C) 2005 - David C Luff - david.luff@nottingham.ac.uk
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+// $Id$
+
+#include "kln89_page_usr.hxx"
+
+KLN89UsrPage::KLN89UsrPage(KLN89* parent) 
+: KLN89Page(parent) {
+       _nSubPages = 4;
+       _subPage = 0;
+       _name = "USR";
+}
+
+KLN89UsrPage::~KLN89UsrPage() {
+}
+
+void KLN89UsrPage::Update(double dt) {
+       bool actPage = (_kln89->_activePage->GetName() == "ACT" ? true : false);
+       bool crsr = (_kln89->_mode == KLN89_MODE_CRSR);
+       bool blink = _kln89->_blink;
+       
+       if(_subPage == 0) {
+               // Hardwire no-waypoint output for now
+               _kln89->DrawText("0", 2, 1, 3);
+               _kln89->DrawText("USR at:", 2, 7, 3);
+               if(!(crsr && _uLinePos == 6 && blink)) _kln89->DrawText("User Pos L/L?", 2, 1, 2);
+               if(!(crsr && _uLinePos == 7 && blink)) _kln89->DrawText("User Pos R/D?", 2, 1, 1);
+               if(!(crsr && _uLinePos == 8 && blink)) _kln89->DrawText("Present Pos?", 2, 1, 0);
+       }
+       
+       KLN89Page::Update(dt);
+}
diff --git a/src/Instrumentation/KLN89/kln89_page_usr.hxx b/src/Instrumentation/KLN89/kln89_page_usr.hxx
new file mode 100644 (file)
index 0000000..dd8bbc3
--- /dev/null
@@ -0,0 +1,39 @@
+// kln89_page_*.[ch]xx - this file is one of the "pages" that
+//                       are used in the KLN89 GPS unit simulation. 
+//
+// Written by David Luff, started 2005.
+//
+// Copyright (C) 2005 - David C Luff - david.luff@nottingham.ac.uk
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+// $Id$
+
+#ifndef _KLN89_PAGE_USR_HXX
+#define _KLN89_PAGE_USR_HXX
+
+#include "kln89.hxx"
+
+class KLN89UsrPage : public KLN89Page {
+
+public:
+       KLN89UsrPage(KLN89* parent);
+       ~KLN89UsrPage();
+       
+       void Update(double dt);
+       
+};
+
+#endif // _KLN89_PAGE_USR_HXX
diff --git a/src/Instrumentation/KLN89/kln89_page_vor.cxx b/src/Instrumentation/KLN89/kln89_page_vor.cxx
new file mode 100644 (file)
index 0000000..3456c6e
--- /dev/null
@@ -0,0 +1,217 @@
+// kln89_page_*.[ch]xx - this file is one of the "pages" that
+//                       are used in the KLN89 GPS unit simulation. 
+//
+// Written by David Luff, started 2005.
+//
+// Copyright (C) 2005 - David C Luff - david.luff@nottingham.ac.uk
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+// $Id$
+
+#include "kln89_page_vor.hxx"
+
+KLN89VorPage::KLN89VorPage(KLN89* parent) 
+: KLN89Page(parent) {
+       _nSubPages = 2;
+       _subPage = 0;
+       _name = "VOR";
+       _vor_id = "OSI";        // TODO - check a property for an initial value to allow user-override.
+       np = NULL;
+}
+
+KLN89VorPage::~KLN89VorPage() {
+}
+
+void KLN89VorPage::Update(double dt) {
+       bool actPage = (_kln89->_activePage->GetName() == "ACT" ? true : false);
+       bool multi;  // Not set by FindFirst...
+       bool exact = false;
+       if(_vor_id.size() == 3) exact = true;
+       if(np == NULL) {
+               np = _kln89->FindFirstVorById(_vor_id, multi, exact);
+       } else if(np->get_ident() != _vor_id) {
+               np = _kln89->FindFirstVorById(_vor_id, multi, exact);
+       }
+       //if(np == NULL) cout << "NULL... ";
+       //if(b == false) cout << "false...\n";
+       /*
+       if(np && b) {
+               cout << "VOR FOUND!\n";
+       } else {
+               cout << ":-(\n";
+       }
+       */
+       if(np) {
+               //cout << np->id << '\n';
+               _vor_id = np->get_ident();
+               if(_kln89->GetActiveWaypoint()) {
+                       if(_vor_id == _kln89->GetActiveWaypoint()->id) {
+                               if(!(_kln89->_waypointAlert && _kln89->_blink)) {
+                                       // Active waypoint arrow
+                                       _kln89->DrawSpecialChar(4, 2, 0, 3);
+                               }
+                       }
+               }
+               if(_kln89->_mode != KLN89_MODE_CRSR) {
+                       if(!_entInvert) {
+                               if(!actPage) {
+                                       _kln89->DrawText(np->get_ident(), 2, 1, 3);
+                               } else {
+                                       // If it's the ACT page, The ID is shifted slightly right to make space for the waypoint index.
+                                       _kln89->DrawText(np->get_ident(), 2, 4, 3);
+                                       char buf[3];
+                                       int n = snprintf(buf, 3, "%i", _kln89->GetActiveWaypointIndex() + 1);
+                                       _kln89->DrawText((string)buf, 2, 3 - n, 3);
+                               }
+                       } else {
+                               if(!_kln89->_blink) {
+                                       _kln89->DrawText(np->get_ident(), 2, 1, 3, false, 99);
+                                       _kln89->DrawEnt();
+                               }
+                       }
+               }
+               if(_subPage == 0) {
+                       //// TODO - will almost certainly have to process freq below for FG
+                       _kln89->DrawFreq(np->get_freq(), 2, 9, 3);
+                       // TODO - trim VOR-DME from the name, convert to uppercase, abbreviate, etc
+                       _kln89->DrawText(np->get_name(), 2, 0, 2);
+                       //cout << np->lat << "... ";
+                       _kln89->DrawLatitude(np->get_lat(), 2, 3, 1);
+                       _kln89->DrawLongitude(np->get_lon(), 2, 3, 0);
+               } else {
+                       _kln89->DrawText("Mag Var", 2, 0, 2);
+                       ////float mvf = np->magvar * SG_RADIANS_TO_DEGREES;
+                       //// TODO FIXME BELOW
+                       float mvf = 0.0;
+                       _kln89->DrawChar((mvf <= 0 ? 'E' : 'W'), 2, 9, 2);
+                       int mvi = (int)(fabs(mvf) + 0.5);
+                       string mvs = GPSitoa(mvi);
+                       _kln89->DrawText(mvs, 2, 13 - mvs.size(), 2);
+                       _kln89->DrawSpecialChar(0, 2, 13, 2);
+                       _kln89->DrawDirDistField(np->get_lat() * SG_DEGREES_TO_RADIANS, np->get_lon() * SG_DEGREES_TO_RADIANS, 2, 0, 0, 
+                                                _to_flag, (_kln89->_mode == KLN89_MODE_CRSR && _uLinePos == 4 ? true : false));
+               }
+       } else {
+               if(_kln89->_mode != KLN89_MODE_CRSR) _kln89->DrawText(_vor_id, 2, 1, 3);
+               if(_subPage == 0) {
+                       _kln89->DrawText("---.--", 2, 9, 3);
+                       _kln89->DrawText("--------------", 2, 0, 2);
+                       _kln89->DrawText("- -- --.--'", 2, 3, 1);
+                       _kln89->DrawText("---- --.--'", 2, 3, 0);
+                       _kln89->DrawSpecialChar(0, 2, 7, 1);
+                       _kln89->DrawSpecialChar(0, 2, 7, 0);
+               }
+       }
+       
+       if(_kln89->_mode == KLN89_MODE_CRSR) {
+               if(_uLinePos > 0 && _uLinePos < 4) {
+                       // TODO - blink as well
+                       _kln89->Underline(2, _uLinePos, 3, 1);
+               }
+               for(unsigned int i = 0; i < _vor_id.size(); ++i) {
+                       if(_uLinePos != (i + 1)) {
+                               _kln89->DrawChar(_vor_id[i], 2, i + 1, 3);
+                       } else {
+                               if(!_kln89->_blink) _kln89->DrawChar(_vor_id[i], 2, i + 1, 3);
+                       }
+               }
+       }
+       
+       _id = _vor_id;
+                       
+       KLN89Page::Update(dt);
+}
+
+void KLN89VorPage::SetId(string s) {
+       _last_vor_id = _vor_id;
+       _save_vor_id = _vor_id;
+       _vor_id = s;
+       np = NULL;
+}
+
+void KLN89VorPage::CrsrPressed() {
+       if(_kln89->_mode == KLN89_MODE_DISP) return;
+       if(_kln89->_obsMode) {
+               _uLinePos = 0;
+       } else {
+               _uLinePos = 1;
+       }
+       if(_subPage == 0) {
+               _maxULinePos = 17;
+       } else {
+               _maxULinePos = 4;
+       }
+}
+
+void KLN89VorPage::ClrPressed() {
+       if(_subPage == 1 && _uLinePos == 4) {
+               _to_flag = !_to_flag;
+       }
+}
+
+void KLN89VorPage::EntPressed() {
+       if(_entInvert) {
+               _entInvert = false;
+               _last_vor_id = _vor_id;
+               _vor_id = _save_vor_id;
+       }
+}
+
+void KLN89VorPage::Knob2Left1() {
+       if(_kln89->_mode != KLN89_MODE_CRSR || _uLinePos == 0) {
+               KLN89Page::Knob2Left1();
+       } else {
+               if(_uLinePos < 4) {
+                       // Same logic for both pages - set the ID
+                       _vor_id = _vor_id.substr(0, _uLinePos);
+                       // ASSERT(_uLinePos > 0);
+                       if(_uLinePos == (_vor_id.size() + 1)) {
+                               _vor_id += '9';
+                       } else {
+                               _vor_id[_uLinePos - 1] = _kln89->DecChar(_vor_id[_uLinePos - 1], (_uLinePos == 1 ? false : true));
+                       }
+               } else {
+                       if(_subPage == 0) {
+                               // set by name
+                       } else {
+                               // NO-OP - from/to field is switched by clr button, not inner knob.
+                       }
+               }
+       }
+}
+
+void KLN89VorPage::Knob2Right1() {
+       if(_kln89->_mode != KLN89_MODE_CRSR || _uLinePos == 0) {
+               KLN89Page::Knob2Right1();
+       } else {
+               if(_uLinePos < 4) {
+                       // Same logic for both pages - set the ID
+                       _vor_id = _vor_id.substr(0, _uLinePos);
+                       // ASSERT(_uLinePos > 0);
+                       if(_uLinePos == (_vor_id.size() + 1)) {
+                               _vor_id += 'A';
+                       } else {
+                               _vor_id[_uLinePos - 1] = _kln89->IncChar(_vor_id[_uLinePos - 1], (_uLinePos == 1 ? false : true));
+                       }
+               } else {
+                       if(_subPage == 0) {
+                               // set by name
+                       } else {
+                               // NO-OP - from/to field is switched by clr button, not inner knob.
+                       }
+               }
+       }
+}
diff --git a/src/Instrumentation/KLN89/kln89_page_vor.hxx b/src/Instrumentation/KLN89/kln89_page_vor.hxx
new file mode 100644 (file)
index 0000000..a9fc412
--- /dev/null
@@ -0,0 +1,52 @@
+// kln89_page_*.[ch]xx - this file is one of the "pages" that
+//                       are used in the KLN89 GPS unit simulation. 
+//
+// Written by David Luff, started 2005.
+//
+// Copyright (C) 2005 - David C Luff - david.luff@nottingham.ac.uk
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+// $Id$
+
+#ifndef _KLN89_PAGE_VOR_HXX
+#define _KLN89_PAGE_VOR_HXX
+
+#include "kln89.hxx"
+
+class KLN89VorPage : public KLN89Page {
+
+public:
+       KLN89VorPage(KLN89* parent);
+       ~KLN89VorPage();
+       
+       void Update(double dt);
+       
+       void CrsrPressed();
+       void ClrPressed();
+       void EntPressed();
+       void Knob2Left1();
+       void Knob2Right1();
+       
+       void SetId(string s);
+       
+private:
+       string _vor_id;
+       string _last_vor_id;
+       string _save_vor_id;
+       FGNavRecord* np;
+};
+
+#endif // _KLN89_PAGE_VOR_HXX