]> git.mxchange.org Git - flightgear.git/blob - src/Instrumentation/KLN89/kln89_page_apt.cxx
Merge branch 'next' of D:\Git_New\flightgear into next
[flightgear.git] / src / Instrumentation / KLN89 / kln89_page_apt.cxx
1 // kln89_page_*.[ch]xx - this file is one of the "pages" that
2 //                       are used in the KLN89 GPS unit simulation. 
3 //
4 // Written by David Luff, started 2005.
5 //
6 // Copyright (C) 2005 - David C Luff - david.luff@nottingham.ac.uk
7 //
8 // This program is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU General Public License as
10 // published by the Free Software Foundation; either version 2 of the
11 // License, or (at your option) any later version.
12 //
13 // This program is distributed in the hope that it will be useful, but
14 // WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 // General Public License for more details.
17 //
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the Free Software
20 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 //
22 // $Id$
23
24 #ifdef HAVE_CONFIG_H
25 #  include "config.h"
26 #endif
27
28 #include "kln89_page_apt.hxx"
29 #if ENABLE_ATCDCL
30 #    include <ATCDCL/commlist.hxx>
31 #else
32   #include <ATC/atcutils.hxx>
33 #endif
34 #include <Main/globals.hxx>
35 #include <Airports/runways.hxx>
36 #include <Airports/simple.hxx>
37
38 // This function is copied from Airports/runways.cxx
39 // TODO - Make the original properly available and remove this instance!!!!
40 // Return reverse rwy number
41 // eg 01 -> 19
42 // 03L -> 21R
43 static string GetReverseRunwayNo(string rwyno) {        
44     // cout << "Original rwyno = " << rwyNo << '\n';
45     
46     // standardize input number
47     string tmp = rwyno.substr(1, 1);
48     if (( tmp == "L" || tmp == "R" || tmp == "C" ) || (rwyno.size() == 1)) {
49                 tmp = rwyno;
50                 rwyno = "0" + tmp;
51                 SG_LOG( SG_GENERAL, SG_INFO,
52                         "Standardising rwy number from " << tmp << " to " << rwyno );
53     }
54     
55     char buf[4];
56     int rn = atoi(rwyno.substr(0,2).c_str());
57     rn += 18;
58     while(rn > 36) {
59                 rn -= 36;
60     }
61     sprintf(buf, "%02i", rn);
62     if(rwyno.size() == 3) {
63                 if(rwyno.substr(2,1) == "L") {
64                         buf[2] = 'R';
65                         buf[3] = '\0';
66                 } else if (rwyno.substr(2,1) == "R") {
67                         buf[2] = 'L';
68                         buf[3] = '\0';
69                 } else if (rwyno.substr(2,1) == "C") {
70                         buf[2] = 'C';
71                         buf[3] = '\0';
72                 } else if (rwyno.substr(2,1) == "T") {
73                         buf[2] = 'T';
74                         buf[3] = '\0';
75                 } else {
76                         SG_LOG(SG_GENERAL, SG_ALERT, "Unknown runway code "
77                         << rwyno << " passed to GetReverseRunwayNo(...)");
78                 }
79     }
80     return(buf);
81 }
82
83 KLN89AptPage::KLN89AptPage(KLN89* parent) 
84 : KLN89Page(parent) {
85         _nSubPages = 8;
86         _subPage = 0;
87         _name = "APT";
88         _apt_id = "KHWD";
89         // Make sure that _last_apt_id doesn't match at startup to force airport data to be fetched on first update.
90         _last_apt_id = "XXXX";
91         _nRwyPages = 1;
92         _curRwyPage = 0;
93         _nFreqPages = 1;
94         _curFreqPage = 0;
95         ap = NULL;
96         _iapStart = 0;
97         _iafStart = 0;
98         _fStart = 0;
99         _iaps.clear();
100         _iafDialog = false;
101         _addDialog = false;
102         _replaceDialog = false;
103         _curIap = 0;
104         _curIaf = 0;
105 }
106
107 KLN89AptPage::~KLN89AptPage() {
108 }
109
110 void KLN89AptPage::Update(double dt) {
111         bool actPage = (_kln89->_activePage->GetName() == "ACT" ? true : false);
112         bool multi;  // Not set by FindFirst...
113         bool exact = false;
114         if(_apt_id.size() == 4) exact = true;
115         // TODO - move this search out to where the button is pressed, and cache the result!
116         if(_apt_id != _last_apt_id || ap == NULL) ap = _kln89->FindFirstAptById(_apt_id, multi, exact);
117         //if(np == NULL) cout << "NULL... ";
118         //if(b == false) cout << "false...\n";
119         /*
120         if(np && b) {
121                 cout << "VOR FOUND!\n";
122         } else {
123                 cout << ":-(\n";
124         }
125         */
126         
127         if(ap) {
128                 //cout << "Valid airport found! id = " << ap->getId() << ", elev = " << ap->getElevation() << '\n';
129                 if(_apt_id != _last_apt_id) {
130                         UpdateAirport(ap->getId());
131                         _last_apt_id = _apt_id;
132                         _curFreqPage = 0;
133                         _curRwyPage = 0;
134                 }
135                 _apt_id = ap->getId();
136                 if(_kln89->GetActiveWaypoint()) {
137                         if(_apt_id == _kln89->GetActiveWaypoint()->id) {
138                                 if(!(_kln89->_waypointAlert && _kln89->_blink)) {
139                                         // Active waypoint arrow
140                                         _kln89->DrawSpecialChar(4, 2, 0, 3);
141                                 }
142                         }
143                 }
144                 if(_kln89->_mode != KLN89_MODE_CRSR) {
145                         if(!(_subPage == 7 && (_iafDialog || _addDialog || _replaceDialog))) {  // Don't draw the airport name when the IAP dialogs are active
146                                 if(!_entInvert) {
147                                         if(!actPage) {
148                                                 _kln89->DrawText(ap->getId(), 2, 1, 3);
149                                         } else {
150                                                 // If it's the ACT page, The ID is shifted slightly right to make space for the waypoint index.
151                                                 _kln89->DrawText(ap->getId(), 2, 4, 3);
152                                                 char buf[3];
153                                                 int n = snprintf(buf, 3, "%i", _kln89->GetActiveWaypointIndex() + 1);
154                                                 _kln89->DrawText((string)buf, 2, 3 - n, 3);
155                                         }
156                                 } else {
157                                         if(!_kln89->_blink) {
158                                                 _kln89->DrawText(ap->getId(), 2, 1, 3, false, 99);
159                                                 _kln89->DrawEnt();
160                                         }
161                                 }
162                         }
163                 }
164                 if(_subPage == 0) {
165                         // Name
166                         _kln89->DrawText(ap->getName(), 2, 0, 2);
167                         // Elevation
168                         _kln89->DrawText(_kln89->_altUnits == GPS_ALT_UNITS_FT ? "ft" : "m", 2, 14, 3);
169                         char buf[6];
170                         int n = snprintf(buf, 5, "%i", (_kln89->_altUnits == GPS_ALT_UNITS_FT ? (int)(ap->getElevation()) : (int)((double)ap->getElevation() * SG_FEET_TO_METER)));
171                         _kln89->DrawText((string)buf, 2, 14 - n, 3);
172                         // Town
173                         airport_id_str_map_iterator itr = _kln89->_airportTowns.find(_apt_id);
174                         if(itr != _kln89->_airportTowns.end()) {
175                                 _kln89->DrawText(itr->second, 2, 0, 1);
176                         }
177                         // State / Province / Country
178                         itr = _kln89->_airportStates.find(_apt_id);
179                         if(itr != _kln89->_airportStates.end()) {
180                                 _kln89->DrawText(itr->second, 2, 0, 0);
181                         }
182                 } else if(_subPage == 1) {
183                         _kln89->DrawLatitude(ap->getLatitude(), 2, 3, 2);
184                         _kln89->DrawLongitude(ap->getLongitude(), 2, 3, 1);
185                         _kln89->DrawDirDistField(ap->getLatitude() * SG_DEGREES_TO_RADIANS, ap->getLongitude() * SG_DEGREES_TO_RADIANS, 
186                                                  2, 0, 0, _to_flag, (_kln89->_mode == KLN89_MODE_CRSR && _uLinePos == 5 ? true : false));
187                 } else if(_subPage == 2) {
188                         // Try and calculate a realistic difference from UTC based on longitude
189                         // Since 0 longitude is the middle of UTC, the boundaries will be at 7.5, 22.5, 37.5 etc.
190                         int hrDiff = ((int)((fabs(ap->getLongitude())) + 7.5)) / 15;
191                         _kln89->DrawText("UTC", 2, 0, 2);
192                         if(hrDiff != 0) {
193                                 _kln89->DrawText(ap->getLongitude() >= 0.0 ? "+" : "-", 2, 3, 2);
194                                 char buf[3];
195                                 snprintf(buf, 3, "%02i", hrDiff);
196                                 _kln89->DrawText((string)buf, 2, 4, 2);
197                                 _kln89->DrawText("(   DT)", 2, 6, 2);
198                                 if(ap->getLongitude() >= 0.0) {
199                                         hrDiff++;
200                                 } else {
201                                         hrDiff--;
202                                 }
203                                 _kln89->DrawText(ap->getLongitude() >= 0.0 ? "+" : "-", 2, 7, 2);
204                                 snprintf(buf, 3, "%02i", hrDiff);
205                                 _kln89->DrawText((string)buf, 2, 8, 2);
206                         }
207                         // I guess we can make a heuristic guess as to fuel availability from the runway sizes
208                         // For now assume that airports with asphalt or concrete runways will have at least 100L,
209                         // and that runways over 4000ft will have JET.
210                         if(_aptRwys[0]->surface() <= 2) {
211                                 if(_aptRwys[0]->lengthFt() >= 4000) {
212                                         _kln89->DrawText("JET 100L", 2, 0, 1);
213                                 } else {
214                                         _kln89->DrawText("100L", 2, 0, 1);
215                                 }
216                         }
217                         if(_iaps.empty()) {
218                                 _kln89->DrawText("NO APR", 2, 0, 0);
219                         } else {
220                                 // TODO - output proper differentiation of ILS and NP APR and NP APR type eg GPS(R)
221                                 _kln89->DrawText("NP APR", 2, 0, 0);
222                         }
223                 } else if(_subPage == 3) {
224                         if(_nRwyPages > 1) {
225                                 _kln89->DrawChar('+', 1, 3, 0);
226                         }
227                         unsigned int i = _curRwyPage * 2;
228                         string s;
229                         if(i < _aptRwys.size()) {
230                                 // Rwy No.
231                                 string s = _aptRwys[i]->ident();
232                                 _kln89->DrawText(s, 2, 9, 3);
233                                 _kln89->DrawText("/", 2, 12, 3);
234                                 _kln89->DrawText(GetReverseRunwayNo(s), 2, 13, 3);
235                                 // Length
236                                 s = GPSitoa(int(float(_aptRwys[i]->lengthFt()) * (_kln89->_altUnits == GPS_ALT_UNITS_FT ? 1.0 : SG_FEET_TO_METER) + 0.5));
237                                 _kln89->DrawText(s, 2, 5 - s.size(), 2);
238                                 _kln89->DrawText((_kln89->_altUnits == GPS_ALT_UNITS_FT ? "ft" : "m"), 2, 5, 2);
239                                 // Surface
240                                 // TODO - why not store these strings as an array?
241                                 switch(_aptRwys[i]->surface()) {
242                                 case 1:
243                                         // Asphalt - fall through
244                                 case 2:
245                                         // Concrete
246                                         _kln89->DrawText("HRD", 2, 9, 2);
247                                         break;
248                                 case 3:
249                                 case 8:
250                                         // Turf / Turf helipad
251                                         _kln89->DrawText("TRF", 2, 9, 2);
252                                         break;
253                                 case 4:
254                                 case 9:
255                                         // Dirt / Dirt helipad
256                                         _kln89->DrawText("DRT", 2, 9, 2);
257                                         break;
258                                 case 5:
259                                         // Gravel
260                                         _kln89->DrawText("GRV", 2, 9, 2);
261                                         break;
262                                 case 6:
263                                         // Asphalt helipad - fall through
264                                 case 7:
265                                         // Concrete helipad
266                                         _kln89->DrawText("HRD", 2, 9, 2);
267                                         break;
268                                 case 12:
269                                         // Lakebed
270                                         _kln89->DrawText("CLY", 2, 9, 2);
271                                 default:
272                                         // erm? ...
273                                         _kln89->DrawText("MAT", 2, 9, 2);
274                                 }
275                         }
276                         i++;
277                         if(i < _aptRwys.size()) {
278                                 // Rwy No.
279                                 string s = _aptRwys[i]->ident();
280                                 _kln89->DrawText(s, 2, 9, 1);
281                                 _kln89->DrawText("/", 2, 12, 1);
282                                 _kln89->DrawText(GetReverseRunwayNo(s), 2, 13, 1);
283                                 // Length
284                                 s = GPSitoa(int(float(_aptRwys[i]->lengthFt()) * (_kln89->_altUnits == GPS_ALT_UNITS_FT ? 1.0 : SG_FEET_TO_METER) + 0.5));
285                                 _kln89->DrawText(s, 2, 5 - s.size(), 0);
286                                 _kln89->DrawText((_kln89->_altUnits == GPS_ALT_UNITS_FT ? "ft" : "m"), 2, 5, 0);
287                                 // Surface
288                                 // TODO - why not store these strings as an array?
289                                 switch(_aptRwys[i]->surface()) {
290                                 case 1:
291                                         // Asphalt - fall through
292                                 case 2:
293                                         // Concrete
294                                         _kln89->DrawText("HRD", 2, 9, 0);
295                                         break;
296                                 case 3:
297                                 case 8:
298                                         // Turf / Turf helipad
299                                         _kln89->DrawText("TRF", 2, 9, 0);
300                                         break;
301                                 case 4:
302                                 case 9:
303                                         // Dirt / Dirt helipad
304                                         _kln89->DrawText("DRT", 2, 9, 0);
305                                         break;
306                                 case 5:
307                                         // Gravel
308                                         _kln89->DrawText("GRV", 2, 9, 0);
309                                         break;
310                                 case 6:
311                                         // Asphalt helipad - fall through
312                                 case 7:
313                                         // Concrete helipad
314                                         _kln89->DrawText("HRD", 2, 9, 0);
315                                         break;
316                                 case 12:
317                                         // Lakebed
318                                         _kln89->DrawText("CLY", 2, 9, 0);
319                                 default:
320                                         // erm? ...
321                                         _kln89->DrawText("MAT", 2, 9, 0);
322                                 }
323                         }
324                 } else if(_subPage == 4) {
325                         if(_nFreqPages > 1) {
326                                 _kln89->DrawChar('+', 1, 3, 0);
327                         }
328                         unsigned int i = _curFreqPage * 3;
329                         if(i < _aptFreqs.size()) {
330                                 _kln89->DrawText(_aptFreqs[i].service, 2, 0, 2);
331                                 _kln89->DrawFreq(_aptFreqs[i].freq, 2, 7, 2);
332                         }
333                         i++;
334                         if(i < _aptFreqs.size()) {
335                                 _kln89->DrawText(_aptFreqs[i].service, 2, 0, 1);
336                                 _kln89->DrawFreq(_aptFreqs[i].freq, 2, 7, 1);
337                         }
338                         i++;
339                         if(i < _aptFreqs.size()) {
340                                 _kln89->DrawText(_aptFreqs[i].service, 2, 0, 0);
341                                 _kln89->DrawFreq(_aptFreqs[i].freq, 2, 7, 0);
342                         }
343                 } else if(_subPage == 5) {
344                         // TODO - user ought to be allowed to leave persistent remarks
345                         _kln89->DrawText("[Remarks]", 2, 2, 2);
346                 } else if(_subPage == 6) {
347                         // We don't have SID/STAR database yet
348                         // TODO
349                         _kln89->DrawText("No SID/STAR", 2, 3, 2);
350                         _kln89->DrawText("In Data Base", 2, 2, 1);
351                         _kln89->DrawText("For This Airport", 2, 0, 0);
352                 } else if(_subPage == 7) {
353                         if(_iaps.empty()) {
354                                 _kln89->DrawText("IAP", 2, 11, 3);
355                                 _kln89->DrawText("No Approach", 2, 3, 2);
356                                 _kln89->DrawText("In Data Base", 2, 2, 1);
357                                 _kln89->DrawText("For This Airport", 2, 0, 0);
358                         } else {
359                                 if(_iafDialog) {
360                                         _kln89->DrawText(_iaps[_curIap]->_ident, 2, 1, 3);
361                                         _kln89->DrawText(_iaps[_curIap]->_rwyStr, 2, 7, 3);
362                                         _kln89->DrawText(_iaps[_curIap]->_aptIdent, 2, 12, 3);
363                                         _kln89->DrawText("IAF", 2, 2, 2);
364                                         unsigned int line = 0;
365                                         for(unsigned int i=_iafStart; i<_approachRoutes.size(); ++i) {
366                                                 if(line == 2) {
367                                                         i = _approachRoutes.size() - 1;
368                                                 }
369                                                 // Assume that the IAF number is always single digit!
370                                                 _kln89->DrawText(GPSitoa(i+1), 2, 6, 2-line);
371                                                 if(!(_kln89->_mode == KLN89_MODE_CRSR && _kln89->_blink && _uLinePos == (line + 1))) {
372                                                         _kln89->DrawText(_approachRoutes[i]->waypoints[0]->id, 2, 8, 2-line);
373                                                 }
374                                                 if(_kln89->_mode == KLN89_MODE_CRSR && _uLinePos == (line + 1) && !(_kln89->_blink )) {
375                                                         _kln89->Underline(2, 8, 2-line, 5);
376                                                 }
377                                                 ++line;
378                                         }
379                                         if(_uLinePos > 0 && !(_kln89->_blink)) {
380                                                 _kln89->DrawEnt();
381                                         }
382                                 } else if(_addDialog) {
383                                         _kln89->DrawText(_iaps[_curIap]->_ident, 2, 1, 3);
384                                         _kln89->DrawText(_iaps[_curIap]->_rwyStr, 2, 7, 3);
385                                         _kln89->DrawText(_iaps[_curIap]->_aptIdent, 2, 12, 3);
386                                         string s = GPSitoa(_fStart + 1);
387                                         _kln89->DrawText(s, 2, 2-s.size(), 2);
388                                         s = GPSitoa(_kln89->_approachFP->waypoints.size());
389                                         _kln89->DrawText(s, 2, 2-s.size(), 1);
390                                         if(!(_uLinePos == _fStart+1 && _kln89->_blink)) {
391                                                 _kln89->DrawText(_kln89->_approachFP->waypoints[_fStart]->id, 2, 4, 2);
392                                                 if(_uLinePos == _fStart+1) _kln89->Underline(2, 4, 2, 6);
393                                         }
394                                         if(!(_uLinePos == _maxULinePos-1 && _kln89->_blink)) {
395                                                 _kln89->DrawText(_kln89->_approachFP->waypoints[_kln89->_approachFP->waypoints.size()-1]->id, 2, 4, 1);
396                                                 if(_uLinePos == _maxULinePos-1) _kln89->Underline(2, 4, 1, 6);
397                                         }
398                                         if(!(_uLinePos > _kln89->_approachFP->waypoints.size() && _kln89->_blink)) {
399                                                 _kln89->DrawText("ADD TO FPL 0?", 2, 2, 0);
400                                                 if(_uLinePos > _kln89->_approachFP->waypoints.size()) {
401                                                         _kln89->Underline(2, 2, 0, 13);
402                                                         _kln89->DrawEnt();
403                                                 }
404                                         }
405                                 } else if(_replaceDialog) {
406                                         _kln89->DrawText(_iaps[_curIap]->_ident, 2, 1, 3);
407                                         _kln89->DrawText(_iaps[_curIap]->_rwyStr, 2, 7, 3);
408                                         _kln89->DrawText(_iaps[_curIap]->_aptIdent, 2, 12, 3);
409                                         _kln89->DrawText("Replace Existing", 2, 0, 2);
410                                         _kln89->DrawText("Approach", 2, 4, 1);
411                                         if(_uLinePos > 0 && !(_kln89->_blink)) {
412                                                 _kln89->DrawText("APPROVE?", 2, 4, 0);
413                                                 _kln89->Underline(2, 4, 0, 8);
414                                                 _kln89->DrawEnt();
415                                         }
416                                 } else {
417                                         _kln89->DrawText("IAP", 2, 11, 3);
418                                         bool selApp = false;
419                                         if(_kln89->_mode == KLN89_MODE_CRSR && _uLinePos > 4) {
420                                                 selApp = true;
421                                                 if(!_kln89->_blink) _kln89->DrawEnt();
422                                         }
423                                         // _maxULine pos should be 4 + iaps.size() at this point.
424                                         // Draw a maximum of 3 IAPs.
425                                         // If there are more than 3 IAPs for this airport, then we need to offset the start
426                                         // of the list if _uLinePos is pointing at the 4th or later IAP.
427                                         unsigned int offset = 0;
428                                         unsigned int index;
429                                         if(_uLinePos > 7) {
430                                                 offset = _uLinePos - 7;
431                                         }
432                                         for(unsigned int i=0; i<3; ++i) {
433                                                 index = offset + i;
434                                                 if(index < _iaps.size()) {
435                                                         string s = GPSitoa(index+1);
436                                                         _kln89->DrawText(s, 2, 2 - s.size(), 2-i);
437                                                         if(!(selApp && _uLinePos == index+5 && _kln89->_blink)) {
438                                                                 _kln89->DrawText(_iaps[index]->_ident, 2, 3, 2-i);
439                                                                 _kln89->DrawText(_iaps[index]->_rwyStr, 2, 9, 2-i);
440                                                         }
441                                                         if(selApp && _uLinePos == index+5 && !_kln89->_blink) {
442                                                                 _kln89->Underline(2, 3, 2-i, 9);
443                                                         }
444                                                 } else {
445                                                         break;
446                                                 }
447                                         }
448                                 }
449                         }
450                 }
451         } else {
452                 if(_kln89->_mode != KLN89_MODE_CRSR) _kln89->DrawText(_apt_id, 2, 1, 3);
453                 if(_subPage == 0) {
454                         /*
455                         _kln89->DrawText("----.-", 2, 9, 3);
456                         _kln89->DrawText("--------------", 2, 0, 2);
457                         _kln89->DrawText("- -- --.--'", 2, 3, 1);
458                         _kln89->DrawText("---- --.--'", 2, 3, 0);
459                         _kln89->DrawSpecialChar(0, 2, 7, 1);
460                         _kln89->DrawSpecialChar(0, 2, 7, 0);
461                         */
462                 }
463         }
464         
465         if(_kln89->_mode == KLN89_MODE_CRSR) {
466                 if(!(_subPage == 7 && (_iafDialog || _addDialog || _replaceDialog))) {
467                         if(_uLinePos > 0 && _uLinePos < 5) {
468                                 // TODO - blink as well
469                                 _kln89->Underline(2, _uLinePos, 3, 1);
470                         }
471                         for(unsigned int i = 0; i < _apt_id.size(); ++i) {
472                                 if(_uLinePos != (i + 1)) {
473                                         _kln89->DrawChar(_apt_id[i], 2, i + 1, 3);
474                                 } else {
475                                         if(!_kln89->_blink) _kln89->DrawChar(_apt_id[i], 2, i + 1, 3);
476                                 }
477                         }
478                 }
479         }
480         
481         _id = _apt_id;
482
483         KLN89Page::Update(dt);
484 }
485
486 void KLN89AptPage::SetId(const string& s) {
487         if(s != _apt_id || s != _last_apt_id) {
488                 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.
489                 /*
490                         DCL: Hmmm - I wrote the comment above, but I don't quite understand it!
491                         I'm not quite sure why I don't simply set _apt_id here (and NOT _last_apt_id)
492                         and let the logic in Update(...) handle the airport details cache update.
493                 */
494         }
495         _last_apt_id = _apt_id;
496         _save_apt_id = _apt_id;
497         _apt_id = s;
498 }
499
500 // Update the cached airport details
501 void KLN89AptPage::UpdateAirport(const string& id) {
502         // Frequencies
503         _aptFreqs.clear();
504         ATCData ad;
505         AptFreq aq;
506         //cout << "UpdateAirport called, id = " << id << '\n';
507         // TODO - the logic below only returns one service per type per airport - they can be on more than one freq though.
508         if(current_commlist->FindByCode(id, ad, ATIS)) {
509                 //cout << "Found ATIS\n";
510                 aq.service = "ATIS*";
511                 aq.freq = ad.freq;
512                 _aptFreqs.push_back(aq);
513         }
514         if(current_commlist->FindByCode(id, ad, GROUND)) {
515                 aq.service = "GRND*";
516                 aq.freq = ad.freq;
517                 _aptFreqs.push_back(aq);
518         }
519         if(current_commlist->FindByCode(id, ad, TOWER)) {
520                 aq.service = "TWR *";
521                 aq.freq = ad.freq;
522                 _aptFreqs.push_back(aq);
523         }
524         if(current_commlist->FindByCode(id, ad, APPROACH)) {
525                 aq.service = "APR";
526                 aq.freq = ad.freq;
527                 _aptFreqs.push_back(aq);
528         }
529         _nFreqPages = (unsigned int)ceil((float(_aptFreqs.size())) / 3.0f);
530         
531         // Runways
532         _aptRwys.clear();
533   const FGAirport* apt = fgFindAirportID(id);
534   assert(apt);
535   
536   // build local array, longest runway first
537   for (unsigned int r=0; r<apt->numRunways(); ++r) {
538     FGRunway* rwy(apt->getRunwayByIndex(r));
539     if ((r > 0) && (rwy->lengthFt() > _aptRwys.front()->lengthFt())) {
540       _aptRwys.insert(_aptRwys.begin(), rwy);
541     } else {
542       _aptRwys.push_back(rwy);
543     }
544   }
545   
546         _nRwyPages = (_aptRwys.size() + 1) / 2; // 2 runways per page.
547         if(_nFreqPages < 1) _nFreqPages = 1;
548         if(_nRwyPages < 1) _nRwyPages = 1;
549         
550         // Instrument approaches
551         // Only non-precision for now - TODO - handle precision approaches if necessary
552         _iaps.clear();
553         iap_map_iterator itr = _kln89->_np_iap.find(id);
554         if(itr != _kln89->_np_iap.end()) {
555                 _iaps = itr->second;
556         }
557         if(_subPage == 7) { 
558                 if(_iafDialog || _addDialog || _replaceDialog) {
559                         // Eek - major logic error if an airport details cache update occurs
560                         // with one of these dialogs active.
561                         // TODO - output a warning.
562                         //cout << "HELP!!!!!!!!!!\n";
563                 } else {
564                         _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!
565                 }
566         }
567 }
568
569 void KLN89AptPage::CrsrPressed() {
570         if(_kln89->_mode == KLN89_MODE_DISP) {
571                 if(_subPage == 7) {
572                         // Pressing crsr jumps back to vanilla IAP page.
573                         _iafDialog = false;
574                         _addDialog = false;
575                         _replaceDialog = false;
576                 }
577                 return;
578         }
579         if(_kln89->_obsMode) {
580                 _uLinePos = 0;
581         } else {
582                 _uLinePos = 1;
583         }
584         if(_subPage == 0) {
585                 _maxULinePos = 32;
586         } else if(_subPage == 7) {
587                 // Don't *think* we need some of this since some of it we can only get to by pressing ENT, not CRSR.
588                 if(_iafDialog) {
589                         _maxULinePos = _approachRoutes.size();
590                         _uLinePos = 1;
591                 } else if(_addDialog) {
592                         _maxULinePos = 1;
593                         _uLinePos = 1;
594                 } else if(_replaceDialog) {
595                         _maxULinePos = 1;
596                         _uLinePos = 1;
597                 } else {
598                         _maxULinePos = 4 + _iaps.size();
599                         if(_iaps.empty()) {
600                                 _uLinePos = 1;
601                         } else {
602                                 _uLinePos = 5;
603                         }
604                 }
605         } else {
606                 _maxULinePos = 5;
607         }
608 }
609
610 void KLN89AptPage::ClrPressed() {
611         if(_subPage == 1 && _uLinePos == 5) {
612                 _to_flag = !_to_flag;
613         } else if(_subPage == 7) {
614                 // Clear backs out IAP selection one step at a time
615                 if(_iafDialog) {
616                         _iafDialog = false;
617                         _maxULinePos = 4 + _iaps.size();
618                         if(_iaps.empty()) {
619                                 _uLinePos = 1;
620                         } else {
621                                 _uLinePos = 5;
622                         }
623                 } else if(_addDialog) {
624                         _addDialog = false;
625                         if(_approachRoutes.size() > 1) {
626                                 _iafDialog = true;
627                                 _maxULinePos = 1;
628                                 // Don't reset _curIaf since it is remembed.
629                                 _uLinePos = 1 + _curIaf;        // TODO - make this robust to more than 3 IAF
630                         } else {
631                                 _maxULinePos = 4 + _iaps.size();
632                                 if(_iaps.empty()) {
633                                         _uLinePos = 1;
634                                 } else {
635                                         _uLinePos = 5;
636                                 }
637                         }                       
638                 } else if(_replaceDialog) {
639                         _replaceDialog = false;
640                         _addDialog = true;
641                         _maxULinePos = 1;
642                         _uLinePos = 1;
643                 }
644         }
645 }
646
647 void KLN89AptPage::EntPressed() {
648         if(_entInvert) {
649                 _entInvert = false;
650                 if(_kln89->_dtoReview) {
651                         _kln89->DtoInitiate(_apt_id);
652                 } else {
653                         _last_apt_id = _apt_id;
654                         _apt_id = _save_apt_id;
655                 }
656         } else if(_subPage == 7 && _kln89->_mode == KLN89_MODE_CRSR && _uLinePos > 0) {
657                 // We are selecting an approach
658                 if(_iafDialog) {
659                         if(_uLinePos > 0) {
660                                 // Record the IAF that was picked
661                                 if(_uLinePos == 3) {
662                                         _curIaf = _approachRoutes.size() - 1;
663                                 } else {
664                                         _curIaf = _uLinePos - 1 + _iafStart;
665                                 }
666                                 //cout << "_curIaf = " << _curIaf << '\n';
667                                 // TODO - delete the waypoints inside _approachFP before clearing them!!!!!!!
668                                 _kln89->_approachFP->waypoints.clear();
669                                 GPSWaypoint* wp = new GPSWaypoint;
670                                 *wp = *(_approachRoutes[_curIaf]->waypoints[0]);        // Need to make copies here since we're going to alter ID and type sometimes
671                                 string iafid = wp->id;
672                                 _kln89->_approachFP->waypoints.push_back(wp);
673                                 for(unsigned int i=0; i<_IAP.size(); ++i) {
674                                         if(_IAP[i]->id != iafid) {      // Don't duplicate waypoints that are part of the initial fix list and the approach procedure list.
675                         // FIXME - allow the same waypoint to be both the IAF and the FAF in some
676                         // approaches that have a procedure turn eg. KDLL
677                         // Also allow MAF to be the same as IAF!
678                                                 wp = new GPSWaypoint;
679                                                 *wp = *_IAP[i];
680                                                 //cout << "Adding waypoint " << wp->id << ", type is " << wp->appType << '\n';
681                                                 //if(wp->appType == GPS_FAF) wp->id += 'f';
682                                                 //if(wp->appType == GPS_MAP) wp->id += 'm';
683                                                 //cout << "New id = " << wp->id << '\n';
684                                                 _kln89->_approachFP->waypoints.push_back(wp);
685                                         }
686                                 }
687                                 _iafDialog = false;
688                                 _addDialog = true;
689                                 _maxULinePos = _kln89->_approachFP->waypoints.size() + 1;
690                                 _uLinePos = _maxULinePos;
691                         }
692                 } else if(_addDialog) {
693                         if(_uLinePos == _maxULinePos) {
694                                 _addDialog = false;
695                                 if(_kln89->ApproachLoaded()) {
696                                         _replaceDialog = true;
697                                         _uLinePos = 1;
698                                         _maxULinePos = 1;
699                                 } else {
700                     // Now load the approach into the active flightplan.
701                     // As far as I can tell, the rules are this:
702                     // 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).
703                     // If the airport is not in the flightplan, append the approach to the flightplan, even if it is closer than the current active leg,
704                     // in which case reorientate to flightplan might put us on the approach, but unable to activate it.
705                     // However, it appears from the sim as if this can indeed happen if the user is not carefull.
706                     bool added = false;
707                     for(unsigned int i=0; i<_kln89->_activeFP->waypoints.size(); ++i) {
708                         if(_kln89->_activeFP->waypoints[i]->id == _apt_id) {
709                             _kln89->_activeFP->waypoints.insert(_kln89->_activeFP->waypoints.begin()+i, _kln89->_approachFP->waypoints.begin(), _kln89->_approachFP->waypoints.end());
710                             added = true;
711                             break;
712                         }
713                     }
714                     if(!added) {
715                         _kln89->_activeFP->waypoints.insert(_kln89->_activeFP->waypoints.end(), _kln89->_approachFP->waypoints.begin(), _kln89->_approachFP->waypoints.end());
716                     }
717                                         _kln89->_approachID = _apt_id;
718                                         _kln89->_approachAbbrev = _iaps[_curIap]->_ident;
719                                         _kln89->_approachRwyStr = _iaps[_curIap]->_rwyStr;
720                                         _kln89->_approachLoaded = true;
721                                         //_kln89->_messageStack.push_back("*Press ALT To Set Baro");
722                                         // Actually - this message is only sent when we go into appraoch-arm mode.
723                                         // TODO - check the flightplan for consistency
724                                         _kln89->OrientateToActiveFlightPlan();
725                                         _kln89->_mode = KLN89_MODE_DISP;
726                                         _kln89->_curPage = 7;
727                                         _kln89->_activePage = _kln89->_pages[7];        // Do we need to clean up here at all before jumping?
728                                 }
729                         }
730                 } else if(_replaceDialog) {
731                         // TODO - load the approach!
732                 } else if(_uLinePos > 4) {
733                         _approachRoutes.clear();
734                         _IAP.clear();
735                         _curIaf = 0;
736                         _approachRoutes = ((FGNPIAP*)(_iaps[_uLinePos-5]))->_approachRoutes;
737                         _IAP = ((FGNPIAP*)(_iaps[_uLinePos-5]))->_IAP;
738                         _curIap = _uLinePos - 5;        // TODO - handle the start of list ! no. 1, and the end of list not sequential!
739                         _uLinePos = 1;
740                         if(_approachRoutes.size() > 1) {
741                                 // More than 1 IAF - display the selection dialog
742                                 _iafDialog = true;
743                                 _maxULinePos = _approachRoutes.size();
744                         } else {
745                                 // There is only 1 IAF, so load the waypoints into the approach flightplan here.
746                                 // TODO - there is nasty code duplication loading the approach FP between the case here where we have only one
747                                 // IAF and the case where we must choose the IAF from a list.  Try to tidy this after it is all working properly.
748                                 _kln89->_approachFP->waypoints.clear();
749                                 GPSWaypoint* wp = new GPSWaypoint;
750                                 *wp = *(_approachRoutes[0]->waypoints[0]);      // Need to make copies here since we're going to alter ID and type sometimes
751                                 string iafid = wp->id;
752                                 _kln89->_approachFP->waypoints.push_back(wp);
753                                 for(unsigned int i=0; i<_IAP.size(); ++i) {
754                                         if(_IAP[i]->id != iafid) {      // Don't duplicate waypoints that are part of the initial fix list and the approach procedure list.
755                                                 // FIXME - allow the same waypoint to be both the IAF and the FAF in some
756                                                 // approaches that have a procedure turn eg. KDLL
757                                                 // Also allow MAF to be the same as IAF!
758                                                 wp = new GPSWaypoint;
759                                                 *wp = *_IAP[i];
760                                                 _kln89->_approachFP->waypoints.push_back(wp);
761                                         }
762                                 }
763                                 _addDialog = true;
764                                 _maxULinePos = 1;
765                         }
766                 }
767         }
768 }
769
770 void KLN89AptPage::Knob1Left1() {
771         if(_kln89->_mode == KLN89_MODE_CRSR && _subPage == 7 && _addDialog) {
772                 if(_uLinePos == _maxULinePos) {
773                         _uLinePos--;
774                         if(_kln89->_approachFP->waypoints.size() > 1) _fStart = _kln89->_approachFP->waypoints.size() - 2;
775                 } else if(_uLinePos == _maxULinePos - 1) {
776                         _uLinePos--;
777                 } else if(_uLinePos > 0) {
778                         if(_fStart == 0) {
779                                 _uLinePos--;
780                         } else {
781                                 _uLinePos--;
782                                 _fStart--;
783                         }
784                 }
785         } else {
786                 KLN89Page::Knob1Left1();
787         }
788 }
789
790 void KLN89AptPage::Knob1Right1() {
791         if(_kln89->_mode == KLN89_MODE_CRSR && _subPage == 7 && _addDialog) {
792                 if(_uLinePos == _maxULinePos) {
793                         // no-op
794                 } else if(_uLinePos == _maxULinePos - 1) {
795                         _uLinePos++;
796                         _fStart = 0;
797                 } else if(_uLinePos > 0) {
798                         if(_fStart >= _kln89->_approachFP->waypoints.size() - 2) {
799                                 _uLinePos++;
800                         } else {
801                                 _uLinePos++;
802                                 _fStart++;
803                         }
804                 } else if(_uLinePos == 0) {
805                         _uLinePos++;
806                         _fStart = 0;
807                 }
808         } else {
809                 KLN89Page::Knob1Right1();
810         }
811 }
812
813 void KLN89AptPage::Knob2Left1() {
814         if(_kln89->_mode != KLN89_MODE_CRSR || _uLinePos == 0) {
815                 if(_uLinePos == 0 && _kln89->_mode == KLN89_MODE_CRSR && _kln89->_obsMode) {
816                         KLN89Page::Knob2Left1();
817                 } else if(_subPage == 5) {
818                         _subPage = 4;
819                         _curFreqPage = _nFreqPages - 1;
820                 } else if(_subPage == 4) {
821                         // Freqency pages
822                         if(_curFreqPage == 0) {
823                                 _subPage = 3;
824                                 _curRwyPage = _nRwyPages - 1;
825                         } else {
826                                 _curFreqPage--;
827                         }
828                 } else if(_subPage == 3) {
829                         if(_curRwyPage == 0) {
830                                 KLN89Page::Knob2Left1();
831                         } else {
832                                 _curRwyPage--;
833                         }
834                 } else if(_subPage == 0) {
835                         _subPage = 7;
836                         // We have to set _uLinePos here even though the cursor isn't pressed, to
837                         // ensure that the list displays properly.
838                         if(_iaps.empty()) {
839                                 _uLinePos = 1;
840                         } else {
841                                 _uLinePos = 5;
842                         }
843                 } else {
844                         KLN89Page::Knob2Left1();
845                 }
846         } else {
847                 if(_uLinePos < 5 && !(_subPage == 7 && (_iafDialog || _addDialog || _replaceDialog))) {
848                         // Same logic for all pages - set the ID
849                         _apt_id = _apt_id.substr(0, _uLinePos);
850                         // ASSERT(_uLinePos > 0);
851                         if(_uLinePos == (_apt_id.size() + 1)) {
852                                 _apt_id += '9';
853                         } else {
854                                 _apt_id[_uLinePos - 1] = _kln89->DecChar(_apt_id[_uLinePos - 1], (_uLinePos == 1 ? false : true));
855                         }
856                 } else {
857                         if(_subPage == 0) {
858                                 // TODO - set by name
859                         } else {
860                                 // NO-OP - to/fr is cycled by clr button
861                         }
862                 }
863         }
864 }
865
866 void KLN89AptPage::Knob2Right1() {
867         if(_kln89->_mode != KLN89_MODE_CRSR || _uLinePos == 0) {
868                 if(_uLinePos == 0 && _kln89->_mode == KLN89_MODE_CRSR && _kln89->_obsMode) {
869                         KLN89Page::Knob2Right1();
870                 } else if(_subPage == 2) {
871                         _subPage = 3;
872                         _curRwyPage = 0;
873                 } else if(_subPage == 3) {
874                         if(_curRwyPage == _nRwyPages - 1) {
875                                 _subPage = 4;
876                                 _curFreqPage = 0;
877                         } else {
878                                 _curRwyPage++;
879                         }
880                 } else if(_subPage == 4) {
881                         if(_curFreqPage == _nFreqPages - 1) {
882                                 _subPage = 5;
883                         } else {
884                                 _curFreqPage++;
885                         }
886                 } else if(_subPage == 6) {
887                         _subPage = 7;
888                         // We have to set _uLinePos here even though the cursor isn't pressed, to
889                         // ensure that the list displays properly.
890                         if(_iaps.empty()) {
891                                 _uLinePos = 1;
892                         } else {
893                                 _uLinePos = 5;
894                         }
895                 } else {
896                         KLN89Page::Knob2Right1();
897                 }
898         } else {
899                 if(_uLinePos < 5 && !(_subPage == 7 && (_iafDialog || _addDialog || _replaceDialog))) {
900                         // Same logic for all pages - set the ID
901                         _apt_id = _apt_id.substr(0, _uLinePos);
902                         // ASSERT(_uLinePos > 0);
903                         if(_uLinePos == (_apt_id.size() + 1)) {
904                                 _apt_id += 'A';
905                         } else {
906                                 _apt_id[_uLinePos - 1] = _kln89->IncChar(_apt_id[_uLinePos - 1], (_uLinePos == 1 ? false : true));
907                         }
908                 } else {
909                         if(_subPage == 0) {
910                                 // TODO - set by name
911                         } else {
912                                 // NO-OP - to/fr is cycled by clr button
913                         }
914                 }
915         }
916 }