1 // kln89_page_*.[ch]xx - this file is one of the "pages" that
2 // are used in the KLN89 GPS unit simulation.
4 // Written by David Luff, started 2005.
6 // Copyright (C) 2005 - David C Luff - daveluff AT ntlworld.com
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.
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.
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.
28 #include "kln89_page_fpl.hxx"
35 KLN89FplPage::KLN89FplPage(KLN89* parent)
55 KLN89FplPage::~KLN89FplPage() {
58 void KLN89FplPage::Update(double dt) {
61 // NOTE - we need to draw the active leg arrow outside of this block to avoid the _delFP check.
62 // TODO - we really ought to merge the page 0 and other pages drawing code with a couple of lines of extra logic.
63 if(_subPage == 0 && !_delFP) { // Note that in the _delFP case, the active flightplan gets a header, and hence the same geometry as the other fps, so we draw it there.
65 // NOTE THAT FOR THE ACTIVE FLIGHT PLAN, TOP POSITION IS STILL 4 in the underline position scheme, to make
66 // copy and paste easier!!!!
68 // ---------------------------------- Copy the active FlightPlan and insert approach header and fence if required ---------------
69 // For synatical convienience
70 //vector<GPSWaypoint*> waylist = _kln89->_flightPlans[_subPage]->waypoints;
71 // Copy every waypoint for now.
72 // This is inefficient, but allows us to insert dummy waypoints to represent the header and fence
73 // in our *local copy* of the flightplan, if an approach is loaded. There must be a better way to do this!!!!
74 vector<GPSWaypoint> waylist;
75 for(unsigned int i=0; i<_kln89->_flightPlans[_subPage]->waypoints.size(); ++i) {
76 waylist.push_back(*_kln89->_flightPlans[_subPage]->waypoints[i]);
80 if(_kln89->_approachLoaded) {
83 wp.type = GPS_WP_VIRT;
85 for(unsigned int i=0; i<waylist.size(); ++i) {
86 // Insert the hdr immediately before the IAF
87 if(waylist[i].appType == GPS_IAF) {
88 waylist.insert(waylist.begin()+i, wp);
89 // Need to insert empty string into the params to keep them in sync
90 // Guard against empty params list.
91 // This shouldn't happen, but currently it can until ETE and UTC params are implemeted
92 // (and better the ugly params list ripped out and replaced with something more maintainable!).
93 if(!_params.empty()) {
94 _params.insert(_params.begin()+i-1, "");
101 wp.type = GPS_WP_VIRT;
102 wp.appType = GPS_FENCE;
103 for(unsigned int i=0; i<waylist.size(); ++i) {
104 // Insert the fence between the MAF and the MAP
105 if(waylist[i].appType == GPS_MAHP) {
106 waylist.insert(waylist.begin()+i, wp);
107 // Need to insert empty string into the params to keep them in sync
108 // Guard against empty params list. See comments a few lines above.
109 if(!_params.empty()) {
110 _params.insert(_params.begin()+i-1, "");
118 // Now make up a vector of waypoint numbers, since they aren't aligned with list position anymore
122 for(unsigned int i=0; i<waylist.size(); ++i) {
123 if(waylist[i].appType != GPS_HDR && waylist[i].appType != GPS_FENCE) {
124 numlist.push_back(num);
127 numlist.push_back(-1);
132 for(unsigned int i=0; i<waylist.size(); ++i) {
133 //cout << i + 1 - hfcount << ": ID= " << waylist[i].id;
134 if(waylist[i].appType == GPS_HDR) {
138 if(waylist[i].appType == GPS_FENCE) {
144 //----------------------------------------- end active FP copy ------------------------------------------------
146 // Recalculate which waypoint is displayed at the top of the list if required (generally if this page has lost focus).
147 int idx = _kln89->GetActiveWaypointIndex();
149 if(waylist.size() <= 1) {
151 } else if(waylist.size() <= 4) {
154 // Make the active waypoint the second WP displayed
160 //cout << "HeaderPos = " << _hdrPos << ", fencePos = " << _fencePos << ", _fplPos = " << _fplPos << ", active waypoint index = " << _parent->GetActiveWaypointIndex() << '\n';
161 if(_hdrPos >= 0 && idx >= _hdrPos) {
163 if(_fencePos >= 0 && (idx + 1) >= _fencePos) {
167 _resetFplPos0 = false;
170 // Increment the active waypoint position if required due hdr and fence here not above so it gets called every frame
171 if(_hdrPos >= 0 && idx >= _hdrPos) {
173 if(_fencePos >= 0 && idx >= _fencePos) {
178 // Draw the leg arrow etc
179 int diff = idx - (int)_fplPos;
182 // No active waypoint
183 } else if(diff < 0) {
184 // Off screen to the top
185 } else if(diff > 2) {
190 // Only the head is blinked during waypoint alerting
191 if(!(_kln89->_waypointAlert && _kln89->_blink)) {
192 _kln89->DrawSpecialChar(4, 2, 0, 3-drawPos);
194 // If the active waypoint is immediately after an approach header then we need to do an extra-long tail leg
195 if(_hdrPos >= 0 && idx == _hdrPos + 1) {
196 if(drawPos > 0 && !_kln89->_dto) _kln89->DrawLongLegTail(3-drawPos);
198 if(drawPos > 0 && !_kln89->_dto) _kln89->DrawLegTail(3-drawPos);
201 //cout << "Top pos is " << _fplPos0 << ' ';
203 if(_kln89->_mode == KLN89_MODE_CRSR) {
205 _kln89->Underline(2, 13, 3, 3);
206 } else if(_uLinePos >= 4) {
209 _kln89->Underline(2, 5, 3 - (_uLinePos - 4), 4);
210 } else if(_wLinePos == 4) {
211 _kln89->Underline(2, 4, 3 - (_uLinePos - 4), 4);
213 _kln89->Underline(2, 4, 3 - (_uLinePos - 4), _wLinePos);
214 _kln89->Underline(2, 5 + _wLinePos, 3 - (_uLinePos - 4), 4 - _wLinePos);
216 if(!_kln89->_blink) {
217 //_kln89->DrawText(_entWp->id, 2, 4, 2 - (_uLinePos - 4), false, _wLinePos);
221 _kln89->Underline(2, 4, 3 - (_uLinePos - 4), 5);
225 // ----------------------------------
227 // Sanity check the top position - remember that we can have an extra blank one at the bottom even if CRSR is off if crsr is switched on then off
228 if((int)_fplPos > ((int)waylist.size()) - 3) _fplPos = (((int)waylist.size()) - 3 < 0 ? 0 : waylist.size() - 3);
229 unsigned int last_pos;
230 if(waylist.empty()) {
233 last_pos = ((int)_fplPos == ((int)waylist.size()) - 3 ? waylist.size() : waylist.size() - 1);
235 //cout << "Initialising last_pos, last_pos = " << last_pos << '\n';
236 if(waylist.size() < 4) last_pos = waylist.size();
238 // Don't draw the cyclic field header if the top waypoint is the approach header
239 // Not sure if this also applies to the fence - don't think so but TODO - check!
240 if(!waylist.empty() && _fplPos < waylist.size()) {
241 if(waylist[_fplPos].appType != GPS_HDR) {
242 _kln89->DrawChar('>', 2, 12, 3);
243 if(!(_kln89->_mode == KLN89_MODE_CRSR && _uLinePos == 3 && _kln89->_blink)) {
250 // NOTE: this is the drawing routine for the ACTIVE flightplan, due to the number
251 // of differences there is a seperate routine for the stored flightplans.
253 // There are 4 rows to display flightplan data on the KLN89. Draw each row.
254 for(unsigned int i=0; i<4; ++i) {
255 // Sanity check - we should no longer tickle this.
256 if((_fplPos+i) > waylist.size()) {
260 // Draw the number and (optional) colon for each row.
262 int n = (i < 3 ? _fplPos + i + 1 : last_pos + 1);
263 if(_kln89->_approachLoaded) {
265 if(n > _fencePos) --n;
267 string s = GPSitoa(n);
268 if(_fplPos+i < waylist.size()) {
269 // Don't draw the waypoint number for the header or fence
270 if((waylist[_fplPos+i].appType == GPS_HDR || waylist[_fplPos+i].appType == GPS_FENCE)
271 && i != 3) { // By definition, the header and fence lines can't be displayed on the last line hence the unconditional !i==3 is safe.
274 // Don't draw the colon for waypoints that are part of the published approach
275 if(waylist[_fplPos+i].appType == GPS_APP_NONE) {
280 // We must be drawing the next entry field at the end of the list - this has a colon
284 if(!(_delWp && _uLinePos == i+4)) _kln89->DrawText(s, 2, 4 - (s[s.size()-1] == ':' ? s.size() : s.size()+1), 3 - i);
286 // Done drawing numbers and colons.
289 if(_delWp && _uLinePos == i+4) {
290 if(!_kln89->_blink) {
291 _kln89->DrawText("Del", 2, 0, 3-i);
292 _kln89->DrawChar('?', 2, 10, 3-i);
293 _kln89->Underline(2, 0, 3-i, 11);
296 } else if(_kln89->_mode == KLN89_MODE_CRSR && _bEntWp && _uLinePos == i+4) {
297 if(!_kln89->_blink) {
298 if(_wLinePos >= _entWpStr.size()) {
299 _kln89->DrawText(_entWpStr, 2, 4, 3-i);
300 _kln89->DrawChar(' ', 2, 4+_wLinePos, 3-i, false, true);
302 _kln89->DrawText(_entWpStr.substr(0, _wLinePos), 2, 4, 3-i);
303 _kln89->DrawChar(_entWpStr[_wLinePos], 2, 4+_wLinePos, 3-i, false, true);
304 _kln89->DrawText(_entWpStr.substr(_wLinePos+1, _entWpStr.size()-_wLinePos-1), 2, 5+_wLinePos, 3-i);
307 // Draw the param - this is "----" during waypoint entry (not for the first row though or we draw through the label!)
309 _kln89->DrawText("----", 2, 12, 3-i);
315 if(i == 3 || _fplPos + i == waylist.size()) {
316 //cout << "_uLinePos = " << _uLinePos << ", i = " << i << ", waylist.size() = " << waylist.size() << endl;
317 if(!(_kln89->_mode == KLN89_MODE_CRSR && _uLinePos == (i + 4) && _kln89->_blink)) {
318 // Draw the underline symbol at the end of the flightplan
319 _kln89->DrawText(last_pos < waylist.size() ? waylist[last_pos].GetAprId() : "_____", 2, 4, 3-i);
321 //cout << "last_pos = " << last_pos << endl;
322 if(last_pos > 0 && last_pos < waylist.size() && i > 0) {
324 if(_actFpMode == 0) { // DIS
325 if(_kln89->_mode == KLN89_MODE_CRSR && _bEntWp && _uLinePos < i+4) {
326 // This means that we are beyond the waypoint being entered. In DIS mode
327 // this means that we dash out the field.
328 _kln89->DrawText("----", 2, 12, 3-i);
330 string s = _params[last_pos - 1];
331 _kln89->DrawText(s, 2, 16-s.size(), 3-i);
333 } else if(_actFpMode == 3) { // DTK
334 string s = _params[last_pos - 1];
335 _kln89->DrawText(s, 2, 15-s.size(), 3-i);
336 _kln89->DrawSpecialChar(0, 2, 15, 3-i);
341 if(!(_kln89->_mode == KLN89_MODE_CRSR && _uLinePos == (i + 4) && _kln89->_blink)) {
342 if(waylist[_fplPos+i].appType == GPS_HDR) {
344 _kln89->DrawText("DELETE APPR?", 2, 1, 3-i);
345 } else if(_changeAppr) {
346 _kln89->DrawText("CHANGE APPR?", 2, 1, 3-i);
348 _kln89->DrawText(_kln89->_approachAbbrev, 2, 1, 3-i);
349 _kln89->DrawText(_kln89->_approachRwyStr, 2, 7, 3-i);
350 _kln89->DrawText(_kln89->_approachID, 2, 12, 3-i);
352 } else if(waylist[_fplPos+i].appType == GPS_FENCE) {
353 _kln89->DrawText("*NO WPT SEQ", 2, 0, 3-i);
355 _kln89->DrawText(waylist[_fplPos+i].GetAprId(), 2, 4, 3-i);
361 //cout << "i > 0 param draw...\n";
362 if(_actFpMode == 0) { // DIS
363 if(_kln89->_mode == KLN89_MODE_CRSR && _bEntWp && _uLinePos < i+4) {
364 // This means that we are beyond the waypoint being entered. In DIS mode
365 // this means that we dash out the field.
366 _kln89->DrawText("----", 2, 12, 3-i);
368 string s = _params[_fplPos + i - 1];
369 _kln89->DrawText(s, 2, 16-s.size(), 3-i);
371 } else if(_actFpMode == 3) { // DTK
372 // TODO - figure out properly what to do in DTK mode when beyond a waypoint being entered.
373 // I *think* it is OK to do the same as here, but maybe not for the waypoint immediately
374 // beyond the one being entered - need to check.
375 string s = _params[_fplPos + i - 1];
376 _kln89->DrawText(s, 2, 15-s.size(), 3-i);
377 _kln89->DrawSpecialChar(0, 2, 15, 3-i);
382 } else { // Not active flightplan
383 //cout << "Top pos is " << _fplPos << ' ';
384 // For synatical convienience
385 //int nWp = (_subPage == 0 && !_delFP ? 4 : 3); // number of waypoints to display
386 vector<GPSWaypoint*> waylist = _kln89->_flightPlans[_subPage]->waypoints;
387 if(waylist.empty()) {
388 if(!(_kln89->_mode == KLN89_MODE_CRSR && _uLinePos == 1 && _kln89->_blink)) {
389 _kln89->DrawText(_delFP ? "Delete FPL?" : "Copy FPL 0?", 2, 0, 3);
392 if(!(_kln89->_mode == KLN89_MODE_CRSR && (_uLinePos == 1 || _uLinePos == 2) && _kln89->_blink)) {
393 _kln89->DrawText(_delFP ? "Delete FPL?" : "Use?", 2, 0, 3);
395 if(!(_kln89->_mode == KLN89_MODE_CRSR && _uLinePos == 2 && _kln89->_blink)) {
396 if(!_delFP) _kln89->DrawText("Inverted?", 2, 5, 3);
400 // ----------------------------------
401 if(_kln89->_mode == KLN89_MODE_CRSR) {
403 if(!_kln89->_blink) {
404 _kln89->Underline(2, 0, 3, (waylist.empty() || _delFP ? 11 : 4)); // This underline is blinked
407 } else if(_uLinePos == 2) {
408 // assert(!waylist.empty());
409 if(!_kln89->_blink) {
410 _kln89->Underline(2, 0, 3, 14); // This underline is blinked
413 } else if(_uLinePos == 3) {
414 _kln89->Underline(2, 13, 2, 3);
415 } else if(_uLinePos >= 4) {
418 _kln89->Underline(2, 5, 2 - (_uLinePos - 4), 4);
419 } else if(_wLinePos == 4) {
420 _kln89->Underline(2, 4, 2 - (_uLinePos - 4), 4);
422 _kln89->Underline(2, 4, 2 - (_uLinePos - 4), _wLinePos);
423 _kln89->Underline(2, 5 + _wLinePos, 2 - (_uLinePos - 4), 4 - _wLinePos);
425 if(!_kln89->_blink) {
426 //_kln89->DrawText(_entWp->id, 2, 4, 2 - (_uLinePos - 4), false, _wLinePos);
430 if(!_delWp) _kln89->Underline(2, 4, 2 - (_uLinePos - 4), 5);
434 // ----------------------------------
436 _kln89->DrawChar('>', 2, 12, 2);
437 if(!(_kln89->_mode == KLN89_MODE_CRSR && _uLinePos == 3 && _kln89->_blink)) DrawFpMode(2);
438 // Sanity check the top position - remember that we can have an extra blank one at the bottom even if CRSR is off if crsr is switched on then off
439 if((int)_fplPos > ((int)waylist.size()) - 2) _fplPos = (((int)waylist.size()) - 2 < 0 ? 0 : waylist.size() - 2);
440 unsigned int last_pos;
441 if(waylist.empty()) {
444 last_pos = ((int)_fplPos == ((int)waylist.size()) - 2 ? waylist.size() : waylist.size() - 1);
446 if(waylist.size() < 3) last_pos = waylist.size();
447 for(unsigned int i=0; i<3; ++i) {
448 string s = GPSitoa(i < 2 ? _fplPos + i + 1 : last_pos + 1);
450 if(!(_delWp && _uLinePos == i+4)) _kln89->DrawText(s, 2, 4 - s.size(), 2 - i);
452 if(_delWp && _uLinePos == i+4) {
453 if(!_kln89->_blink) {
454 _kln89->DrawText("Del", 2, 0, 2-i);
455 _kln89->DrawChar('?', 2, 10, 2-i);
456 _kln89->Underline(2, 0, 2-i, 11);
459 } else if(_kln89->_mode == KLN89_MODE_CRSR && _bEntWp && _uLinePos == i+4) {
460 // This means that we are drawing a waypoint currently being entered
461 if(!_kln89->_blink) {
462 if(_wLinePos >= _entWpStr.size()) {
463 _kln89->DrawText(_entWpStr, 2, 4, 2-i);
464 _kln89->DrawChar(' ', 2, 4+_wLinePos, 2-i, false, true);
466 _kln89->DrawText(_entWpStr.substr(0, _wLinePos), 2, 4, 2-i);
467 _kln89->DrawChar(_entWpStr[_wLinePos], 2, 4+_wLinePos, 2-i, false, true);
468 _kln89->DrawText(_entWpStr.substr(_wLinePos+1, _entWpStr.size()-_wLinePos-1), 2, 5+_wLinePos, 2-i);
471 // Draw the param - this is "----" during waypoint entry (not for the first row though or we draw through the label!)
473 _kln89->DrawText("----", 2, 12, 2-i);
479 if(i == 2 || _fplPos + i == waylist.size()) {
480 if(!(_kln89->_mode == KLN89_MODE_CRSR && _uLinePos == (i + 4) && _kln89->_blink)) {
481 _kln89->DrawText(last_pos < waylist.size() ? waylist[last_pos]->id : "_____", 2, 4, 2-i);
483 if(last_pos > 0 && last_pos < waylist.size() && i > 0) {
485 // TODO - we should also handle DTK mode params here.
486 if(_fpMode == 0) { // DIS
487 if(_kln89->_mode == KLN89_MODE_CRSR && _bEntWp && _uLinePos < i+4) {
488 // This means that we are beyond the waypoint being entered. In DIS mode
489 // this means that we dash out the field.
490 _kln89->DrawText("----", 2, 12, 2-i);
492 string s = _params[last_pos - 1];
493 _kln89->DrawText(s, 2, 16-s.size(), 2-i);
499 if(!(_kln89->_mode == KLN89_MODE_CRSR && _uLinePos == (i + 4) && _kln89->_blink)) {
500 _kln89->DrawText(waylist[_fplPos+i]->id, 2, 4, 2-i);
504 // TODO - we should also handle DTK mode params here.
505 if(_fpMode == 0) { // DIS
506 if(_kln89->_mode == KLN89_MODE_CRSR && _bEntWp && _uLinePos < i+4) {
507 // This means that we are beyond the waypoint being entered. In DIS mode
508 // this means that we dash out the field.
509 _kln89->DrawText("----", 2, 12, 2-i);
511 string s = _params[_fplPos + i - 1];
512 _kln89->DrawText(s, 2, 16-s.size(), 2-i);
521 KLN89Page::Update(dt);
524 void KLN89FplPage::DrawFpMode(int ypos) {
527 if(_actFpMode == 1) {
529 } else if(_actFpMode == 2) {
530 s = "UTC"; // TODO - alter depending on chosen timezone
531 } else if(_actFpMode == 3) {
532 s = (_kln89->_obsMode ? "OBS" : "Dtk");
539 _kln89->DrawText(s, 2, 13, ypos);
542 // Calculate the displayable parameters for the currently displayed flightplan.
543 // These are Distance, ETE, ETA (UTC) and DTK for the active flight plan, and Distance and DTK for the stored flightplans.
544 // These are then converted into strings and pushed onto a string list (_params) which matches the flightplan,
545 // which is a really really really ugly and potentially bug-prone and hard to maintain way of doing this.
546 // TODO: When the unit is fully working rip out _params and replace with a better solution.
547 void KLN89FplPage::Calc() {
549 GPSFlightPlan* fp = _kln89->_flightPlans[_subPage];
550 vector<GPSWaypoint*> wv = fp->waypoints;
551 // Some parameters are calculated differently for the active and the stored flightplans, so
552 // do the two cases seperately.
554 // Active FP - parameters are only displayed for the active waypoint onwards for the active plan,
555 // and distance is cumulative from the user position.
556 if(0 == _actFpMode) {
558 double cum_tot = 0.0;
560 cum_tot += _kln89->GetGreatCircleDistance(_kln89->_gpsLat, _kln89->_gpsLon, wv[0]->lat, wv[0]->lon);
562 for(unsigned int i=1; i<wv.size(); ++i) {
563 cum_tot += _kln89->GetGreatCircleDistance(wv[i-1]->lat, wv[i-1]->lon, wv[i]->lat, wv[i]->lon); // TODO - add units switch!
564 int n = (int)(cum_tot + 0.5);
565 _params.push_back(GPSitoa(n));
567 } else if(1 == _actFpMode) {
569 } else if(2 == _actFpMode) {
573 for(unsigned int i=1; i<wv.size(); ++i) {
574 double dtk = _kln89->GetMagHeadingFromTo(wv[i-1]->lat, wv[i-1]->lon, wv[i]->lat, wv[i]->lon);
575 int n = (int)(dtk + 0.5);
576 _params.push_back(GPSitoa(n));
583 double cum_tot = 0.0;
584 for(unsigned int i=1; i<wv.size(); ++i) {
585 cum_tot += _kln89->GetGreatCircleDistance(wv[i-1]->lat, wv[i-1]->lon, wv[i]->lat, wv[i]->lon); // TODO - add units switch!
586 int n = (int)(cum_tot + 0.5);
587 _params.push_back(GPSitoa(n));
591 for(unsigned int i=1; i<wv.size(); ++i) {
592 double dtk = _kln89->GetMagHeadingFromTo(wv[i-1]->lat, wv[i-1]->lon, wv[i]->lat, wv[i]->lon);
593 int n = (int)(dtk + 0.5);
594 _params.push_back(GPSitoa(n));
600 void KLN89FplPage::CrsrPressed() {
601 if(_delFP || _delAppr) {
602 _delFP = _delAppr = false;
603 _kln89->_mode = KLN89_MODE_DISP;
608 if(_kln89->_mode == KLN89_MODE_DISP) {
611 for(unsigned int i = 0; i < _kln89->_flightPlans[_subPage]->waypoints.size(); ++i) {
612 if(_kln89->_flightPlans[_subPage]->waypoints[i] == _entWp) {
613 _kln89->_flightPlans[_subPage]->waypoints.erase(_kln89->_flightPlans[_subPage]->waypoints.begin() + i);
622 if(_kln89->_obsMode) {
625 if(_kln89->_flightPlans[_subPage]->IsEmpty()) {
628 _uLinePos = (_subPage == 0 ? 3 : 1);
634 void KLN89FplPage::ClrPressed() {
635 if(_delFP || _delAppr) {
636 _delFP = _delAppr = false;
637 _kln89->_mode = KLN89_MODE_DISP;
639 if(KLN89_MODE_CRSR == _kln89->_mode) {
640 // TODO - see if we need to delete a waypoint
643 // If we are already displaying a clear waypoint dialog in response to the CLR button,
644 // then a further press of the CLR button cancels the dialog.
645 _kln89->_mode = KLN89_MODE_DISP;
648 // If we are currently entering a waypoint, then CLR deletes it unconditionally
649 // without a confirmation dialog and cancels waypoint entry.
650 int pos = _uLinePos - 4 + _fplPos;
651 // Sanity check - the calculated wp position should never be off the end of the waypoint list.
652 if(pos > static_cast<int>(_kln89->_flightPlans[_subPage]->waypoints.size()) - 1) {
653 SG_LOG(SG_GENERAL, SG_ALERT, "ERROR - _uLinePos too big in KLN89FplPage::ClrPressed!\n");
656 _kln89->_flightPlans[_subPage]->waypoints.erase(_kln89->_flightPlans[_subPage]->waypoints.begin() + pos);
661 // We can also get here from the waypoint review page, so clear _bEntExp as well
663 // Do we need to re-calc _fplPos here?
665 // First check that we're not trying to delete an approach waypoint. Note that we can delete the approach by deleting the header though.
666 // Check for approach waypoints or header/fences in flightplan 0
667 int n = _fplPos + _uLinePos - 4;
669 bool fencePos = false;
670 //cout << "_fplPos = " << _fplPos << ", _uLinePos = " << _uLinePos << ", n = " << n << ", _hdrPos = " << _hdrPos << ", _fencePos = " << _fencePos << '\n';
672 //cout << "HEADER POS\n";
676 //cout << "FENCE POS\n";
679 if(_hdrPos >= 0 && n > _hdrPos) --n;
680 if(_fencePos >= 0 && n >= _fencePos) --n; // This one needs to be >= since n is already decremented by 1 in the line above!
681 //cout << "New n = " << n << '\n';
685 } else if(fencePos) {
688 } else if(n >= static_cast<int>(_kln89->_flightPlans[_subPage]->waypoints.size())) {
689 // no-op - off the end of the list on the entry field
690 } else if(_kln89->_flightPlans[_subPage]->waypoints[n]->appType == GPS_APP_NONE) {
692 _kln89->_mode = KLN89_MODE_CRSR;
695 ShowScratchpadMessage("Invald", " Del ");
698 } else if(_uLinePos == 3) {
701 if(_actFpMode > 3) _actFpMode = 0;
704 if(_fpMode > 1) _fpMode = 0;
710 _kln89->_mode = KLN89_MODE_CRSR;
715 void KLN89FplPage::CleanUp() {
716 // TODO - possibly need to clean up _delWp here as well, since it goes off if dto and then ent are pressed.
719 for(unsigned int i = 0; i < _kln89->_flightPlans[_subPage]->waypoints.size(); ++i) {
720 if(_kln89->_flightPlans[_subPage]->waypoints[i] == _entWp) {
721 _kln89->_flightPlans[_subPage]->waypoints.erase(_kln89->_flightPlans[_subPage]->waypoints.begin() + i);
727 KLN89Page::CleanUp();
730 void KLN89FplPage::LooseFocus() {
732 _resetFplPos0 = true;
736 _scratchpadMsg = false;
739 void KLN89FplPage::EntPressed() {
741 _kln89->ClearFlightPlan(_subPage);
744 int pos = _uLinePos - 4 + _fplPos;
745 // Sanity check - the calculated wp position should never be off the end of the waypoint list.
746 if(pos > static_cast<int>(_kln89->_flightPlans[_subPage]->waypoints.size()) - 1) {
747 SG_LOG(SG_GENERAL, SG_ALERT, "ERROR - _uLinePos too big in KLN89FplPage::EntPressed!\n");
750 _kln89->_flightPlans[_subPage]->waypoints.erase(_kln89->_flightPlans[_subPage]->waypoints.begin() + pos);
752 // Do we need to re-calc _fplPos here?
753 } else if(_bEntExp) {
754 // We get here if we have just approved a waypoint review for addition with the ENT button
757 _entWp = NULL; // DON'T delete it! - it's been pushed onto the waypoint list at this point.
759 _kln89->_cleanUpPage = -1;
761 // The cursor should be moved either to the next waypoint in the list, or to the empty position at
762 // the end of the list if the waypoint just entered was the last one in the list. Unfortunately
763 // that means that we have to deal with the horrible _uLinePos / _fplPos interaction yet again :-(
765 // We can't handle this case by calling K1R1, since we want to jump the field type
768 // Just call Knob1Right1 and let that handle the horrible logic :-)
773 // TODO - should be able to get rid of this switch I think and use the enum values.
774 switch(_entWp->type) {
776 _kln89->_activePage = _kln89->_pages[0];
777 _kln89->_curPage = 0;
778 ((KLN89Page*)_kln89->_pages[0])->SetEntInvert(true);
781 _kln89->_activePage = _kln89->_pages[1];
782 _kln89->_curPage = 1;
783 ((KLN89Page*)_kln89->_pages[1])->SetEntInvert(true);
786 _kln89->_activePage = _kln89->_pages[2];
787 _kln89->_curPage = 2;
788 ((KLN89Page*)_kln89->_pages[2])->SetEntInvert(true);
791 _kln89->_activePage = _kln89->_pages[3];
792 _kln89->_curPage = 3;
793 ((KLN89Page*)_kln89->_pages[3])->SetEntInvert(true);
796 _kln89->_activePage = _kln89->_pages[4];
797 _kln89->_curPage = 4;
798 ((KLN89Page*)_kln89->_pages[4])->SetEntInvert(true);
801 SG_LOG(SG_GENERAL, SG_ALERT, "Error - unknown waypoint type found in KLN89::FplPage::EntPressed()\n");
803 _kln89->_activePage->SetId(_entWp->id);
804 _kln89->_entJump = _kln89->_clrJump = 7;
805 _kln89->_cleanUpPage = 7;
806 _kln89->_jumpRestoreCrsr = true;
807 _kln89->_mode = KLN89_MODE_DISP;
810 } else if(_uLinePos == 1) {
811 if(_kln89->_flightPlans[_subPage]->IsEmpty()) {
813 for(unsigned int i=0; i<_kln89->_flightPlans[0]->waypoints.size(); ++i) {
814 GPSWaypoint* wp = new GPSWaypoint;
815 *wp = *(_kln89->_flightPlans[0]->waypoints[i]);
816 _kln89->_flightPlans[_subPage]->waypoints.push_back(wp);
820 _kln89->ClearFlightPlan(0);
821 for(unsigned int i=0; i<_kln89->_flightPlans[_subPage]->waypoints.size(); ++i) {
822 GPSWaypoint* wp = new GPSWaypoint;
823 *wp = *(_kln89->_flightPlans[_subPage]->waypoints[i]);
824 _kln89->_flightPlans[0]->waypoints.push_back(wp);
826 _kln89->OrientateToActiveFlightPlan();
829 _kln89->CrsrPressed();
830 } else if(_uLinePos == 2) {
831 if(_kln89->_flightPlans[_subPage]->IsEmpty()) {
835 _kln89->ClearFlightPlan(0);
836 for(unsigned int i=0; i<_kln89->_flightPlans[_subPage]->waypoints.size(); ++i) {
837 GPSWaypoint* wp = new GPSWaypoint;
838 *wp = *(_kln89->_flightPlans[_subPage]->waypoints[i]);
839 // FIXME - very inefficient - use a reverse iterator on the source array and push_back instead!!!!!!!!
840 _kln89->_flightPlans[0]->waypoints.insert(_kln89->_flightPlans[0]->waypoints.begin(), wp);
842 _kln89->OrientateToActiveFlightPlan();
844 _kln89->CrsrPressed();
849 void KLN89FplPage::Knob1Left1() {
857 if(_kln89->_mode == KLN89_MODE_CRSR) {
859 if(_wLinePos > 0) _wLinePos--;
861 // _uLinePos with empty/not-empty plan: 1 = Copy FPL 0? / Use?, 2 = unused if empty / Invert?, 3 = >Dis/Dtk field, 4+ = Waypoint 1+
864 } else if(_uLinePos == 1 || _uLinePos == 2) {
866 } else if(_uLinePos == 3) {
868 } else if(_uLinePos == 4) {
869 if(_kln89->_flightPlans[_subPage]->IsEmpty()) {
870 _uLinePos = (_subPage == 0 ? 0 : 1);
871 } else if(_fplPos == 0) {
872 _uLinePos = (_subPage == 0 ? 0 : 2);
876 } else if(_uLinePos == 5) {
882 if(_subPage == 0 && _uLinePos > 3) {
883 int ix = _fplPos + (_uLinePos - 4);
884 if(_fencePos >= 0 && ix >= _fencePos) ix--;
885 if(_hdrPos >= 0 && ix >= _hdrPos) ix--;
886 if(ix >= static_cast<int>(_kln89->_activeFP->waypoints.size())) {
889 _fp0SelWpId = _kln89->_activeFP->waypoints[ix]->id;
893 //cout << "Not page 0, or not in waypoints, clearing id!\n";
899 void KLN89FplPage::Knob1Right1() {
907 if(_kln89->_mode == KLN89_MODE_CRSR) {
909 if(_wLinePos < 4) _wLinePos++;
911 // _uLinePos with empty/not-empty plan:
912 // 1 = Copy FPL 0? / Use?, 2 = unused if empty / Invert?, 3 = >Dis/Dtk field, 4+ = Waypoint 1+
914 _uLinePos = (_subPage == 0 ? 4 : 1);
915 } else if(_uLinePos == 1) {
916 _uLinePos = (_kln89->_flightPlans[_subPage]->IsEmpty() ? 4 : 2);
917 } else if(_uLinePos == 2) {
919 } else if(_uLinePos == 3) {
920 if(!_kln89->_flightPlans[_subPage]->IsEmpty()) _uLinePos = 5;
921 } else if(_uLinePos == 4) {
923 } else if((_subPage == 0 && _uLinePos == 6) || (_subPage > 0 && _uLinePos == 5)) {
924 // Urrggh - complicated!
926 // 1: We're on the entry field at the end of the list, and can't move any more.
927 // 2: We're on the last or second-last field, and move to the last position
928 // 3: We're on a field before the second-last one, and don't move, but change the list-head position
929 // And 4: _subPage 0 can be complicated by the presence of header/fence lines in an approach.
932 if(_hdrPos >= 0) hfcount++;
933 if(_fencePos >= 0) hfcount++;
935 if(_kln89->_flightPlans[_subPage]->waypoints.size() == 1 || _fplPos == _kln89->_flightPlans[_subPage]->waypoints.size() + hfcount - 1) {
937 } else if((int)_fplPos >= static_cast<int>(_kln89->_flightPlans[_subPage]->waypoints.size()) + hfcount - (_subPage == 0 ? 4 : 3)) {
942 } else if(_uLinePos == 5) {
943 // Must be _subPage 0
946 // Must be the last line - either _uLinePos 6 or 7 depending on _subPage
947 const unsigned thresh = (_subPage == 0 ? 3 : 2);
948 if(_kln89->_flightPlans[_subPage]->waypoints.size() == thresh || _fplPos == _kln89->_flightPlans[_subPage]->waypoints.size() - thresh) {
955 if(_subPage == 0 && _uLinePos > 3) {
956 int ix = _fplPos + (_uLinePos - 4);
957 if(_fencePos >= 0 && ix >= _fencePos) ix--;
958 if(_hdrPos >= 0 && ix >= _hdrPos) ix--;
959 if(ix >= static_cast<int>(_kln89->_activeFP->waypoints.size())) {
962 _fp0SelWpId = _kln89->_activeFP->waypoints[ix]->id;
966 //cout << "Not page 0, or not in waypoints, clearing id!\n";
972 void KLN89FplPage::Knob2Left1() {
979 if(_kln89->_mode != KLN89_MODE_CRSR || _uLinePos == 0) {
980 if(_kln89->_mode != KLN89_MODE_CRSR) _resetFplPos0 = true;
981 KLN89Page::Knob2Left1();
984 // Check for approach waypoints or header/fences in flightplan 0
985 int n = _fplPos + _uLinePos - 4;
987 bool fencePos = false;
989 //cout << "_fplPos = " << _fplPos << ", _uLinePos = " << _uLinePos << ", n = " << n << ", _hdrPos = " << _hdrPos << ", _fencePos = " << _fencePos << '\n';
991 //cout << "HEADER POS\n";
995 //cout << "FENCE POS\n";
998 if(_hdrPos >= 0 && n > _hdrPos) --n;
999 if(_fencePos >= 0 && n >= _fencePos) --n; // This one needs to be >= since n is already decremented by 1 in the line above!
1000 //cout << "New n = " << n << '\n';
1002 if(n < static_cast<int>(_kln89->_flightPlans[_subPage]->waypoints.size())) {
1003 if(_kln89->_flightPlans[_subPage]->waypoints[n]->appType != GPS_APP_NONE) {
1009 // TODO - not sure what we actually do in this condition
1011 } else if(fencePos) {
1014 ShowScratchpadMessage("Invald", " Add ");
1016 if(!_entWpStr.size()) {
1017 _entWpStr += _kln89->DecChar(_kln89->_defaultFirstChar, false, true);
1018 } else if((_wLinePos + 1) > _entWpStr.size()) {
1019 // I don't think we can ever reach this state since I think it only ever applies to the
1020 // first char and gets caught by the line above, but it can stay for now just in case.
1023 _entWpStr[_wLinePos] = _kln89->DecChar(_entWpStr[_wLinePos], (_wLinePos == 0 ? false : true));
1026 _fp0SelWpId.clear(); // Waypoints don't become the DTO default whilst being entered.
1028 GPSWaypoint* wp = _kln89->FindFirstById(_entWpStr.substr(0, _wLinePos+1));
1030 // No ID matches the partial ID entered so _entWpStr must be shortened to the cursor
1031 // position if it was longer due to a match on the previous character.
1032 if(_entWpStr.size() > _wLinePos+1) {
1033 _entWpStr = _entWpStr.substr(0, _wLinePos+1);
1036 // There is a matching full ID to the entered partial ID, so copy the full ID
1040 *_entWp = *wp; // copy
1044 if(_fplPos + (_uLinePos - 4) >= _kln89->_flightPlans[_subPage]->waypoints.size()) {
1045 _kln89->_flightPlans[_subPage]->waypoints.push_back(_entWp);
1047 _kln89->_flightPlans[_subPage]->waypoints.insert(_kln89->_flightPlans[_subPage]->waypoints.begin()+(_fplPos + (_uLinePos - 4)), _entWp);
1056 void KLN89FplPage::Knob2Right1() {
1063 if(_kln89->_mode != KLN89_MODE_CRSR || _uLinePos == 0) {
1064 if(_kln89->_mode != KLN89_MODE_CRSR) _resetFplPos0 = true;
1065 KLN89Page::Knob2Right1();
1068 // Check for approach waypoints or header/fences in flightplan 0
1069 int n = _fplPos + _uLinePos - 4;
1070 bool hdrPos = false;
1071 bool fencePos = false;
1073 //cout << "_fplPos = " << _fplPos << ", _uLinePos = " << _uLinePos << ", n = " << n << ", _hdrPos = " << _hdrPos << ", _fencePos = " << _fencePos << '\n';
1075 //cout << "HEADER POS\n";
1078 if(n == _fencePos) {
1079 //cout << "FENCE POS\n";
1082 if(_hdrPos >= 0 && n > _hdrPos) --n;
1083 if(_fencePos >= 0 && n >= _fencePos) --n; // This one needs to be >= since n is already decremented by 1 in the line above!
1084 //cout << "New n = " << n << '\n';
1086 if(n < static_cast<int>(_kln89->_flightPlans[_subPage]->waypoints.size())) {
1087 if(_kln89->_flightPlans[_subPage]->waypoints[n]->appType != GPS_APP_NONE) {
1093 // TODO - not sure what we actually do in this condition
1095 } else if(fencePos) {
1098 ShowScratchpadMessage("Invald", " Add ");
1100 if(!_entWpStr.size()) {
1101 _entWpStr += _kln89->_defaultFirstChar;
1102 } else if((_wLinePos + 1) > _entWpStr.size()) {
1103 // I don't think we can ever reach this state since I think it only ever applies to the
1104 // first char and gets caught by the line above, but it can stay for now just in case.
1107 _entWpStr[_wLinePos] = _kln89->IncChar(_entWpStr[_wLinePos], (_wLinePos == 0 ? false : true));
1110 _fp0SelWpId.clear(); // Waypoints don't become the DTO default whilst being entered.
1112 GPSWaypoint* wp = _kln89->FindFirstById(_entWpStr.substr(0, _wLinePos+1));
1114 // No ID matches the partial ID entered so _entWpStr must be shortened to the cursor
1115 // position if it was longer due to a match on the previous character.
1116 if(_entWpStr.size() > _wLinePos+1) {
1117 _entWpStr = _entWpStr.substr(0, _wLinePos+1);
1120 // There is a matching full ID to the entered partial ID, so copy the full ID
1124 *_entWp = *wp; // copy
1128 if(_fplPos + (_uLinePos - 4) >= _kln89->_flightPlans[_subPage]->waypoints.size()) {
1129 _kln89->_flightPlans[_subPage]->waypoints.push_back(_entWp);
1131 _kln89->_flightPlans[_subPage]->waypoints.insert(_kln89->_flightPlans[_subPage]->waypoints.begin()+(_fplPos + (_uLinePos - 4)), _entWp);