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