1 /***************************************************************************
2  *
3  * Project:  OpenCPN
4  * Purpose:  Chart Canvas
5  * Author:   David Register
6  *
7  ***************************************************************************
8  *   Copyright (C) 2018 by David S. Register                               *
9  *                                                                         *
10  *   This program is free software; you can redistribute it and/or modify  *
11  *   it under the terms of the GNU General Public License as published by  *
12  *   the Free Software Foundation; either version 2 of the License, or     *
13  *   (at your option) any later version.                                   *
14  *                                                                         *
15  *   This program is distributed in the hope that it will be useful,       *
16  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
17  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
18  *   GNU General Public License for more details.                          *
19  *                                                                         *
20  *   You should have received a copy of the GNU General Public License     *
21  *   along with this program; if not, write to the                         *
22  *   Free Software Foundation, Inc.,                                       *
23  *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,  USA.         *
24  **************************************************************************/
25 
26 // For compilers that support precompilation, includes "wx.h".
27 #include "wx/wxprec.h"
28 
29 #ifndef  WX_PRECOMP
30 #include "wx/wx.h"
31 #endif //precompiled headers
32 #include "wx/image.h"
33 #include <wx/graphics.h>
34 #include <wx/listbook.h>
35 #include <wx/clipbrd.h>
36 #include <wx/aui/aui.h>
37 #include "wx/progdlg.h"
38 
39 #include "config.h"
40 #include "dychart.h"
41 #include "OCPNPlatform.h"
42 
43 #ifdef __WXOSX__
44 #include "DarkMode.h"
45 #endif
46 
47 #include <wx/listimpl.cpp>
48 
49 #include "chcanv.h"
50 #include "TCWin.h"
51 #include "geodesic.h"
52 #include "styles.h"
53 #include "routeman.h"
54 #include "piano.h"
55 #include "navutil.h"
56 #include "kml.h"
57 #include "concanv.h"
58 #include "thumbwin.h"
59 #include "chartdb.h"
60 #include "chartimg.h"
61 #include "chart1.h"
62 #include "cutil.h"
63 #include "MarkInfo.h"
64 #include "RoutePropDlgImpl.h"
65 #include "TrackPropDlg.h"
66 #include "tcmgr.h"
67 #include "routemanagerdialog.h"
68 #include "pluginmanager.h"
69 #include "ocpn_pixel.h"
70 #include "ocpndc.h"
71 #include "undo.h"
72 #include "toolbar.h"
73 #include "multiplexer.h"
74 #include "timers.h"
75 #include "tide_time.h"
76 #include "glTextureDescriptor.h"
77 #include "ChInfoWin.h"
78 #include "Quilt.h"
79 #include "SelectItem.h"
80 #include "Select.h"
81 #include "FontMgr.h"
82 #include "AIS_Decoder.h"
83 #include "AIS_Target_Data.h"
84 #include "AISTargetAlertDialog.h"
85 #include "SendToGpsDlg.h"
86 #include "compass.h"
87 #include "OCPNRegion.h"
88 #include "gshhs.h"
89 #include "canvasMenu.h"
90 #include "wx28compat.h"
91 #include "Track.h"
92 #include "Route.h"
93 #include "OCPN_AUIManager.h"
94 #include "MUIBar.h"
95 #include "CanvasConfig.h"
96 #include "CanvasOptions.h"
97 #include "mbtiles.h"
98 
99 #ifdef __OCPN__ANDROID__
100 #include "androidUTIL.h"
101 #endif
102 
103 #ifdef ocpnUSE_GL
104 #include "glChartCanvas.h"
105 #endif
106 
107 #include "cm93.h"                   // for chart outline draw
108 #include "s57chart.h"               // for ArrayOfS57Obj
109 #include "s52plib.h"
110 #include "s52utils.h"
111 
112 #include "ais.h"
113 
114 #ifdef __MSVC__
115 #define _CRTDBG_MAP_ALLOC
116 #include <stdlib.h>
117 #include <crtdbg.h>
118 #define DEBUG_NEW new(_NORMAL_BLOCK, __FILE__, __LINE__ )
119 #define new DEBUG_NEW
120 #endif
121 
122 #ifndef __WXMSW__
123 #include <signal.h>
124 #include <setjmp.h>
125 
126 
127 
128 #endif
129 
130 extern float  g_ChartScaleFactorExp;
131 extern float  g_ShipScaleFactorExp;
132 
133 #include <vector>
134 //#include <wx-3.0/wx/aui/auibar.h>
135 
136 #if defined(__MSVC__) &&  (_MSC_VER < 1700)
137 #define  trunc(d) ((d>0) ? floor(d) : ceil(d))
138 #endif
139 
140 //  Define to enable the invocation of a temporary menubar by pressing the Alt key.
141 //  Not implemented for Windows XP, as it interferes with Alt-Tab processing.
142 #define OCPN_ALT_MENUBAR 1
143 
144 
145 //    Profiling support
146 //#include "/usr/include/valgrind/callgrind.h"
147 
148 // ----------------------------------------------------------------------------
149 // Useful Prototypes
150 // ----------------------------------------------------------------------------
151 extern bool G_FloatPtInPolygon ( MyFlPoint *rgpts, int wnumpts, float x, float y ) ;
152 extern void catch_signals(int signo);
153 
154 extern void AlphaBlending( ocpnDC& dc, int x, int y, int size_x, int size_y, float radius,
155                                        wxColour color, unsigned char transparency );
156 
157 extern double           g_ChartNotRenderScaleFactor;
158 extern double           gLat, gLon, gCog, gSog, gHdt;
159 //extern double           vLat, vLon;
160 extern ChartDB          *ChartData;
161 extern bool             bDBUpdateInProgress;
162 extern ColorScheme      global_color_scheme;
163 extern int              g_nbrightness;
164 
165 extern ConsoleCanvas    *console;
166 extern OCPNPlatform     *g_Platform;
167 
168 extern RouteList        *pRouteList;
169 extern TrackList        *pTrackList;
170 extern MyConfig         *pConfig;
171 extern Select           *pSelect;
172 extern Routeman         *g_pRouteMan;
173 extern ThumbWin         *pthumbwin;
174 extern TCMgr            *ptcmgr;
175 extern Select           *pSelectTC;
176 extern Select           *pSelectAIS;
177 extern WayPointman      *pWayPointMan;
178 extern MarkInfoDlg      *g_pMarkInfoDialog;
179 extern RoutePropDlgImpl *pRoutePropDialog;
180 extern TrackPropDlg     *pTrackPropDialog;
181 extern ActiveTrack      *g_pActiveTrack;
182 
183 
184 extern RoutePoint       *pAnchorWatchPoint1;
185 extern RoutePoint       *pAnchorWatchPoint2;
186 extern double           AnchorPointMinDist;
187 extern bool             AnchorAlertOn1;
188 extern bool             AnchorAlertOn2;
189 extern int              g_nAWMax;
190 extern int              g_iDistanceFormat;
191 
192 extern RouteManagerDialog *pRouteManagerDialog;
193 extern GoToPositionDialog *pGoToPositionDialog;
194 extern wxString GetLayerName(int id);
195 extern wxString         g_uploadConnection;
196 extern bool             g_bsimplifiedScalebar;
197 
198 extern bool             bDrawCurrentValues;
199 
200 extern s52plib          *ps52plib;
201 
202 extern bool             bGPSValid;
203 extern bool             g_bTempShowMenuBar;
204 extern bool             g_bShowMenuBar;
205 extern bool             g_bShowCompassWin;
206 
207 extern AIS_Decoder      *g_pAIS;
208 extern bool             g_bShowAreaNotices;
209 extern int              g_Show_Target_Name_Scale;
210 
211 extern MyFrame          *gFrame;
212 
213 extern int              g_iNavAidRadarRingsNumberVisible;
214 extern float            g_fNavAidRadarRingsStep;
215 extern int              g_pNavAidRadarRingsStepUnits;
216 extern bool             g_bWayPointPreventDragging;
217 extern bool             g_bEnableZoomToCursor;
218 extern bool             g_bShowChartBar;
219 extern bool             g_bInlandEcdis;
220 extern int              g_ENCSoundingScaleFactor;
221 
222 
223 extern AISTargetQueryDialog    *g_pais_query_dialog_active;
224 extern int              g_ais_query_dialog_x, g_ais_query_dialog_y;
225 
226 extern int              g_S57_dialog_sx, g_S57_dialog_sy;
227 
228 extern PopUpDSlide       *pPopupDetailSlider;
229 extern int              g_detailslider_dialog_x, g_detailslider_dialog_y;
230 
231 extern bool             g_b_overzoom_x;                      // Allow high overzoom
232 extern double           g_plus_minus_zoom_factor;
233 
234 extern int              g_OwnShipIconType;
235 extern double           g_n_ownship_length_meters;
236 extern double           g_n_ownship_beam_meters;
237 extern double           g_n_gps_antenna_offset_y;
238 extern double           g_n_gps_antenna_offset_x;
239 extern int              g_n_ownship_min_mm;
240 
241 
242 extern double           g_COGAvg;               // only needed for debug....
243 
244 extern int              g_click_stop;
245 extern double           g_ownship_predictor_minutes;
246 extern double           g_ownship_HDTpredictor_miles;
247 
248 extern bool              g_bquiting;
249 extern AISTargetListDialog *g_pAISTargetList;
250 extern wxString         g_sAIS_Alert_Sound_File;
251 
252 extern PlugInManager    *g_pi_manager;
253 
254 extern OCPN_AUIManager  *g_pauimgr;
255 
256 extern bool             g_bopengl;
257 extern bool             g_bdisable_opengl;
258 
259 extern bool             g_bFullScreenQuilt;
260 
261 extern bool             g_bsmoothpanzoom;
262 
263 bool                    g_bDebugOGL;
264 
265 extern bool             g_b_assume_azerty;
266 
267 extern ChartGroupArray  *g_pGroupArray;
268 extern wxString         g_default_routepoint_icon;
269 
270 extern S57QueryDialog   *g_pObjectQueryDialog;
271 extern ocpnStyle::StyleManager* g_StyleManager;
272 extern wxArrayOfConnPrm *g_pConnectionParams;
273 
274 extern OcpnSound*        g_anchorwatch_sound;
275 
276 extern bool              g_bShowTrue, g_bShowMag;
277 extern bool              g_btouch;
278 extern bool              g_bresponsive;
279 
280 
281 #ifdef ocpnUSE_GL
282 #endif
283 
284 extern bool              g_bShowFPS;
285 extern double            g_gl_ms_per_frame;
286 extern bool              g_benable_rotate;
287 extern bool              g_bRollover;
288 
289 extern bool              g_bSpaceDropMark;
290 extern bool              g_bAutoHideToolbar;
291 extern int               g_nAutoHideToolbar;
292 extern bool              g_bDeferredInitDone;
293 
294 extern wxString          g_CmdSoundString;
295 extern bool              g_boptionsactive;
296 
297 //  TODO why are these static?
298 static int mouse_x;
299 static int mouse_y;
300 static bool mouse_leftisdown;
301 
302 bool g_brouteCreating;
303 
304 bool g_bShowTrackPointTime;
305 
306 int r_gamma_mult;
307 int g_gamma_mult;
308 int b_gamma_mult;
309 int gamma_state;
310 bool g_brightness_init;
311 int   last_brightness;
312 
313 int                     g_cog_predictor_width;
314 extern double           g_display_size_mm;
315 
316 //extern bool             g_bshowToolbar;
317 extern ocpnFloatingToolbarDialog *g_MainToolbar;
318 extern wxColour         g_colourOwnshipRangeRingsColour;
319 
320 // LIVE ETA OPTION
321 bool                    g_bShowLiveETA;
322 double                  g_defaultBoatSpeed;
323 double                  g_defaultBoatSpeedUserUnit;
324 
325 extern int              g_nAIS_activity_timer;
326 extern bool             g_bskew_comp;
327 extern float            g_compass_scalefactor;
328 extern int              g_COGAvgSec; // COG average period (sec.) for Course Up Mode
329 
330 wxGLContext             *g_pGLcontext;   //shared common context
331 
332 extern bool             g_useMUI;
333 extern unsigned int     g_canvasConfig;
334 extern wxString         g_lastPluginMessage;
335 
336 extern ChartCanvas      *g_focusCanvas;
337 extern ChartCanvas      *g_overlayCanvas;
338 
339 extern float            g_toolbar_scalefactor;
340 extern SENCThreadManager *g_SencThreadManager;
341 
342 // "Curtain" mode parameters
343 wxDialog                *g_pcurtain;
344 
345 #define MIN_BRIGHT 10
346 #define MAX_BRIGHT 100
347 
348 
349 //------------------------------------------------------------------------------
350 //    ChartCanvas Implementation
351 //------------------------------------------------------------------------------
BEGIN_EVENT_TABLE(ChartCanvas,wxWindow)352 BEGIN_EVENT_TABLE ( ChartCanvas, wxWindow )
353     EVT_PAINT ( ChartCanvas::OnPaint )
354     EVT_ACTIVATE ( ChartCanvas::OnActivate )
355     EVT_SIZE ( ChartCanvas::OnSize )
356     EVT_MOUSE_EVENTS ( ChartCanvas::MouseEvent )
357     EVT_TIMER ( DBLCLICK_TIMER, ChartCanvas::MouseTimedEvent )
358     EVT_TIMER ( PAN_TIMER, ChartCanvas::PanTimerEvent )
359     EVT_TIMER ( MOVEMENT_TIMER, ChartCanvas::MovementTimerEvent )
360     EVT_TIMER ( MOVEMENT_STOP_TIMER, ChartCanvas::MovementStopTimerEvent )
361     EVT_TIMER ( CURTRACK_TIMER, ChartCanvas::OnCursorTrackTimerEvent )
362     EVT_TIMER ( ROT_TIMER, ChartCanvas::RotateTimerEvent )
363     EVT_TIMER ( ROPOPUP_TIMER, ChartCanvas::OnRolloverPopupTimerEvent )
364     EVT_TIMER ( ROUTEFINISH_TIMER, ChartCanvas::OnRouteFinishTimerEvent )
365     EVT_KEY_DOWN(ChartCanvas::OnKeyDown )
366     EVT_KEY_UP(ChartCanvas::OnKeyUp )
367     EVT_CHAR(ChartCanvas::OnKeyChar)
368     EVT_MOUSE_CAPTURE_LOST(ChartCanvas::LostMouseCapture )
369     EVT_KILL_FOCUS(ChartCanvas::OnKillFocus)
370     EVT_SET_FOCUS(ChartCanvas::OnSetFocus)
371     EVT_MENU(-1, ChartCanvas::OnToolLeftClick)
372     EVT_TIMER ( DEFERRED_FOCUS_TIMER, ChartCanvas::OnDeferredFocusTimerEvent )
373 
374 
375 END_EVENT_TABLE()
376 
377 // Define a constructor for my canvas
378 ChartCanvas::ChartCanvas ( wxFrame *frame, int canvasIndex ) :
379      wxWindow ( frame, wxID_ANY,    wxPoint ( 20,20 ), wxSize ( 5,5 ), wxNO_BORDER )
380 {
381     parent_frame = ( MyFrame * ) frame;       // save a pointer to parent
382     m_canvasIndex = canvasIndex;
383 
384     pscratch_bm = NULL;
385 
386     SetBackgroundColour ( wxColour(0,0,0) );
387     SetBackgroundStyle ( wxBG_STYLE_CUSTOM );  // on WXMSW, this prevents flashing on color scheme change
388 
389     m_groupIndex = 0;
390     m_bDrawingRoute = false;
391     m_bRouteEditing = false;
392     m_bMarkEditing = false;
393 	m_bRoutePoinDragging = false;
394     m_bIsInRadius = false;
395     m_bMayToggleMenuBar = true;
396 
397     m_bFollow = false;
398     m_bShowNavobjects = true;
399     m_bTCupdate = false;
400     m_bAppendingRoute = false;          // was true in MSW, why??
401     pThumbDIBShow = NULL;
402     m_bShowCurrent = false;
403     m_bShowTide = false;
404     bShowingCurrent = false;
405     pCwin = NULL;
406     warp_flag = false;
407     m_bzooming = false;
408     m_b_paint_enable = true;
409     m_routeState = 0;
410 
411     pss_overlay_bmp = NULL;
412     pss_overlay_mask = NULL;
413     m_bChartDragging = false;
414     m_bMeasure_Active = false;
415     m_bMeasure_DistCircle = false;
416     m_pMeasureRoute = NULL;
417     m_pTrackRolloverWin = NULL;
418     m_pRouteRolloverWin = NULL;
419     m_pAISRolloverWin = NULL;
420     m_bedge_pan = false;
421     m_disable_edge_pan = false;
422     m_dragoffsetSet = false;
423     m_bautofind = false;
424     m_bFirstAuto = true;
425     m_groupIndex = 0;
426     m_singleChart = NULL;
427     m_upMode = NORTH_UP_MODE;
428     m_bShowAIS = true;
429     m_bShowAISScaled = false;
430 
431     m_vLat = 0.;
432     m_vLon = 0.;
433 
434     m_pCIWin = NULL;
435 
436     m_pSelectedRoute              = NULL;
437     m_pSelectedTrack              = NULL;
438     m_pRoutePointEditTarget       = NULL;
439     m_pFoundPoint                 = NULL;
440     m_pMouseRoute                 = NULL;
441     m_prev_pMousePoint            = NULL;
442     m_pEditRouteArray             = NULL;
443     m_pFoundRoutePoint            = NULL;
444     m_FinishRouteOnKillFocus = true;
445 
446 
447     m_pRolloverRouteSeg           = NULL;
448     m_pRolloverTrackSeg           = NULL;
449     m_bsectors_shown              = false;
450 
451     m_bbrightdir = false;
452     r_gamma_mult = 1;
453     g_gamma_mult = 1;
454     b_gamma_mult = 1;
455 
456 
457     m_pos_image_user_day        = NULL;
458     m_pos_image_user_dusk       = NULL;
459     m_pos_image_user_night      = NULL;
460     m_pos_image_user_grey_day   = NULL;
461     m_pos_image_user_grey_dusk  = NULL;
462     m_pos_image_user_grey_night = NULL;
463 
464     m_zoom_factor = 1;
465     m_rotation_speed = 0;
466     m_mustmove = 0;
467 
468     m_OSoffsetx = 0.;
469     m_OSoffsety = 0.;
470 
471     m_pos_image_user_yellow_day = NULL;
472     m_pos_image_user_yellow_dusk = NULL;
473     m_pos_image_user_yellow_night = NULL;
474 
475     SetOwnShipState( SHIP_INVALID );
476 
477     undo = new Undo(this);
478 
479     VPoint.Invalidate();
480 
481     m_glcc = NULL;
482 
483     m_toolBar = NULL;
484     m_toolbar_scalefactor = 1.0;
485     m_toolbarOrientation = wxTB_HORIZONTAL;
486     m_focus_indicator_pix = 1;
487 
488     m_pCurrentStack = NULL;
489     m_bpersistent_quilt = false;
490     m_piano_ctx_menu = NULL;
491     m_Compass = NULL;
492 
493     g_ChartNotRenderScaleFactor = 2.0;
494     m_bShowScaleInStatusBar = true;
495 
496     m_muiBar = NULL;
497     m_bShowScaleInStatusBar = false;
498 
499     m_bShowOutlines = false;
500     m_bDisplayGrid = false;
501     m_bShowDepthUnits = true;
502     m_encDisplayCategory = (int)STANDARD;
503 
504     m_encShowLights = true;
505     m_encShowAnchor = true;
506     m_encShowDataQual = false;
507     m_bShowGPS = true;
508     m_pQuilt = new Quilt( this );
509     SetQuiltMode(true);
510     SetAlertString(_T(""));
511 
512     SetupGlCanvas( );
513 /*
514 #ifdef ocpnUSE_GL
515     if ( !g_bdisable_opengl )
516     {
517         if(g_bopengl){
518             wxLogMessage( _T("Creating glChartCanvas") );
519             m_glcc = new glChartCanvas(this);
520 
521         // We use one context for all GL windows, so that textures etc will be automatically shared
522             if(IsPrimaryCanvas()){
523                 wxGLContext *pctx = new wxGLContext(m_glcc);
524                 m_glcc->SetContext(pctx);
525                 g_pGLcontext = pctx;                // Save a copy of the common context
526             }
527             else{
528 #ifdef __WXOSX__
529                 m_glcc->SetContext(new wxGLContext(m_glcc, g_pGLcontext));
530 #else
531                 m_glcc->SetContext(g_pGLcontext);   // If not primary canvas, use the saved common context
532 #endif
533             }
534         }
535     }
536 #endif
537 */
538     singleClickEventIsValid = false;
539 
540 //    Build the cursors
541 
542     ocpnStyle::Style* style = g_StyleManager->GetCurrentStyle();
543 
544 #if !defined(__WXMSW__) && !defined(__WXQT__)
545 
546     wxImage ICursorLeft = style->GetIcon( _T("left") ).ConvertToImage();
547     wxImage ICursorRight = style->GetIcon( _T("right") ).ConvertToImage();
548     wxImage ICursorUp = style->GetIcon( _T("up") ).ConvertToImage();
549     wxImage ICursorDown = style->GetIcon( _T("down") ).ConvertToImage();
550     wxImage ICursorPencil = style->GetIcon( _T("pencil") ).ConvertToImage();
551     wxImage ICursorCross = style->GetIcon( _T("cross") ).ConvertToImage();
552 
553 //#if wxCHECK_VERSION(2, 8, 12)
554 //#else
555     ICursorLeft.ConvertAlphaToMask(128);
556     ICursorRight.ConvertAlphaToMask(128);
557     ICursorUp.ConvertAlphaToMask(128);
558     ICursorDown.ConvertAlphaToMask(128);
559     ICursorPencil.ConvertAlphaToMask(10);
560     ICursorCross.ConvertAlphaToMask(10);
561 //#endif
562 
563     if ( ICursorLeft.Ok() )
564     {
565         ICursorLeft.SetOption ( wxIMAGE_OPTION_CUR_HOTSPOT_X, 0 );
566         ICursorLeft.SetOption ( wxIMAGE_OPTION_CUR_HOTSPOT_Y, 15 );
567         pCursorLeft = new wxCursor ( ICursorLeft );
568     }
569     else
570         pCursorLeft = new wxCursor ( wxCURSOR_ARROW );
571 
572     if ( ICursorRight.Ok() )
573     {
574         ICursorRight.SetOption ( wxIMAGE_OPTION_CUR_HOTSPOT_X, 31 );
575         ICursorRight.SetOption ( wxIMAGE_OPTION_CUR_HOTSPOT_Y, 15 );
576         pCursorRight = new wxCursor ( ICursorRight );
577     }
578     else
579         pCursorRight = new wxCursor ( wxCURSOR_ARROW );
580 
581     if ( ICursorUp.Ok() )
582     {
583         ICursorUp.SetOption ( wxIMAGE_OPTION_CUR_HOTSPOT_X, 15 );
584         ICursorUp.SetOption ( wxIMAGE_OPTION_CUR_HOTSPOT_Y, 0 );
585         pCursorUp = new wxCursor ( ICursorUp );
586     }
587     else
588         pCursorUp = new wxCursor ( wxCURSOR_ARROW );
589 
590     if ( ICursorDown.Ok() )
591     {
592         ICursorDown.SetOption ( wxIMAGE_OPTION_CUR_HOTSPOT_X, 15 );
593         ICursorDown.SetOption ( wxIMAGE_OPTION_CUR_HOTSPOT_Y, 31 );
594         pCursorDown = new wxCursor ( ICursorDown );
595     }
596     else
597         pCursorDown = new wxCursor ( wxCURSOR_ARROW );
598 
599     if ( ICursorPencil.Ok() )
600     {
601         ICursorPencil.SetOption ( wxIMAGE_OPTION_CUR_HOTSPOT_X, 0 );
602         ICursorPencil.SetOption ( wxIMAGE_OPTION_CUR_HOTSPOT_Y, 16);
603         pCursorPencil = new wxCursor ( ICursorPencil );
604     }
605     else
606         pCursorPencil = new wxCursor ( wxCURSOR_ARROW );
607 
608     if ( ICursorCross.Ok() )
609     {
610         ICursorCross.SetOption ( wxIMAGE_OPTION_CUR_HOTSPOT_X, 13 );
611         ICursorCross.SetOption ( wxIMAGE_OPTION_CUR_HOTSPOT_Y, 12);
612         pCursorCross = new wxCursor ( ICursorCross );
613     }
614     else
615         pCursorCross = new wxCursor ( wxCURSOR_ARROW );
616 
617 #else
618 
619     wxImage ICursorLeft = style->GetIcon( _T("left") ).ConvertToImage();
620     wxImage ICursorRight = style->GetIcon( _T("right") ).ConvertToImage();
621     wxImage ICursorUp = style->GetIcon( _T("up") ).ConvertToImage();
622     wxImage ICursorDown = style->GetIcon( _T("down") ).ConvertToImage();
623     wxImage ICursorPencil = style->GetIcon( _T("pencil") ).ConvertToImage();
624     wxImage ICursorCross = style->GetIcon( _T("cross") ).ConvertToImage();
625 
626     if( ICursorLeft.Ok() ) {
627         ICursorLeft.SetOption( wxIMAGE_OPTION_CUR_HOTSPOT_X, 0 );
628         ICursorLeft.SetOption( wxIMAGE_OPTION_CUR_HOTSPOT_Y, 15 );
629         pCursorLeft = new wxCursor( ICursorLeft );
630     } else
631         pCursorLeft = new wxCursor( wxCURSOR_ARROW );
632 
633     if( ICursorRight.Ok() ) {
634         ICursorRight.SetOption( wxIMAGE_OPTION_CUR_HOTSPOT_X, 31 );
635         ICursorRight.SetOption( wxIMAGE_OPTION_CUR_HOTSPOT_Y, 15 );
636         pCursorRight = new wxCursor( ICursorRight );
637     } else
638         pCursorRight = new wxCursor( wxCURSOR_ARROW );
639 
640     if( ICursorUp.Ok() ) {
641         ICursorUp.SetOption( wxIMAGE_OPTION_CUR_HOTSPOT_X, 15 );
642         ICursorUp.SetOption( wxIMAGE_OPTION_CUR_HOTSPOT_Y, 0 );
643         pCursorUp = new wxCursor( ICursorUp );
644     } else
645         pCursorUp = new wxCursor( wxCURSOR_ARROW );
646 
647     if( ICursorDown.Ok() ) {
648         ICursorDown.SetOption( wxIMAGE_OPTION_CUR_HOTSPOT_X, 15 );
649         ICursorDown.SetOption( wxIMAGE_OPTION_CUR_HOTSPOT_Y, 31 );
650         pCursorDown = new wxCursor( ICursorDown );
651     } else
652         pCursorDown = new wxCursor( wxCURSOR_ARROW );
653 
654     if( ICursorPencil.Ok() ) {
655         ICursorPencil.SetOption( wxIMAGE_OPTION_CUR_HOTSPOT_X, 0 );
656         ICursorPencil.SetOption( wxIMAGE_OPTION_CUR_HOTSPOT_Y, 15 );
657         pCursorPencil = new wxCursor( ICursorPencil );
658     } else
659         pCursorPencil = new wxCursor( wxCURSOR_ARROW );
660 
661     if( ICursorCross.Ok() ) {
662         ICursorCross.SetOption( wxIMAGE_OPTION_CUR_HOTSPOT_X, 13 );
663         ICursorCross.SetOption( wxIMAGE_OPTION_CUR_HOTSPOT_Y, 12 );
664         pCursorCross = new wxCursor( ICursorCross );
665     } else
666         pCursorCross = new wxCursor( wxCURSOR_ARROW );
667 
668 #endif      // MSW, X11
669     pCursorArrow = new wxCursor( wxCURSOR_ARROW );
670     pPlugIn_Cursor = NULL;
671 
672     SetCursor( *pCursorArrow );
673 
674     pPanTimer = new wxTimer( this, PAN_TIMER );
675     pPanTimer->Stop();
676 
677     pMovementTimer = new wxTimer( this, MOVEMENT_TIMER );
678     pMovementTimer->Stop();
679 
680     pMovementStopTimer = new wxTimer( this, MOVEMENT_STOP_TIMER );
681     pMovementStopTimer->Stop();
682 
683     pRotDefTimer = new wxTimer( this, ROT_TIMER );
684     pRotDefTimer->Stop();
685 
686     m_DoubleClickTimer = new wxTimer( this, DBLCLICK_TIMER );
687     m_DoubleClickTimer->Stop();
688 
689     m_panx = m_pany = 0;
690     m_panspeed = 0;
691 
692     pCurTrackTimer = new wxTimer( this, CURTRACK_TIMER );
693     pCurTrackTimer->Stop();
694     m_curtrack_timer_msec = 10;
695 
696     m_wheelzoom_stop_oneshot = 0;
697     m_last_wheel_dir = 0;
698 
699     m_RolloverPopupTimer.SetOwner( this, ROPOPUP_TIMER );
700 
701     m_deferredFocusTimer.SetOwner( this, DEFERRED_FOCUS_TIMER );
702 
703     m_rollover_popup_timer_msec = 20;
704 
705     m_routeFinishTimer.SetOwner( this, ROUTEFINISH_TIMER );
706 
707     m_b_rot_hidef = true;
708 
709     proute_bm = NULL;
710     m_prot_bm = NULL;
711 
712     m_upMode = NORTH_UP_MODE;
713     m_bLookAhead = false;
714     m_VPRotate = 0;
715 
716 // Set some benign initial values
717 
718     m_cs = GLOBAL_COLOR_SCHEME_DAY;
719     VPoint.clat = 0;
720     VPoint.clon = 0;
721     VPoint.view_scale_ppm = 1;
722     VPoint.Invalidate();
723 
724     m_canvas_scale_factor = 1.;
725 
726     m_canvas_width = 1000;
727 
728     m_overzoomTextWidth = 0;
729     m_overzoomTextHeight = 0;
730 
731 //    Create the default world chart
732     pWorldBackgroundChart = new GSHHSChart;
733 
734 //    Create the default depth unit emboss maps
735     m_pEM_Feet = NULL;
736     m_pEM_Meters = NULL;
737     m_pEM_Fathoms = NULL;
738 
739     CreateDepthUnitEmbossMaps( GLOBAL_COLOR_SCHEME_DAY );
740 
741     m_pEM_OverZoom = NULL;
742     SetOverzoomFont();
743     CreateOZEmbossMapData( GLOBAL_COLOR_SCHEME_DAY );
744 
745 //    Build icons for tide/current points
746     m_bmTideDay = style->GetIcon( _T("tidesml") );
747 
748 //    Dusk
749     m_bmTideDusk = CreateDimBitmap( m_bmTideDay, .50 );
750 
751 //    Night
752     m_bmTideNight = CreateDimBitmap( m_bmTideDay, .20 );
753 
754 //    Build Dusk/Night  ownship icons
755     double factor_dusk = 0.5;
756     double factor_night = 0.25;
757 
758     //Red
759     m_os_image_red_day = style->GetIcon( _T("ship-red") ).ConvertToImage();
760 
761     int rimg_width = m_os_image_red_day.GetWidth();
762     int rimg_height = m_os_image_red_day.GetHeight();
763 
764     m_os_image_red_dusk = m_os_image_red_day.Copy();
765     m_os_image_red_night = m_os_image_red_day.Copy();
766 
767     for( int iy = 0; iy < rimg_height; iy++ ) {
768         for( int ix = 0; ix < rimg_width; ix++ ) {
769             if( !m_os_image_red_day.IsTransparent( ix, iy ) ) {
770                 wxImage::RGBValue rgb( m_os_image_red_day.GetRed( ix, iy ),
771                                        m_os_image_red_day.GetGreen( ix, iy ),
772                                        m_os_image_red_day.GetBlue( ix, iy ) );
773                 wxImage::HSVValue hsv = wxImage::RGBtoHSV( rgb );
774                 hsv.value = hsv.value * factor_dusk;
775                 wxImage::RGBValue nrgb = wxImage::HSVtoRGB( hsv );
776                 m_os_image_red_dusk.SetRGB( ix, iy, nrgb.red, nrgb.green, nrgb.blue );
777 
778                 hsv = wxImage::RGBtoHSV( rgb );
779                 hsv.value = hsv.value * factor_night;
780                 nrgb = wxImage::HSVtoRGB( hsv );
781                 m_os_image_red_night.SetRGB( ix, iy, nrgb.red, nrgb.green, nrgb.blue );
782             }
783         }
784     }
785 
786     //Grey
787     m_os_image_grey_day = style->GetIcon( _T("ship-red") ).ConvertToImage().ConvertToGreyscale();
788 
789     int gimg_width = m_os_image_grey_day.GetWidth();
790     int gimg_height = m_os_image_grey_day.GetHeight();
791 
792     m_os_image_grey_dusk = m_os_image_grey_day.Copy();
793     m_os_image_grey_night = m_os_image_grey_day.Copy();
794 
795     for( int iy = 0; iy < gimg_height; iy++ ) {
796         for( int ix = 0; ix < gimg_width; ix++ ) {
797             if( !m_os_image_grey_day.IsTransparent( ix, iy ) ) {
798                 wxImage::RGBValue rgb( m_os_image_grey_day.GetRed( ix, iy ),
799                                        m_os_image_grey_day.GetGreen( ix, iy ),
800                                        m_os_image_grey_day.GetBlue( ix, iy ) );
801                 wxImage::HSVValue hsv = wxImage::RGBtoHSV( rgb );
802                 hsv.value = hsv.value * factor_dusk;
803                 wxImage::RGBValue nrgb = wxImage::HSVtoRGB( hsv );
804                 m_os_image_grey_dusk.SetRGB( ix, iy, nrgb.red, nrgb.green, nrgb.blue );
805 
806                 hsv = wxImage::RGBtoHSV( rgb );
807                 hsv.value = hsv.value * factor_night;
808                 nrgb = wxImage::HSVtoRGB( hsv );
809                 m_os_image_grey_night.SetRGB( ix, iy, nrgb.red, nrgb.green, nrgb.blue );
810             }
811         }
812     }
813 
814 
815     // Yellow
816     m_os_image_yellow_day = m_os_image_red_day.Copy();
817 
818     gimg_width = m_os_image_yellow_day.GetWidth();
819     gimg_height = m_os_image_yellow_day.GetHeight();
820 
821     m_os_image_yellow_dusk = m_os_image_red_day.Copy();
822     m_os_image_yellow_night = m_os_image_red_day.Copy();
823 
824     for( int iy = 0; iy < gimg_height; iy++ ) {
825         for( int ix = 0; ix < gimg_width; ix++ ) {
826             if( !m_os_image_yellow_day.IsTransparent( ix, iy ) ) {
827                 wxImage::RGBValue rgb( m_os_image_yellow_day.GetRed( ix, iy ),
828                                        m_os_image_yellow_day.GetGreen( ix, iy ),
829                                        m_os_image_yellow_day.GetBlue( ix, iy ) );
830                 wxImage::HSVValue hsv = wxImage::RGBtoHSV( rgb );
831                 hsv.hue += 60./360.;             //shift to yellow
832                 wxImage::RGBValue nrgb = wxImage::HSVtoRGB( hsv );
833                 m_os_image_yellow_day.SetRGB( ix, iy, nrgb.red, nrgb.green, nrgb.blue );
834 
835                 hsv = wxImage::RGBtoHSV( rgb );
836                 hsv.value = hsv.value * factor_dusk;
837                 hsv.hue += 60./360.;             // shift to yellow
838                 nrgb = wxImage::HSVtoRGB( hsv );
839                 m_os_image_yellow_dusk.SetRGB( ix, iy, nrgb.red, nrgb.green, nrgb.blue );
840 
841                 hsv = wxImage::RGBtoHSV( rgb );
842                 hsv.hue += 60./360.;             //shift to yellow
843                 hsv.value = hsv.value * factor_night;
844                 nrgb = wxImage::HSVtoRGB( hsv );
845                 m_os_image_yellow_night.SetRGB( ix, iy, nrgb.red, nrgb.green, nrgb.blue );
846             }
847         }
848     }
849 
850 
851     //  Set initial pointers to ownship images
852     m_pos_image_red = &m_os_image_red_day;
853     m_pos_image_yellow = &m_os_image_yellow_day;
854     m_pos_image_grey = &m_os_image_grey_day;
855 
856     SetUserOwnship();
857 
858     m_pBrightPopup = NULL;
859 
860 #ifdef ocpnUSE_GL
861     if ( !g_bdisable_opengl )
862         m_pQuilt->EnableHighDefinitionZoom( true );
863 #endif
864 
865     m_pgridFont = FontMgr::Get().FindOrCreateFont( 8, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL,
866          wxFONTWEIGHT_NORMAL, FALSE, wxString( _T ( "Arial" ) ) );
867 
868     m_Piano = new Piano(this);
869 
870     m_bShowCompassWin = g_bShowCompassWin;
871 
872     m_Compass = new ocpnCompass(this);
873     m_Compass->SetScaleFactor(g_compass_scalefactor);
874     m_Compass->Show(m_bShowCompassWin);
875 
876     m_bToolbarEnable = false;
877     m_pianoFrozen = false;
878 
879     SetMinSize(wxSize(200,200));
880 }
881 
~ChartCanvas()882 ChartCanvas::~ChartCanvas()
883 {
884 
885     delete pThumbDIBShow;
886 
887 //    Delete Cursors
888     delete pCursorLeft;
889     delete pCursorRight;
890     delete pCursorUp;
891     delete pCursorDown;
892     delete pCursorArrow;
893     delete pCursorPencil;
894     delete pCursorCross;
895 
896     delete pPanTimer;
897     delete pMovementTimer;
898     delete pMovementStopTimer;
899     delete pCurTrackTimer;
900     delete pRotDefTimer;
901     delete m_DoubleClickTimer;
902 
903     delete m_pTrackRolloverWin;
904     delete m_pRouteRolloverWin;
905     delete m_pAISRolloverWin;
906     delete m_pBrightPopup;
907 
908     delete m_pCIWin;
909 
910     delete pscratch_bm;
911 
912     m_dc_route.SelectObject( wxNullBitmap );
913     delete proute_bm;
914 
915     delete pWorldBackgroundChart;
916     delete pss_overlay_bmp;
917 
918     delete m_pEM_Feet;
919     delete m_pEM_Meters;
920     delete m_pEM_Fathoms;
921 
922     delete m_pEM_OverZoom;
923 //        delete m_pEM_CM93Offset;
924 
925 
926     delete m_prot_bm;
927 
928     delete m_pos_image_user_day;
929     delete m_pos_image_user_dusk;
930     delete m_pos_image_user_night;
931     delete m_pos_image_user_grey_day;
932     delete m_pos_image_user_grey_dusk;
933     delete m_pos_image_user_grey_night;
934     delete m_pos_image_user_yellow_day;
935     delete m_pos_image_user_yellow_dusk;
936     delete m_pos_image_user_yellow_night;
937 
938     delete undo;
939 #ifdef ocpnUSE_GL
940     if( !g_bdisable_opengl ) {
941         delete m_glcc;
942 
943 #if wxCHECK_VERSION(2, 9, 0)
944         if(IsPrimaryCanvas() && g_bopengl)
945             delete g_pGLcontext;
946 #endif
947     }
948 #endif
949 
950     // Delete the MUI bar, but make sure there is no pointer to it during destroy.
951     // wx tries to deliver events to this canvas during destroy.
952     MUIBar *muiBar = m_muiBar;
953     m_muiBar = 0;
954     delete muiBar;
955     delete m_pQuilt;
956 }
957 
CanvasApplyLocale()958 void ChartCanvas::CanvasApplyLocale()
959 {
960     CreateDepthUnitEmbossMaps( m_cs );
961     CreateOZEmbossMapData( m_cs );
962 }
963 
SetupGlCanvas()964 void ChartCanvas::SetupGlCanvas( )
965 {
966 #ifndef __OCPN__ANDROID__
967 #ifdef ocpnUSE_GL
968     if ( !g_bdisable_opengl )
969     {
970         if(g_bopengl){
971             wxLogMessage( _T("Creating glChartCanvas") );
972             m_glcc = new glChartCanvas(this);
973 
974         // We use one context for all GL windows, so that textures etc will be automatically shared
975             if(IsPrimaryCanvas()){
976                 //qDebug() << "Creating Primary Context";
977 
978 //             wxGLContextAttrs ctxAttr;
979 //             ctxAttr.PlatformDefaults().CoreProfile().OGLVersion(3, 2).EndList();
980 //             wxGLContext *pctx = new wxGLContext(m_glcc, NULL, &ctxAttr);
981                 wxGLContext *pctx = new wxGLContext(m_glcc);
982                 m_glcc->SetContext(pctx);
983                 g_pGLcontext = pctx;                // Save a copy of the common context
984             }
985             else{
986 #ifdef __WXOSX__
987                 m_glcc->SetContext(new wxGLContext(m_glcc, g_pGLcontext));
988 #else
989                 m_glcc->SetContext(g_pGLcontext);   // If not primary canvas, use the saved common context
990 #endif
991             }
992         }
993     }
994 #endif
995 #endif
996 
997 #ifdef __OCPN__ANDROID__   //ocpnUSE_GL
998     if ( !g_bdisable_opengl )
999     {
1000         if(g_bopengl){
1001             //qDebug() << "SetupGlCanvas";
1002             wxLogMessage( _T("Creating glChartCanvas") );
1003 
1004         // We use one context for all GL windows, so that textures etc will be automatically shared
1005             if(IsPrimaryCanvas()){
1006                 qDebug() << "Creating Primary glChartCanvas";
1007 
1008 //             wxGLContextAttrs ctxAttr;
1009 //             ctxAttr.PlatformDefaults().CoreProfile().OGLVersion(3, 2).EndList();
1010 //             wxGLContext *pctx = new wxGLContext(m_glcc, NULL, &ctxAttr);
1011                 m_glcc = new glChartCanvas(this);
1012 
1013                 wxGLContext *pctx = new wxGLContext(m_glcc);
1014                 m_glcc->SetContext(pctx);
1015                 g_pGLcontext = pctx;                // Save a copy of the common context
1016                 m_glcc->m_pParentCanvas = this;
1017                 //m_glcc->Reparent(this);
1018             }
1019             else{
1020                 qDebug() << "Creating Secondary glChartCanvas";
1021                 //QGLContext *pctx = gFrame->GetPrimaryCanvas()->GetglCanvas()->GetQGLContext();
1022                 //qDebug() << "pctx: " << pctx;
1023 
1024 
1025                  m_glcc = new glChartCanvas(gFrame, gFrame->GetPrimaryCanvas()->GetglCanvas());   //Shared
1026 //                 m_glcc = new glChartCanvas(this, pctx);   //Shared
1027 //                 m_glcc = new glChartCanvas(this, wxPoint(900, 0));
1028                  wxGLContext *pwxctx = new wxGLContext(m_glcc);
1029                  m_glcc->SetContext(pwxctx);
1030                  m_glcc->m_pParentCanvas = this;
1031                  //m_glcc->Reparent(this);
1032 
1033 
1034 
1035 
1036             }
1037         }
1038     }
1039 #endif
1040 
1041 }
1042 
OnKillFocus(wxFocusEvent & WXUNUSED (event))1043 void ChartCanvas::OnKillFocus( wxFocusEvent& WXUNUSED(event) )
1044 {
1045     RefreshRect( wxRect(0, 0, GetClientSize().x, m_focus_indicator_pix ), false );
1046 
1047     // On Android, we get a KillFocus on just about every keystroke.
1048     //  Why?
1049 #ifdef __OCPN__ANDROID__
1050     return;
1051 #endif
1052 
1053     // Special logic:
1054     //  On OSX in GL mode, each mouse click causes a kill and immediate regain of canvas focus.  Why???  Who knows...
1055     //  So, we provide for this case by starting a timer if required to actually Finish() a route on a legitimate
1056     //  focus change, but not if the focus is quickly regained ( <20 msec.) on this canvas.
1057 #ifdef __WXOSX__
1058     if(m_routeState && m_FinishRouteOnKillFocus)
1059         m_routeFinishTimer.Start(20, wxTIMER_ONE_SHOT);
1060 #else
1061     if(m_routeState && m_FinishRouteOnKillFocus)
1062         FinishRoute();
1063 #endif
1064 }
1065 
OnSetFocus(wxFocusEvent & WXUNUSED (event))1066 void ChartCanvas::OnSetFocus( wxFocusEvent& WXUNUSED(event) )
1067 {
1068     m_routeFinishTimer.Stop();
1069 
1070     // Try to keep the global top-line menubar selections up to date with the current "focus" canvas
1071     gFrame->UpdateGlobalMenuItems( this );
1072 
1073     RefreshRect( wxRect(0, 0, GetClientSize().x, m_focus_indicator_pix ), false );
1074 }
1075 
1076 
OnRouteFinishTimerEvent(wxTimerEvent & event)1077 void ChartCanvas::OnRouteFinishTimerEvent( wxTimerEvent& event )
1078 {
1079     if(m_routeState && m_FinishRouteOnKillFocus)
1080         FinishRoute();
1081 }
1082 
1083 
ApplyCanvasConfig(canvasConfig * pcc)1084 void ChartCanvas::ApplyCanvasConfig(canvasConfig *pcc)
1085 {
1086     SetViewPoint( pcc->iLat, pcc->iLon, pcc->iScale, 0., pcc->iRotation );
1087     m_vLat = pcc->iLat;
1088     m_vLon = pcc->iLon;
1089 
1090     m_restore_dbindex = pcc->DBindex;
1091     m_bFollow = pcc->bFollow;
1092     if ( pcc->GroupID < 0 )
1093         pcc->GroupID = 0;
1094 
1095     if( pcc->GroupID > (int) g_pGroupArray->GetCount() )
1096         m_groupIndex = 0;
1097     else
1098         m_groupIndex = pcc->GroupID;
1099 
1100     if( pcc->bQuilt != GetQuiltMode() )
1101         ToggleCanvasQuiltMode();
1102 
1103     ShowTides(pcc->bShowTides);
1104     ShowCurrents(pcc->bShowCurrents);
1105 
1106     SetShowDepthUnits( pcc->bShowDepthUnits );
1107     SetShowGrid( pcc->bShowGrid );
1108     SetShowOutlines( pcc->bShowOutlines );
1109 
1110     SetShowAIS(pcc->bShowAIS);
1111     SetAttenAIS(pcc->bAttenAIS);
1112 
1113     // ENC options
1114     SetShowENCText( pcc->bShowENCText );
1115     m_encDisplayCategory = pcc->nENCDisplayCategory;
1116     m_encShowDepth = pcc->bShowENCDepths;
1117     m_encShowLightDesc = pcc->bShowENCLightDescriptions;
1118     m_encShowBuoyLabels = pcc->bShowENCBuoyLabels;
1119     m_encShowLights = pcc->bShowENCLights;
1120 
1121     bool courseUp = pcc->bCourseUp;
1122     bool headUp = pcc->bHeadUp;
1123     m_upMode = NORTH_UP_MODE;
1124     if(courseUp)
1125         m_upMode = COURSE_UP_MODE;
1126     else if(headUp)
1127         m_upMode = HEAD_UP_MODE;
1128 
1129     m_bLookAhead = pcc->bLookahead;
1130 
1131     m_singleChart = NULL;
1132 
1133 }
1134 
ApplyGlobalSettings()1135 void ChartCanvas::ApplyGlobalSettings()
1136 {
1137     // GPS compas window
1138     m_bShowCompassWin = g_bShowCompassWin;
1139     if(m_Compass){
1140         m_Compass->Show(m_bShowCompassWin);
1141         if(m_bShowCompassWin)
1142             m_Compass->UpdateStatus();
1143     }
1144 }
1145 
1146 
CheckGroupValid(bool showMessage,bool switchGroup0)1147 void ChartCanvas::CheckGroupValid( bool showMessage, bool switchGroup0)
1148 {
1149     bool groupOK = CheckGroup( m_groupIndex );
1150 
1151     if(!groupOK){
1152         SetGroupIndex( m_groupIndex, true );
1153     }
1154 
1155 }
1156 
SetShowGPS(bool bshow)1157 void ChartCanvas::SetShowGPS( bool bshow )
1158 {
1159     if(m_bShowGPS != bshow){
1160         delete m_Compass;
1161         m_Compass = new ocpnCompass( this, bshow );
1162         m_Compass->SetScaleFactor(g_compass_scalefactor);
1163         m_Compass->Show(m_bShowCompassWin);
1164     }
1165     m_bShowGPS = bshow;
1166 
1167 }
1168 
SetShowGPSCompassWindow(bool bshow)1169 void ChartCanvas::SetShowGPSCompassWindow( bool bshow )
1170 {
1171     if(m_Compass){
1172         m_Compass->Show(m_bShowCompassWin);
1173         if(m_bShowCompassWin)
1174             m_Compass->UpdateStatus();
1175     }
1176 
1177 }
1178 
1179 
SetToolbarEnable(bool bShow)1180 void ChartCanvas::SetToolbarEnable( bool bShow )
1181 {
1182     return;
1183 
1184     //if(GetToolbarEnable() != bShow)
1185     {
1186         m_bToolbarEnable = bShow;
1187 //         if(!m_toolBar)
1188 //             RequestNewCanvasToolbar( true );
1189         if(GetToolbar())
1190             GetToolbar()->Show( bShow );
1191     }
1192 }
1193 
GetPianoHeight()1194 int ChartCanvas::GetPianoHeight()
1195 {
1196     int height = 0;
1197     if(g_bShowChartBar && GetPiano())
1198         height = m_Piano->GetHeight();
1199 
1200     return height;
1201 }
1202 
ConfigureChartBar()1203 void ChartCanvas::ConfigureChartBar()
1204 {
1205     ocpnStyle::Style* style = g_StyleManager->GetCurrentStyle();
1206 
1207     m_Piano->SetVizIcon( new wxBitmap( style->GetIcon( _T("viz") ) ) );
1208     m_Piano->SetInVizIcon( new wxBitmap( style->GetIcon( _T("redX") ) ) );
1209 
1210     if( GetQuiltMode() ) {
1211         m_Piano->SetRoundedRectangles( true );
1212     }
1213     m_Piano->SetTMercIcon( new wxBitmap( style->GetIcon( _T("tmercprj") ) ) );
1214     m_Piano->SetPolyIcon( new wxBitmap( style->GetIcon( _T("polyprj") ) ) );
1215     m_Piano->SetSkewIcon( new wxBitmap( style->GetIcon( _T("skewprj") ) ) );
1216 
1217 }
1218 
ShowTides(bool bShow)1219 void ChartCanvas::ShowTides(bool bShow)
1220 {
1221     gFrame->LoadHarmonics();
1222 
1223     if( ptcmgr->IsReady() ) {
1224         SetbShowTide( bShow );
1225         if( m_toolBar )
1226             m_toolBar->GetToolbar()->ToggleTool( ID_TIDE, bShow );
1227         wxString tip = _("Show Tides");
1228         if(bShow)
1229             tip = _("Hide Tides");
1230         if( m_toolBar )
1231             m_toolBar->SetToolShortHelp( ID_TIDE, tip );
1232 
1233         parent_frame->SetMenubarItemState( ID_MENU_SHOW_TIDES, bShow );
1234     } else {
1235         wxLogMessage( _T("Chart1::Event...TCMgr Not Available") );
1236         SetbShowTide( false );
1237         if( m_toolBar )
1238             m_toolBar->GetToolbar()->ToggleTool( ID_TIDE, false );
1239         parent_frame->SetMenubarItemState( ID_MENU_SHOW_TIDES, false );
1240     }
1241 
1242     if( GetMUIBar() && GetMUIBar()->GetCanvasOptions())
1243         GetMUIBar()->GetCanvasOptions()->RefreshControlValues();
1244 
1245 
1246     // TODO
1247 //     if( GetbShowTide() ) {
1248 //         FrameTCTimer.Start( TIMER_TC_VALUE_SECONDS * 1000, wxTIMER_CONTINUOUS );
1249 //         SetbTCUpdate( true );                        // force immediate update
1250 //     } else
1251 //         FrameTCTimer.Stop();
1252 
1253 }
1254 
ShowCurrents(bool bShow)1255 void ChartCanvas::ShowCurrents(bool bShow)
1256 {
1257     gFrame->LoadHarmonics();
1258 
1259     if( ptcmgr->IsReady() ) {
1260         SetbShowCurrent( bShow );
1261         if( m_toolBar )
1262             m_toolBar->GetToolbar()->ToggleTool( ID_CURRENT, bShow );
1263         wxString tip = _("Show Currents");
1264         if(bShow)
1265             tip = _("Hide Currents");
1266         if( m_toolBar )
1267             m_toolBar->SetToolShortHelp( ID_CURRENT, tip );
1268 
1269         parent_frame->SetMenubarItemState( ID_MENU_SHOW_CURRENTS, bShow );
1270     } else {
1271         wxLogMessage( _T("Chart1::Event...TCMgr Not Available") );
1272         SetbShowCurrent( false );
1273         if( m_toolBar )
1274             m_toolBar->GetToolbar()->ToggleTool( ID_CURRENT, false );
1275         parent_frame->SetMenubarItemState( ID_MENU_SHOW_CURRENTS, false );
1276     }
1277 
1278     if( GetMUIBar() && GetMUIBar()->GetCanvasOptions())
1279         GetMUIBar()->GetCanvasOptions()->RefreshControlValues();
1280 
1281 
1282     // TODO
1283 //     if( GetbShowCurrent() ) {
1284 //         FrameTCTimer.Start( TIMER_TC_VALUE_SECONDS * 1000, wxTIMER_CONTINUOUS );
1285 //         SetbTCUpdate( true );                        // force immediate update
1286 //     } else
1287 //         FrameTCTimer.Stop();
1288 
1289 }
1290 
1291 
1292 //TODO
1293 extern bool     g_bPreserveScaleOnX;
1294 extern ChartDummy *pDummyChart;
1295 extern int      g_sticky_chart;
1296 
canvasRefreshGroupIndex(void)1297 void ChartCanvas::canvasRefreshGroupIndex( void )
1298 {
1299     SetGroupIndex(m_groupIndex);
1300 }
1301 
SetGroupIndex(int index,bool autoSwitch)1302 void ChartCanvas::SetGroupIndex( int index, bool autoSwitch )
1303 {
1304     SetAlertString(_T(""));
1305 
1306     int new_index = index;
1307     if( index > (int) g_pGroupArray->GetCount() )
1308         new_index = 0;
1309 
1310     bool bgroup_override = false;
1311     int old_group_index = new_index;
1312 
1313     if( !CheckGroup( new_index ) ) {
1314         new_index = 0;
1315         bgroup_override = true;
1316     }
1317 
1318     if(!autoSwitch && ( index <= (int) g_pGroupArray->GetCount()))
1319         new_index = index;
1320 
1321     //    Get the currently displayed chart native scale, and the current ViewPort
1322     int current_chart_native_scale = GetCanvasChartNativeScale();
1323     ViewPort vp = GetVP();
1324 
1325     m_groupIndex = new_index;
1326 
1327     // Are there  ENCs in this group
1328     if(ChartData)
1329         m_bENCGroup = ChartData->IsENCInGroup( m_groupIndex );
1330 
1331     //  Update the MUIBar for ENC availability
1332     if(m_muiBar)
1333         m_muiBar->SetCanvasENCAvailable(m_bENCGroup);
1334 
1335     //  Allow the chart database to pre-calculate the MBTile inclusion test boolean...
1336     ChartData->CheckExclusiveTileGroup(m_canvasIndex);
1337 
1338     //  Invalidate the "sticky" chart on group change, since it might not be in the new group
1339     g_sticky_chart = -1;
1340 
1341     //    We need a chartstack and quilt to figure out which chart to open in the new group
1342     UpdateCanvasOnGroupChange();
1343 
1344     int dbi_now = -1;
1345     if(GetQuiltMode())
1346         dbi_now = GetQuiltReferenceChartIndex();
1347 
1348     int dbi_hint = FindClosestCanvasChartdbIndex( current_chart_native_scale );
1349 
1350     // If a new reference chart is indicated, set a good scale for it.
1351     if((dbi_now != dbi_hint) || !GetQuiltMode()){
1352         double best_scale = GetBestStartScale(dbi_hint, vp);
1353         SetVPScale( best_scale );
1354     }
1355 
1356     if(GetQuiltMode())
1357         dbi_hint = GetQuiltReferenceChartIndex();
1358 
1359     //    Refresh the canvas, selecting the "best" chart,
1360     //    applying the prior ViewPort exactly
1361     canvasChartsRefresh( dbi_hint );
1362 
1363     if(!autoSwitch && bgroup_override){
1364         // show a short timed message box
1365         wxString msg( _("Group \"") );
1366 
1367         ChartGroup *pGroup = g_pGroupArray->Item( new_index - 1 );
1368         msg += pGroup->m_group_name;
1369 
1370         msg += _("\" is empty.");
1371 
1372         OCPNMessageBox( this, msg, _("OpenCPN Group Notice"), wxICON_INFORMATION, 2 );
1373 
1374         return;
1375     }
1376 
1377 
1378         //    Message box is deferred so that canvas refresh occurs properly before dialog
1379     if( bgroup_override ) {
1380             wxString msg( _("Group \"") );
1381 
1382             ChartGroup *pGroup = g_pGroupArray->Item( old_group_index - 1 );
1383             msg += pGroup->m_group_name;
1384 
1385             msg += _("\" is empty, switching to \"All Active Charts\" group.");
1386 
1387             OCPNMessageBox( this, msg, _("OpenCPN Group Notice"), wxOK, 5 );
1388     }
1389 }
1390 
CheckGroup(int igroup)1391 bool ChartCanvas::CheckGroup( int igroup )
1392 {
1393     if(!ChartData)
1394         return true;                            //  Not known yet...
1395 
1396     if( igroup == 0 )
1397         return true;              // "all charts" is always OK
1398 
1399     if ( igroup < 0 )               // negative group is an error
1400         return false;
1401 
1402     ChartGroup *pGroup = g_pGroupArray->Item( igroup - 1 );
1403 
1404     if( pGroup->m_element_array.empty() )   //  truly empty group prompts a warning, and auto-shift to group 0
1405         return false;
1406 
1407     for( auto& elem : pGroup->m_element_array ) {
1408         for( unsigned int ic = 0; ic < (unsigned int) ChartData->GetChartTableEntries(); ic++ ) {
1409             ChartTableEntry *pcte = ChartData->GetpChartTableEntry( ic );
1410             wxString chart_full_path( pcte->GetpFullPath(), wxConvUTF8 );
1411 
1412             if( chart_full_path.StartsWith( elem->m_element_name ) )
1413                return true;
1414         }
1415     }
1416 
1417     //  If necessary, check for GSHHS
1418     for( auto& elem : pGroup->m_element_array ) {
1419         wxString element_root = elem->m_element_name;
1420         wxString test_string = _T("GSHH");
1421         if(element_root.Upper().Contains(test_string))
1422             return true;
1423     }
1424 
1425     return false;
1426 }
1427 
1428 
canvasChartsRefresh(int dbi_hint)1429 void ChartCanvas::canvasChartsRefresh( int dbi_hint )
1430 {
1431     if( !ChartData )
1432         return;
1433 
1434     OCPNPlatform::ShowBusySpinner();
1435 
1436     double old_scale = GetVPScale();
1437     InvalidateQuilt();
1438     SetQuiltRefChart( -1 );
1439 
1440     m_singleChart = NULL;
1441 
1442     //delete m_pCurrentStack;
1443     //m_pCurrentStack = NULL;
1444 
1445     //    Build a new ChartStack
1446     if(!m_pCurrentStack){
1447         m_pCurrentStack = new ChartStack;
1448         ChartData->BuildChartStack( m_pCurrentStack, m_vLat, m_vLon, m_groupIndex );
1449     }
1450 
1451     if( -1 != dbi_hint ) {
1452         if( GetQuiltMode() ) {
1453             GetpCurrentStack()->SetCurrentEntryFromdbIndex( dbi_hint );
1454             SetQuiltRefChart( dbi_hint );
1455         } else {
1456             //      Open the saved chart
1457             ChartBase *pTentative_Chart;
1458             pTentative_Chart = ChartData->OpenChartFromDB( dbi_hint, FULL_INIT );
1459 
1460             if( pTentative_Chart ) {
1461                 /* m_singleChart is always NULL here, (set above) should this go before that? */
1462                 if( m_singleChart )
1463                     m_singleChart->Deactivate();
1464 
1465                 m_singleChart = pTentative_Chart;
1466                 m_singleChart->Activate();
1467 
1468                 GetpCurrentStack()->CurrentStackEntry = ChartData->GetStackEntry( GetpCurrentStack(),
1469                                                                                   m_singleChart->GetFullPath() );
1470             }
1471             //else
1472                 //SetChartThumbnail( dbi_hint );       // need to reset thumbnail on failed chart open
1473         }
1474 
1475         //refresh_Piano();
1476     } else {
1477         //    Select reference chart from the stack, as though clicked by user
1478         //    Make it the smallest scale chart on the stack
1479         GetpCurrentStack()->CurrentStackEntry = GetpCurrentStack()->nEntry - 1;
1480         int selected_index = GetpCurrentStack()->GetCurrentEntrydbIndex();
1481         SetQuiltRefChart( selected_index );
1482     }
1483 
1484     //    Validate the correct single chart, or set the quilt mode as appropriate
1485     SetupCanvasQuiltMode();
1486     if( !GetQuiltMode() && m_singleChart == 0) {
1487         // use a dummy like in DoChartUpdate
1488         if (NULL == pDummyChart )
1489             pDummyChart = new ChartDummy;
1490         m_singleChart = pDummyChart;
1491         SetVPScale( old_scale );
1492     }
1493 
1494     ReloadVP();
1495 
1496     UpdateCanvasControlBar();
1497     UpdateGPSCompassStatusBox( true );
1498 
1499     SetCursor( wxCURSOR_ARROW );
1500 
1501     OCPNPlatform::HideBusySpinner();
1502 }
1503 
1504 
DoCanvasUpdate(void)1505 bool ChartCanvas::DoCanvasUpdate( void )
1506 {
1507 
1508     double tLat, tLon;           // Chart Stack location
1509     double vpLat, vpLon;         // ViewPort location
1510 
1511     bool bNewChart = false;
1512     bool bNewView = false;
1513     bool bCanvasChartAutoOpen = true;                             // debugging
1514 
1515     bool bNewPiano = false;
1516     bool bOpenSpecified;
1517     ChartStack LastStack;
1518     ChartBase *pLast_Ch;
1519 
1520     ChartStack WorkStack;
1521 
1522     if( bDBUpdateInProgress )
1523         return false;
1524     if( !ChartData ) return false;
1525 
1526     if(ChartData->IsBusy())
1527         return false;
1528 
1529     //    Startup case:
1530     //    Quilting is enabled, but the last chart seen was not quiltable
1531     //    In this case, drop to single chart mode, set persistence flag,
1532     //    And open the specified chart
1533 //TODO implement this
1534 //     if( m_bFirstAuto && ( g_restore_dbindex >= 0 ) ) {
1535 //         if( GetQuiltMode() ) {
1536 //             if( !IsChartQuiltableRef( g_restore_dbindex ) ) {
1537 //                 gFrame->ToggleQuiltMode();
1538 //                 m_bpersistent_quilt = true;
1539         //                 m_singleChart = NULL;
1540 //             }
1541 //         }
1542 //     }
1543 
1544     //      If in auto-follow mode, use the current glat,glon to build chart stack.
1545     //      Otherwise, use vLat, vLon gotten from click on chart canvas, or other means
1546 
1547     if( m_bFollow ) {
1548         tLat = gLat;
1549         tLon = gLon;
1550 
1551         // Set the ViewPort center based on the OWNSHIP offset
1552         double dx = m_OSoffsetx;
1553         double dy = m_OSoffsety;
1554         double d_east = dx / GetVP().view_scale_ppm;
1555         double d_north = dy / GetVP().view_scale_ppm;
1556 
1557         if(GetUpMode() == NORTH_UP_MODE){
1558             fromSM( d_east, d_north, gLat, gLon, &vpLat, &vpLon );
1559         }
1560         else{
1561             double offset_angle = atan2(d_north, d_east);
1562             double offset_distance = sqrt((d_north * d_north) + (d_east * d_east));
1563             double chart_angle =  GetVPRotation();
1564             double target_angle = chart_angle + offset_angle;
1565             double d_east_mod = offset_distance * cos( target_angle );
1566             double d_north_mod = offset_distance * sin( target_angle );
1567             fromSM( d_east_mod, d_north_mod, gLat, gLon, &vpLat, &vpLon );
1568         }
1569 
1570         // on lookahead mode, adjust the vp center point
1571         if( m_bLookAhead && bGPSValid && !m_MouseDragging ) {
1572             double angle = g_COGAvg + ( GetVPRotation() * 180. / PI );
1573 
1574             double pixel_deltay = fabs( cos( angle * PI / 180. ) ) * GetCanvasHeight() / 4;
1575             double pixel_deltax = fabs( sin( angle * PI / 180. ) ) * GetCanvasWidth() / 4;
1576 
1577             double pixel_delta_tent = sqrt(
1578                 ( pixel_deltay * pixel_deltay ) + ( pixel_deltax * pixel_deltax ) );
1579 
1580             double pixel_delta = 0;
1581 
1582             //    The idea here is to cancel the effect of LookAhead for slow gSog, to avoid
1583             //    jumping of the vp center point during slow maneuvering, or at anchor....
1584             if( !std::isnan(gSog) ) {
1585                 if( gSog < 1.0 ) pixel_delta = 0.;
1586                 else
1587                     if( gSog >= 3.0 ) pixel_delta = pixel_delta_tent;
1588                     else
1589                         pixel_delta = pixel_delta_tent * ( gSog - 1.0 ) / 2.0;
1590             }
1591 
1592             double meters_to_shift = cos( gLat * PI / 180. ) * pixel_delta / GetVPScale();
1593 
1594             double dir_to_shift = g_COGAvg;
1595 
1596             ll_gc_ll( gLat, gLon, dir_to_shift, meters_to_shift / 1852., &vpLat, &vpLon );
1597         }
1598         else if(m_bLookAhead && !bGPSValid){
1599             m_OSoffsetx = 0;            // center ownship on loss of GPS
1600             m_OSoffsety = 0;
1601             vpLat = gLat;
1602             vpLon = gLon;
1603         }
1604 
1605 
1606     } else {
1607         tLat = m_vLat;
1608         tLon = m_vLon;
1609         vpLat = m_vLat;
1610         vpLon = m_vLon;
1611 
1612     }
1613 
1614     // Calculate change in VP, in pixels, using a simple SM projection
1615     // if change in pixels is smaller than 2% of screen size, do not change the VP
1616     // This will avoid "jitters" at large scale.
1617     if(GetVP().view_scale_ppm > 1.0){
1618         double easting, northing;
1619         toSM( GetVP().clat, GetVP().clon, vpLat, vpLon,  &easting, &northing );
1620         if( (fabs(easting * GetVP().view_scale_ppm) < (GetVP().pix_width * 2 / 100)) ||
1621             (fabs(northing * GetVP().view_scale_ppm) < (GetVP().pix_height * 2 / 100)) ){
1622             vpLat = GetVP().clat;
1623             vpLon = GetVP().clon;
1624         }
1625     }
1626 
1627 
1628     if( GetQuiltMode() ) {
1629         int current_db_index = -1;
1630         if( m_pCurrentStack )
1631             current_db_index = m_pCurrentStack->GetCurrentEntrydbIndex(); // capture the currently selected Ref chart dbIndex
1632         else
1633             m_pCurrentStack = new ChartStack;
1634 
1635         //  This logic added to enable opening a chart when there is no
1636             //  previous chart indication, either from inital startup, or from adding new chart directory
1637         if( m_bautofind && (-1 == GetQuiltReferenceChartIndex()) && m_pCurrentStack ){
1638             if (m_pCurrentStack->nEntry) {
1639                 int new_dbIndex = m_pCurrentStack->GetDBIndex(m_pCurrentStack->nEntry-1);    // smallest scale
1640                 SelectQuiltRefdbChart(new_dbIndex, true);
1641                 m_bautofind = false;
1642             }
1643         }
1644 
1645          ChartData->BuildChartStack( m_pCurrentStack, tLat, tLon, m_groupIndex );
1646          m_pCurrentStack->SetCurrentEntryFromdbIndex( current_db_index );
1647 
1648          if( m_bFirstAuto ) {
1649 
1650              //  Allow the chart database to pre-calculate the MBTile inclusion test boolean...
1651             ChartData->CheckExclusiveTileGroup(m_canvasIndex);
1652 
1653                     double proposed_scale_onscreen = GetCanvasScaleFactor() / GetVPScale(); // as set from config load
1654 
1655                         int initial_db_index = m_restore_dbindex;
1656                         if( initial_db_index < 0 ) {
1657                             if( m_pCurrentStack->nEntry ) {
1658                                 initial_db_index = m_pCurrentStack->GetDBIndex( m_pCurrentStack->nEntry - 1 );
1659                             } else
1660                                 m_bautofind = true; //initial_db_index = 0;
1661                         }
1662 
1663                         if( m_pCurrentStack->nEntry ) {
1664 
1665                             int initial_type = ChartData->GetDBChartType( initial_db_index );
1666 
1667                             //    Check to see if the target new chart is quiltable as a reference chart
1668 
1669                             if( !IsChartQuiltableRef( initial_db_index ) ) {
1670                                 // If it is not quiltable, then walk the stack up looking for a satisfactory chart
1671                                 // i.e. one that is quiltable and of the same type
1672                                 // XXX if there's none?
1673                                 int stack_index = 0;
1674 
1675                                 if ( stack_index >= 0 ){
1676                                     while( ( stack_index < m_pCurrentStack->nEntry - 1 ) ) {
1677                                         int test_db_index = m_pCurrentStack->GetDBIndex( stack_index );
1678                                         if( IsChartQuiltableRef( test_db_index )
1679                                             && ( initial_type == ChartData->GetDBChartType( initial_db_index ) ) ) {
1680                                             initial_db_index = test_db_index;
1681                                             break;
1682                                         }
1683                                         stack_index++;
1684                                     }
1685                                 }
1686                             }
1687 
1688                             ChartBase *pc = ChartData->OpenChartFromDB( initial_db_index, FULL_INIT );
1689                             if( pc ) {
1690                                 SetQuiltRefChart( initial_db_index );
1691                                 m_pCurrentStack->SetCurrentEntryFromdbIndex( initial_db_index );
1692                             }
1693 
1694                             // Check proposed scale, see how much underzoom results
1695                             // Adjust as necessary to prevent slow loading on initial startup
1696                             // For MBTILES we skip this test because they are always shown in reasonable range of scale
1697                             if(pc){
1698                                 if (pc->GetChartType() != CHART_TYPE_MBTILES)
1699                                     proposed_scale_onscreen = wxMin(proposed_scale_onscreen,
1700                                                                     4.0 * pc->GetNativeScale());
1701                                 else
1702                                     proposed_scale_onscreen = wxMin(proposed_scale_onscreen,
1703                                                                     32.0 * pc->GetNativeScale());
1704                             }
1705                         }
1706 
1707                         bNewView |= SetViewPoint( vpLat, vpLon,
1708                                                        GetCanvasScaleFactor() / proposed_scale_onscreen, 0,
1709                                                        GetVPRotation() );
1710 
1711                 }
1712                 // else
1713                 bNewView |= SetViewPoint( vpLat, vpLon, GetVPScale(), 0, GetVPRotation() );
1714 
1715                 goto update_finish;
1716 
1717     }
1718 
1719     //  Single Chart Mode from here....
1720     pLast_Ch = m_singleChart;
1721     ChartTypeEnum new_open_type;
1722     ChartFamilyEnum new_open_family;
1723     if( pLast_Ch ) {
1724         new_open_type = pLast_Ch->GetChartType();
1725         new_open_family = pLast_Ch->GetChartFamily();
1726     } else {
1727         new_open_type = CHART_TYPE_KAP;
1728         new_open_family = CHART_FAMILY_RASTER;
1729     }
1730 
1731     bOpenSpecified = m_bFirstAuto;
1732 
1733     //  Make sure the target stack is valid
1734     if( NULL == m_pCurrentStack )
1735         m_pCurrentStack = new ChartStack;
1736 
1737                                                                     // Build a chart stack based on tLat, tLon
1738     if( 0 == ChartData->BuildChartStack( &WorkStack, tLat, tLon, g_sticky_chart, m_groupIndex ) ) {      // Bogus Lat, Lon?
1739         if( NULL == pDummyChart ) {
1740             pDummyChart = new ChartDummy;
1741             bNewChart = true;
1742         }
1743 
1744         if( m_singleChart ) if( m_singleChart->GetChartType() != CHART_TYPE_DUMMY ) bNewChart = true;
1745 
1746         m_singleChart = pDummyChart;
1747 
1748         //    If the current viewpoint is invalid, set the default scale to something reasonable.
1749                                                                     double set_scale = GetVPScale();
1750                                                                     if( !GetVP().IsValid() ) set_scale = 1. / 20000.;
1751 
1752                                                                     bNewView |= SetViewPoint( tLat, tLon, set_scale, 0, GetVPRotation() );
1753 
1754                                                                     //      If the chart stack has just changed, there is new status
1755                                                                     if(WorkStack.nEntry && m_pCurrentStack->nEntry){
1756                                                                         if( !ChartData->EqualStacks( &WorkStack, m_pCurrentStack ) ) {
1757                                                                             bNewPiano = true;
1758                                                                             bNewChart = true;
1759                                                                         }
1760                                                                     }
1761 
1762                                                                     //      Copy the new (by definition empty) stack into the target stack
1763                                                                     ChartData->CopyStack( m_pCurrentStack, &WorkStack );
1764 
1765                                                                     goto update_finish;
1766     }
1767 
1768     //              Check to see if Chart Stack has changed
1769     if( !ChartData->EqualStacks( &WorkStack, m_pCurrentStack ) ) {
1770         //      New chart stack, so...
1771         bNewPiano = true;
1772 
1773         //      Save a copy of the current stack
1774         ChartData->CopyStack( &LastStack, m_pCurrentStack );
1775 
1776         //      Copy the new stack into the target stack
1777         ChartData->CopyStack( m_pCurrentStack, &WorkStack );
1778 
1779         //  Is Current Chart in new stack?
1780 
1781         int tEntry = -1;
1782         if( NULL != m_singleChart )                                  // this handles startup case
1783             tEntry = ChartData->GetStackEntry( m_pCurrentStack, m_singleChart->GetFullPath() );
1784 
1785         if( tEntry != -1 ) {                // m_singleChart is in the new stack
1786             m_pCurrentStack->CurrentStackEntry = tEntry;
1787             bNewChart = false;
1788         }
1789 
1790         else                           // m_singleChart is NOT in new stack
1791         {                                       // So, need to open a new chart
1792         //      Find the largest scale raster chart that opens OK
1793 
1794         ChartBase *pProposed = NULL;
1795 
1796         if( bCanvasChartAutoOpen ) {
1797             bool search_direction = false;        // default is to search from lowest to highest
1798             int start_index = 0;
1799 
1800             //    A special case:  If panning at high scale, open largest scale chart first
1801             if( ( LastStack.CurrentStackEntry == LastStack.nEntry - 1 ) || ( LastStack.nEntry == 0 ) ) {
1802                 search_direction = true;
1803                 start_index = m_pCurrentStack->nEntry - 1;
1804             }
1805 
1806                 //    Another special case, open specified index on program start
1807             if( bOpenSpecified ) {
1808                 search_direction = false;
1809                 start_index = 0;
1810                 if( ( start_index < 0 ) | ( start_index >= m_pCurrentStack->nEntry ) )
1811                     start_index = 0;
1812 
1813                 new_open_type = CHART_TYPE_DONTCARE;
1814             }
1815 
1816             pProposed = ChartData->OpenStackChartConditional( m_pCurrentStack, start_index,
1817                                                                   search_direction, new_open_type, new_open_family );
1818 
1819                 //    Try to open other types/families of chart in some priority
1820             if( NULL == pProposed ) pProposed = ChartData->OpenStackChartConditional(
1821                     m_pCurrentStack, start_index, search_direction, CHART_TYPE_CM93COMP,
1822                     CHART_FAMILY_VECTOR );
1823 
1824             if( NULL == pProposed ) pProposed = ChartData->OpenStackChartConditional(
1825                     m_pCurrentStack, start_index, search_direction, CHART_TYPE_CM93COMP,
1826                     CHART_FAMILY_RASTER );
1827 
1828             bNewChart = true;
1829 
1830         }     // bCanvasChartAutoOpen
1831 
1832         else
1833             pProposed = NULL;
1834 
1835         //  If no go, then
1836             //  Open a Dummy Chart
1837             if( NULL == pProposed ) {
1838                 if( NULL == pDummyChart ) {
1839                     pDummyChart = new ChartDummy;
1840                     bNewChart = true;
1841                 }
1842 
1843                 if( pLast_Ch ) if( pLast_Ch->GetChartType() != CHART_TYPE_DUMMY ) bNewChart = true;
1844 
1845                                                                     pProposed = pDummyChart;
1846             }
1847 
1848             // Arriving here, pProposed points to an opened chart, or NULL.
1849             if( m_singleChart ) m_singleChart->Deactivate();
1850             m_singleChart = pProposed;
1851 
1852             if( m_singleChart ) {
1853                 m_singleChart->Activate();
1854                 m_pCurrentStack->CurrentStackEntry = ChartData->GetStackEntry( m_pCurrentStack,
1855                                                                                m_singleChart->GetFullPath() );
1856             }
1857         }   // need new chart
1858 
1859         // Arriving here, m_singleChart is opened and OK, or NULL
1860         if( NULL != m_singleChart ) {
1861 
1862             //      Setup the view using the current scale
1863             double set_scale = GetVPScale();
1864 
1865             //    If the current viewpoint is invalid, set the default scale to something reasonable.
1866             if( !GetVP().IsValid() )
1867                 set_scale = 1. / 20000.;
1868             else {                                    // otherwise, match scale if elected.
1869                 double proposed_scale_onscreen;
1870 
1871                 if( m_bFollow ) {          // autoset the scale only if in autofollow
1872                     double new_scale_ppm = m_singleChart->GetNearestPreferredScalePPM( GetVPScale() );
1873                     proposed_scale_onscreen = GetCanvasScaleFactor() / new_scale_ppm;
1874                 }
1875                 else
1876                     proposed_scale_onscreen = GetCanvasScaleFactor() / set_scale;
1877 
1878 
1879                 //  This logic will bring a new chart onscreen at roughly twice the true paper scale equivalent.
1880                     //  Note that first chart opened on application startup (bOpenSpecified = true) will open at the config saved scale
1881                     if( bNewChart && !g_bPreserveScaleOnX && !bOpenSpecified ) {
1882                         proposed_scale_onscreen = m_singleChart->GetNativeScale() / 2;
1883                         double equivalent_vp_scale = GetCanvasScaleFactor()
1884                         / proposed_scale_onscreen;
1885                         double new_scale_ppm = m_singleChart->GetNearestPreferredScalePPM(
1886                             equivalent_vp_scale );
1887                         proposed_scale_onscreen = GetCanvasScaleFactor() / new_scale_ppm;
1888                     }
1889 
1890                     if( m_bFollow ) {     // bounds-check the scale only if in autofollow
1891                     proposed_scale_onscreen =
1892                     wxMin(proposed_scale_onscreen, m_singleChart->GetNormalScaleMax(GetCanvasScaleFactor(), GetCanvasWidth()));
1893                     proposed_scale_onscreen =
1894                     wxMax(proposed_scale_onscreen, m_singleChart->GetNormalScaleMin(GetCanvasScaleFactor(), g_b_overzoom_x));
1895                     }
1896 
1897                     set_scale = GetCanvasScaleFactor() / proposed_scale_onscreen;
1898             }
1899 
1900             bNewView |= SetViewPoint( vpLat, vpLon, set_scale,
1901                                       m_singleChart->GetChartSkew() * PI / 180., GetVPRotation() );
1902 
1903         }
1904     }         // new stack
1905 
1906     else                                                                 // No change in Chart Stack
1907     {
1908         if( ( m_bFollow ) && m_singleChart )
1909             bNewView |= SetViewPoint( vpLat, vpLon, GetVPScale(), m_singleChart->GetChartSkew() * PI / 180., GetVPRotation() );
1910     }
1911 
1912     update_finish:
1913 
1914     //    Ask for a new tool bar if the stack is going to or coming from only one entry.
1915      if(GetToolbar()){
1916          if(m_pCurrentStack){
1917             bool toolbar_scale_tools_show = m_pCurrentStack && m_pCurrentStack->b_valid && ( m_pCurrentStack->nEntry > 1 );
1918             bool scale_tools_shown = m_toolBar->m_toolbar_scale_tools_shown;
1919 
1920             if( toolbar_scale_tools_show != scale_tools_shown){
1921                 if( !m_bFirstAuto )
1922                     RequestNewCanvasToolbar( false );
1923             }
1924          }
1925      }
1926 
1927 //TODO
1928 //     if( bNewPiano ) UpdateControlBar();
1929 
1930                                                                     //  Update the ownship position on thumbnail chart, if shown
1931     if( pthumbwin && pthumbwin->IsShown() ) {
1932         if( pthumbwin->pThumbChart ){
1933             if( pthumbwin->pThumbChart->UpdateThumbData( gLat, gLon ) )
1934                 pthumbwin->Refresh( TRUE );
1935         }
1936     }
1937 
1938     m_bFirstAuto = false;                           // Auto open on program start
1939 
1940     //  If we need a Refresh(), do it here...
1941     //  But don't duplicate a Refresh() done by SetViewPoint()
1942     if( bNewChart && !bNewView )
1943         Refresh( false );
1944 
1945 #ifdef ocpnUSE_GL
1946     // If a new chart, need to invalidate gl viewport for refresh
1947     // so the fbo gets flushed
1948     if(m_glcc && g_bopengl & bNewChart)
1949         GetglCanvas()->Invalidate();
1950 #endif
1951 
1952     return bNewChart | bNewView;
1953 }
1954 
SelectQuiltRefdbChart(int db_index,bool b_autoscale)1955 void ChartCanvas::SelectQuiltRefdbChart( int db_index, bool b_autoscale )
1956 {
1957     if( m_pCurrentStack )
1958         m_pCurrentStack->SetCurrentEntryFromdbIndex( db_index );
1959 
1960     SetQuiltRefChart( db_index );
1961     if (ChartData) {
1962         ChartBase *pc = ChartData->OpenChartFromDB( db_index, FULL_INIT );
1963         if( pc ) {
1964             if(b_autoscale) {
1965                 double best_scale_ppm = GetBestVPScale( pc );
1966                 SetVPScale( best_scale_ppm );
1967             }
1968         }
1969         else
1970             SetQuiltRefChart( -1 );
1971     }
1972     else
1973         SetQuiltRefChart( -1 );
1974 }
1975 
SelectQuiltRefChart(int selected_index)1976 void ChartCanvas::SelectQuiltRefChart( int selected_index )
1977 {
1978     std::vector<int>  piano_chart_index_array = GetQuiltExtendedStackdbIndexArray();
1979     int current_db_index = piano_chart_index_array[selected_index];
1980 
1981     SelectQuiltRefdbChart( current_db_index );
1982 }
1983 
GetBestVPScale(ChartBase * pchart)1984 double ChartCanvas::GetBestVPScale( ChartBase *pchart )
1985 {
1986     if( pchart ) {
1987         double proposed_scale_onscreen = GetCanvasScaleFactor() / GetVPScale();
1988 
1989         if( ( g_bPreserveScaleOnX ) || ( CHART_TYPE_CM93COMP == pchart->GetChartType() ) ) {
1990             double new_scale_ppm = GetVPScale();
1991             proposed_scale_onscreen = GetCanvasScaleFactor() / new_scale_ppm;
1992         } else {
1993             //  This logic will bring the new chart onscreen at roughly twice the true paper scale equivalent.
1994             proposed_scale_onscreen = pchart->GetNativeScale() / 2;
1995             double equivalent_vp_scale = GetCanvasScaleFactor() / proposed_scale_onscreen;
1996             double new_scale_ppm = pchart->GetNearestPreferredScalePPM( equivalent_vp_scale );
1997             proposed_scale_onscreen = GetCanvasScaleFactor() / new_scale_ppm;
1998         }
1999 
2000         // Do not allow excessive underzoom, even if the g_bPreserveScaleOnX flag is set.
2001         // Otherwise, we get severe performance problems on all platforms
2002 
2003         double max_underzoom_multiplier = 2.0;
2004         if(GetVP().b_quilt){
2005             double scale_max = m_pQuilt->GetNomScaleMin(pchart->GetNativeScale(), pchart->GetChartType(), pchart->GetChartFamily());
2006             max_underzoom_multiplier = scale_max / pchart->GetNativeScale();
2007         }
2008 
2009         proposed_scale_onscreen =
2010         wxMin(proposed_scale_onscreen,
2011               pchart->GetNormalScaleMax(GetCanvasScaleFactor(), GetCanvasWidth()) * max_underzoom_multiplier);
2012 
2013         //  And, do not allow excessive overzoom either
2014         proposed_scale_onscreen =
2015         wxMax(proposed_scale_onscreen, pchart->GetNormalScaleMin(GetCanvasScaleFactor(), false));
2016 
2017         return GetCanvasScaleFactor() / proposed_scale_onscreen;
2018     } else
2019         return 1.0;
2020 }
2021 
SetupCanvasQuiltMode(void)2022 void ChartCanvas::SetupCanvasQuiltMode( void )
2023 {
2024 
2025     if( GetQuiltMode() )                               // going to quilt mode
2026     {
2027         ChartData->LockCache();
2028 
2029         m_Piano->SetNoshowIndexArray( m_quilt_noshow_index_array );
2030 
2031         ocpnStyle::Style* style = g_StyleManager->GetCurrentStyle();
2032 
2033         m_Piano->SetVizIcon( new wxBitmap( style->GetIcon( _T("viz") ) ) );
2034         m_Piano->SetInVizIcon( new wxBitmap( style->GetIcon( _T("redX") ) ) );
2035         m_Piano->SetTMercIcon( new wxBitmap( style->GetIcon( _T("tmercprj") ) ) );
2036         m_Piano->SetSkewIcon( new wxBitmap( style->GetIcon( _T("skewprj") ) ) );
2037 
2038         m_Piano->SetRoundedRectangles( true );
2039 
2040         //    Select the proper Ref chart
2041         int target_new_dbindex = -1;
2042         if( m_pCurrentStack ) {
2043             target_new_dbindex = GetQuiltReferenceChartIndex();    //m_pCurrentStack->GetCurrentEntrydbIndex();
2044 
2045             if(-1 != target_new_dbindex){
2046                 if( !IsChartQuiltableRef( target_new_dbindex ) ){
2047 
2048                     int proj = ChartData->GetDBChartProj(target_new_dbindex);
2049                     int type = ChartData->GetDBChartType(target_new_dbindex);
2050 
2051                     // walk the stack up looking for a satisfactory chart
2052                     int stack_index = m_pCurrentStack->CurrentStackEntry;
2053 
2054                     while((stack_index < m_pCurrentStack->nEntry-1) && (stack_index >= 0)) {
2055                         int proj_tent = ChartData->GetDBChartProj( m_pCurrentStack->GetDBIndex(stack_index));
2056                         int type_tent = ChartData->GetDBChartType( m_pCurrentStack->GetDBIndex(stack_index));
2057 
2058                         if(IsChartQuiltableRef(m_pCurrentStack->GetDBIndex(stack_index))){
2059                             if((proj == proj_tent) && (type_tent == type)){
2060                                 target_new_dbindex = m_pCurrentStack->GetDBIndex(stack_index);
2061                                 break;
2062                             }
2063                         }
2064                         stack_index++;
2065                     }
2066                 }
2067             }
2068         }
2069 
2070         if( IsChartQuiltableRef( target_new_dbindex ) )
2071             SelectQuiltRefdbChart( target_new_dbindex, false );        // Try not to allow a scale change
2072         else
2073             SelectQuiltRefdbChart( -1, false );
2074 
2075         m_singleChart = NULL;                  // Bye....
2076 
2077             //TODOSetChartThumbnail( -1 );            //Turn off thumbnails for sure
2078 
2079             //  Re-qualify the quilt reference chart selection
2080             AdjustQuiltRefChart(  );
2081 
2082         //  Restore projection type saved on last quilt mode toggle
2083             //TODO
2084 //             if(g_sticky_projection != -1)
2085 //                 GetVP().SetProjectionType(g_sticky_projection);
2086 //             else
2087 //                 GetVP().SetProjectionType(PROJECTION_MERCATOR);
2088         GetVP().SetProjectionType(PROJECTION_UNKNOWN);
2089 
2090     } else                                                  // going to SC Mode
2091     {
2092         std::vector<int>  empty_array;
2093         m_Piano->SetActiveKeyArray( empty_array );
2094         m_Piano->SetNoshowIndexArray( empty_array );
2095         m_Piano->SetEclipsedIndexArray( empty_array );
2096 
2097         ocpnStyle::Style* style = g_StyleManager->GetCurrentStyle();
2098         m_Piano->SetVizIcon( new wxBitmap( style->GetIcon( _T("viz") ) ) );
2099         m_Piano->SetInVizIcon( new wxBitmap( style->GetIcon( _T("redX") ) ) );
2100         m_Piano->SetTMercIcon( new wxBitmap( style->GetIcon( _T("tmercprj") ) ) );
2101         m_Piano->SetSkewIcon( new wxBitmap( style->GetIcon( _T("skewprj") ) ) );
2102 
2103         m_Piano->SetRoundedRectangles( false );
2104         //TODO  Make this a member g_sticky_projection = GetVP().m_projection_type;
2105 
2106     }
2107 
2108     //    When shifting from quilt to single chart mode, select the "best" single chart to show
2109     if( !GetQuiltMode() ) {
2110         if( ChartData && ChartData->IsValid() ) {
2111             UnlockQuilt();
2112 
2113             double tLat, tLon;
2114             if( m_bFollow == true ) {
2115                 tLat = gLat;
2116                 tLon = gLon;
2117             } else {
2118                 tLat = m_vLat;
2119                 tLon = m_vLon;
2120             }
2121 
2122             if( !m_singleChart ) {
2123 
2124                 // Build a temporary chart stack based on tLat, tLon
2125                 ChartStack TempStack;
2126                 ChartData->BuildChartStack( &TempStack, tLat, tLon, g_sticky_chart, m_groupIndex );
2127 
2128                 //    Iterate over the quilt charts actually shown, looking for the largest scale chart that will be in the new chartstack....
2129                 //    This will (almost?) always be the reference chart....
2130 
2131                 ChartBase *Candidate_Chart = NULL;
2132                 int cur_max_scale = (int) 1e8;
2133 
2134                 ChartBase *pChart = GetFirstQuiltChart();
2135                 while( pChart ) {
2136                     //  Is this pChart in new stack?
2137                     int tEntry = ChartData->GetStackEntry( &TempStack, pChart->GetFullPath() );
2138                     if( tEntry != -1 ) {
2139                         if( pChart->GetNativeScale() < cur_max_scale ) {
2140                             Candidate_Chart = pChart;
2141                             cur_max_scale = pChart->GetNativeScale();
2142                         }
2143                     }
2144                     pChart = GetNextQuiltChart();
2145                 }
2146 
2147                 m_singleChart = Candidate_Chart;
2148 
2149                 //    If the quilt is empty, there is no "best" chart.
2150                 //    So, open the smallest scale chart in the current stack
2151                 if( NULL == m_singleChart ) {
2152                     m_singleChart = ChartData->OpenStackChartConditional( &TempStack,
2153                                                                        TempStack.nEntry - 1, true, CHART_TYPE_DONTCARE,
2154                                                                        CHART_FAMILY_DONTCARE );
2155                 }
2156             }
2157 
2158             //  Invalidate all the charts in the quilt,
2159             // as any cached data may be region based and not have fullscreen coverage
2160             InvalidateAllQuiltPatchs();
2161 
2162             if( m_singleChart ) {
2163                 int dbi = ChartData->FinddbIndex( m_singleChart->GetFullPath() );
2164                 std::vector<int>  one_array;
2165                 one_array.push_back( dbi );
2166                 m_Piano->SetActiveKeyArray( one_array );
2167             }
2168 
2169             if( m_singleChart ) {
2170                 GetVP().SetProjectionType(m_singleChart->GetChartProjectionType());
2171             }
2172 
2173         }
2174         //    Invalidate the current stack so that it will be rebuilt on next tick
2175         if( m_pCurrentStack )
2176             m_pCurrentStack->b_valid = false;
2177     }
2178 
2179 }
2180 
2181 
IsTempMenuBarEnabled()2182 bool ChartCanvas::IsTempMenuBarEnabled()
2183 {
2184 #ifdef __WXMSW__
2185     int major;
2186     wxGetOsVersion(&major);
2187     return (major > 5);   //  For Windows, function is only available on Vista and above
2188 #else
2189     return true;
2190 #endif
2191 }
2192 
GetCanvasRangeMeters()2193 double ChartCanvas::GetCanvasRangeMeters()
2194 {
2195     int width, height;
2196     GetSize(&width, &height);
2197     int minDimension =  wxMin(width, height);
2198 
2199     double range  = (minDimension / GetVP().view_scale_ppm)/2;
2200     range *= cos(GetVP().clat *PI/180.);
2201     return range;
2202 }
2203 
SetCanvasRangeMeters(double range)2204 void ChartCanvas::SetCanvasRangeMeters( double range )
2205 {
2206     int width, height;
2207     GetSize(&width, &height);
2208     int minDimension =  wxMin(width, height);
2209 
2210     double scale_ppm = minDimension / (range / cos(GetVP().clat *PI/180.));
2211     SetVPScale( scale_ppm / 2 );
2212 
2213 }
2214 
2215 
SetUserOwnship()2216 bool ChartCanvas::SetUserOwnship(){
2217     //  Look for user defined ownship image
2218     //  This may be found in the shared data location along with other user defined icons.
2219     //  and will be called "ownship.xpm" or "ownship.png"
2220     if( pWayPointMan && pWayPointMan->DoesIconExist( _T("ownship") ) ) {
2221 
2222         double factor_dusk = 0.5;
2223         double factor_night = 0.25;
2224 
2225         wxBitmap *pbmp = pWayPointMan->GetIconBitmap( _T("ownship") );
2226         m_pos_image_user_day = new wxImage;
2227         *m_pos_image_user_day = pbmp->ConvertToImage();
2228         if(!m_pos_image_user_day->HasAlpha())
2229             m_pos_image_user_day->InitAlpha();
2230 
2231         int gimg_width = m_pos_image_user_day->GetWidth();
2232         int gimg_height = m_pos_image_user_day->GetHeight();
2233 
2234         // Make dusk and night images
2235         m_pos_image_user_dusk = new wxImage;
2236         m_pos_image_user_night = new wxImage;
2237 
2238         *m_pos_image_user_dusk = m_pos_image_user_day->Copy();
2239         *m_pos_image_user_night = m_pos_image_user_day->Copy();
2240 
2241         for( int iy = 0; iy < gimg_height; iy++ ) {
2242             for( int ix = 0; ix < gimg_width; ix++ ) {
2243                 if( !m_pos_image_user_day->IsTransparent( ix, iy ) ) {
2244                     wxImage::RGBValue rgb( m_pos_image_user_day->GetRed( ix, iy ),
2245                                            m_pos_image_user_day->GetGreen( ix, iy ),
2246                                            m_pos_image_user_day->GetBlue( ix, iy ) );
2247                     wxImage::HSVValue hsv = wxImage::RGBtoHSV( rgb );
2248                     hsv.value = hsv.value * factor_dusk;
2249                     wxImage::RGBValue nrgb = wxImage::HSVtoRGB( hsv );
2250                     m_pos_image_user_dusk->SetRGB( ix, iy, nrgb.red, nrgb.green, nrgb.blue );
2251 
2252                     hsv = wxImage::RGBtoHSV( rgb );
2253                     hsv.value = hsv.value * factor_night;
2254                     nrgb = wxImage::HSVtoRGB( hsv );
2255                     m_pos_image_user_night->SetRGB( ix, iy, nrgb.red, nrgb.green, nrgb.blue );
2256                 }
2257             }
2258         }
2259 
2260         //  Make some alternate greyed out day/dusk/night images
2261         m_pos_image_user_grey_day = new wxImage;
2262         *m_pos_image_user_grey_day = m_pos_image_user_day->ConvertToGreyscale();
2263 
2264         m_pos_image_user_grey_dusk = new wxImage;
2265         m_pos_image_user_grey_night = new wxImage;
2266 
2267         *m_pos_image_user_grey_dusk = m_pos_image_user_grey_day->Copy();
2268         *m_pos_image_user_grey_night = m_pos_image_user_grey_day->Copy();
2269 
2270         for( int iy = 0; iy < gimg_height; iy++ ) {
2271             for( int ix = 0; ix < gimg_width; ix++ ) {
2272                 if( !m_pos_image_user_grey_day->IsTransparent( ix, iy ) ) {
2273                     wxImage::RGBValue rgb( m_pos_image_user_grey_day->GetRed( ix, iy ),
2274                                            m_pos_image_user_grey_day->GetGreen( ix, iy ),
2275                                            m_pos_image_user_grey_day->GetBlue( ix, iy ) );
2276                     wxImage::HSVValue hsv = wxImage::RGBtoHSV( rgb );
2277                     hsv.value = hsv.value * factor_dusk;
2278                     wxImage::RGBValue nrgb = wxImage::HSVtoRGB( hsv );
2279                     m_pos_image_user_grey_dusk->SetRGB( ix, iy, nrgb.red, nrgb.green, nrgb.blue );
2280 
2281                     hsv = wxImage::RGBtoHSV( rgb );
2282                     hsv.value = hsv.value * factor_night;
2283                     nrgb = wxImage::HSVtoRGB( hsv );
2284                     m_pos_image_user_grey_night->SetRGB( ix, iy, nrgb.red, nrgb.green, nrgb.blue );
2285                 }
2286             }
2287         }
2288 
2289         //  Make a yellow image for rendering under low accuracy chart conditions
2290         m_pos_image_user_yellow_day = new wxImage;
2291         m_pos_image_user_yellow_dusk = new wxImage;
2292         m_pos_image_user_yellow_night = new wxImage;
2293 
2294         *m_pos_image_user_yellow_day = m_pos_image_user_grey_day->Copy();
2295         *m_pos_image_user_yellow_dusk = m_pos_image_user_grey_day->Copy();
2296         *m_pos_image_user_yellow_night = m_pos_image_user_grey_day->Copy();
2297 
2298         for( int iy = 0; iy < gimg_height; iy++ ) {
2299             for( int ix = 0; ix < gimg_width; ix++ ) {
2300                 if( !m_pos_image_user_grey_day->IsTransparent( ix, iy ) ) {
2301                     wxImage::RGBValue rgb( m_pos_image_user_grey_day->GetRed( ix, iy ),
2302                                            m_pos_image_user_grey_day->GetGreen( ix, iy ),
2303                                            m_pos_image_user_grey_day->GetBlue( ix, iy ) );
2304 
2305                     //  Simply remove all "blue" from the greyscaled image...
2306                     //  so, what is not black becomes yellow.
2307                     wxImage::HSVValue hsv = wxImage::RGBtoHSV( rgb );
2308                     wxImage::RGBValue nrgb = wxImage::HSVtoRGB( hsv );
2309                     m_pos_image_user_yellow_day->SetRGB( ix, iy, nrgb.red, nrgb.green, 0 );
2310 
2311                     hsv = wxImage::RGBtoHSV( rgb );
2312                     hsv.value = hsv.value * factor_dusk;
2313                     nrgb = wxImage::HSVtoRGB( hsv );
2314                     m_pos_image_user_yellow_dusk->SetRGB( ix, iy, nrgb.red, nrgb.green, 0 );
2315 
2316                     hsv = wxImage::RGBtoHSV( rgb );
2317                     hsv.value = hsv.value * factor_night;
2318                     nrgb = wxImage::HSVtoRGB( hsv );
2319                     m_pos_image_user_yellow_night->SetRGB( ix, iy, nrgb.red, nrgb.green, 0 );
2320                 }
2321             }
2322         }
2323 
2324         return true;
2325     }
2326     else
2327         return false;
2328 }
2329 
SetDisplaySizeMM(double size)2330 void ChartCanvas::SetDisplaySizeMM( double size )
2331 {
2332     m_display_size_mm = size;
2333 
2334     //int sx, sy;
2335     //wxDisplaySize( &sx, &sy );
2336 
2337     // Calculate pixels per mm for later reference
2338     wxSize sd = g_Platform->getDisplaySize();
2339     double max_physical = wxMax(sd.x, sd.y);
2340 
2341     m_pix_per_mm = ( max_physical ) / ( (double) m_display_size_mm );
2342     m_canvas_scale_factor = ( max_physical ) / (m_display_size_mm /1000.);
2343 
2344 
2345     if( ps52plib )
2346         ps52plib->SetPPMM( m_pix_per_mm );
2347 
2348      wxString msg;
2349      msg.Printf(_T("Metrics:  m_display_size_mm: %g     g_Platform->getDisplaySize():  %d:%d   "), m_display_size_mm, sd.x, sd.y);
2350      wxLogMessage(msg);
2351 
2352     int ssx, ssy;
2353     ::wxDisplaySize(&ssx, &ssy);
2354     msg.Printf(_T("wxDisplaySize(): %d %d"), ssx, ssy);
2355      wxLogMessage(msg);
2356 
2357      m_focus_indicator_pix = /*std::round*/wxRound(1 * GetPixPerMM());
2358 
2359 }
2360 #if 0
2361 void ChartCanvas::OnEvtCompressProgress( OCPN_CompressProgressEvent & event )
2362 {
2363     wxString msg(event.m_string.c_str(), wxConvUTF8);
2364     // if cpus are removed between runs
2365     if(pprog_threads > 0 && compress_msg_array.GetCount() >  (unsigned int)pprog_threads) {
2366         compress_msg_array.RemoveAt(pprog_threads, compress_msg_array.GetCount() - pprog_threads);
2367     }
2368 
2369     if(compress_msg_array.GetCount() > (unsigned int)event.thread )
2370     {
2371         compress_msg_array.RemoveAt(event.thread);
2372         compress_msg_array.Insert( msg, event.thread);
2373     }
2374     else
2375         compress_msg_array.Add(msg);
2376 
2377 
2378     wxString combined_msg;
2379     for(unsigned int i=0 ; i < compress_msg_array.GetCount() ; i++) {
2380         combined_msg += compress_msg_array[i];
2381         combined_msg += _T("\n");
2382     }
2383 
2384     bool skip = false;
2385     pprog->Update(pprog_count, combined_msg, &skip );
2386     pprog->SetSize(pprog_size);
2387     if(skip)
2388         b_skipout = skip;
2389 }
2390 #endif
InvalidateGL()2391 void ChartCanvas::InvalidateGL()
2392 {
2393     if(!m_glcc)
2394         return;
2395 #ifdef ocpnUSE_GL
2396         if(g_bopengl)
2397             m_glcc->Invalidate();
2398 #endif
2399     if(m_Compass)
2400         m_Compass->UpdateStatus( true );
2401 }
2402 
GetCanvasChartNativeScale()2403 int ChartCanvas::GetCanvasChartNativeScale()
2404 {
2405     int ret = 1;
2406     if( !VPoint.b_quilt ) {
2407         if( m_singleChart ) ret = m_singleChart->GetNativeScale();
2408     } else
2409         ret = (int) m_pQuilt->GetRefNativeScale();
2410 
2411     return ret;
2412 
2413 }
2414 
GetChartAtCursor()2415 ChartBase* ChartCanvas::GetChartAtCursor() {
2416     ChartBase* target_chart;
2417     if( m_singleChart && ( m_singleChart->GetChartFamily() == CHART_FAMILY_VECTOR ) )
2418         target_chart = m_singleChart;
2419     else
2420         if( VPoint.b_quilt )
2421             target_chart = m_pQuilt->GetChartAtPix( VPoint, wxPoint( mouse_x, mouse_y ) );
2422         else
2423             target_chart = NULL;
2424     return target_chart;
2425 }
2426 
GetOverlayChartAtCursor()2427 ChartBase* ChartCanvas::GetOverlayChartAtCursor() {
2428     ChartBase* target_chart;
2429     if( VPoint.b_quilt )
2430         target_chart = m_pQuilt->GetOverlayChartAtPix( VPoint, wxPoint( mouse_x, mouse_y ) );
2431     else
2432         target_chart = NULL;
2433     return target_chart;
2434 }
2435 
FindClosestCanvasChartdbIndex(int scale)2436 int ChartCanvas::FindClosestCanvasChartdbIndex( int scale )
2437 {
2438     int new_dbIndex = -1;
2439     if( !VPoint.b_quilt ) {
2440         if( m_pCurrentStack ) {
2441             for( int i = 0; i < m_pCurrentStack->nEntry; i++ ) {
2442                 int sc = ChartData->GetStackChartScale( m_pCurrentStack, i, NULL, 0 );
2443                 if( sc >= scale ) {
2444                     new_dbIndex = m_pCurrentStack->GetDBIndex( i );
2445                     break;
2446                 }
2447             }
2448         }
2449     } else {
2450         //    Using the current quilt, select a useable reference chart
2451         //    Said chart will be in the extended (possibly full-screen) stack,
2452         //    And will have a scale equal to or just greater than the stipulated value
2453         unsigned int im = m_pQuilt->GetExtendedStackIndexArray().size();
2454         if( im > 0 ) {
2455             for( unsigned int is = 0; is < im; is++ ) {
2456                 const ChartTableEntry &m = ChartData->GetChartTableEntry(
2457                                                m_pQuilt->GetExtendedStackIndexArray()[is] );
2458                 if( ( m.Scale_ge(scale ) )/* && (m_reference_family == m.GetChartFamily())*/) {
2459                     new_dbIndex = m_pQuilt->GetExtendedStackIndexArray()[is];
2460                     break;
2461                 }
2462             }
2463         }
2464     }
2465 
2466     return new_dbIndex;
2467 }
2468 
EnablePaint(bool b_enable)2469 void ChartCanvas::EnablePaint(bool b_enable)
2470 {
2471     m_b_paint_enable = b_enable;
2472 #ifdef ocpnUSE_GL
2473     if(m_glcc)
2474         m_glcc->EnablePaint(b_enable);
2475 #endif
2476 }
2477 
IsQuiltDelta()2478 bool ChartCanvas::IsQuiltDelta()
2479 {
2480     return m_pQuilt->IsQuiltDelta( VPoint );
2481 }
2482 
UnlockQuilt()2483 void ChartCanvas::UnlockQuilt()
2484 {
2485     m_pQuilt->UnlockQuilt();
2486 }
2487 
GetQuiltIndexArray(void)2488 std::vector<int>  ChartCanvas::GetQuiltIndexArray( void )
2489 {
2490     return m_pQuilt->GetQuiltIndexArray();;
2491 }
2492 
SetQuiltMode(bool b_quilt)2493 void ChartCanvas::SetQuiltMode( bool b_quilt )
2494 {
2495     VPoint.b_quilt = b_quilt;
2496     VPoint.b_FullScreenQuilt = g_bFullScreenQuilt;
2497 }
2498 
GetQuiltMode(void)2499 bool ChartCanvas::GetQuiltMode( void )
2500 {
2501     return VPoint.b_quilt;
2502 }
2503 
GetQuiltReferenceChartIndex(void)2504 int ChartCanvas::GetQuiltReferenceChartIndex(void)
2505 {
2506     return m_pQuilt->GetRefChartdbIndex();
2507 }
2508 
InvalidateAllQuiltPatchs(void)2509 void ChartCanvas::InvalidateAllQuiltPatchs( void )
2510 {
2511     m_pQuilt->InvalidateAllQuiltPatchs();
2512 }
2513 
GetLargestScaleQuiltChart()2514 ChartBase *ChartCanvas::GetLargestScaleQuiltChart()
2515 {
2516     return m_pQuilt->GetLargestScaleChart();
2517 }
2518 
GetFirstQuiltChart()2519 ChartBase *ChartCanvas::GetFirstQuiltChart()
2520 {
2521     return m_pQuilt->GetFirstChart();
2522 }
2523 
GetNextQuiltChart()2524 ChartBase *ChartCanvas::GetNextQuiltChart()
2525 {
2526     return m_pQuilt->GetNextChart();
2527 }
2528 
GetQuiltChartCount()2529 int ChartCanvas::GetQuiltChartCount()
2530 {
2531     return m_pQuilt->GetnCharts();
2532 }
2533 
SetQuiltChartHiLiteIndex(int dbIndex)2534 void ChartCanvas::SetQuiltChartHiLiteIndex( int dbIndex )
2535 {
2536     m_pQuilt->SetHiliteIndex( dbIndex );
2537 }
2538 
GetQuiltCandidatedbIndexArray(bool flag1,bool flag2)2539 std::vector<int>  ChartCanvas::GetQuiltCandidatedbIndexArray( bool flag1, bool flag2 )
2540 {
2541     return m_pQuilt->GetCandidatedbIndexArray( flag1, flag2 );
2542 }
2543 
GetQuiltRefChartdbIndex(void)2544 int ChartCanvas::GetQuiltRefChartdbIndex( void )
2545 {
2546     return m_pQuilt->GetRefChartdbIndex();
2547 }
2548 
GetQuiltExtendedStackdbIndexArray()2549 std::vector<int>  ChartCanvas::GetQuiltExtendedStackdbIndexArray()
2550 {
2551     return m_pQuilt->GetExtendedStackIndexArray();
2552 }
2553 
GetQuiltEclipsedStackdbIndexArray()2554 std::vector<int>  ChartCanvas::GetQuiltEclipsedStackdbIndexArray()
2555 {
2556     return m_pQuilt->GetEclipsedStackIndexArray();
2557 }
2558 
InvalidateQuilt(void)2559 void ChartCanvas::InvalidateQuilt( void )
2560 {
2561     return m_pQuilt->Invalidate();
2562 }
2563 
GetQuiltMaxErrorFactor()2564 double ChartCanvas::GetQuiltMaxErrorFactor()
2565 {
2566     return m_pQuilt->GetMaxErrorFactor();
2567 }
2568 
IsChartQuiltableRef(int db_index)2569 bool ChartCanvas::IsChartQuiltableRef( int db_index )
2570 {
2571     return m_pQuilt->IsChartQuiltableRef( db_index );
2572 }
2573 
IsChartLargeEnoughToRender(ChartBase * chart,ViewPort & vp)2574 bool ChartCanvas::IsChartLargeEnoughToRender( ChartBase* chart, ViewPort& vp )
2575 {
2576     double chartMaxScale = chart->GetNormalScaleMax( GetCanvasScaleFactor(), GetCanvasWidth() );
2577     return ( chartMaxScale*g_ChartNotRenderScaleFactor > vp.chart_scale );
2578 }
2579 
StartMeasureRoute()2580 void ChartCanvas::StartMeasureRoute()
2581 {
2582     if( !m_routeState ) {  // no measure tool if currently creating route
2583         if( m_bMeasure_Active ) {
2584             g_pRouteMan->DeleteRoute( m_pMeasureRoute );
2585             m_pMeasureRoute = NULL;
2586         }
2587 
2588         m_bMeasure_Active = true;
2589         m_nMeasureState = 1;
2590         m_bDrawingRoute = false;
2591 
2592         SetCursor( *pCursorPencil );
2593         Refresh();
2594     }
2595 }
2596 
CancelMeasureRoute()2597 void ChartCanvas::CancelMeasureRoute()
2598 {
2599     m_bMeasure_Active = false;
2600     m_nMeasureState = 0;
2601     m_bDrawingRoute = false;
2602 
2603     g_pRouteMan->DeleteRoute( m_pMeasureRoute );
2604     m_pMeasureRoute = NULL;
2605 
2606     SetCursor( *pCursorArrow );
2607 }
2608 
GetVP()2609 ViewPort &ChartCanvas::GetVP()
2610 {
2611     return VPoint;
2612 }
2613 
SetVP(ViewPort & vp)2614 void ChartCanvas::SetVP(ViewPort &vp)
2615 {
2616     VPoint = vp;
2617 }
2618 
2619 // void ChartCanvas::SetFocus()
2620 // {
2621 //     printf("set %d\n", m_canvasIndex);
2622 //     //wxWindow:SetFocus();
2623 // }
2624 
2625 
TriggerDeferredFocus()2626 void ChartCanvas::TriggerDeferredFocus()
2627 {
2628 //#if defined(__WXGTK__) || defined(__WXOSX__)
2629 
2630     m_deferredFocusTimer.Start(20, true);
2631 
2632 #if defined(__WXGTK__) || defined(__WXOSX__)
2633     gFrame->Raise();
2634 #endif
2635 
2636 //    gFrame->Raise();
2637 //#else
2638 //    SetFocus();
2639 //    Refresh(true);
2640 //#endif
2641 }
2642 
OnDeferredFocusTimerEvent(wxTimerEvent & event)2643 void ChartCanvas::OnDeferredFocusTimerEvent( wxTimerEvent &event)
2644 {
2645     SetFocus();
2646     Refresh(true);
2647 
2648 
2649 }
2650 
OnKeyChar(wxKeyEvent & event)2651 void ChartCanvas::OnKeyChar( wxKeyEvent &event )
2652 {
2653     if(g_pi_manager)
2654         if(g_pi_manager->SendKeyEventToPlugins( event ))
2655             return;                     // PlugIn did something, and does not want the canvas to do anything else
2656 
2657     int key_char = event.GetKeyCode();
2658 
2659     if(g_benable_rotate){
2660 
2661         switch( key_char ) {
2662             case ']':
2663                 RotateCanvas( 1 );
2664                 break;
2665 
2666             case '[':
2667                 RotateCanvas( -1 );
2668                 break;
2669 
2670             case '\\':
2671                 DoRotateCanvas(0);
2672                 break;
2673         }
2674     }
2675 
2676     event.Skip();
2677 }
2678 
OnKeyDown(wxKeyEvent & event)2679 void ChartCanvas::OnKeyDown( wxKeyEvent &event )
2680 {
2681     if(g_pi_manager)
2682         if(g_pi_manager->SendKeyEventToPlugins( event ))
2683             return;                     // PlugIn did something, and does not want the canvas to do anything else
2684 
2685     bool b_handled = false;
2686 
2687     m_modkeys = event.GetModifiers();
2688 
2689     int panspeed = m_modkeys == wxMOD_ALT ? 2 : 100;
2690 
2691 #ifdef OCPN_ALT_MENUBAR
2692 #ifndef __WXOSX__
2693     // If the permanent menubar is disabled, we show it temporarily when Alt is pressed or when
2694     // Alt + a letter is presssed (for the top-menu-level hotkeys).
2695     // The toggling normally takes place in OnKeyUp, but here we handle some special cases.
2696     if ( IsTempMenuBarEnabled() && event.AltDown()  &&  !g_bShowMenuBar ) {
2697         // If Alt + a letter is pressed, and the menubar is hidden, show it now
2698         if ( event.GetKeyCode() >= 'A' && event.GetKeyCode() <= 'Z' ) {
2699             if ( !g_bTempShowMenuBar ) {
2700                 g_bTempShowMenuBar = true;
2701                 parent_frame->ApplyGlobalSettings(false);
2702             }
2703             m_bMayToggleMenuBar = false; // don't hide it again when we release Alt
2704             event.Skip();
2705             return;
2706         }
2707         // If another key is pressed while Alt is down, do NOT toggle the menus when Alt is released
2708         if ( event.GetKeyCode() != WXK_ALT ) {
2709             m_bMayToggleMenuBar = false;
2710         }
2711     }
2712 #endif
2713 #endif
2714 
2715     // HOTKEYS
2716     switch( event.GetKeyCode() ) {
2717 
2718     case WXK_TAB:
2719         //parent_frame->SwitchKBFocus( this );
2720         break;
2721 
2722     case WXK_MENU:
2723         int x, y;
2724         event.GetPosition( &x, &y );
2725         m_FinishRouteOnKillFocus = false;
2726         CallPopupMenu(x, y);
2727         m_FinishRouteOnKillFocus = true;
2728         break;
2729 
2730     case WXK_ALT:
2731         m_modkeys |= wxMOD_ALT;
2732         break;
2733 
2734     case WXK_CONTROL:
2735         m_modkeys |= wxMOD_CONTROL;
2736         break;
2737 
2738     case WXK_LEFT:
2739         if( m_modkeys == wxMOD_CONTROL ) parent_frame->DoStackDown( this );
2740         else if(g_bsmoothpanzoom) {
2741             StartTimedMovement();
2742             m_panx = -1;
2743         } else {
2744             PanCanvas( -panspeed, 0 );
2745         }
2746         b_handled = true;
2747         break;
2748 
2749     case WXK_UP:
2750         if(g_bsmoothpanzoom) {
2751             StartTimedMovement();
2752             m_pany = -1;
2753         } else
2754             PanCanvas( 0, -panspeed );
2755         b_handled = true;
2756         break;
2757 
2758     case WXK_RIGHT:
2759         if( m_modkeys == wxMOD_CONTROL ) parent_frame->DoStackUp( this );
2760         else if(g_bsmoothpanzoom) {
2761             StartTimedMovement();
2762             m_panx = 1;
2763         } else
2764             PanCanvas( panspeed, 0 );
2765         b_handled = true;
2766 
2767         break;
2768 
2769     case WXK_DOWN:
2770         if(g_bsmoothpanzoom) {
2771             StartTimedMovement();
2772             m_pany = 1;
2773         } else
2774             PanCanvas( 0, panspeed );
2775         b_handled = true;
2776         break;
2777 
2778     case WXK_F2:
2779         TogglebFollow();
2780         break;
2781 
2782     case WXK_F3: {
2783         SetShowENCText( !GetShowENCText() );
2784         Refresh(true);
2785         InvalidateGL();
2786         break;
2787     }
2788     case WXK_F4:
2789         if( !m_bMeasure_Active ) {
2790             if (event.ShiftDown())
2791                 m_bMeasure_DistCircle = true;
2792             else
2793                 m_bMeasure_DistCircle = false;
2794 
2795             StartMeasureRoute();
2796         }
2797         else{
2798             CancelMeasureRoute();
2799 
2800             SetCursor( *pCursorArrow );
2801 
2802             SurfaceToolbar();
2803             InvalidateGL();
2804             Refresh( false );
2805         }
2806 
2807         break;
2808 
2809     case WXK_F5:
2810         parent_frame->ToggleColorScheme();
2811         gFrame->Raise();
2812         TriggerDeferredFocus();
2813         break;
2814 
2815     case WXK_F6: {
2816         int mod = m_modkeys & wxMOD_SHIFT;
2817         if( mod != m_brightmod ) {
2818             m_brightmod = mod;
2819             m_bbrightdir = !m_bbrightdir;
2820         }
2821 
2822         if( !m_bbrightdir ) {
2823             g_nbrightness -= 10;
2824             if( g_nbrightness <= MIN_BRIGHT ) {
2825                 g_nbrightness = MIN_BRIGHT;
2826                 m_bbrightdir = true;
2827             }
2828         } else {
2829             g_nbrightness += 10;
2830             if( g_nbrightness >= MAX_BRIGHT ) {
2831                 g_nbrightness = MAX_BRIGHT;
2832                 m_bbrightdir = false;
2833             }
2834         }
2835 
2836         SetScreenBrightness( g_nbrightness );
2837         ShowBrightnessLevelTimedPopup( g_nbrightness / 10, 1, 10 );
2838 
2839         SetFocus();             // just in case the external program steals it....
2840         gFrame->Raise();        // And reactivate the application main
2841 
2842         break;
2843     }
2844 
2845     case WXK_F7:
2846         parent_frame->DoStackDown( this );
2847         break;
2848 
2849     case WXK_F8:
2850         parent_frame->DoStackUp( this );
2851         break;
2852 
2853 #ifndef __WXOSX__
2854     case WXK_F9:
2855         ToggleCanvasQuiltMode();
2856         break;
2857 #endif
2858 
2859     case WXK_F11:
2860         parent_frame->ToggleFullScreen();
2861         b_handled = true;
2862         break;
2863 
2864     case WXK_F12: {
2865         if( m_modkeys == wxMOD_ALT )
2866             m_nMeasureState = *(volatile int *)(0);     // generate a fault for testing
2867 
2868         ToggleChartOutlines();
2869         break;
2870     }
2871 
2872     case WXK_PAUSE:                   // Drop MOB
2873          parent_frame->ActivateMOB();
2874          break;
2875 
2876     //NUMERIC PAD
2877     case WXK_NUMPAD_ADD:              // '+' on NUM PAD
2878     case WXK_PAGEUP:{
2879         ZoomCanvas( g_plus_minus_zoom_factor, false );
2880         break;
2881     }
2882     case WXK_NUMPAD_SUBTRACT:   // '-' on NUM PAD
2883     case WXK_PAGEDOWN:{
2884         ZoomCanvas( 1.0 / g_plus_minus_zoom_factor, false );
2885         break;
2886     }
2887 	case WXK_DELETE:
2888 	case WXK_BACK:
2889 		if (m_bMeasure_Active) {
2890             if (m_nMeasureState > 2) {
2891                 m_pMeasureRoute->DeletePoint(m_pMeasureRoute->GetLastPoint());
2892                 m_pMeasureRoute->m_lastMousePointIndex = m_pMeasureRoute->GetnPoints();
2893                 m_nMeasureState--;
2894                 gFrame->RefreshAllCanvas();
2895             }
2896             else {
2897                 CancelMeasureRoute();
2898                 StartMeasureRoute();
2899             }
2900 		}
2901 	break;
2902     default:
2903         break;
2904 
2905     }
2906 
2907     if( event.GetKeyCode() < 128 )            //ascii
2908     {
2909         int key_char = event.GetKeyCode();
2910 
2911         //      Handle both QWERTY and AZERTY keyboard separately for a few control codes
2912         if( !g_b_assume_azerty ) {
2913             switch( key_char ) {
2914             case '+': case '=':
2915                 ZoomCanvas( g_plus_minus_zoom_factor, false );
2916                 break;
2917 
2918             case '-': case '_':
2919                 ZoomCanvas( 1.0 / g_plus_minus_zoom_factor, false );
2920                 break;
2921 
2922             }
2923 
2924 #ifdef __WXMAC__
2925             if(g_benable_rotate){
2926                 switch( key_char ) {
2927 
2928             // On other platforms these are handled in OnKeyChar, which (apparently) works better in some locales.
2929             // On OS X it is better to handle them here, since pressing Alt (which should change the rotation speed)
2930             // changes the key char and so prevents the keys from working.
2931                 case ']':
2932                     RotateCanvas( 1 );
2933                     break;
2934 
2935                 case '[':
2936                     RotateCanvas( -1 );
2937                     break;
2938 
2939                 case '\\':
2940                     DoRotateCanvas(0);
2941                     break;
2942                 }
2943             }
2944 #endif
2945         } else {   //AZERTY
2946             switch( key_char ) {
2947             case 43:
2948                 ZoomCanvas( g_plus_minus_zoom_factor, false );
2949                 break;
2950 
2951             case 54:                     // '-'  alpha/num pad
2952 //            case 56:                     // '_'  alpha/num pad
2953                 ZoomCanvas( 1.0 / g_plus_minus_zoom_factor, false );
2954                 break;
2955             }
2956         }
2957 
2958 
2959         if ( event.ControlDown() )
2960             key_char -= 64;
2961 
2962         if (key_char >= '0' && key_char <= '9')
2963             SetGroupIndex( key_char - '0' );
2964         else
2965 
2966         switch( key_char ) {
2967         case 'A':
2968             SetShowENCAnchor(!GetShowENCAnchor());
2969             ReloadVP();
2970 
2971             break;
2972 
2973         case 'C':
2974             parent_frame->ToggleColorScheme();
2975             break;
2976 
2977         case 'D':
2978         {
2979             int x,y;
2980             event.GetPosition( &x, &y );
2981             ChartTypeEnum ChartType = CHART_TYPE_UNKNOWN;
2982             ChartFamilyEnum ChartFam = CHART_FAMILY_UNKNOWN;
2983                 // First find out what kind of chart is being used
2984             if( !pPopupDetailSlider ) {
2985                 if( VPoint.b_quilt )
2986                     {
2987                         if (m_pQuilt)
2988                         {
2989                             if (m_pQuilt->GetChartAtPix( VPoint, wxPoint( x, y )) ) // = null if no chart loaded for this point
2990                             {
2991                                 ChartType = m_pQuilt->GetChartAtPix( VPoint, wxPoint( x, y ) )->GetChartType();
2992                                 ChartFam = m_pQuilt->GetChartAtPix( VPoint, wxPoint( x, y ) )->GetChartFamily();
2993                             }
2994                     }
2995                     }
2996                 else
2997                     {
2998                         if (m_singleChart){
2999                             ChartType = m_singleChart->GetChartType();
3000                             ChartFam =  m_singleChart->GetChartFamily();
3001                         }
3002                     }
3003                     //If a charttype is found show the popupslider
3004                 if ( (ChartType != CHART_TYPE_UNKNOWN) || (ChartFam != CHART_FAMILY_UNKNOWN) )
3005                     {
3006                         pPopupDetailSlider = new PopUpDSlide( this, -1, ChartType, ChartFam,
3007                             wxPoint( g_detailslider_dialog_x, g_detailslider_dialog_y ),
3008                             wxDefaultSize, wxSIMPLE_BORDER, _T("") );
3009                         if (pPopupDetailSlider)
3010                             pPopupDetailSlider->Show();
3011                     }
3012                 }
3013             else //( !pPopupDetailSlider ) close popupslider
3014             {
3015                 if (pPopupDetailSlider) pPopupDetailSlider->Close();
3016                 pPopupDetailSlider = NULL;
3017             }
3018             break;
3019         }
3020 
3021        case 'L':
3022             SetShowENCLights(!GetShowENCLights());
3023             ReloadVP();
3024 
3025             break;
3026 
3027         case 'M':
3028             if (event.ShiftDown())
3029                 m_bMeasure_DistCircle = true;
3030             else
3031                 m_bMeasure_DistCircle = false;
3032 
3033             StartMeasureRoute();
3034             break;
3035 
3036         case 'N':
3037             if( g_bInlandEcdis && ps52plib){
3038                 SetENCDisplayCategory( (_DisCat)STANDARD );
3039             }
3040             break;
3041 
3042         case 'O':
3043             ToggleChartOutlines();
3044             break;
3045 
3046         case 'Q':
3047             ToggleCanvasQuiltMode();
3048             break;
3049 
3050         case 'P':
3051             parent_frame->ToggleTestPause();
3052             break;
3053 #if 0
3054         case 'R':
3055             parent_frame->ToggleRocks();
3056             break;
3057 #endif
3058         case 'S':
3059             SetShowENCDepth( !m_encShowDepth );
3060             ReloadVP();
3061             break;
3062 
3063         case 'T':
3064             SetShowENCText(!GetShowENCText());
3065             ReloadVP();
3066             break;
3067 
3068         case 'U':
3069             SetShowENCDataQual(!GetShowENCDataQual());
3070             ReloadVP();
3071             break;
3072 
3073         case 'V':
3074             m_bShowNavobjects = !m_bShowNavobjects;
3075             Refresh( true );
3076             break;
3077 
3078         case 1:                      // Ctrl A
3079             TogglebFollow();
3080 
3081             break;
3082 
3083         case 2:                      // Ctrl B
3084             if ( g_bShowMenuBar == false )
3085                 parent_frame->ToggleChartBar( this );
3086             break;
3087 
3088         case 13:             // Ctrl M // Drop Marker at cursor
3089         {
3090             if(event.ControlDown())
3091                 gFrame->DropMarker(false);
3092             break;
3093         }
3094 
3095         case 14:             // Ctrl N - Activate next waypoint in a route
3096         {
3097             if( Route * r = g_pRouteMan->GetpActiveRoute() ) {
3098                 int indexActive = r->GetIndexOf( r->m_pRouteActivePoint );
3099                 if( ( indexActive + 1 ) <= r->GetnPoints() ) {
3100                     g_pRouteMan->ActivateNextPoint( r, true );
3101                     InvalidateGL();
3102                     Refresh( false );
3103                 }
3104             }
3105             break;
3106         }
3107 
3108         case 15:             // Ctrl O - Drop Marker at boat's position
3109         {
3110             if(!g_bShowMenuBar)
3111                 gFrame->DropMarker(true);
3112             break;
3113         }
3114 
3115         case 32:            // Special needs use space bar
3116         {
3117             if ( g_bSpaceDropMark )
3118                 gFrame->DropMarker( true );
3119             break;
3120         }
3121 
3122         case -32:                     // Ctrl Space            //    Drop MOB
3123         {
3124             if( m_modkeys == wxMOD_CONTROL ) parent_frame->ActivateMOB();
3125 
3126             break;
3127         }
3128 
3129         case -20:                       // Ctrl ,
3130         {
3131             parent_frame->DoSettings();
3132             break;
3133         }
3134         case 17:                       // Ctrl Q
3135             parent_frame->Close();
3136             return;
3137 
3138         case 18:                       // Ctrl R
3139             StartRoute();
3140             return;
3141 
3142         case 20:                       // Ctrl T
3143             if( NULL == pGoToPositionDialog ) // There is one global instance of the Go To Position Dialog
3144                 pGoToPositionDialog = new GoToPositionDialog( this );
3145             pGoToPositionDialog->SetCanvas( this );
3146             pGoToPositionDialog->Show();
3147             break;
3148 
3149         case 25:                       // Ctrl Y
3150             if( undo->AnythingToRedo() ) {
3151                 undo->RedoNextAction();
3152                 InvalidateGL();
3153                 Refresh( false );
3154             }
3155             break;
3156 
3157         case 26:
3158             if ( event.ShiftDown() ) { // Shift-Ctrl-Z
3159                 if( undo->AnythingToRedo() ) {
3160                     undo->RedoNextAction();
3161                     InvalidateGL();
3162                     Refresh( false );
3163                 }
3164             } else {                   // Ctrl Z
3165                 if( undo->AnythingToUndo() ) {
3166                     undo->UndoLastAction();
3167                     InvalidateGL();
3168                     Refresh( false );
3169                 }
3170             }
3171             break;
3172 
3173         case 27:
3174             // Generic break
3175             if( m_bMeasure_Active ) {
3176                 CancelMeasureRoute();
3177 
3178                 SetCursor( *pCursorArrow );
3179 
3180                 SurfaceToolbar();
3181                 gFrame->RefreshAllCanvas();
3182             }
3183 
3184             if( m_routeState )         // creating route?
3185             {
3186                 FinishRoute();
3187                 SurfaceToolbar();
3188                 InvalidateGL();
3189                 Refresh( false );
3190             }
3191 
3192             break;
3193 
3194         case 7:                       // Ctrl G
3195             switch( gamma_state ) {
3196             case ( 0 ):
3197                 r_gamma_mult = 0;
3198                 g_gamma_mult = 1;
3199                 b_gamma_mult = 0;
3200                 gamma_state = 1;
3201                 break;
3202             case ( 1 ):
3203                 r_gamma_mult = 1;
3204                 g_gamma_mult = 0;
3205                 b_gamma_mult = 0;
3206                 gamma_state = 2;
3207                 break;
3208             case ( 2 ):
3209                 r_gamma_mult = 1;
3210                 g_gamma_mult = 1;
3211                 b_gamma_mult = 1;
3212                 gamma_state = 0;
3213                 break;
3214             }
3215             SetScreenBrightness( g_nbrightness );
3216 
3217             break;
3218 
3219         case 9:                      // Ctrl I
3220            if(event.ControlDown()){
3221                 m_bShowCompassWin = !m_bShowCompassWin;
3222                 SetShowGPSCompassWindow(m_bShowCompassWin);
3223                 Refresh( false );
3224            }
3225            break;
3226 
3227         default:
3228             break;
3229 
3230         }           // switch
3231     }
3232 
3233 #ifndef __WXMAC__
3234     // Allow OnKeyChar to catch the key events too.
3235     // On OS X this is unnecessary since we handle all key events here.
3236     if(!b_handled)
3237         event.Skip();
3238 #endif
3239 }
3240 
OnKeyUp(wxKeyEvent & event)3241 void ChartCanvas::OnKeyUp( wxKeyEvent &event )
3242 {
3243     if(g_pi_manager)
3244         if(g_pi_manager->SendKeyEventToPlugins( event ))
3245             return;                     // PlugIn did something, and does not want the canvas to do anything else
3246 
3247     switch( event.GetKeyCode() ) {
3248     case WXK_TAB:
3249         parent_frame->SwitchKBFocus( this );
3250         break;
3251 
3252     case WXK_LEFT:
3253     case WXK_RIGHT:
3254         m_panx = 0;
3255         if(!m_pany)
3256             m_panspeed = 0;
3257         break;
3258 
3259     case WXK_UP:
3260     case WXK_DOWN:
3261         m_pany = 0;
3262         if(!m_panx)
3263             m_panspeed = 0;
3264         break;
3265 
3266     case WXK_NUMPAD_ADD:              // '+' on NUM PAD
3267     case WXK_NUMPAD_SUBTRACT:   // '-' on NUM PAD
3268     case WXK_PAGEUP:
3269     case WXK_PAGEDOWN:
3270         if(m_mustmove)
3271             DoMovement(m_mustmove);
3272 
3273         m_zoom_factor = 1;
3274         break;
3275 
3276     case WXK_ALT:
3277         m_modkeys &= ~wxMOD_ALT;
3278 #ifdef OCPN_ALT_MENUBAR
3279 #ifndef __WXOSX__
3280         // If the permanent menu bar is disabled, and we are not in the middle of another key combo,
3281         // then show the menu bar temporarily when Alt is released (or hide it if already visible).
3282         if ( IsTempMenuBarEnabled() && !g_bShowMenuBar  &&  m_bMayToggleMenuBar ) {
3283             g_bTempShowMenuBar = !g_bTempShowMenuBar;
3284             parent_frame->ApplyGlobalSettings(false);
3285         }
3286         m_bMayToggleMenuBar = true;
3287 #endif
3288 #endif
3289         break;
3290 
3291     case WXK_CONTROL:
3292         m_modkeys &= ~wxMOD_CONTROL;
3293         break;
3294 
3295     }
3296 
3297     if( event.GetKeyCode() < 128 )            //ascii
3298     {
3299         int key_char = event.GetKeyCode();
3300 
3301         //      Handle both QWERTY and AZERTY keyboard separately for a few control codes
3302         if( !g_b_assume_azerty ) {
3303             switch( key_char ) {
3304             case '+':     case '=':
3305             case '-':     case '_':
3306               case 54:    case 56:    // '_'  alpha/num pad
3307                 DoMovement(m_mustmove);
3308 
3309                 //m_zoom_factor = 1;
3310                 break;
3311             case '[': case ']':
3312                 DoMovement(m_mustmove);
3313                 m_rotation_speed = 0;
3314                 break;
3315             }
3316         } else {
3317             switch( key_char ) {
3318             case 43:
3319             case 54:                     // '-'  alpha/num pad
3320             case 56:                     // '_'  alpha/num pad
3321                 DoMovement(m_mustmove);
3322 
3323                 m_zoom_factor = 1;
3324                 break;
3325             }
3326         }
3327     }
3328     event.Skip();
3329 }
3330 
ToggleChartOutlines(void)3331 void ChartCanvas::ToggleChartOutlines( void )
3332 {
3333     m_bShowOutlines = !m_bShowOutlines;
3334 
3335     Refresh( false );
3336 
3337     #ifdef ocpnUSE_GL         // opengl renders chart outlines as part of the chart this needs a full refresh
3338     if( g_bopengl )
3339         InvalidateGL();
3340     #endif
3341 }
3342 
3343 
ToggleLookahead()3344 void ChartCanvas::ToggleLookahead( )
3345 {
3346     m_bLookAhead = !m_bLookAhead;
3347     m_OSoffsetx = 0;            // center ownship
3348     m_OSoffsety = 0;
3349 
3350 }
3351 
SetUpMode(int mode)3352 void ChartCanvas::SetUpMode( int mode)
3353 {
3354     m_upMode = mode;
3355 
3356     if( mode != NORTH_UP_MODE ) {
3357         //    Stuff the COGAvg table in case COGUp is selected
3358         double stuff = 0;
3359         if( !std::isnan(gCog) )
3360             stuff = gCog;
3361 
3362         if( g_COGAvgSec > 0) {
3363             for( int i = 0; i < g_COGAvgSec; i++ )
3364                 gFrame->COGTable[i] = stuff;
3365         }
3366         g_COGAvg = stuff;
3367         gFrame->FrameCOGTimer.Start( 100, wxTIMER_CONTINUOUS );
3368     } else {
3369         if ( !g_bskew_comp && (fabs(GetVPSkew()) > 0.0001))
3370             SetVPRotation(GetVPSkew());
3371         else
3372             SetVPRotation(0); /* reset to north up */
3373     }
3374 
3375 
3376     if( GetMUIBar() && GetMUIBar()->GetCanvasOptions())
3377         GetMUIBar()->GetCanvasOptions()->RefreshControlValues();
3378 
3379     UpdateGPSCompassStatusBox( true );
3380     gFrame->DoChartUpdate();
3381 }
3382 
DoCanvasCOGSet(void)3383 bool ChartCanvas::DoCanvasCOGSet( void )
3384 {
3385     if( GetUpMode() == NORTH_UP_MODE )
3386         return false;
3387 
3388     if (std::isnan(g_COGAvg))
3389         return true;
3390 
3391     double old_VPRotate = m_VPRotate;
3392 
3393     if ((GetUpMode() == HEAD_UP_MODE) && !std::isnan(gHdt) ) {
3394         m_VPRotate = -gHdt * PI / 180.;
3395     }
3396     else if(GetUpMode() == COURSE_UP_MODE)
3397         m_VPRotate = -g_COGAvg * PI / 180.;
3398 
3399 
3400     SetVPRotation( m_VPRotate );
3401     bool bnew_chart = DoCanvasUpdate();
3402 
3403     if( ( bnew_chart ) || ( old_VPRotate != m_VPRotate ) )
3404         ReloadVP();
3405 
3406     return true;
3407 }
3408 
StopMovement()3409 void ChartCanvas::StopMovement( )
3410 {
3411     m_panx = m_pany = 0;
3412     m_panspeed = 0;
3413     m_zoom_factor = 1;
3414     m_rotation_speed = 0;
3415     m_mustmove = 0;
3416 #if 0
3417 #if !defined(__WXGTK__) && !defined(__WXQT__)
3418     SetFocus();
3419     gFrame->Raise();
3420 #endif
3421 #endif
3422 }
3423 
3424 /* instead of integrating in timer callbacks
3425    (which do not always get called fast enough)
3426    we can perform the integration of movement
3427    at each render frame based on the time change */
StartTimedMovement(bool stoptimer)3428 bool ChartCanvas::StartTimedMovement( bool stoptimer )
3429 {
3430     // Start/restart the stop movement timer
3431     if(stoptimer)
3432         pMovementStopTimer->Start( 800, wxTIMER_ONE_SHOT );
3433 
3434     if(!pMovementTimer->IsRunning()){
3435 //        printf("timer not running, starting\n");
3436         pMovementTimer->Start( 1, wxTIMER_ONE_SHOT );
3437     }
3438 
3439     if(m_panx || m_pany || m_zoom_factor!=1 || m_rotation_speed) {
3440         // already moving, gets called again because of key-repeat event
3441         return false;
3442     }
3443 
3444     m_last_movement_time = wxDateTime::UNow();
3445 
3446     /* jumpstart because paint gets called right away, if we want first frame to move */
3447 //    m_last_movement_time -= wxTimeSpan::Milliseconds(100);
3448 
3449 //    Refresh( false );
3450 
3451     return true;
3452 }
3453 
DoTimedMovement()3454 void ChartCanvas::DoTimedMovement()
3455 {
3456     if( m_pan_drag == wxPoint(0, 0) && !m_panx && !m_pany && m_zoom_factor==1 && !m_rotation_speed)
3457         return; /* not moving */
3458 
3459     wxDateTime now = wxDateTime::UNow();
3460     long dt = 0;
3461     if(m_last_movement_time.IsValid())
3462         dt = (now - m_last_movement_time).GetMilliseconds().ToLong();
3463 
3464     m_last_movement_time = now;
3465 
3466     if(dt > 500) /* if we are running very slow, don't integrate too fast */
3467         dt = 500;
3468 
3469     DoMovement(dt);
3470 }
3471 
DoMovement(long dt)3472 void ChartCanvas::DoMovement( long dt )
3473 {
3474     /* if we get here quickly assume 1ms so that some movement occurs */
3475     if(dt == 0)
3476         dt = 1;
3477 
3478     m_mustmove -= dt;
3479     if(m_mustmove < 0)
3480         m_mustmove = 0;
3481 
3482     if(m_pan_drag.x || m_pan_drag.y) {
3483         PanCanvas( m_pan_drag.x, m_pan_drag.y );
3484         m_pan_drag.x = m_pan_drag.y = 0;
3485     }
3486 
3487     if(m_panx || m_pany) {
3488         const double slowpan = .1, maxpan = 2;
3489         if( m_modkeys == wxMOD_ALT )
3490             m_panspeed = slowpan;
3491         else {
3492             m_panspeed += (double)dt/500; /* apply acceleration */
3493             m_panspeed = wxMin( maxpan, m_panspeed );
3494         }
3495         PanCanvas( m_panspeed * m_panx * dt, m_panspeed * m_pany * dt);
3496     }
3497 
3498     if(m_zoom_factor != 1) {
3499         double alpha = 400, beta = 1.5;
3500         double zoom_factor = (exp(dt / alpha) - 1) / beta + 1;
3501 
3502         if( m_modkeys == wxMOD_ALT )
3503             zoom_factor = pow(zoom_factor, .15);
3504 
3505         if(m_zoom_factor < 1)
3506             zoom_factor = 1/zoom_factor;
3507 
3508         //  Try to hit the zoom target exactly.
3509         //if(m_wheelzoom_stop_oneshot > 0)
3510         {
3511             if(zoom_factor > 1){
3512                 if(  VPoint.chart_scale / zoom_factor <= m_zoom_target)
3513                     zoom_factor = VPoint.chart_scale / m_zoom_target;
3514             }
3515 
3516             else if(zoom_factor < 1){
3517                 if(  VPoint.chart_scale / zoom_factor >= m_zoom_target)
3518                     zoom_factor = VPoint.chart_scale / m_zoom_target;
3519             }
3520         }
3521 
3522         if( fabs(zoom_factor - 1) > 1e-4)
3523             DoZoomCanvas( zoom_factor, m_bzooming_to_cursor );
3524 
3525         if(m_wheelzoom_stop_oneshot > 0) {
3526             if(m_wheelstopwatch.Time() > m_wheelzoom_stop_oneshot){
3527                 m_wheelzoom_stop_oneshot = 0;
3528                 StopMovement( );
3529             }
3530 
3531             //      Don't overshoot the zoom target.
3532             if(zoom_factor > 1){
3533                 if(  VPoint.chart_scale <= m_zoom_target){
3534                     m_wheelzoom_stop_oneshot = 0;
3535                     StopMovement( );
3536                 }
3537             }
3538             else if(zoom_factor < 1){
3539                 if(  VPoint.chart_scale >= m_zoom_target){
3540                     m_wheelzoom_stop_oneshot = 0;
3541                     StopMovement( );
3542                 }
3543             }
3544         }
3545     }
3546 
3547     if( m_rotation_speed ) { /* in degrees per second */
3548         double speed = m_rotation_speed;
3549         if( m_modkeys == wxMOD_ALT)
3550             speed /= 10;
3551         DoRotateCanvas( VPoint.rotation + speed * PI / 180 * dt / 1000.0);
3552     }
3553 }
3554 
SetColorScheme(ColorScheme cs)3555 void ChartCanvas::SetColorScheme( ColorScheme cs )
3556 {
3557     SetAlertString(_T(""));
3558 
3559     //    Setup ownship image pointers
3560     switch( cs ) {
3561     case GLOBAL_COLOR_SCHEME_DAY:
3562         m_pos_image_red = &m_os_image_red_day;
3563         m_pos_image_grey = &m_os_image_grey_day;
3564         m_pos_image_yellow = &m_os_image_yellow_day;
3565         m_pos_image_user = m_pos_image_user_day;
3566         m_pos_image_user_grey = m_pos_image_user_grey_day;
3567         m_pos_image_user_yellow = m_pos_image_user_yellow_day;
3568         m_cTideBitmap = m_bmTideDay;
3569         m_cCurrentBitmap = m_bmCurrentDay;
3570 
3571         break;
3572     case GLOBAL_COLOR_SCHEME_DUSK:
3573         m_pos_image_red = &m_os_image_red_dusk;
3574         m_pos_image_grey = &m_os_image_grey_dusk;
3575         m_pos_image_yellow = &m_os_image_yellow_dusk;
3576         m_pos_image_user = m_pos_image_user_dusk;
3577         m_pos_image_user_grey = m_pos_image_user_grey_dusk;
3578         m_pos_image_user_yellow = m_pos_image_user_yellow_dusk;
3579         m_cTideBitmap = m_bmTideDusk;
3580         m_cCurrentBitmap = m_bmCurrentDusk;
3581         break;
3582     case GLOBAL_COLOR_SCHEME_NIGHT:
3583         m_pos_image_red = &m_os_image_red_night;
3584         m_pos_image_grey = &m_os_image_grey_night;
3585         m_pos_image_yellow = &m_os_image_yellow_night;
3586         m_pos_image_user = m_pos_image_user_night;
3587         m_pos_image_user_grey = m_pos_image_user_grey_night;
3588         m_pos_image_user_yellow = m_pos_image_user_yellow_night;
3589         m_cTideBitmap = m_bmTideNight;
3590         m_cCurrentBitmap = m_bmCurrentNight;
3591         break;
3592     default:
3593         m_pos_image_red = &m_os_image_red_day;
3594         m_pos_image_grey = &m_os_image_grey_day;
3595         m_pos_image_yellow = &m_os_image_yellow_day;
3596         m_pos_image_user = m_pos_image_user_day;
3597         m_pos_image_user_grey = m_pos_image_user_grey_day;
3598         m_pos_image_user_yellow = m_pos_image_user_yellow_day;
3599         m_cTideBitmap = m_bmTideDay;
3600         m_cCurrentBitmap = m_bmCurrentDay;
3601         break;
3602     }
3603 
3604     CreateDepthUnitEmbossMaps( cs );
3605     CreateOZEmbossMapData( cs );
3606 
3607     //  Set up fog effect base color
3608     m_fog_color = wxColor( 170, 195, 240 );  // this is gshhs (backgound world chart) ocean color
3609     float dim = 1.0;
3610     switch( cs ){
3611         case GLOBAL_COLOR_SCHEME_DUSK:
3612             dim = 0.5;
3613             break;
3614         case GLOBAL_COLOR_SCHEME_NIGHT:
3615             dim = 0.25;
3616             break;
3617         default:
3618             break;
3619     }
3620     m_fog_color.Set( m_fog_color.Red()*dim, m_fog_color.Green()*dim, m_fog_color.Blue()*dim );
3621 
3622     //  Really dark
3623 #if 0
3624     if( cs == GLOBAL_COLOR_SCHEME_DUSK || cs == GLOBAL_COLOR_SCHEME_NIGHT ) {
3625         SetBackgroundColour( wxColour(0,0,0) );
3626 
3627         SetWindowStyleFlag( (GetWindowStyleFlag() & ~wxSIMPLE_BORDER) | wxNO_BORDER);
3628     }
3629     else{
3630         SetWindowStyleFlag( (GetWindowStyleFlag() & ~wxNO_BORDER) | wxSIMPLE_BORDER);
3631 #ifndef __WXMAC__
3632         SetBackgroundColour( wxNullColour );
3633 #endif
3634     }
3635 #endif
3636 
3637     UpdateToolbarColorScheme( cs );
3638 
3639     m_Piano->SetColorScheme( cs );
3640 
3641     m_Compass->SetColorScheme( cs );
3642 
3643     if(m_muiBar)
3644         m_muiBar->SetColorScheme( cs );
3645 
3646     if(pWorldBackgroundChart)
3647         pWorldBackgroundChart->SetColorScheme( cs );
3648 #ifdef ocpnUSE_GL
3649     if( g_bopengl && m_glcc ){
3650         m_glcc->SetColorScheme( cs );
3651         g_glTextureManager->ClearAllRasterTextures();
3652         //m_glcc->FlushFBO();
3653     }
3654 #endif
3655     SetbTCUpdate( true );                        // force re-render of tide/current locators
3656     m_brepaint_piano = true;
3657 
3658     ReloadVP();
3659 
3660     m_cs = cs;
3661 }
3662 
CreateDimBitmap(wxBitmap & Bitmap,double factor)3663 wxBitmap ChartCanvas::CreateDimBitmap( wxBitmap &Bitmap, double factor )
3664 {
3665     wxImage img = Bitmap.ConvertToImage();
3666     int sx = img.GetWidth();
3667     int sy = img.GetHeight();
3668 
3669     wxImage new_img( img );
3670 
3671     for( int i = 0; i < sx; i++ ) {
3672         for( int j = 0; j < sy; j++ ) {
3673             if( !img.IsTransparent( i, j ) ) {
3674                 new_img.SetRGB( i, j, (unsigned char) ( img.GetRed( i, j ) * factor ),
3675                                 (unsigned char) ( img.GetGreen( i, j ) * factor ),
3676                                 (unsigned char) ( img.GetBlue( i, j ) * factor ) );
3677             }
3678         }
3679     }
3680 
3681     wxBitmap ret = wxBitmap( new_img );
3682 
3683     return ret;
3684 
3685 }
3686 
ShowBrightnessLevelTimedPopup(int brightness,int min,int max)3687 void ChartCanvas::ShowBrightnessLevelTimedPopup( int brightness, int min, int max )
3688 {
3689     wxFont *pfont = FontMgr::Get().FindOrCreateFont( 40, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD );
3690 
3691     if( !m_pBrightPopup ) {
3692         //    Calculate size
3693         int x, y;
3694         GetTextExtent( _T("MAX"), &x, &y, NULL, NULL, pfont );
3695 
3696         m_pBrightPopup = new TimedPopupWin( this, 3);
3697 
3698         m_pBrightPopup->SetSize(x, y);
3699         m_pBrightPopup->Move(120,120);
3700     }
3701 
3702     int bmpsx = m_pBrightPopup->GetSize().x;
3703     int bmpsy = m_pBrightPopup->GetSize().y;
3704 
3705     wxBitmap bmp( bmpsx, bmpsx );
3706     wxMemoryDC mdc( bmp );
3707 
3708     mdc.SetTextForeground( GetGlobalColor( _T("GREEN4") ) );
3709     mdc.SetBackground( wxBrush( GetGlobalColor( _T("UINFD") ) ) );
3710     mdc.SetPen( wxPen( wxColour( 0, 0, 0 ) ) );
3711     mdc.SetBrush( wxBrush( GetGlobalColor( _T("UINFD") ) ) );
3712     mdc.Clear();
3713 
3714     mdc.DrawRectangle( 0, 0, bmpsx, bmpsy );
3715 
3716     mdc.SetFont( *pfont );
3717     wxString val;
3718 
3719     if( brightness == max ) val = _T("MAX");
3720     else
3721         if( brightness == min ) val = _T("MIN");
3722         else
3723             val.Printf( _T("%3d"), brightness );
3724 
3725     mdc.DrawText( val, 0, 0 );
3726 
3727     mdc.SelectObject( wxNullBitmap );
3728 
3729     m_pBrightPopup->SetBitmap( bmp );
3730     m_pBrightPopup->Show();
3731     m_pBrightPopup->Refresh();
3732 
3733 
3734 }
3735 
3736 
RotateTimerEvent(wxTimerEvent & event)3737 void ChartCanvas::RotateTimerEvent( wxTimerEvent& event )
3738 {
3739     m_b_rot_hidef = true;
3740     ReloadVP();
3741 }
3742 
3743 
OnRolloverPopupTimerEvent(wxTimerEvent & event)3744 void ChartCanvas::OnRolloverPopupTimerEvent( wxTimerEvent& event )
3745 {
3746     if(!g_bRollover)
3747     return;
3748 
3749     bool b_need_refresh = false;
3750 
3751     //  Handle the AIS Rollover Window first
3752     bool showAISRollover = false;
3753     if( g_pAIS && g_pAIS->GetNumTargets() && m_bShowAIS ) {
3754         SelectItem *pFind = pSelectAIS->FindSelection( this, m_cursor_lat, m_cursor_lon, SELTYPE_AISTARGET );
3755         if( pFind ) {
3756             int FoundAIS_MMSI = (wxIntPtr) pFind->m_pData1;
3757             AIS_Target_Data *ptarget = g_pAIS->Get_Target_Data_From_MMSI( FoundAIS_MMSI );
3758 
3759             if( ptarget ) {
3760                 showAISRollover = true;
3761 
3762                 if( NULL == m_pAISRolloverWin ) {
3763                     m_pAISRolloverWin = new RolloverWin( this );
3764                     m_pAISRolloverWin->IsActive( false );
3765                     b_need_refresh = true;
3766                 }
3767                 else if( m_pAISRolloverWin->IsActive() && m_AISRollover_MMSI && m_AISRollover_MMSI != FoundAIS_MMSI ){
3768                     //      Sometimes the mouse moves fast enough to get over a new AIS target before
3769                     //      the one-shot has fired to remove the old target.
3770                     //      Result:  wrong target data is shown.
3771                     //      Detect this case,close the existing rollover ASAP, and restart the timer.
3772                     m_RolloverPopupTimer.Start( 50, wxTIMER_ONE_SHOT );
3773                     m_pAISRolloverWin->IsActive( false );
3774                     m_AISRollover_MMSI = 0;
3775                     Refresh();
3776                     return;
3777                 }
3778 
3779                 m_AISRollover_MMSI = FoundAIS_MMSI;
3780 
3781                 if( !m_pAISRolloverWin->IsActive() ) {
3782 
3783                     wxString s = ptarget->GetRolloverString();
3784                     m_pAISRolloverWin->SetString( s );
3785 
3786                     wxSize win_size = GetSize();
3787                     if( console && console->IsShown() ) win_size.x -= console->GetSize().x;
3788 
3789                     m_pAISRolloverWin->SetBestPosition( mouse_x, mouse_y, 16, 16, AIS_ROLLOVER, win_size );
3790 
3791                     m_pAISRolloverWin->SetBitmap( AIS_ROLLOVER );
3792                     m_pAISRolloverWin->IsActive( true );
3793                     b_need_refresh = true;
3794                 }
3795             }
3796         }
3797         else {
3798             m_AISRollover_MMSI = 0;
3799             showAISRollover = false;
3800         }
3801     }
3802 
3803     //  Maybe turn the rollover off
3804     if( m_pAISRolloverWin && m_pAISRolloverWin->IsActive() && !showAISRollover ) {
3805         m_pAISRolloverWin->IsActive( false );
3806         m_AISRollover_MMSI = 0;
3807         b_need_refresh = true;
3808     }
3809 
3810     // Now the Route info rollover
3811     // Show the route segment info
3812     bool showRouteRollover = false;
3813 
3814     if( NULL == m_pRolloverRouteSeg ) {
3815         //    Get a list of all selectable sgements, and search for the first visible segment as the rollover target.
3816 
3817         SelectableItemList SelList = pSelect->FindSelectionList( this, m_cursor_lat, m_cursor_lon, SELTYPE_ROUTESEGMENT );
3818         wxSelectableItemListNode *node = SelList.GetFirst();
3819         while( node ) {
3820             SelectItem *pFindSel = node->GetData();
3821 
3822             Route *pr = (Route *) pFindSel->m_pData3;        //candidate
3823 
3824             if( pr && pr->IsVisible() ) {
3825                 m_pRolloverRouteSeg = pFindSel;
3826                 showRouteRollover = true;
3827 
3828                 if( NULL == m_pRouteRolloverWin ) {
3829                     m_pRouteRolloverWin = new RolloverWin( this, 10 );
3830                     m_pRouteRolloverWin->IsActive( false );
3831                 }
3832 
3833                 if( !m_pRouteRolloverWin->IsActive() ) {
3834                     wxString s;
3835                     RoutePoint *segShow_point_a = (RoutePoint *) m_pRolloverRouteSeg->m_pData1;
3836                     RoutePoint *segShow_point_b = (RoutePoint *) m_pRolloverRouteSeg->m_pData2;
3837 
3838                     double brg, dist;
3839                     DistanceBearingMercator( segShow_point_b->m_lat, segShow_point_b->m_lon,
3840                                              segShow_point_a->m_lat, segShow_point_a->m_lon, &brg, &dist );
3841 
3842                     if( !pr->m_bIsInLayer )
3843                         s.Append( _("Route") + _T(": ") );
3844                     else
3845                         s.Append( _("Layer Route: ") );
3846 
3847                     if( pr->m_RouteNameString.IsEmpty() ) s.Append( _("(unnamed)") );
3848                     else
3849                         s.Append( pr->m_RouteNameString );
3850 
3851                     s << _T("\n") << _("Total Length: ") << FormatDistanceAdaptive( pr->m_route_length)
3852                     << _T("\n") << _("Leg: from ") << segShow_point_a->GetName()
3853                     << _(" to ") << segShow_point_b->GetName()
3854                     << _T("\n");
3855 
3856                     if( g_bShowTrue )
3857                         s << wxString::Format( wxString("%03d°  ", wxConvUTF8 ), (int)brg );
3858                     if( g_bShowMag ){
3859                         double latAverage = (segShow_point_b->m_lat + segShow_point_a->m_lat)/2;
3860                         double lonAverage = (segShow_point_b->m_lon + segShow_point_a->m_lon)/2;
3861                         double varBrg = gFrame->GetMag( brg, latAverage, lonAverage);
3862 
3863                         s << wxString::Format( wxString("%03d°(M)  ", wxConvUTF8 ), (int)varBrg );
3864                     }
3865 
3866                     s << FormatDistanceAdaptive( dist );
3867 
3868                     // Compute and display cumulative distance from route start point to current
3869                     // leg end point and RNG,TTG,ETA from ship to current leg end point for active route
3870                     double shiptoEndLeg = 0.;
3871                     bool validActive = false;
3872                     if( pr->IsActive() && pr->pRoutePointList->GetFirst()->GetData()->m_bIsActive )
3873                             validActive = true;
3874 
3875                     if( segShow_point_a != pr->pRoutePointList->GetFirst()->GetData() ) {
3876                         wxRoutePointListNode *node = (pr->pRoutePointList)->GetFirst()->GetNext();
3877                         RoutePoint *prp;
3878                         float dist_to_endleg = 0;
3879                         wxString t;
3880 
3881                         while( node ) {
3882                             prp = node->GetData();
3883                             if( validActive )
3884                                 shiptoEndLeg += prp->m_seg_len;
3885                             else if ( prp->m_bIsActive )
3886                                 validActive = true;
3887                             dist_to_endleg += prp->m_seg_len;
3888                             if( prp->IsSame( segShow_point_a ) ) break;
3889                             node = node->GetNext();
3890                         }
3891                         s << _T(" (+") << FormatDistanceAdaptive( dist_to_endleg ) << _T(")");
3892                     }
3893                     //write from ship to end selected leg point data if the route is active
3894                     if(validActive) {
3895                         s << _T("\n") << _("From Ship To") << _T(" ") << segShow_point_b->GetName() << _T("\n");
3896                         shiptoEndLeg += g_pRouteMan->GetCurrentRngToActivePoint();//add distance from ship to active point
3897                         shiptoEndLeg += segShow_point_b->m_seg_len; //add the lenght of the selected leg
3898                         s << FormatDistanceAdaptive( shiptoEndLeg );
3899                         //ensure sog/cog are valid and vmg is positive to keep data coherent
3900                         double vmg = 0.;
3901                         if( !std::isnan(gCog) && !std::isnan(gSog) )
3902                             vmg = gSog * cos( ( g_pRouteMan->GetCurrentBrgToActivePoint() - gCog ) * PI / 180. );
3903                         if( vmg > 0. ) {
3904                             float ttg_sec = ( shiptoEndLeg / gSog ) * 3600.;
3905                             wxTimeSpan ttg_span = wxTimeSpan::Seconds( (long) ttg_sec );
3906                             s << _T(" - ") << wxString( ttg_sec > SECONDS_PER_DAY ?
3907                                           ttg_span.Format(_("%Dd %H:%M")) : ttg_span.Format(_("%H:%M")) );
3908                             wxDateTime dtnow, eta;
3909                             eta = dtnow.SetToCurrent().Add( ttg_span );
3910                             s  << _T(" - ") << eta.Format(_T("%b")).Mid(0, 4) << eta.Format(_T(" %d %H:%M"));
3911                         } else
3912                             s << _T("   ----   ----");
3913                     }
3914                     m_pRouteRolloverWin->SetString( s );
3915 
3916                     wxSize win_size = GetSize();
3917                     if( console && console->IsShown() ) win_size.x -= console->GetSize().x;
3918                     m_pRouteRolloverWin->SetBestPosition( mouse_x, mouse_y, 16, 16, LEG_ROLLOVER,
3919                                                      win_size );
3920                     m_pRouteRolloverWin->SetBitmap( LEG_ROLLOVER );
3921                     m_pRouteRolloverWin->IsActive( true );
3922                     b_need_refresh = true;
3923                     showRouteRollover = true;
3924                     break;
3925                 }
3926             } else
3927                 node = node->GetNext();
3928         }
3929     } else {
3930         //    Is the cursor still in select radius, and not timed out?
3931         if( !pSelect->IsSelectableSegmentSelected( this, m_cursor_lat, m_cursor_lon, m_pRolloverRouteSeg ) )
3932             showRouteRollover = false;
3933         else if(m_pRouteRolloverWin && !m_pRouteRolloverWin->IsActive())
3934             showRouteRollover = false;
3935         else
3936             showRouteRollover = true;
3937     }
3938 
3939     //    If currently creating a route, do not show this rollover window
3940     if( m_routeState )
3941         showRouteRollover = false;
3942 
3943     //    Similar for AIS target rollover window
3944     if( m_pAISRolloverWin && m_pAISRolloverWin->IsActive() )
3945         showRouteRollover = false;
3946 
3947     if( m_pRouteRolloverWin /*&& m_pRouteRolloverWin->IsActive()*/ && !showRouteRollover ) {
3948         m_pRouteRolloverWin->IsActive( false );
3949         m_pRolloverRouteSeg = NULL;
3950         m_pRouteRolloverWin->Destroy();
3951         m_pRouteRolloverWin = NULL;
3952         b_need_refresh = true;
3953     } else if( m_pRouteRolloverWin && showRouteRollover ) {
3954         m_pRouteRolloverWin->IsActive( true );
3955         b_need_refresh = true;
3956     }
3957 
3958     // Now the Track info rollover
3959     // Show the track segment info
3960     bool showTrackRollover = false;
3961 
3962     if( NULL == m_pRolloverTrackSeg ) {
3963         //    Get a list of all selectable sgements, and search for the first visible segment as the rollover target.
3964 
3965         SelectableItemList SelList = pSelect->FindSelectionList( this, m_cursor_lat, m_cursor_lon, SELTYPE_TRACKSEGMENT );
3966         wxSelectableItemListNode *node = SelList.GetFirst();
3967         while( node ) {
3968             SelectItem *pFindSel = node->GetData();
3969 
3970             Track *pt = (Track *) pFindSel->m_pData3;        //candidate
3971 
3972             if( pt && pt->IsVisible() ) {
3973                 m_pRolloverTrackSeg = pFindSel;
3974                 showTrackRollover = true;
3975 
3976                 if( NULL == m_pTrackRolloverWin ) {
3977 		   m_pTrackRolloverWin = new RolloverWin( this, 10 );
3978                     m_pTrackRolloverWin->IsActive( false );
3979                 }
3980 
3981                 if( !m_pTrackRolloverWin->IsActive() ) {
3982                     wxString s;
3983                     TrackPoint *segShow_point_a = (TrackPoint *) m_pRolloverTrackSeg->m_pData1;
3984                     TrackPoint *segShow_point_b = (TrackPoint *) m_pRolloverTrackSeg->m_pData2;
3985 
3986                     double brg, dist;
3987                     DistanceBearingMercator( segShow_point_b->m_lat, segShow_point_b->m_lon,
3988                                              segShow_point_a->m_lat, segShow_point_a->m_lon, &brg, &dist );
3989 
3990                     if( !pt->m_bIsInLayer )
3991                         s.Append( _("Track") + _T(": ") );
3992                     else
3993                         s.Append( _("Layer Track: ") );
3994 
3995                     if( pt->GetName().IsEmpty() )
3996                         s.Append( _("(unnamed)") );
3997                     else
3998                         s.Append( pt->GetName() );
3999                     double tlenght = pt->Length();
4000                     s << _T("\n") << _("Total Track: ") << FormatDistanceAdaptive(tlenght);
4001                     if( pt->GetLastPoint()->GetTimeString() && pt->GetPoint(0)->GetTimeString() ) {
4002                         wxTimeSpan ttime = pt->GetLastPoint()->GetCreateTime() - pt->GetPoint(0)->GetCreateTime();
4003                         double htime = ttime.GetSeconds().ToDouble() / 3600.;
4004                         s << wxString::Format( _T("  %.1f "), (float)(tlenght / htime) ) << getUsrSpeedUnit();
4005                         s << wxString(htime > 24.? ttime.Format(_T("  %Dd %H:%M")): ttime.Format(_T("  %H:%M")));
4006                     }
4007                     if (g_bShowTrackPointTime && segShow_point_b->GetTimeString())
4008                         s << _T("\n") << _("Segment Created: ") << segShow_point_b->GetTimeString();
4009 
4010                     s << _T("\n");
4011                     if( g_bShowTrue )
4012                         s << wxString::Format( wxString("%03d°  ", wxConvUTF8 ), (int)brg );
4013                     if( g_bShowMag ){
4014                         double latAverage = (segShow_point_b->m_lat + segShow_point_a->m_lat)/2;
4015                         double lonAverage = (segShow_point_b->m_lon + segShow_point_a->m_lon)/2;
4016                         double varBrg = gFrame->GetMag( brg, latAverage, lonAverage);
4017 
4018                         s << wxString::Format( wxString("%03d°(M)  ", wxConvUTF8 ), (int)varBrg );
4019                     }
4020 
4021                     s << FormatDistanceAdaptive( dist );
4022 
4023                     if(segShow_point_a->GetTimeString() && segShow_point_b->GetTimeString()){
4024                         double segmentSpeed = toUsrSpeed( dist / ( (segShow_point_b->GetCreateTime() - segShow_point_a->GetCreateTime()).GetSeconds().ToDouble() / 3600.) );
4025                         s << wxString::Format( _T("  %.1f "), (float)segmentSpeed ) << getUsrSpeedUnit();
4026                     }
4027 
4028                     m_pTrackRolloverWin->SetString( s );
4029 
4030                     wxSize win_size = GetSize();
4031                     if( console && console->IsShown() ) win_size.x -= console->GetSize().x;
4032                     m_pTrackRolloverWin->SetBestPosition( mouse_x, mouse_y, 16, 16, LEG_ROLLOVER,
4033                                                      win_size );
4034                     m_pTrackRolloverWin->SetBitmap( LEG_ROLLOVER );
4035                     m_pTrackRolloverWin->IsActive( true );
4036                     b_need_refresh = true;
4037                     showTrackRollover = true;
4038                     break;
4039                 }
4040             } else
4041                 node = node->GetNext();
4042         }
4043     } else {
4044         //    Is the cursor still in select radius, and not timed out?
4045         if( !pSelect->IsSelectableSegmentSelected( this, m_cursor_lat, m_cursor_lon, m_pRolloverTrackSeg ) )
4046             showTrackRollover = false;
4047         else if(m_pTrackRolloverWin && !m_pTrackRolloverWin->IsActive())
4048             showTrackRollover = false;
4049         else
4050             showTrackRollover = true;
4051     }
4052 
4053     //    Similar for AIS target rollover window
4054     if( m_pAISRolloverWin && m_pAISRolloverWin->IsActive() )
4055         showTrackRollover = false;
4056 
4057     //    Similar for route rollover window
4058     if( m_pRouteRolloverWin && m_pRouteRolloverWin->IsActive() )
4059         showTrackRollover = false;
4060 
4061     //TODO  We onlt show tracks on primary canvas....
4062     //if(!IsPrimaryCanvas())
4063     //    showTrackRollover = false;
4064 
4065     if( m_pTrackRolloverWin /*&& m_pTrackRolloverWin->IsActive()*/ && !showTrackRollover ) {
4066         m_pTrackRolloverWin->IsActive( false );
4067         m_pRolloverTrackSeg = NULL;
4068         m_pTrackRolloverWin->Destroy();
4069         m_pTrackRolloverWin = NULL;
4070         b_need_refresh = true;
4071     } else if( m_pTrackRolloverWin && showTrackRollover ) {
4072         m_pTrackRolloverWin->IsActive( true );
4073         b_need_refresh = true;
4074     }
4075 
4076     if( b_need_refresh )
4077         Refresh();
4078 }
4079 
OnCursorTrackTimerEvent(wxTimerEvent & event)4080 void ChartCanvas::OnCursorTrackTimerEvent( wxTimerEvent& event )
4081 {
4082     if( s57_CheckExtendedLightSectors( this, mouse_x, mouse_y, VPoint, extendedSectorLegs ) ){
4083         if(!m_bsectors_shown) {
4084             ReloadVP( false );
4085             m_bsectors_shown = true;
4086         }
4087     }
4088     else {
4089         if( m_bsectors_shown ) {
4090             ReloadVP( false );
4091             m_bsectors_shown = false;
4092         }
4093     }
4094 
4095 //      This is here because GTK status window update is expensive..
4096 //            cairo using pango rebuilds the font every time so is very inefficient
4097 //      Anyway, only update the status bar when this timer expires
4098 #if defined(__WXGTK__) || defined(__WXQT__)
4099     {
4100         //    Check the absolute range of the cursor position
4101         //    There could be a window wherein the chart geoereferencing is not valid....
4102         double cursor_lat, cursor_lon;
4103         GetCanvasPixPoint ( mouse_x, mouse_y, cursor_lat, cursor_lon );
4104 
4105         if((fabs(cursor_lat) < 90.) && (fabs(cursor_lon) < 360.))
4106         {
4107             while(cursor_lon < -180.)
4108                 cursor_lon += 360.;
4109 
4110             while(cursor_lon > 180.)
4111                 cursor_lon -= 360.;
4112 
4113             SetCursorStatus(cursor_lat, cursor_lon);
4114         }
4115     }
4116 #endif
4117 }
4118 
SetCursorStatus(double cursor_lat,double cursor_lon)4119 void ChartCanvas::SetCursorStatus( double cursor_lat, double cursor_lon )
4120 {
4121     if ( !parent_frame->m_pStatusBar )
4122         return;
4123 
4124     wxString s1;
4125     s1 += _T(" ");
4126     s1 += toSDMM(1, cursor_lat);
4127     s1 += _T("   ");
4128     s1 += toSDMM(2, cursor_lon);
4129 
4130     if(STAT_FIELD_CURSOR_LL >= 0)
4131         parent_frame->SetStatusText ( s1, STAT_FIELD_CURSOR_LL );
4132 
4133     if( STAT_FIELD_CURSOR_BRGRNG < 0 )
4134         return;
4135 
4136     double brg, dist;
4137     wxString s;
4138     DistanceBearingMercator(cursor_lat, cursor_lon, gLat, gLon, &brg, &dist);
4139     if( g_bShowMag )
4140         s.Printf( wxString("%03d°(M)  ", wxConvUTF8 ), (int)gFrame->GetMag( brg ) );
4141     else
4142         s.Printf( wxString("%03d°  ", wxConvUTF8 ), (int)brg );
4143 
4144     s << FormatDistanceAdaptive( dist );
4145 
4146     // CUSTOMIZATION - LIVE ETA OPTION
4147     // -------------------------------------------------------
4148     // Calculate an "live" ETA based on route starting from the current
4149     // position of the boat and goes to the cursor of the mouse.
4150     // In any case, an standard ETA will be calculated with a default speed
4151     // of the boat to give an estimation of the route (in particular if GPS
4152     // is off).
4153 
4154     // Display only if option "live ETA" is selected in Settings > Display > General.
4155     if (g_bShowLiveETA)
4156     {
4157 
4158         float realTimeETA;
4159         float boatSpeed;
4160         float boatSpeedDefault = g_defaultBoatSpeed;
4161 
4162         // Calculate Estimate Time to Arrival (ETA) in minutes
4163         // Check before is value not closed to zero (it will make an very big number...)
4164         if (!std::isnan(gSog))
4165         {
4166             boatSpeed = gSog;
4167             if (boatSpeed < 0.5)
4168             {
4169                 realTimeETA = 0;
4170             }
4171             else
4172             {
4173                 realTimeETA = dist / boatSpeed * 60;
4174             }
4175         }
4176         else
4177         {
4178             realTimeETA = 0;
4179         }
4180 
4181         // Add space after distance display
4182         s << " ";
4183         // Display ETA
4184         s << minutesToHoursDays(realTimeETA);
4185 
4186         // In any case, display also an ETA with default speed at 6knts
4187 
4188         s << " [@";
4189         s << wxString::Format(_T("%d"), (int)toUsrSpeed(boatSpeedDefault, -1));
4190         s << wxString::Format(_T("%s"), getUsrSpeedUnit(-1));
4191         s << " ";
4192         s << minutesToHoursDays(dist/boatSpeedDefault*60);
4193         s << "]";
4194 
4195     }
4196     // END OF - LIVE ETA OPTION
4197 
4198     parent_frame->SetStatusText ( s, STAT_FIELD_CURSOR_BRGRNG );
4199 }
4200 
4201 // CUSTOMIZATION - FORMAT MINUTES
4202 // -------------------------------------------------------
4203 // New function to format minutes into a more readable format:
4204 //  * Hours + minutes, or
4205 //  * Days + hours.
minutesToHoursDays(float timeInMinutes)4206 wxString minutesToHoursDays(float timeInMinutes)
4207 {
4208     wxString s;
4209 
4210     if (timeInMinutes == 0)
4211     {
4212         s << "--min";
4213     }
4214 
4215     // Less than 60min, keep time in minutes
4216     else if (timeInMinutes < 60 && timeInMinutes != 0)
4217     {
4218         s << wxString::Format(_T("%d"), (int)timeInMinutes);
4219         s << "min";
4220     }
4221 
4222     // Between 1h and less than 24h, display time in hours, minutes
4223     else if (timeInMinutes >= 60 && timeInMinutes < 24 * 60)
4224     {
4225 
4226         int hours;
4227         int min;
4228         hours = (int)timeInMinutes / 60;
4229         min = (int)timeInMinutes % 60;
4230 
4231         if (min == 0)
4232         {
4233             s << wxString::Format(_T("%d"), hours );
4234             s << "h";
4235         }
4236         else
4237         {
4238             s << wxString::Format(_T("%d"), hours );
4239             s << "h";
4240             s << wxString::Format(_T("%d"), min );
4241             s << "min";
4242         }
4243 
4244     }
4245 
4246     // More than 24h, display time in days, hours
4247     else if (timeInMinutes > 24 * 60)
4248     {
4249 
4250         int days;
4251         int hours;
4252         days = (int)(timeInMinutes / 60) / 24;
4253         hours = (int)(timeInMinutes / 60) % 24;
4254 
4255         if (hours == 0)
4256         {
4257             s << wxString::Format(_T("%d"), days );
4258             s << "d";
4259         }
4260         else
4261         {
4262             s << wxString::Format(_T("%d"), days );
4263             s << "d";
4264             s << wxString::Format(_T("%d"), hours );
4265             s << "h";
4266         }
4267 
4268     }
4269 
4270     return s;
4271 }
4272 
4273 // END OF CUSTOMIZATION - FORMAT MINUTES
4274 // Thanks open source code ;-)
4275 // -------------------------------------------------------
4276 
4277 
GetCursorLatLon(double * lat,double * lon)4278 void ChartCanvas::GetCursorLatLon( double *lat, double *lon )
4279 {
4280     double clat, clon;
4281     GetCanvasPixPoint( mouse_x, mouse_y, clat, clon );
4282     *lat = clat;
4283     *lon = clon;
4284 }
4285 
GetDoubleCanvasPointPix(double rlat,double rlon,wxPoint2DDouble * r)4286 void ChartCanvas::GetDoubleCanvasPointPix( double rlat, double rlon, wxPoint2DDouble *r )
4287 {
4288     return GetDoubleCanvasPointPixVP( GetVP(), rlat, rlon, r );
4289 }
4290 
GetDoubleCanvasPointPixVP(ViewPort & vp,double rlat,double rlon,wxPoint2DDouble * r)4291 void ChartCanvas::GetDoubleCanvasPointPixVP( ViewPort &vp, double rlat, double rlon, wxPoint2DDouble *r )
4292 {
4293     // If the Current Chart is a raster chart, and the
4294     // requested lat/long is within the boundaries of the chart,
4295     // and the VP is not rotated,
4296     // then use the embedded BSB chart georeferencing algorithm
4297     // for greater accuracy
4298     // Additionally, use chart embedded georef if the projection is TMERC
4299     //  i.e. NOT MERCATOR and NOT POLYCONIC
4300 
4301     // If for some reason the chart rejects the request by returning an error,
4302     // then fall back to Viewport Projection estimate from canvas parameters
4303     if( !g_bopengl && m_singleChart && ( m_singleChart->GetChartFamily() == CHART_FAMILY_RASTER )
4304         && ( ( ( fabs( vp.rotation ) < .0001 ) && ( fabs( vp.skew ) < .0001 ) )
4305         || ( ( m_singleChart->GetChartProjectionType() != PROJECTION_MERCATOR )
4306         && ( m_singleChart->GetChartProjectionType() != PROJECTION_TRANSVERSE_MERCATOR )
4307         && ( m_singleChart->GetChartProjectionType() != PROJECTION_POLYCONIC ) ) )
4308         && ( m_singleChart->GetChartProjectionType() == vp.m_projection_type )
4309         && ( m_singleChart->GetChartType() != CHART_TYPE_PLUGIN) )
4310     {
4311         ChartBaseBSB *Cur_BSB_Ch = dynamic_cast<ChartBaseBSB *>( m_singleChart );
4312         //                        bool bInside = G_FloatPtInPolygon ( ( MyFlPoint * ) Cur_BSB_Ch->GetCOVRTableHead ( 0 ),
4313         //                                                            Cur_BSB_Ch->GetCOVRTablenPoints ( 0 ), rlon, rlat );
4314         //                        bInside = true;
4315         //                        if ( bInside )
4316         if( Cur_BSB_Ch ) {
4317             //    This is a Raster chart....
4318             //    If the VP is changing, the raster chart parameters may not yet be setup
4319             //    So do that before accessing the chart's embedded georeferencing
4320             Cur_BSB_Ch->SetVPRasterParms( vp );
4321             double rpixxd, rpixyd;
4322             if( 0 == Cur_BSB_Ch->latlong_to_pix_vp( rlat, rlon, rpixxd, rpixyd, vp ) ) {
4323                 r->m_x = rpixxd;
4324                 r->m_y = rpixyd;
4325                 return;
4326             }
4327         }
4328     }
4329 
4330     //    if needed, use the VPoint scaling estimator,
4331     *r = vp.GetDoublePixFromLL( rlat, rlon );
4332 }
4333 
4334 
4335 // This routine might be deleted and all of the rendering improved
4336 // to have floating point accuracy
GetCanvasPointPix(double rlat,double rlon,wxPoint * r)4337 bool ChartCanvas::GetCanvasPointPix( double rlat, double rlon, wxPoint *r )
4338 {
4339     return GetCanvasPointPixVP( GetVP(), rlat, rlon, r);
4340 }
4341 
GetCanvasPointPixVP(ViewPort & vp,double rlat,double rlon,wxPoint * r)4342 bool ChartCanvas::GetCanvasPointPixVP( ViewPort &vp, double rlat, double rlon, wxPoint *r )
4343 {
4344     wxPoint2DDouble p;
4345     GetDoubleCanvasPointPixVP(vp, rlat, rlon, &p);
4346 
4347     // some projections give nan values when invisible values (other side of world) are requested
4348     // we should stop using integer coordinates or return false here (and test it everywhere)
4349     if(std::isnan(p.m_x)) {
4350         *r = wxPoint(INVALID_COORD, INVALID_COORD);
4351         return false;
4352     }
4353 
4354     *r = wxPoint(wxRound(p.m_x), wxRound(p.m_y));
4355     return true;
4356 }
4357 
4358 
GetCanvasPixPoint(double x,double y,double & lat,double & lon)4359 void ChartCanvas::GetCanvasPixPoint( double x, double y, double &lat, double &lon )
4360 {
4361     // If the Current Chart is a raster chart, and the
4362     // requested x,y is within the boundaries of the chart,
4363     // and the VP is not rotated,
4364     // then use the embedded BSB chart georeferencing algorithm
4365     // for greater accuracy
4366     // Additionally, use chart embedded georef if the projection is TMERC
4367     //  i.e. NOT MERCATOR and NOT POLYCONIC
4368 
4369     // If for some reason the chart rejects the request by returning an error,
4370     // then fall back to Viewport Projection  estimate from canvas parameters
4371     bool bUseVP = true;
4372 
4373     if( !g_bopengl && m_singleChart && ( m_singleChart->GetChartFamily() == CHART_FAMILY_RASTER )
4374         && ( ( ( fabs( GetVP().rotation ) < .0001 ) && ( fabs( GetVP().skew ) < .0001 ) )
4375         || ( ( m_singleChart->GetChartProjectionType() != PROJECTION_MERCATOR )
4376         && ( m_singleChart->GetChartProjectionType() != PROJECTION_TRANSVERSE_MERCATOR )
4377         && ( m_singleChart->GetChartProjectionType() != PROJECTION_POLYCONIC ) ) )
4378         && ( m_singleChart->GetChartProjectionType() == GetVP().m_projection_type )
4379         && ( m_singleChart->GetChartType() != CHART_TYPE_PLUGIN ) )
4380     {
4381         ChartBaseBSB *Cur_BSB_Ch = dynamic_cast<ChartBaseBSB *>( m_singleChart );
4382 
4383         // TODO     maybe need iterative process to validate bInside
4384         //          first pass is mercator, then check chart boundaries
4385 
4386         if( Cur_BSB_Ch ) {
4387             //    This is a Raster chart....
4388             //    If the VP is changing, the raster chart parameters may not yet be setup
4389             //    So do that before accessing the chart's embedded georeferencing
4390             Cur_BSB_Ch->SetVPRasterParms( GetVP() );
4391 
4392             double slat, slon;
4393             if( 0 == Cur_BSB_Ch->vp_pix_to_latlong( GetVP(), x, y, &slat, &slon ) ) {
4394                 lat = slat;
4395 
4396                 if( slon < -180. ) slon += 360.;
4397                 else if( slon > 180. ) slon -= 360.;
4398 
4399                 lon = slon;
4400                 bUseVP = false;
4401             }
4402         }
4403     }
4404 
4405     //    if needed, use the VPoint scaling estimator
4406     if( bUseVP ) {
4407         GetVP().GetLLFromPix( wxPoint2DDouble( x, y ), &lat, &lon );
4408     }
4409 }
4410 
ZoomCanvasSimple(double factor)4411 void ChartCanvas::ZoomCanvasSimple( double factor )
4412 {
4413     DoZoomCanvas( factor, false );
4414     extendedSectorLegs.clear();
4415 }
4416 
4417 
ZoomCanvas(double factor,bool can_zoom_to_cursor,bool stoptimer)4418 void ChartCanvas::ZoomCanvas( double factor, bool can_zoom_to_cursor, bool stoptimer )
4419 {
4420     m_bzooming_to_cursor = can_zoom_to_cursor && g_bEnableZoomToCursor;
4421 
4422     if( g_bsmoothpanzoom ) {
4423         if(StartTimedMovement(stoptimer)) {
4424             m_mustmove += 150; /* for quick presses register as 200 ms duration */
4425             m_zoom_factor = factor;
4426         }
4427 
4428         m_zoom_target =  VPoint.chart_scale / factor;
4429     } else {
4430         if( m_modkeys == wxMOD_ALT )
4431             factor = pow(factor, .15);
4432 
4433         DoZoomCanvas( factor, can_zoom_to_cursor );
4434     }
4435 
4436     extendedSectorLegs.clear();
4437 }
4438 
DoZoomCanvas(double factor,bool can_zoom_to_cursor)4439 void ChartCanvas::DoZoomCanvas( double factor,  bool can_zoom_to_cursor )
4440 {
4441     // possible on startup
4442     if( !ChartData )
4443         return;
4444     if(!m_pCurrentStack)
4445         return;
4446 
4447     if(g_bShowCompassWin){
4448         m_bShowCompassWin = true;
4449         SetShowGPSCompassWindow( true );    // Cancel effects of Ctrl-I
4450     }
4451 
4452     /* TODO: queue the quilted loading code to a background thread
4453        so yield is never called from here, and also rendering is not delayed */
4454 
4455     //    Cannot allow Yield() re-entrancy here
4456     if( m_bzooming ) return;
4457     m_bzooming = true;
4458 
4459     double old_ppm = GetVP().view_scale_ppm;
4460 
4461     //  Capture current cursor position for zoom to cursor
4462     double zlat = m_cursor_lat;
4463     double zlon = m_cursor_lon;
4464 
4465     double proposed_scale_onscreen = GetVP().chart_scale / factor; // GetCanvasScaleFactor() / ( GetVPScale() * factor );
4466     bool b_do_zoom = false;
4467 
4468     if(factor > 1)
4469     {
4470         b_do_zoom = true;
4471 
4472         //double zoom_factor = factor;
4473 
4474         ChartBase *pc = NULL;
4475 
4476         if( !VPoint.b_quilt ) {
4477             pc = m_singleChart;
4478         } else {
4479             int new_db_index = m_pQuilt->AdjustRefOnZoomIn( proposed_scale_onscreen );
4480             if( new_db_index >= 0 )
4481                 pc = ChartData->OpenChartFromDB( new_db_index, FULL_INIT );
4482 
4483             if(m_pCurrentStack)
4484                 m_pCurrentStack->SetCurrentEntryFromdbIndex( new_db_index ); // highlite the correct bar entry
4485         }
4486 
4487         if( pc ) {
4488 //             double target_scale_ppm = GetVPScale() * zoom_factor;
4489 //             proposed_scale_onscreen = GetCanvasScaleFactor() / target_scale_ppm;
4490 
4491             //  Query the chart to determine the appropriate zoom range
4492             double min_allowed_scale = 800;    // Roughly, latitude dependent for mercator charts
4493 
4494             if( proposed_scale_onscreen < min_allowed_scale ) {
4495                 if( min_allowed_scale == GetCanvasScaleFactor() / ( GetVPScale() ) ) {
4496                     m_zoom_factor = 1; /* stop zooming */
4497                     b_do_zoom = false;
4498                 } else
4499                     proposed_scale_onscreen = min_allowed_scale;
4500             }
4501 
4502         }
4503         else {
4504             proposed_scale_onscreen = wxMax( proposed_scale_onscreen, 800.);
4505         }
4506 
4507 
4508     } else if(factor < 1) {
4509         b_do_zoom = true;
4510 
4511         ChartBase *pc = NULL;
4512 
4513         bool b_smallest = false;
4514 
4515         if( !VPoint.b_quilt ) {             // not quilted
4516             pc = m_singleChart;
4517 
4518             if( pc ) {
4519                 //      If m_singleChart is not on the screen, unbound the zoomout
4520                 LLBBox viewbox = VPoint.GetBBox();
4521 //                wxBoundingBox chart_box;
4522                 int current_index = ChartData->FinddbIndex( pc->GetFullPath() );
4523                 double max_allowed_scale;
4524 
4525                 max_allowed_scale = GetCanvasScaleFactor() / m_absolute_min_scale_ppm;
4526 
4527                 //  We can allow essentially unbounded zoomout in single chart mode
4528 //                if( ChartData->GetDBBoundingBox( current_index, &chart_box ) &&
4529 //                    !viewbox.IntersectOut( chart_box ) )
4530 //                    //  Clamp the minimum scale zoom-out to the value specified by the chart
4531 //                    max_allowed_scale = wxMin(max_allowed_scale, 4.0 *
4532 //                                              pc->GetNormalScaleMax( GetCanvasScaleFactor(),
4533 //                                                                     GetCanvasWidth() ) );
4534                 if(proposed_scale_onscreen > max_allowed_scale) {
4535                     m_zoom_factor = 1; /* stop zooming */
4536                     proposed_scale_onscreen = max_allowed_scale;
4537                 }
4538             }
4539 
4540         } else {
4541             int new_db_index = m_pQuilt->AdjustRefOnZoomOut( proposed_scale_onscreen );
4542             if( new_db_index >= 0 ) pc = ChartData->OpenChartFromDB( new_db_index, FULL_INIT );
4543 
4544             if(m_pCurrentStack)
4545                 m_pCurrentStack->SetCurrentEntryFromdbIndex( new_db_index ); // highlite the correct bar entry
4546 
4547             b_smallest = m_pQuilt->IsChartSmallestScale( new_db_index );
4548 
4549             if( b_smallest || (0 == m_pQuilt->GetExtendedStackCount()))
4550                 proposed_scale_onscreen = wxMin(proposed_scale_onscreen,
4551                                                 GetCanvasScaleFactor() / m_absolute_min_scale_ppm);
4552         }
4553 
4554         //set a minimum scale
4555         if( ( GetCanvasScaleFactor() / proposed_scale_onscreen ) < m_absolute_min_scale_ppm )
4556             b_do_zoom = false;
4557     }
4558 
4559     double new_scale = GetVPScale() * (GetVP().chart_scale / proposed_scale_onscreen);
4560     if( b_do_zoom ) {
4561         if( can_zoom_to_cursor && g_bEnableZoomToCursor) {
4562             //  Arrange to combine the zoom and pan into one operation for smoother appearance
4563             SetVPScale( new_scale, false );   // adjust, but deferred refresh
4564 
4565             wxPoint r;
4566             GetCanvasPointPix( zlat, zlon, &r );
4567             PanCanvas( r.x - mouse_x, r.y - mouse_y );  // this will give the Refresh()
4568 
4569             //ClearbFollow();      // update the follow flag
4570         }
4571         else{
4572             if(m_bFollow){      //  Adjust the Viewpoint to keep ownship at the same pixel point on-screen
4573                 double offx, offy;
4574                 toSM(GetVP().clat, GetVP().clon, gLat, gLon, &offx, &offy);
4575 
4576                 double offset_angle = atan2(offy, offx);
4577                 double offset_distance = sqrt((offy * offy) + (offx * offx));
4578                 double chart_angle =  GetVPRotation() ;
4579                 double target_angle = chart_angle - offset_angle;
4580                 double d_east_mod = offset_distance * cos( target_angle );
4581                 double d_north_mod = offset_distance * sin( target_angle );
4582 
4583                 m_OSoffsetx = d_east_mod * old_ppm;
4584                 m_OSoffsety = -d_north_mod * old_ppm;
4585 
4586                 double d_east_mods = d_east_mod / new_scale;
4587                 double d_north_mods = d_north_mod / new_scale;
4588 
4589                 double nlat, nlon;
4590                 fromSM( d_east_mods, d_north_mods, gLat, gLon, &nlat, &nlon );
4591                 SetViewPoint( nlat, nlon, new_scale, GetVP().skew, GetVP().rotation);
4592                 DoCanvasUpdate();
4593             }
4594             else
4595                 SetVPScale( new_scale );
4596         }
4597     }
4598 
4599     m_bzooming = false;
4600 
4601 }
4602 
RotateCanvas(double dir)4603 void ChartCanvas::RotateCanvas( double dir )
4604 {
4605     SetUpMode( NORTH_UP_MODE);
4606 
4607     if(g_bsmoothpanzoom) {
4608         if(StartTimedMovement()) {
4609             m_mustmove += 150; /* for quick presses register as 200 ms duration */
4610             m_rotation_speed = dir*60;
4611         }
4612     } else {
4613         double speed = dir*10;
4614         if( m_modkeys == wxMOD_ALT)
4615             speed /= 20;
4616         DoRotateCanvas(VPoint.rotation + PI/180 * speed);
4617     }
4618 }
4619 
DoRotateCanvas(double rotation)4620 void ChartCanvas::DoRotateCanvas( double rotation )
4621 {
4622     while(rotation < 0) rotation += 2*PI;
4623     while(rotation > 2*PI) rotation -= 2*PI;
4624 
4625     if(rotation == VPoint.rotation || std::isnan(rotation))
4626         return;
4627 
4628     SetVPRotation( rotation );
4629     parent_frame->UpdateRotationState( VPoint.rotation);
4630 }
4631 
DoTiltCanvas(double tilt)4632 void ChartCanvas::DoTiltCanvas( double tilt )
4633 {
4634     while(tilt < 0) tilt = 0;
4635     while(tilt > .95) tilt = .95;
4636 
4637     if(tilt == VPoint.tilt || std::isnan(tilt))
4638         return;
4639 
4640     VPoint.tilt = tilt;
4641     Refresh( false );
4642 }
4643 
TogglebFollow(void)4644 void ChartCanvas::TogglebFollow( void )
4645 {
4646     if( !m_bFollow )
4647         SetbFollow();
4648     else
4649         ClearbFollow();
4650 }
4651 
ClearbFollow(void)4652 void ChartCanvas::ClearbFollow( void )
4653 {
4654     m_bFollow = false;      // update the follow flag
4655 
4656 
4657     if( m_toolBar )
4658         m_toolBar->GetToolbar()->ToggleTool( ID_FOLLOW, false );
4659     parent_frame->SetMenubarItemState( ID_MENU_NAV_FOLLOW, false );
4660 
4661     UpdateFollowButtonState();
4662 
4663     DoCanvasUpdate();
4664     ReloadVP();
4665     parent_frame->SetChartUpdatePeriod( );
4666 
4667 }
4668 
SetbFollow(void)4669 void ChartCanvas::SetbFollow( void )
4670 {
4671     JumpToPosition(gLat, gLon, GetVPScale());
4672     m_bFollow = true;
4673 
4674     if( m_toolBar )
4675         m_toolBar->GetToolbar()->ToggleTool( ID_FOLLOW, true );
4676     parent_frame->SetMenubarItemState( ID_MENU_NAV_FOLLOW, true );
4677 
4678     UpdateFollowButtonState();
4679 
4680     // Is the OWNSHIP on-screen?
4681     // If not, then reset the OWNSHIP offset to 0 (center screen)
4682     if( (fabs(m_OSoffsetx) > VPoint.pix_width / 2) || (fabs(m_OSoffsety) > VPoint.pix_height / 2) ){
4683         m_OSoffsetx = 0;
4684         m_OSoffsety = 0;
4685     }
4686 
4687     DoCanvasUpdate();
4688     ReloadVP();
4689     parent_frame->SetChartUpdatePeriod( );
4690 }
4691 
UpdateFollowButtonState(void)4692 void ChartCanvas::UpdateFollowButtonState( void )
4693 {
4694    if(m_muiBar){
4695         if(!m_bFollow)
4696             m_muiBar->SetFollowButtonState( 0 );
4697         else{
4698             if(m_bLookAhead)
4699                 m_muiBar->SetFollowButtonState( 2 );
4700             else
4701                 m_muiBar->SetFollowButtonState( 1 );
4702         }
4703    }
4704 
4705 #ifdef __OCPN__ANDROID__
4706    if(!m_bFollow)
4707       androidSetFollowTool(0);
4708    else
4709    {
4710         if(m_bLookAhead)
4711             androidSetFollowTool(2);
4712         else
4713             androidSetFollowTool(1);
4714    }
4715 #endif
4716 
4717 }
4718 
JumpToPosition(double lat,double lon,double scale_ppm)4719 void ChartCanvas::JumpToPosition( double lat, double lon, double scale_ppm )
4720 {
4721     if (lon > 180.0)
4722         lon -= 360.0;
4723     m_vLat = lat;
4724     m_vLon = lon;
4725     StopMovement();
4726     m_bFollow = false;
4727 
4728     if( !GetQuiltMode() ) {
4729         double skew = 0;
4730         if(m_singleChart)
4731             skew = m_singleChart->GetChartSkew() * PI / 180.;
4732         SetViewPoint( lat, lon, scale_ppm, skew, GetVPRotation() );
4733     } else {
4734         if (scale_ppm != GetVPScale()) {
4735             // XXX should be done in SetViewPoint
4736             VPoint.chart_scale = m_canvas_scale_factor / ( scale_ppm );
4737             AdjustQuiltRefChart();
4738         }
4739         SetViewPoint( lat, lon, scale_ppm, 0, GetVPRotation() );
4740     }
4741 
4742     ReloadVP();
4743 
4744     if( m_toolBar )
4745         m_toolBar->GetToolbar()->ToggleTool( ID_FOLLOW, false );
4746 
4747     UpdateFollowButtonState();
4748 
4749     //TODO
4750 //    if( g_pi_manager ) {
4751 //        g_pi_manager->SendViewPortToRequestingPlugIns( cc1->GetVP() );
4752 //    }
4753 }
4754 
4755 
PanCanvas(double dx,double dy)4756 bool ChartCanvas::PanCanvas( double dx, double dy )
4757 {
4758     if( !ChartData )
4759         return false;
4760 
4761     extendedSectorLegs.clear();
4762 
4763     //double clat = VPoint.clat, clon = VPoint.clon;
4764     double dlat, dlon;
4765     wxPoint2DDouble p(VPoint.pix_width / 2.0, VPoint.pix_height / 2.0);
4766 
4767     int iters = 0;
4768     for(;;) {
4769         GetCanvasPixPoint( p.m_x + trunc(dx), p.m_y + trunc(dy), dlat, dlon );
4770 
4771         if(iters++ > 5)
4772             return false;
4773         if(!std::isnan(dlat))
4774             break;
4775 
4776         dx *= .5, dy *= .5;
4777         if(fabs(dx) < 1 && fabs(dy) < 1)
4778             return false;
4779     }
4780 
4781     // avoid overshooting the poles
4782     if(dlat > 90)
4783         dlat = 90;
4784     else if(dlat < -90)
4785         dlat = -90;
4786 
4787     if( dlon > 360. ) dlon -= 360.;
4788     if( dlon < -360. ) dlon += 360.;
4789 
4790     //    This should not really be necessary, but round-trip georef on some charts is not perfect,
4791     //    So we can get creep on repeated unidimensional pans, and corrupt chart cacheing.......
4792 
4793     //    But this only works on north-up projections
4794     // TODO: can we remove this now?
4795 //     if( ( ( fabs( GetVP().skew ) < .001 ) ) && ( fabs( GetVP().rotation ) < .001 ) ) {
4796 //
4797 //         if( dx == 0 ) dlon = clon;
4798 //         if( dy == 0 ) dlat = clat;
4799 //     }
4800 
4801     int cur_ref_dbIndex = m_pQuilt->GetRefChartdbIndex();
4802 
4803     SetViewPoint( dlat, dlon, VPoint.view_scale_ppm, VPoint.skew, VPoint.rotation );
4804 
4805     if( VPoint.b_quilt) {
4806         int new_ref_dbIndex = m_pQuilt->GetRefChartdbIndex();
4807         if( ( new_ref_dbIndex != cur_ref_dbIndex ) && ( new_ref_dbIndex != -1 ) ) {
4808             //Tweak the scale slightly for a new ref chart
4809             ChartBase *pc = ChartData->OpenChartFromDB( new_ref_dbIndex, FULL_INIT );
4810             if( pc ) {
4811                 double tweak_scale_ppm = pc->GetNearestPreferredScalePPM( VPoint.view_scale_ppm );
4812                 SetVPScale( tweak_scale_ppm );
4813             }
4814         }
4815     }
4816 
4817     //  Turn off bFollow only if the ownship has left the screen
4818     double offx, offy;
4819     toSM(dlat, dlon, gLat, gLon, &offx, &offy);
4820 
4821     double offset_angle = atan2(offy, offx);
4822     double offset_distance = sqrt((offy * offy) + (offx * offx));
4823     double chart_angle =  GetVPRotation() ;
4824     double target_angle = chart_angle - offset_angle;
4825     double d_east_mod = offset_distance * cos( target_angle );
4826     double d_north_mod = offset_distance * sin( target_angle );
4827 
4828     m_OSoffsetx = d_east_mod * VPoint.view_scale_ppm;
4829     m_OSoffsety = -d_north_mod * VPoint.view_scale_ppm;
4830 
4831  //   m_OSoffsetx = offx * VPoint.view_scale_ppm;
4832  //   m_OSoffsety = offy * VPoint.view_scale_ppm;
4833 
4834     if( m_bFollow && ((fabs(m_OSoffsetx) > VPoint.pix_width / 2) || (fabs(m_OSoffsety) > VPoint.pix_height / 2)) ){
4835         m_bFollow = false;      // update the follow flag
4836         if( m_toolBar )
4837             m_toolBar->GetToolbar()->ToggleTool( ID_FOLLOW, false );
4838 
4839         UpdateFollowButtonState();
4840     }
4841 
4842     Refresh( false );
4843 
4844     pCurTrackTimer->Start( m_curtrack_timer_msec, wxTIMER_ONE_SHOT );
4845 
4846     return true;
4847 }
4848 
ReloadVP(bool b_adjust)4849 void ChartCanvas::ReloadVP( bool b_adjust )
4850 {
4851     if( g_brightness_init ) SetScreenBrightness( g_nbrightness );
4852 
4853     LoadVP( VPoint, b_adjust );
4854 }
4855 
LoadVP(ViewPort & vp,bool b_adjust)4856 void ChartCanvas::LoadVP( ViewPort &vp, bool b_adjust )
4857 {
4858 #ifdef ocpnUSE_GL
4859     if( g_bopengl && m_glcc ) {
4860         m_glcc->Invalidate();
4861         if( m_glcc->GetSize() != GetSize() ) {
4862             m_glcc->SetSize( GetSize() );
4863         }
4864     }
4865     else
4866 #endif
4867     {
4868         m_cache_vp.Invalidate();
4869         m_bm_cache_vp.Invalidate();
4870     }
4871 
4872     VPoint.Invalidate();
4873 
4874     if( m_pQuilt ) m_pQuilt->Invalidate();
4875 
4876     //  Make sure that the Selected Group is sensible...
4877 //    if( m_groupIndex > (int) g_pGroupArray->GetCount() )
4878 //        m_groupIndex = 0;
4879 //    if( !CheckGroup( m_groupIndex ) )
4880 //        m_groupIndex = 0;
4881 
4882     SetViewPoint( vp.clat, vp.clon, vp.view_scale_ppm, vp.skew, vp.rotation, vp.m_projection_type, b_adjust );
4883 
4884 }
4885 
SetQuiltRefChart(int dbIndex)4886 void ChartCanvas::SetQuiltRefChart( int dbIndex )
4887 {
4888     m_pQuilt->SetReferenceChart( dbIndex );
4889     VPoint.Invalidate();
4890     m_pQuilt->Invalidate();
4891 }
4892 
GetBestStartScale(int dbi_hint,const ViewPort & vp)4893 double ChartCanvas::GetBestStartScale(int dbi_hint, const ViewPort &vp)
4894 {
4895     if(m_pQuilt)
4896     return m_pQuilt->GetBestStartScale(dbi_hint, vp);
4897     else
4898         return vp.view_scale_ppm;
4899 }
4900 
4901 
4902 //      Verify and adjust the current reference chart,
4903 //      so that it will not lead to excessive overzoom or underzoom onscreen
AdjustQuiltRefChart()4904 int ChartCanvas::AdjustQuiltRefChart()
4905 {
4906     int ret = -1;
4907     if(m_pQuilt){
4908         wxASSERT(ChartData);
4909         ChartBase *pc = ChartData->OpenChartFromDB( m_pQuilt->GetRefChartdbIndex(), FULL_INIT );
4910         if( pc ) {
4911             double min_ref_scale = pc->GetNormalScaleMin( m_canvas_scale_factor, false );
4912             double max_ref_scale = pc->GetNormalScaleMax( m_canvas_scale_factor, m_canvas_width );
4913 
4914             if( VPoint.chart_scale < min_ref_scale )  {
4915                 ret = m_pQuilt->AdjustRefOnZoomIn( VPoint.chart_scale );
4916             }
4917             else if( VPoint.chart_scale > max_ref_scale )  {
4918                 ret = m_pQuilt->AdjustRefOnZoomOut( VPoint.chart_scale );
4919             }
4920             else {
4921                 bool brender_ok = IsChartLargeEnoughToRender( pc, VPoint );
4922 
4923                 int ref_family = pc->GetChartFamily();
4924 
4925                 if( !brender_ok ) {
4926                     unsigned int target_stack_index = 0;
4927                     int target_stack_index_check = m_pQuilt->GetExtendedStackIndexArray()[m_pQuilt->GetRefChartdbIndex()]; // Lookup
4928 
4929                     if( wxNOT_FOUND != target_stack_index_check )
4930                         target_stack_index = target_stack_index_check;
4931 
4932                     int extended_array_count = m_pQuilt->GetExtendedStackIndexArray().size();
4933                     while( ( !brender_ok )  && ( (int)target_stack_index < ( extended_array_count - 1 ) ) ) {
4934                         target_stack_index++;
4935                         int test_db_index = m_pQuilt->GetExtendedStackIndexArray()[target_stack_index];
4936 
4937                         if( ( ref_family == ChartData->GetDBChartFamily( test_db_index ) )
4938                             && IsChartQuiltableRef( test_db_index ) ) {
4939                         //    open the target, and check the min_scale
4940                             ChartBase *ptest_chart = ChartData->OpenChartFromDB( test_db_index, FULL_INIT );
4941                             if( ptest_chart ){
4942                                 brender_ok = IsChartLargeEnoughToRender( ptest_chart, VPoint );
4943                             }
4944                         }
4945                     }
4946 
4947                     if(brender_ok){             // found a better reference chart
4948                         int new_db_index = m_pQuilt->GetExtendedStackIndexArray()[target_stack_index];
4949                         if( ( ref_family == ChartData->GetDBChartFamily( new_db_index ) )
4950                             && IsChartQuiltableRef( new_db_index ) ) {
4951                             m_pQuilt->SetReferenceChart( new_db_index );
4952                             ret = new_db_index;
4953                         }
4954                         else
4955                             ret =m_pQuilt->GetRefChartdbIndex();
4956                     }
4957                     else
4958                         ret = m_pQuilt->GetRefChartdbIndex();
4959 
4960                 }
4961                 else
4962                     ret = m_pQuilt->GetRefChartdbIndex();
4963             }
4964         }
4965         else
4966             ret = -1;
4967     }
4968 
4969     return ret;
4970 }
4971 
4972 
UpdateCanvasOnGroupChange(void)4973 void ChartCanvas::UpdateCanvasOnGroupChange( void )
4974 {
4975     delete m_pCurrentStack;
4976     m_pCurrentStack = NULL;
4977     m_pCurrentStack = new ChartStack;
4978     wxASSERT(ChartData);
4979     ChartData->BuildChartStack( m_pCurrentStack, VPoint.clat, VPoint.clon, m_groupIndex );
4980 
4981     if( m_pQuilt ) {
4982     m_pQuilt->Compose( VPoint );
4983     SetFocus();
4984 }
4985 }
4986 
SetViewPointByCorners(double latSW,double lonSW,double latNE,double lonNE)4987 bool ChartCanvas::SetViewPointByCorners( double latSW, double lonSW, double latNE, double lonNE )
4988 {
4989     // Center Point
4990     double latc = (latSW + latNE)/2.0;
4991     double lonc = (lonSW + lonNE)/2.0;
4992 
4993     // Get scale in ppm (latitude)
4994     double ne_easting, ne_northing;
4995     toSM( latNE, lonNE, latc, lonc, &ne_easting, &ne_northing );
4996 
4997     double sw_easting, sw_northing;
4998     toSM( latSW, lonSW, latc, lonc, &sw_easting, &sw_northing );
4999 
5000     double scale_ppm = VPoint.pix_height / fabs(ne_northing - sw_northing);
5001 
5002     return SetViewPoint( latc, lonc, scale_ppm, VPoint.skew, VPoint.rotation );
5003 }
5004 
SetVPScale(double scale,bool refresh)5005 bool ChartCanvas::SetVPScale( double scale, bool refresh )
5006 {
5007     return SetViewPoint( VPoint.clat, VPoint.clon, scale, VPoint.skew, VPoint.rotation,
5008                          VPoint.m_projection_type, true, refresh );
5009 }
5010 
SetVPProjection(int projection)5011 bool ChartCanvas::SetVPProjection( int projection )
5012 {
5013     if(!g_bopengl) // alternative projections require opengl
5014         return false;
5015 
5016     // the view scale varies depending on geographic location and projection
5017     // rescale to keep the relative scale on the screen the same
5018     double prev_true_scale_ppm = m_true_scale_ppm;
5019     return SetViewPoint( VPoint.clat, VPoint.clon, VPoint.view_scale_ppm, VPoint.skew, VPoint.rotation, projection ) &&
5020         SetVPScale(wxMax(VPoint.view_scale_ppm * prev_true_scale_ppm / m_true_scale_ppm, m_absolute_min_scale_ppm));
5021 }
5022 
SetViewPoint(double lat,double lon)5023 bool ChartCanvas::SetViewPoint( double lat, double lon )
5024 {
5025     return SetViewPoint( lat, lon, VPoint.view_scale_ppm, VPoint.skew, VPoint.rotation );
5026 }
5027 
SetViewPoint(double lat,double lon,double scale_ppm,double skew,double rotation,int projection,bool b_adjust,bool b_refresh)5028 bool ChartCanvas::SetViewPoint( double lat, double lon, double scale_ppm, double skew,
5029                                 double rotation, int projection, bool b_adjust, bool b_refresh )
5030 {
5031     bool b_ret = false;
5032 
5033     if(skew > PI) /* so our difference tests work, put in range of +-Pi */
5034         skew -= 2*PI;
5035 
5036     //  Any sensible change?
5037     if (VPoint.IsValid()) {
5038         if( ( fabs( VPoint.view_scale_ppm - scale_ppm )/ scale_ppm < 1e-5 )
5039             && ( fabs( VPoint.skew - skew ) < 1e-9 )
5040             && ( fabs( VPoint.rotation - rotation ) < 1e-9 )
5041             && ( fabs( VPoint.clat - lat ) < 1e-9 )
5042             && ( fabs( VPoint.clon - lon ) < 1e-9 )
5043             && (VPoint.m_projection_type == projection || projection == PROJECTION_UNKNOWN) )
5044                 return false;
5045     }
5046 
5047     if(VPoint.m_projection_type != projection)
5048         VPoint.InvalidateTransformCache(); // invalidate
5049 
5050     //    Take a local copy of the last viewport
5051     ViewPort last_vp = VPoint;
5052 
5053     VPoint.skew = skew;
5054     VPoint.clat = lat;
5055     VPoint.clon = lon;
5056     VPoint.view_scale_ppm = scale_ppm;
5057     if(projection != PROJECTION_UNKNOWN)
5058         VPoint.SetProjectionType(projection);
5059     else
5060         if(VPoint.m_projection_type == PROJECTION_UNKNOWN)
5061             VPoint.SetProjectionType(PROJECTION_MERCATOR);
5062 
5063     // don't allow latitude above 88 for mercator (90 is infinity)
5064     if(VPoint.m_projection_type == PROJECTION_MERCATOR ||
5065        VPoint.m_projection_type == PROJECTION_TRANSVERSE_MERCATOR) {
5066         if(VPoint.clat > 89.5) VPoint.clat = 89.5;
5067         else if(VPoint.clat < -89.5) VPoint.clat = -89.5;
5068     }
5069 
5070     // don't zoom out too far for transverse mercator polyconic until we resolve issues
5071     if(VPoint.m_projection_type == PROJECTION_POLYCONIC ||
5072        VPoint.m_projection_type == PROJECTION_TRANSVERSE_MERCATOR)
5073         VPoint.view_scale_ppm = wxMax(VPoint.view_scale_ppm, 2e-4);
5074 
5075     SetVPRotation( rotation );
5076 
5077     if(!g_bopengl) // tilt is not possible without opengl
5078         VPoint.tilt = 0;
5079 
5080     if( ( VPoint.pix_width <= 0 ) || ( VPoint.pix_height <= 0 ) )    // Canvas parameters not yet set
5081         return false;
5082 
5083     bool bwasValid = VPoint.IsValid();
5084     VPoint.Validate();                     // Mark this ViewPoint as OK
5085 
5086     //  Has the Viewport scale changed?  If so, invalidate the vp
5087     if( last_vp.view_scale_ppm != scale_ppm ) {
5088         m_cache_vp.Invalidate();
5089         InvalidateGL();
5090     }
5091 
5092     //  A preliminary value, may be tweaked below
5093     VPoint.chart_scale = m_canvas_scale_factor / ( scale_ppm );
5094 
5095     // recompute cursor position
5096     // and send to interested plugins if the mouse is actually in this window
5097 
5098     const wxPoint pt = wxGetMousePosition();
5099     int mouseX = pt.x - GetScreenPosition().x;
5100     int mouseY = pt.y - GetScreenPosition().y;
5101     if( (mouseX > 0) && (mouseX < VPoint.pix_width) && (mouseY > 0) && (mouseY < VPoint.pix_height)){
5102         double lat, lon;
5103         GetCanvasPixPoint( mouseX, mouseY, lat, lon );
5104         m_cursor_lat = lat;
5105         m_cursor_lon = lon;
5106         if(g_pi_manager)
5107             g_pi_manager->SendCursorLatLonToAllPlugIns( lat,lon );
5108     }
5109 
5110     if( !VPoint.b_quilt && m_singleChart ) {
5111 
5112         VPoint.SetBoxes();
5113 
5114         //  Allow the chart to adjust the new ViewPort for performance optimization
5115         //  This will normally be only a fractional (i.e.sub-pixel) adjustment...
5116         if( b_adjust ) m_singleChart->AdjustVP( last_vp, VPoint );
5117 
5118         // If there is a sensible change in the chart render, refresh the whole screen
5119         if( ( !m_cache_vp.IsValid() ) || ( m_cache_vp.view_scale_ppm != VPoint.view_scale_ppm ) ) {
5120             Refresh( false );
5121             b_ret = true;
5122         } else {
5123             wxPoint cp_last, cp_this;
5124             GetCanvasPointPix( m_cache_vp.clat, m_cache_vp.clon, &cp_last );
5125             GetCanvasPointPix( VPoint.clat, VPoint.clon, &cp_this );
5126 
5127             if( cp_last != cp_this ) {
5128                 Refresh( false );
5129                 b_ret = true;
5130             }
5131         }
5132         //  Create the stack
5133         if( m_pCurrentStack ) {
5134             assert(ChartData != 0);
5135             int current_db_index;
5136             current_db_index = m_pCurrentStack->GetCurrentEntrydbIndex();       // capture the current
5137 
5138             ChartData->BuildChartStack( m_pCurrentStack, lat, lon, current_db_index, m_groupIndex);
5139             m_pCurrentStack->SetCurrentEntryFromdbIndex( current_db_index );
5140         }
5141 
5142         if(!g_bopengl)
5143             VPoint.b_MercatorProjectionOverride = false;
5144     }
5145 
5146     //  Handle the quilted case
5147     if( VPoint.b_quilt) {
5148 
5149         if( last_vp.view_scale_ppm != scale_ppm ) m_pQuilt->InvalidateAllQuiltPatchs();
5150 
5151         //  Create the quilt
5152         if( ChartData /*&& ChartData->IsValid()*/ ) {
5153             if( !m_pCurrentStack ) return false;
5154 
5155             int current_db_index;
5156             current_db_index = m_pCurrentStack->GetCurrentEntrydbIndex();       // capture the current
5157 
5158             ChartData->BuildChartStack( m_pCurrentStack, lat, lon, m_groupIndex );
5159             m_pCurrentStack->SetCurrentEntryFromdbIndex( current_db_index );
5160 
5161             //   Check to see if the current quilt reference chart is in the new stack
5162             int current_ref_stack_index = -1;
5163             for( int i = 0; i < m_pCurrentStack->nEntry; i++ ) {
5164                 if( m_pQuilt->GetRefChartdbIndex() == m_pCurrentStack->GetDBIndex( i ) ) current_ref_stack_index =
5165                         i;
5166             }
5167 
5168             if( g_bFullScreenQuilt ) {
5169                 current_ref_stack_index = m_pQuilt->GetRefChartdbIndex();
5170             }
5171 
5172             //We might need a new Reference Chart
5173             bool b_needNewRef = false;
5174 
5175             //    If the new stack does not contain the current ref chart....
5176             if( ( -1 == current_ref_stack_index ) && ( m_pQuilt->GetRefChartdbIndex() >= 0 ) )
5177                 b_needNewRef = true;
5178 
5179             // Would the current Ref Chart be excessively underzoomed?
5180             // We need to check this here to be sure, since we cannot know where the reference chart was assigned.
5181             // For instance, the reference chart may have been selected from the config file,
5182             // or from a long jump with a chart family switch implicit.
5183             // Anyway, we check to be sure....
5184             bool renderable = true;
5185             ChartBase* referenceChart = ChartData->OpenChartFromDB( m_pQuilt->GetRefChartdbIndex(), FULL_INIT );
5186             if( referenceChart ) {
5187                 double chartMaxScale = referenceChart->GetNormalScaleMax( GetCanvasScaleFactor(), GetCanvasWidth() );
5188                 renderable = chartMaxScale * 64 >= VPoint.chart_scale;
5189             }
5190             if( !renderable )
5191                 b_needNewRef = true;
5192 
5193 
5194 
5195             //    Need new refchart?
5196             if( b_needNewRef ) {
5197                 const ChartTableEntry &cte_ref = ChartData->GetChartTableEntry(
5198                                                      m_pQuilt->GetRefChartdbIndex() );
5199                 int target_scale = cte_ref.GetScale();
5200                 int target_type = cte_ref.GetChartType();
5201                 int candidate_stack_index;
5202 
5203                 //    reset the ref chart in a way that does not lead to excessive underzoom, for performance reasons
5204                 //    Try to find a chart that is the same type, and has a scale of just smaller than the current ref chart
5205 
5206                 candidate_stack_index = 0;
5207                 while( candidate_stack_index <= m_pCurrentStack->nEntry - 1 ) {
5208                     const ChartTableEntry &cte_candidate = ChartData->GetChartTableEntry(
5209                         m_pCurrentStack->GetDBIndex( candidate_stack_index ) );
5210                     int candidate_scale = cte_candidate.GetScale();
5211                     int candidate_type = cte_candidate.GetChartType();
5212 
5213                     if( ( candidate_scale >= target_scale ) && ( candidate_type == target_type ) ){
5214                         bool renderable = true;
5215                         ChartBase* tentative_referenceChart = ChartData->OpenChartFromDB( m_pCurrentStack->GetDBIndex( candidate_stack_index ),
5216                                                                                 FULL_INIT );
5217                         if( tentative_referenceChart ) {
5218                             double chartMaxScale = tentative_referenceChart->GetNormalScaleMax( GetCanvasScaleFactor(), GetCanvasWidth() );
5219                             renderable = chartMaxScale*1.5 > VPoint.chart_scale;
5220                         }
5221 
5222                         if(renderable)
5223                             break;
5224                     }
5225 
5226                     candidate_stack_index++;
5227                 }
5228 
5229                 //    If that did not work, look for a chart of just larger scale and same type
5230                 if( candidate_stack_index >= m_pCurrentStack->nEntry ) {
5231                     candidate_stack_index = m_pCurrentStack->nEntry - 1;
5232                     while( candidate_stack_index >= 0 ) {
5233                         int idx = m_pCurrentStack->GetDBIndex( candidate_stack_index );
5234                         if ( idx >= 0) {
5235                             const ChartTableEntry &cte_candidate = ChartData->GetChartTableEntry(idx);
5236                             int candidate_scale = cte_candidate.GetScale();
5237                             int candidate_type = cte_candidate.GetChartType();
5238 
5239                             if( ( candidate_scale <= target_scale ) && ( candidate_type == target_type ) )
5240                                 break;
5241                         }
5242                         candidate_stack_index--;
5243                     }
5244                 }
5245 
5246                 // and if that did not work, chose stack entry 0
5247                 if( ( candidate_stack_index >= m_pCurrentStack->nEntry )
5248                         || ( candidate_stack_index < 0 ) ) candidate_stack_index = 0;
5249 
5250                 int new_ref_index = m_pCurrentStack->GetDBIndex( candidate_stack_index );
5251 
5252                 m_pQuilt->SetReferenceChart( new_ref_index ); //maybe???
5253 
5254             }
5255 
5256             if(!g_bopengl) {
5257                 // Preset the VPoint projection type to match what the quilt projection type will be
5258                 int ref_db_index = m_pQuilt->GetRefChartdbIndex(), proj;
5259 
5260                 // Always keep the default Mercator projection if the reference chart is
5261                 // not in the PatchList or the scale is too small for it to render.
5262 
5263                 bool renderable = true;
5264                 ChartBase* referenceChart = ChartData->OpenChartFromDB( ref_db_index, FULL_INIT );
5265                 if( referenceChart ) {
5266                     double chartMaxScale = referenceChart->GetNormalScaleMax( GetCanvasScaleFactor(), GetCanvasWidth() );
5267                     renderable = chartMaxScale*1.5 > VPoint.chart_scale;
5268                     proj = ChartData->GetDBChartProj( ref_db_index );
5269                 } else
5270                     proj = PROJECTION_MERCATOR;
5271 
5272                 VPoint.b_MercatorProjectionOverride = ( m_pQuilt->GetnCharts() == 0 || !renderable );
5273 
5274                 if( VPoint.b_MercatorProjectionOverride )
5275                     proj = PROJECTION_MERCATOR;
5276 
5277                 VPoint.SetProjectionType( proj );
5278             }
5279 
5280             VPoint.SetBoxes();
5281 
5282             //    If this quilt will be a perceptible delta from the existing quilt, then refresh the entire screen
5283             if( m_pQuilt->IsQuiltDelta( VPoint ) ) {
5284                 //  Allow the quilt to adjust the new ViewPort for performance optimization
5285                 //  This will normally be only a fractional (i.e. sub-pixel) adjustment...
5286                 if( b_adjust ) m_pQuilt->AdjustQuiltVP( last_vp, VPoint );
5287 
5288 //                ChartData->ClearCacheInUseFlags();
5289 //                unsigned long hash1 = m_pQuilt->GetXStackHash();
5290 
5291 //                wxStopWatch sw;
5292 
5293 #ifdef __OCPN__ANDROID__
5294                 // This is an optimization for panning on touch screen systems.
5295                 //  The quilt composition is deferred until the OnPaint() message gets finally
5296                 //  removed and processed from the message queue.
5297                 // Takes advantage of the fact that touch-screen pan gestures are usually short in distance,
5298                 //  so not requiring a full quilt rebuild until the pan gesture is complete.
5299                 if( (last_vp.view_scale_ppm != scale_ppm) || !bwasValid ){
5300  //                   qDebug() << "Force compose";
5301                 m_pQuilt->Compose( VPoint );
5302                 }
5303                 else{
5304                     m_pQuilt->Invalidate();
5305                 }
5306 #else
5307                 m_pQuilt->Compose( VPoint );
5308 #endif
5309 
5310 //                printf("comp time %ld\n", sw.Time());
5311 
5312                 //      If the extended chart stack has changed, invalidate any cached render bitmap
5313 //                if(m_pQuilt->GetXStackHash() != hash1) {
5314 //                    m_bm_cache_vp.Invalidate();
5315 //                    InvalidateGL();
5316 //                }
5317 
5318                 ChartData->PurgeCacheUnusedCharts( 0.7 );
5319 
5320                 if(b_refresh)
5321                     Refresh( false );
5322 
5323                 b_ret = true;
5324             }
5325         }
5326 
5327         VPoint.skew = 0.;  // Quilting supports 0 Skew
5328     } else
5329         if(!g_bopengl) {
5330             OcpnProjType projection = PROJECTION_UNKNOWN;
5331             if(m_singleChart) // viewport projection must match chart projection without opengl
5332                 projection = m_singleChart->GetChartProjectionType();
5333             if(projection == PROJECTION_UNKNOWN)
5334                 projection = PROJECTION_MERCATOR;
5335             VPoint.SetProjectionType(projection);
5336         }
5337 
5338     //  Has the Viewport projection changed?  If so, invalidate the vp
5339     if( last_vp.m_projection_type != VPoint.m_projection_type ) {
5340         m_cache_vp.Invalidate();
5341         InvalidateGL();
5342     }
5343 
5344     UpdateCanvasControlBar();           // Refresh the Piano
5345 
5346     VPoint.chart_scale = 1.0;           // fallback default value
5347 
5348     if( !VPoint.GetBBox().GetValid() ) VPoint.SetBoxes();
5349 
5350     if( VPoint.GetBBox().GetValid() ) {
5351 
5352         //      Update the viewpoint reference scale
5353         if( m_singleChart )
5354             VPoint.ref_scale = m_singleChart->GetNativeScale();
5355         else
5356             VPoint.ref_scale = m_pQuilt->GetRefNativeScale();
5357 
5358         //    Calculate the on-screen displayed actual scale
5359         //    by a simple traverse northward from the center point
5360         //    of roughly one eighth of the canvas height
5361         wxPoint2DDouble r, r1;
5362 
5363         double delta_check = (VPoint.pix_height / VPoint.view_scale_ppm) / (1852. * 60);
5364         delta_check /= 8.;
5365 
5366         double check_point = wxMin(89., VPoint.clat);
5367 
5368         while((delta_check + check_point) > 90.)
5369             delta_check /= 2.;
5370 
5371         double rhumbDist;
5372         DistanceBearingMercator( check_point, VPoint.clon,
5373                                  check_point + delta_check, VPoint.clon,
5374                                      0, &rhumbDist );
5375 
5376         GetDoubleCanvasPointPix( check_point, VPoint.clon, &r1 );
5377         GetDoubleCanvasPointPix( check_point + delta_check, VPoint.clon, &r );
5378         double delta_p = sqrt( ((r1.m_y - r.m_y) * (r1.m_y - r.m_y)) + ((r1.m_x - r.m_x) * (r1.m_x - r.m_x)) );
5379 
5380         m_true_scale_ppm = delta_p / (rhumbDist * 1852);
5381 
5382         //        A fall back in case of very high zoom-out, giving delta_y == 0
5383         //        which can probably only happen with vector charts
5384         if( 0.0 == m_true_scale_ppm )
5385             m_true_scale_ppm = scale_ppm;
5386 
5387         //        Another fallback, for highly zoomed out charts
5388         //        This adjustment makes the displayed TrueScale correspond to the
5389         //        same algorithm used to calculate the chart zoom-out limit for ChartDummy.
5390         if( scale_ppm < 1e-4 )
5391             m_true_scale_ppm = scale_ppm;
5392 
5393         if( m_true_scale_ppm )
5394             VPoint.chart_scale = m_canvas_scale_factor / ( m_true_scale_ppm );
5395         else
5396             VPoint.chart_scale = 1.0;
5397 
5398 
5399         // Create a nice renderable string
5400         double round_factor = 1000.;
5401         if(VPoint.chart_scale <= 1000.)
5402             round_factor = 10.;
5403         else if (VPoint.chart_scale <= 10000.)
5404             round_factor = 100.;
5405         else if (VPoint.chart_scale <= 100000.)
5406             round_factor = 1000.;
5407 
5408         double true_scale_display =  wxRound(VPoint.chart_scale / round_factor ) * round_factor;
5409         wxString text;
5410 
5411         m_displayed_scale_factor = VPoint.ref_scale / VPoint.chart_scale;
5412 
5413         if( m_displayed_scale_factor > 10.0 )
5414             text.Printf( _T("%s %4.0f (%1.0fx)"), _("Scale"), true_scale_display, m_displayed_scale_factor );
5415         else if( m_displayed_scale_factor > 1.0 )
5416             text.Printf( _T("%s %4.0f (%1.1fx)"), _("Scale"), true_scale_display, m_displayed_scale_factor );
5417         else if( m_displayed_scale_factor > 0.1 ){
5418             double sfr = wxRound(m_displayed_scale_factor * 10.) / 10.;
5419             text.Printf( _T("%s %4.0f (%1.2fx)"), _("Scale"), true_scale_display, sfr );
5420         }
5421         else if( m_displayed_scale_factor > 0.01 ){
5422             double sfr = wxRound(m_displayed_scale_factor * 100.) / 100.;
5423             text.Printf( _T("%s %4.0f (%1.2fx)"), _("Scale"), true_scale_display, sfr );
5424         }
5425         else  {
5426             text.Printf( _T("%s %4.0f (---)"), _("Scale"), true_scale_display );      // Generally, no chart, so no chart scale factor
5427         }
5428 
5429 #ifdef ocpnUSE_GL
5430             if( g_bopengl && g_bShowFPS){
5431                 wxString fps_str;
5432                 double fps = 0.;
5433                 if( g_gl_ms_per_frame > 0){
5434                     fps = 1000./ g_gl_ms_per_frame;
5435                     fps_str.Printf(_T("  %3d fps"), (int)fps);
5436                 }
5437                 text += fps_str;
5438             }
5439 #endif
5440 
5441         m_scaleValue = true_scale_display;
5442         m_scaleText = text;
5443         if(m_muiBar)
5444             m_muiBar->UpdateDynamicValues();
5445 
5446         if( m_bShowScaleInStatusBar && parent_frame->GetStatusBar() && (parent_frame->GetStatusBar()->GetFieldsCount() > STAT_FIELD_SCALE) ) {
5447                 // Check to see if the text will fit in the StatusBar field...
5448                 bool b_noshow = false;
5449                 {
5450                     int w = 0;
5451                     int h;
5452                     wxClientDC dc(parent_frame->GetStatusBar());
5453                     if( dc.IsOk() ){
5454                         wxFont* templateFont = FontMgr::Get().GetFont( _("StatusBar"), 0 );
5455                         dc.SetFont(*templateFont);
5456                         dc.GetTextExtent(text, &w, &h);
5457 
5458 
5459                     // If text is too long for the allocated field, try to reduce the text string a bit.
5460                         wxRect rect;
5461                         parent_frame->GetStatusBar()->GetFieldRect(STAT_FIELD_SCALE, rect);
5462                         if(w && w > rect.width){
5463                             text.Printf( _T("%s (%1.1fx)"), _("Scale"), m_displayed_scale_factor );
5464                         }
5465 
5466                     //  Test again...if too big still, then give it up.
5467                         dc.GetTextExtent(text, &w, &h);
5468 
5469                         if(w && w > rect.width){
5470                             b_noshow = true;
5471                         }
5472                     }
5473                 }
5474 
5475                 if(!b_noshow)
5476                     parent_frame->SetStatusText( text, STAT_FIELD_SCALE );
5477         }
5478 
5479 
5480     }
5481 
5482     //  Maintain member vLat/vLon
5483     m_vLat = VPoint.clat;
5484     m_vLon = VPoint.clon;
5485 
5486     return b_ret;
5487 }
5488 
5489 
5490 //          Static Icon definitions for some symbols requiring scaling/rotation/translation
5491 //          Very specific wxDC draw commands are necessary to properly render these icons...See the code in ShipDraw()
5492 
5493 //      This icon was adapted and scaled from the S52 Presentation Library version 3_03.
5494 //     Symbol VECGND02
5495 
5496 static int s_png_pred_icon[] = { -10, -10, -10, 10, 10, 10, 10, -10 };
5497 
5498 //      This ownship icon was adapted and scaled from the S52 Presentation Library version 3_03
5499 //      Symbol OWNSHP05
5500 static int s_ownship_icon[] = { 5, -42, 11, -28, 11, 42, -11, 42, -11, -28, -5, -42, -11, 0, 11, 0,
5501                                 0, 42, 0, -42
5502                               };
5503 
PredColor()5504 wxColour ChartCanvas::PredColor()
5505 {
5506     //  RAdjust predictor color change on LOW_ACCURACY ship state in interests of visibility.
5507     if( SHIP_NORMAL == m_ownship_state )
5508         return GetGlobalColor( _T ( "URED" ) );
5509 
5510     else if( SHIP_LOWACCURACY == m_ownship_state )
5511         return GetGlobalColor( _T ( "YELO1" ) );
5512 
5513     return GetGlobalColor( _T ( "NODTA" ) );
5514 }
5515 
ShipColor()5516 wxColour ChartCanvas::ShipColor()
5517 {
5518     //      Establish ship color
5519     //     It changes color based on GPS and Chart accuracy/availability
5520 
5521     if( SHIP_NORMAL != m_ownship_state )
5522         return GetGlobalColor( _T ( "GREY1" ) );
5523 
5524     if( SHIP_LOWACCURACY == m_ownship_state )
5525         return GetGlobalColor( _T ( "YELO1" ) );
5526 
5527     return GetGlobalColor( _T ( "URED" ) );         // default is OK
5528 }
5529 
ShipDrawLargeScale(ocpnDC & dc,wxPoint lShipMidPoint)5530 void ChartCanvas::ShipDrawLargeScale( ocpnDC& dc, wxPoint lShipMidPoint )
5531 {
5532 
5533     dc.SetPen( wxPen( PredColor(), 2 ) );
5534 
5535     if( SHIP_NORMAL == m_ownship_state )
5536         dc.SetBrush( wxBrush( ShipColor(), wxBRUSHSTYLE_TRANSPARENT ) );
5537     else
5538         dc.SetBrush( wxBrush( GetGlobalColor( _T ( "YELO1" ) ) ) );
5539 
5540     dc.DrawEllipse( lShipMidPoint.x - 10, lShipMidPoint.y - 10, 20, 20 );
5541     dc.DrawEllipse( lShipMidPoint.x - 6, lShipMidPoint.y - 6, 12, 12 );
5542 
5543     dc.DrawLine( lShipMidPoint.x - 12, lShipMidPoint.y, lShipMidPoint.x + 12, lShipMidPoint.y );
5544     dc.DrawLine( lShipMidPoint.x, lShipMidPoint.y - 12, lShipMidPoint.x, lShipMidPoint.y + 12 );
5545 }
5546 
ShipIndicatorsDraw(ocpnDC & dc,int img_height,wxPoint GPSOffsetPixels,wxPoint lGPSPoint)5547 void ChartCanvas::ShipIndicatorsDraw( ocpnDC& dc, int img_height,
5548                                       wxPoint GPSOffsetPixels, wxPoint lGPSPoint)
5549 {
5550     // Develop a uniform length for course predictor line dash length, based on physical display size
5551     // Use this reference length to size all other graphics elements
5552     float ref_dim = m_display_size_mm / 24;
5553     ref_dim = wxMin(ref_dim, 12);
5554     ref_dim = wxMax(ref_dim, 6);
5555 
5556     wxColour cPred = PredColor();
5557 
5558     //  Establish some graphic element line widths dependent on the platform display resolution
5559     //double nominal_line_width_pix = wxMax(1.0, floor(g_Platform->GetDisplayDPmm() / 2));             //0.5 mm nominal, but not less than 1 pixel
5560     double nominal_line_width_pix = wxMax(1.0, floor(m_pix_per_mm / 2));             //0.5 mm nominal, but not less than 1 pixel
5561 
5562 
5563     // If the calculated value is greater than the config file spec value, then use it.
5564     if(nominal_line_width_pix > g_cog_predictor_width)
5565         g_cog_predictor_width = nominal_line_width_pix;
5566 
5567     //    Calculate ownship Position Predictor
5568     wxPoint lPredPoint, lHeadPoint;
5569 
5570     float pCog = std::isnan(gCog) ? 0 : gCog;
5571     float pSog = std::isnan(gSog) ? 0 : gSog;
5572 
5573     double pred_lat, pred_lon;
5574     ll_gc_ll( gLat, gLon, pCog, pSog * g_ownship_predictor_minutes / 60., &pred_lat, &pred_lon );
5575     GetCanvasPointPix( pred_lat, pred_lon, &lPredPoint );
5576 
5577     // test to catch the case where COG/HDG line crosses the screen
5578     LLBBox box;
5579 
5580 //    Should we draw the Head vector?
5581 //    Compare the points lHeadPoint and lPredPoint
5582 //    If they differ by more than n pixels, and the head vector is valid, then render the head vector
5583 
5584 
5585     float ndelta_pix = 10.;
5586     double hdg_pred_lat, hdg_pred_lon;
5587     bool b_render_hdt = false;
5588     if( !std::isnan( gHdt ) ) {
5589         //    Calculate ownship Heading pointer as a predictor
5590         ll_gc_ll( gLat, gLon, gHdt, g_ownship_HDTpredictor_miles, &hdg_pred_lat,
5591                   &hdg_pred_lon );
5592         GetCanvasPointPix( hdg_pred_lat, hdg_pred_lon, &lHeadPoint );
5593         float dist = sqrtf( powf(  (float) (lHeadPoint.x - lPredPoint.x), 2) +
5594                             powf(  (float) (lHeadPoint.y - lPredPoint.y), 2) );
5595         if( dist > ndelta_pix /*&& !std::isnan(gSog)*/ ) {
5596             box.SetFromSegment(gLat, gLon, hdg_pred_lat, hdg_pred_lon);
5597             if( !GetVP().GetBBox().IntersectOut(box))
5598                 b_render_hdt = true;
5599         }
5600     }
5601 
5602     // draw course over ground if they are longer than the ship
5603     wxPoint lShipMidPoint;
5604     lShipMidPoint.x = lGPSPoint.x + GPSOffsetPixels.x;
5605     lShipMidPoint.y = lGPSPoint.y + GPSOffsetPixels.y;
5606     float lpp = sqrtf( powf( (float) (lPredPoint.x - lShipMidPoint.x), 2) +
5607                        powf( (float) (lPredPoint.y - lShipMidPoint.y), 2) );
5608 
5609     if(lpp >= img_height / 2) {
5610         box.SetFromSegment(gLat, gLon, pred_lat, pred_lon);
5611         if( !GetVP().GetBBox().IntersectOut(box) && !std::isnan(gCog) && !std::isnan(gSog) ) {
5612 
5613             //      COG Predictor
5614             float dash_length = ref_dim;
5615             wxDash dash_long[2];
5616             dash_long[0] = (int) ( floor(g_Platform->GetDisplayDPmm() * dash_length) / g_cog_predictor_width );  // Long dash , in mm <---------+
5617             dash_long[1] = dash_long[0] / 2.0;                                                                   // Short gap
5618 
5619             // On ultra-hi-res displays, do not allow the dashes to be greater than 250, since it is defined as (char)
5620             if( dash_length > 250.){
5621                 dash_long[0] = 250. /g_cog_predictor_width;
5622                 dash_long[1] = dash_long[0] / 2;
5623             }
5624 
5625             wxPen ppPen2( cPred, g_cog_predictor_width, wxPENSTYLE_USER_DASH );
5626             ppPen2.SetDashes( 2, dash_long );
5627             dc.SetPen( ppPen2 );
5628             dc.StrokeLine( lGPSPoint.x + GPSOffsetPixels.x, lGPSPoint.y + GPSOffsetPixels.y,
5629                                lPredPoint.x + GPSOffsetPixels.x, lPredPoint.y + GPSOffsetPixels.y );
5630 
5631             if( g_cog_predictor_width > 1 ) {
5632                 float line_width = g_cog_predictor_width/3.;
5633 
5634                  wxDash dash_long3[2];
5635                  dash_long3[0] = g_cog_predictor_width / line_width * dash_long[0];
5636                  dash_long3[1] = g_cog_predictor_width / line_width * dash_long[1];
5637 
5638                  wxPen ppPen3( GetGlobalColor( _T ( "UBLCK" ) ), wxMax(1, line_width), wxPENSTYLE_USER_DASH );
5639                  ppPen3.SetDashes( 2, dash_long3 );
5640                  dc.SetPen( ppPen3 );
5641                  dc.StrokeLine( lGPSPoint.x + GPSOffsetPixels.x, lGPSPoint.y + GPSOffsetPixels.y,
5642                                lPredPoint.x + GPSOffsetPixels.x, lPredPoint.y + GPSOffsetPixels.y );
5643             }
5644 
5645 
5646                 // Prepare COG predictor endpoint icon
5647             double png_pred_icon_scale_factor = .4;
5648             if(g_ShipScaleFactorExp > 1.0)
5649                 png_pred_icon_scale_factor *= (log(g_ShipScaleFactorExp) + 1.0) * 1.1;
5650 
5651             wxPoint icon[4];
5652 
5653             float cog_rad = atan2f( (float) ( lPredPoint.y - lShipMidPoint.y ),
5654                                     (float) ( lPredPoint.x - lShipMidPoint.x ) );
5655             cog_rad += (float)PI;
5656 
5657             for( int i = 0; i < 4; i++ ) {
5658                 int j = i * 2;
5659                 double pxa = (double) ( s_png_pred_icon[j] );
5660                 double pya = (double) ( s_png_pred_icon[j + 1] );
5661 
5662                 pya *= png_pred_icon_scale_factor;
5663                 pxa *= png_pred_icon_scale_factor;
5664 
5665                 double px = ( pxa * sin( cog_rad ) ) + ( pya * cos( cog_rad ) );
5666                 double py = ( pya * sin( cog_rad ) ) - ( pxa * cos( cog_rad ) );
5667 
5668                 icon[i].x = (int) wxRound( px ) + lPredPoint.x + GPSOffsetPixels.x;
5669                 icon[i].y = (int) wxRound( py ) + lPredPoint.y + GPSOffsetPixels.y;
5670             }
5671 
5672                 // Render COG endpoint icon
5673             wxPen ppPen1( GetGlobalColor( _T ( "UBLCK" ) ), g_cog_predictor_width/2, wxPENSTYLE_SOLID );
5674             dc.SetPen( ppPen1 );
5675             dc.SetBrush( wxBrush( cPred ) );
5676 
5677             dc.StrokePolygon( 4, icon );
5678         }
5679     }
5680 
5681         //      HDT Predictor
5682         if( b_render_hdt ) {
5683              float hdt_dash_length = ref_dim * 0.4;
5684 
5685             float hdt_width = g_cog_predictor_width * 0.8;
5686             wxDash dash_short[2];
5687             dash_short[0] = (int) ( floor(g_Platform->GetDisplayDPmm() * hdt_dash_length) / hdt_width );  // Short dash , in mm <---------+
5688             dash_short[1] = (int) ( floor(g_Platform->GetDisplayDPmm() * hdt_dash_length * 0.9 ) / hdt_width );  // Short gap            |
5689 
5690             wxPen ppPen2( cPred, hdt_width, wxPENSTYLE_USER_DASH );
5691             ppPen2.SetDashes( 2, dash_short );
5692 
5693             dc.SetPen( ppPen2 );
5694             dc.StrokeLine( lGPSPoint.x + GPSOffsetPixels.x, lGPSPoint.y + GPSOffsetPixels.y,
5695                            lHeadPoint.x + GPSOffsetPixels.x, lHeadPoint.y + GPSOffsetPixels.y );
5696 
5697             wxPen ppPen1( cPred, g_cog_predictor_width/3, wxPENSTYLE_SOLID );
5698             dc.SetPen( ppPen1 );
5699             dc.SetBrush( wxBrush( GetGlobalColor( _T ( "GREY2" ) ) ) );
5700 
5701             double nominal_circle_size_pixels = wxMax(4.0, floor(m_pix_per_mm * (ref_dim / 5.0)));    // not less than 4 pixel
5702 
5703             // Scale the circle to ChartScaleFactor, slightly softened....
5704             if(g_ShipScaleFactorExp > 1.0)
5705                 nominal_circle_size_pixels *= (log(g_ShipScaleFactorExp) + 1.0) * 1.1;
5706 
5707             dc.StrokeCircle( lHeadPoint.x + GPSOffsetPixels.x, lHeadPoint.y + GPSOffsetPixels.y, nominal_circle_size_pixels/2 );
5708         }
5709 
5710         // Draw radar rings if activated
5711         if( g_iNavAidRadarRingsNumberVisible ) {
5712             double factor = 1.00;
5713             if( g_pNavAidRadarRingsStepUnits == 1 )          // nautical miles
5714             factor = 1 / 1.852;
5715 
5716             factor *= g_fNavAidRadarRingsStep;
5717 
5718             double tlat, tlon;
5719             wxPoint r;
5720             ll_gc_ll( gLat, gLon, 0, factor, &tlat, &tlon );
5721             GetCanvasPointPix( tlat, tlon, &r );
5722 
5723             double lpp = sqrt( pow( (double) (lGPSPoint.x - r.x), 2) +
5724             pow( (double) (lGPSPoint.y - r.y), 2 ) );
5725             int pix_radius = (int) lpp;
5726 
5727             extern wxColor GetDimColor(wxColor c);
5728             wxColor rangeringcolour = GetDimColor(g_colourOwnshipRangeRingsColour);
5729 
5730             wxPen ppPen1( rangeringcolour, g_cog_predictor_width );
5731 
5732             dc.SetPen( ppPen1 );
5733             dc.SetBrush( wxBrush( rangeringcolour, wxBRUSHSTYLE_TRANSPARENT ) );
5734 
5735             for( int i = 1; i <= g_iNavAidRadarRingsNumberVisible; i++ )
5736                 dc.StrokeCircle( lGPSPoint.x, lGPSPoint.y, i * pix_radius );
5737         }
5738 }
5739 
ComputeShipScaleFactor(float icon_hdt,int ownShipWidth,int ownShipLength,wxPoint & lShipMidPoint,wxPoint & GPSOffsetPixels,wxPoint lGPSPoint,float & scale_factor_x,float & scale_factor_y)5740 void ChartCanvas::ComputeShipScaleFactor(float icon_hdt,
5741                                          int ownShipWidth, int ownShipLength,
5742                                          wxPoint &lShipMidPoint,
5743                                          wxPoint &GPSOffsetPixels, wxPoint lGPSPoint,
5744                                          float &scale_factor_x, float &scale_factor_y)
5745 {
5746     float screenResolution = m_pix_per_mm;
5747 
5748     //  Calculate the true ship length in exact pixels
5749     double ship_bow_lat, ship_bow_lon;
5750     ll_gc_ll( gLat, gLon, icon_hdt, g_n_ownship_length_meters / 1852., &ship_bow_lat,
5751               &ship_bow_lon );
5752     wxPoint lShipBowPoint;
5753     wxPoint2DDouble b_point = GetVP().GetDoublePixFromLL( ship_bow_lat, ship_bow_lon );
5754     wxPoint2DDouble a_point = GetVP().GetDoublePixFromLL( gLat, gLon );
5755 
5756     float shipLength_px = sqrtf( powf( (float) (b_point.m_x - a_point.m_x), 2) +
5757                                  powf( (float) (b_point.m_y - a_point.m_y), 2) );
5758 
5759     //  And in mm
5760     float shipLength_mm = shipLength_px / screenResolution;
5761 
5762     //  Set minimum ownship drawing size
5763     float ownship_min_mm = g_n_ownship_min_mm;
5764     ownship_min_mm = wxMax(ownship_min_mm, 1.0);
5765 
5766     //  Calculate Nautical Miles distance from midships to gps antenna
5767     float hdt_ant = icon_hdt + 180.;
5768     float dy = ( g_n_ownship_length_meters / 2 - g_n_gps_antenna_offset_y ) / 1852.;
5769     float dx = g_n_gps_antenna_offset_x / 1852.;
5770     if( g_n_gps_antenna_offset_y > g_n_ownship_length_meters / 2 )      //reverse?
5771     {
5772         hdt_ant = icon_hdt;
5773         dy = -dy;
5774     }
5775 
5776     //  If the drawn ship size is going to be clamped, adjust the gps antenna offsets
5777     if( shipLength_mm < ownship_min_mm ) {
5778         dy /= shipLength_mm / ownship_min_mm;
5779         dx /= shipLength_mm / ownship_min_mm;
5780     }
5781 
5782     double ship_mid_lat, ship_mid_lon, ship_mid_lat1, ship_mid_lon1;
5783 
5784     ll_gc_ll( gLat, gLon, hdt_ant, dy, &ship_mid_lat, &ship_mid_lon );
5785     ll_gc_ll( ship_mid_lat, ship_mid_lon, icon_hdt - 90., dx, &ship_mid_lat1, &ship_mid_lon1 );
5786 
5787     GetCanvasPointPix( ship_mid_lat1, ship_mid_lon1, &lShipMidPoint );
5788     GPSOffsetPixels.x = lShipMidPoint.x - lGPSPoint.x;
5789     GPSOffsetPixels.y = lShipMidPoint.y - lGPSPoint.y;
5790 
5791     float scale_factor = shipLength_px / ownShipLength;
5792 
5793     //  Calculate a scale factor that would produce a reasonably sized icon
5794     float scale_factor_min = ownship_min_mm / ( ownShipLength / screenResolution );
5795 
5796     //  And choose the correct one
5797     scale_factor = wxMax(scale_factor, scale_factor_min);
5798 
5799     scale_factor_y = scale_factor;
5800     scale_factor_x = scale_factor_y * ( (float) ownShipLength / ownShipWidth )
5801         / ( (float) g_n_ownship_length_meters / g_n_ownship_beam_meters );
5802 }
5803 
ShipDraw(ocpnDC & dc)5804 void ChartCanvas::ShipDraw( ocpnDC& dc )
5805 {
5806     if( !GetVP().IsValid() ) return;
5807 
5808     wxPoint lGPSPoint, lShipMidPoint, GPSOffsetPixels(0,0);
5809 
5810     //  COG/SOG may be undefined in NMEA data stream
5811     float pCog = std::isnan(gCog) ? 0 : gCog;
5812     float pSog = std::isnan(gSog) ? 0 : gSog;
5813 
5814     GetCanvasPointPix( gLat, gLon, &lGPSPoint );
5815     lShipMidPoint = lGPSPoint;
5816 
5817     //  Draw the icon rotated to the COG
5818     //  or to the Hdt if available
5819     float icon_hdt = pCog;
5820     if( !std::isnan( gHdt ) ) icon_hdt = gHdt;
5821 
5822     //  COG may be undefined in NMEA data stream
5823     if( std::isnan(icon_hdt) ) icon_hdt = 0.0;
5824 
5825 //    Calculate the ownship drawing angle icon_rad using an assumed 10 minute predictor
5826     double osd_head_lat, osd_head_lon;
5827     wxPoint osd_head_point;
5828 
5829     ll_gc_ll( gLat, gLon, icon_hdt, pSog * 10. / 60., &osd_head_lat, &osd_head_lon );
5830 
5831     GetCanvasPointPix( osd_head_lat, osd_head_lon, &osd_head_point );
5832 
5833     float icon_rad = atan2f( (float) ( osd_head_point.y - lShipMidPoint.y ),
5834                              (float) ( osd_head_point.x - lShipMidPoint.x ) );
5835     icon_rad += (float)PI;
5836 
5837     if (pSog < 0.2) icon_rad = ((icon_hdt + 90.) * PI / 180) + GetVP().rotation;
5838 
5839 //    Another draw test ,based on pixels, assuming the ship icon is a fixed nominal size
5840 //    and is just barely outside the viewport        ....
5841     wxBoundingBox bb_screen( 0, 0, GetVP().pix_width, GetVP().pix_height );
5842 
5843     // TODO: fix to include actual size of boat that will be rendered
5844     int img_height = 0;
5845     if( bb_screen.PointInBox( lShipMidPoint, 20 ) ) {
5846         if( GetVP().chart_scale > 300000 )             // According to S52, this should be 50,000
5847         {
5848             ShipDrawLargeScale(dc, lShipMidPoint);
5849             img_height = 20;
5850         } else {
5851 
5852             wxImage pos_image;
5853 
5854             //      Substitute user ownship image if found
5855             if( m_pos_image_user )
5856                 pos_image = m_pos_image_user->Copy();
5857             else if( SHIP_NORMAL == m_ownship_state )
5858                 pos_image = m_pos_image_red->Copy();
5859             if( SHIP_LOWACCURACY == m_ownship_state )
5860                 pos_image = m_pos_image_yellow->Copy();
5861             else if( SHIP_NORMAL != m_ownship_state )
5862                 pos_image = m_pos_image_grey->Copy();
5863 
5864 
5865             //      Substitute user ownship image if found
5866             if( m_pos_image_user ) {
5867                 pos_image = m_pos_image_user->Copy();
5868 
5869                 if( SHIP_LOWACCURACY == m_ownship_state )
5870                     pos_image = m_pos_image_user_yellow->Copy();
5871                 else if( SHIP_NORMAL != m_ownship_state )
5872                     pos_image = m_pos_image_user_grey->Copy();
5873             }
5874 
5875             img_height = pos_image.GetHeight();
5876 
5877             if( g_n_ownship_beam_meters > 0.0 &&
5878                 g_n_ownship_length_meters > 0.0 &&
5879                 g_OwnShipIconType > 0 ) // use large ship
5880             {
5881                 int ownShipWidth = 22; // Default values from s_ownship_icon
5882                 int ownShipLength= 84;
5883                 if( g_OwnShipIconType == 1 ) {
5884                     ownShipWidth = pos_image.GetWidth();
5885                     ownShipLength= pos_image.GetHeight();
5886                 }
5887 
5888                 float scale_factor_x, scale_factor_y;
5889                 ComputeShipScaleFactor
5890                     (icon_hdt, ownShipWidth, ownShipLength, lShipMidPoint,
5891                      GPSOffsetPixels, lGPSPoint, scale_factor_x, scale_factor_y);
5892 
5893                 if( g_OwnShipIconType == 1 ) { // Scaled bitmap
5894                     pos_image.Rescale( ownShipWidth * scale_factor_x, ownShipLength * scale_factor_y,
5895                                        wxIMAGE_QUALITY_HIGH );
5896                     wxPoint rot_ctr( pos_image.GetWidth() / 2, pos_image.GetHeight() / 2 );
5897                     wxImage rot_image = pos_image.Rotate( -( icon_rad - ( PI / 2. ) ), rot_ctr, true );
5898 
5899                     // Simple sharpening algorithm.....
5900                     for( int ip = 0; ip < rot_image.GetWidth(); ip++ )
5901                         for( int jp = 0; jp < rot_image.GetHeight(); jp++ )
5902                             if( rot_image.GetAlpha( ip, jp ) > 64 ) rot_image.SetAlpha( ip, jp, 255 );
5903 
5904                     wxBitmap os_bm( rot_image );
5905 
5906                     int w = os_bm.GetWidth();
5907                     int h = os_bm.GetHeight();
5908                     img_height = h;
5909 
5910                     dc.DrawBitmap( os_bm, lShipMidPoint.x - w / 2, lShipMidPoint.y - h / 2, true );
5911 
5912                     // Maintain dirty box,, missing in __WXMSW__ library
5913                     dc.CalcBoundingBox( lShipMidPoint.x - w / 2, lShipMidPoint.y - h / 2 );
5914                     dc.CalcBoundingBox( lShipMidPoint.x - w / 2 + w, lShipMidPoint.y - h / 2 + h );
5915                 }
5916 
5917                 else if( g_OwnShipIconType == 2 ) { // Scaled Vector
5918                     wxPoint ownship_icon[10];
5919 
5920                     for( int i = 0; i < 10; i++ ) {
5921                         int j = i * 2;
5922                         float pxa = (float) ( s_ownship_icon[j] );
5923                         float pya = (float) ( s_ownship_icon[j + 1] );
5924                         pya *= scale_factor_y;
5925                         pxa *= scale_factor_x;
5926 
5927                         float px = ( pxa * sinf( icon_rad ) ) + ( pya * cosf( icon_rad ) );
5928                         float py = ( pya * sinf( icon_rad ) ) - ( pxa * cosf( icon_rad ) );
5929 
5930                         ownship_icon[i].x = (int) ( px ) + lShipMidPoint.x;
5931                         ownship_icon[i].y = (int) ( py ) + lShipMidPoint.y;
5932                     }
5933 
5934                     wxPen ppPen1( GetGlobalColor( _T ( "UBLCK" ) ), 1, wxPENSTYLE_SOLID );
5935                     dc.SetPen( ppPen1 );
5936                     dc.SetBrush( wxBrush( ShipColor() ) );
5937 
5938                     dc.StrokePolygon( 6, &ownship_icon[0], 0, 0 );
5939 
5940                     //     draw reference point (midships) cross
5941                     dc.StrokeLine( ownship_icon[6].x, ownship_icon[6].y, ownship_icon[7].x,
5942                                    ownship_icon[7].y );
5943                     dc.StrokeLine( ownship_icon[8].x, ownship_icon[8].y, ownship_icon[9].x,
5944                                    ownship_icon[9].y );
5945                 }
5946 
5947                 img_height = ownShipLength * scale_factor_y;
5948 
5949                 //      Reference point, where the GPS antenna is
5950                 int circle_rad = 3;
5951                 if( m_pos_image_user ) circle_rad = 1;
5952 
5953                 dc.SetPen( wxPen( GetGlobalColor( _T ( "UBLCK" ) ), 1 ) );
5954                 dc.SetBrush( wxBrush( GetGlobalColor( _T ( "UIBCK" ) ) ) );
5955                 dc.StrokeCircle( lGPSPoint.x, lGPSPoint.y, circle_rad );
5956             }
5957             else { // Fixed bitmap icon.
5958                 /* non opengl, or suboptimal opengl via ocpndc: */
5959                 wxPoint rot_ctr( pos_image.GetWidth() / 2, pos_image.GetHeight() / 2 );
5960                 wxImage rot_image = pos_image.Rotate( -( icon_rad - ( PI / 2. ) ), rot_ctr, true );
5961 
5962                 // Simple sharpening algorithm.....
5963                 for( int ip = 0; ip < rot_image.GetWidth(); ip++ )
5964                     for( int jp = 0; jp < rot_image.GetHeight(); jp++ )
5965                         if( rot_image.GetAlpha( ip, jp ) > 64 ) rot_image.SetAlpha( ip, jp, 255 );
5966 
5967                 wxBitmap os_bm( rot_image );
5968 
5969                 if(g_ShipScaleFactorExp > 1){
5970                     wxImage scaled_image = os_bm.ConvertToImage();
5971                     double factor = (log(g_ShipScaleFactorExp) + 1.0) * 1.0;   // soften the scale factor a bit
5972                     os_bm = wxBitmap(scaled_image.Scale(scaled_image.GetWidth() * factor,
5973                                                         scaled_image.GetHeight() * factor,
5974                                                         wxIMAGE_QUALITY_HIGH));
5975                 }
5976                 int w = os_bm.GetWidth();
5977                 int h = os_bm.GetHeight();
5978                 img_height = h;
5979 
5980                 dc.DrawBitmap( os_bm, lShipMidPoint.x - w / 2, lShipMidPoint.y - h / 2, true );
5981 
5982                 //      Reference point, where the GPS antenna is
5983                 int circle_rad = 3;
5984                 if( m_pos_image_user ) circle_rad = 1;
5985 
5986                 dc.SetPen( wxPen( GetGlobalColor( _T ( "UBLCK" ) ), 1 ) );
5987                 dc.SetBrush( wxBrush( GetGlobalColor( _T ( "UIBCK" ) ) ) );
5988                 dc.StrokeCircle( lShipMidPoint.x, lShipMidPoint.y, circle_rad );
5989 
5990                 // Maintain dirty box,, missing in __WXMSW__ library
5991                 dc.CalcBoundingBox( lShipMidPoint.x - w / 2, lShipMidPoint.y - h / 2 );
5992                 dc.CalcBoundingBox( lShipMidPoint.x - w / 2 + w, lShipMidPoint.y - h / 2 + h );
5993             }
5994         }        // ownship draw
5995     }
5996 
5997     ShipIndicatorsDraw(dc, img_height, GPSOffsetPixels, lGPSPoint);
5998 }
5999 
6000 /* @ChartCanvas::CalcGridSpacing
6001  **
6002  ** Calculate the major and minor spacing between the lat/lon grid
6003  **
6004  ** @param [r] WindowDegrees [float] displayed number of lat or lan in the window
6005  ** @param [w] MajorSpacing [float &] Major distance between grid lines
6006  ** @param [w] MinorSpacing [float &] Minor distance between grid lines
6007  ** @return [void]
6008  */
CalcGridSpacing(float view_scale_ppm,float & MajorSpacing,float & MinorSpacing)6009 void CalcGridSpacing( float view_scale_ppm, float& MajorSpacing, float&MinorSpacing )
6010 {
6011     // table for calculating the distance between the grids
6012     // [0] view_scale ppm
6013     // [1] spacing between major grid lines in degrees
6014     // [2] spacing between minor grid lines in degrees
6015     const float lltab[][3] =
6016         { {  0.0f, 90.0f, 30.0f },
6017           { .000001f, 45.0f, 15.0f },
6018           { .0002f,   30.0f, 10.0f },
6019           { .0003f,   10.0f, 2.0f  },
6020           { .0008f,   5.0f, 1.0f },
6021           { .001f,    2.0f,          30.0f / 60.0f },
6022           { .003f,    1.0f,          20.0f / 60.0f },
6023           { .006f,    0.5f,          10.0f / 60.0f },
6024           { .03f,     15.0f / 60.0f, 5.0f / 60.0f },
6025           { .01f,     10.0f / 60.0f, 2.0f / 60.0f },
6026           { .06f,     5.0f / 60.0f,  1.0f / 60.0f },
6027           { .1f,      2.0f / 60.0f,  1.0f / 60.0f },
6028           { .4f,      1.0f / 60.0f,  0.5f / 60.0f },
6029           { .6f,      0.5f / 60.0f,  0.1f / 60.0f },
6030           { 1.0f,     0.2f / 60.0f,  0.1f / 60.0f },
6031           { 1e10f,    0.1f / 60.0f,  0.05f / 60.0f }
6032     };
6033 
6034     unsigned int tabi;
6035     for( tabi = 0; tabi < ((sizeof lltab) / (sizeof *lltab)) -1; tabi++ )
6036         if( view_scale_ppm < lltab[tabi][0] )
6037             break;
6038     MajorSpacing = lltab[tabi][1]; // major latitude distance
6039     MinorSpacing = lltab[tabi][2]; // minor latitude distance
6040     return;
6041 }
6042 /* @ChartCanvas::CalcGridText *************************************
6043  **
6044  ** Calculates text to display at the major grid lines
6045  **
6046  ** @param [r] latlon [float] latitude or longitude of grid line
6047  ** @param [r] spacing [float] distance between two major grid lines
6048  ** @param [r] bPostfix [bool] true for latitudes, false for longitudes
6049  **
6050  ** @return
6051  */
6052 
CalcGridText(float latlon,float spacing,bool bPostfix)6053 wxString CalcGridText( float latlon, float spacing, bool bPostfix )
6054 {
6055     int deg = (int) fabs( latlon ); // degrees
6056     float min = fabs( ( fabs( latlon ) - deg ) * 60.0 ); // Minutes
6057     char postfix;
6058 
6059     // calculate postfix letter (NSEW)
6060     if( latlon > 0.0 ) {
6061         if( bPostfix ) {
6062             postfix = 'N';
6063         } else {
6064             postfix = 'E';
6065         }
6066     } else if( latlon < 0.0 ) {
6067         if( bPostfix ) {
6068             postfix = 'S';
6069         } else {
6070             postfix = 'W';
6071         }
6072     } else {
6073         postfix = ' '; // no postfix for equator and greenwich
6074     }
6075     // calculate text, display minutes only if spacing is smaller than one degree
6076 
6077     wxString ret;
6078     if( spacing >= 1.0 ) {
6079         ret.Printf( _T("%3d%c %c"), deg, 0x00b0, postfix );
6080     } else if( spacing >= ( 1.0 / 60.0 ) ) {
6081         ret.Printf( _T("%3d%c%02.0f %c"), deg, 0x00b0, min, postfix );
6082     } else {
6083         ret.Printf( _T("%3d%c%02.2f %c"), deg, 0x00b0, min, postfix );
6084     }
6085 
6086     return ret;
6087 }
6088 
6089 /* @ChartCanvas::GridDraw *****************************************
6090  **
6091  ** Draws major and minor Lat/Lon Grid on the chart
6092  ** - distance between Grid-lm ines are calculated automatic
6093  ** - major grid lines will be across the whole chart window
6094  ** - minor grid lines will be 10 pixel at each edge of the chart window.
6095  **
6096  ** @param [w] dc [wxDC&] the wx drawing context
6097  **
6098  ** @return [void]
6099  ************************************************************************/
GridDraw(ocpnDC & dc)6100 void ChartCanvas::GridDraw( ocpnDC& dc )
6101 {
6102     if( !( m_bDisplayGrid && ( fabs( GetVP().rotation ) < 1e-5 ) ) )
6103         return;
6104 
6105     double nlat, elon, slat, wlon;
6106     float lat, lon;
6107     float dlon;
6108     float gridlatMajor, gridlatMinor, gridlonMajor, gridlonMinor;
6109     wxCoord w, h;
6110     wxPen GridPen( GetGlobalColor( _T ( "SNDG1" ) ), 1, wxPENSTYLE_SOLID );
6111     dc.SetPen( GridPen );
6112     dc.SetFont( *m_pgridFont );
6113     dc.SetTextForeground( GetGlobalColor( _T ( "SNDG1" ) ) );
6114 
6115     w = m_canvas_width;
6116     h = m_canvas_height;
6117 
6118     GetCanvasPixPoint( 0, 0, nlat, wlon ); // get lat/lon of upper left point of the window
6119     GetCanvasPixPoint( w, h, slat, elon ); // get lat/lon of lower right point of the window
6120     dlon = elon - wlon; // calculate how many degrees of longitude are shown in the window
6121     if( dlon < 0.0 ) // concider datum border at 180 degrees longitude
6122     {
6123         dlon = dlon + 360.0;
6124     }
6125     // calculate distance between latitude grid lines
6126     CalcGridSpacing( GetVP().view_scale_ppm, gridlatMajor, gridlatMinor );
6127 
6128     // calculate position of first major latitude grid line
6129     lat = ceil( slat / gridlatMajor ) * gridlatMajor;
6130 
6131     // Draw Major latitude grid lines and text
6132     while( lat < nlat ) {
6133         wxPoint r;
6134         wxString st = CalcGridText( lat, gridlatMajor, true ); // get text for grid line
6135         GetCanvasPointPix( lat, ( elon + wlon ) / 2, &r );
6136         dc.DrawLine( 0, r.y, w, r.y, false );                             // draw grid line
6137         dc.DrawText( st, 0, r.y ); // draw text
6138         lat = lat + gridlatMajor;
6139 
6140         if( fabs( lat - wxRound( lat ) ) < 1e-5 ) lat = wxRound( lat );
6141     }
6142 
6143     // calculate position of first minor latitude grid line
6144     lat = ceil( slat / gridlatMinor ) * gridlatMinor;
6145 
6146     // Draw minor latitude grid lines
6147     while( lat < nlat ) {
6148         wxPoint r;
6149         GetCanvasPointPix( lat, ( elon + wlon ) / 2, &r );
6150         dc.DrawLine( 0, r.y, 10, r.y, false );
6151         dc.DrawLine( w - 10, r.y, w, r.y, false );
6152         lat = lat + gridlatMinor;
6153     }
6154 
6155     // calculate distance between grid lines
6156     CalcGridSpacing( GetVP().view_scale_ppm, gridlonMajor, gridlonMinor );
6157 
6158     // calculate position of first major latitude grid line
6159     lon = ceil( wlon / gridlonMajor ) * gridlonMajor;
6160 
6161     // draw major longitude grid lines
6162     for( int i = 0, itermax = (int) ( dlon / gridlonMajor ); i <= itermax; i++ ) {
6163         wxPoint r;
6164         wxString st = CalcGridText( lon, gridlonMajor, false );
6165         GetCanvasPointPix( ( nlat + slat ) / 2, lon, &r );
6166         dc.DrawLine( r.x, 0, r.x, h, false );
6167         dc.DrawText( st, r.x, 0 );
6168         lon = lon + gridlonMajor;
6169         if( lon > 180.0 ) {
6170             lon = lon - 360.0;
6171         }
6172 
6173         if( fabs( lon - wxRound( lon ) ) < 1e-5 ) lon = wxRound( lon );
6174 
6175     }
6176 
6177     // calculate position of first minor longitude grid line
6178     lon = ceil( wlon / gridlonMinor ) * gridlonMinor;
6179     // draw minor longitude grid lines
6180     for( int i = 0, itermax = (int) ( dlon / gridlonMinor ); i <= itermax; i++ ) {
6181         wxPoint r;
6182         GetCanvasPointPix( ( nlat + slat ) / 2, lon, &r );
6183         dc.DrawLine( r.x, 0, r.x, 10, false );
6184         dc.DrawLine( r.x, h - 10, r.x, h, false );
6185         lon = lon + gridlonMinor;
6186         if( lon > 180.0 ) {
6187             lon = lon - 360.0;
6188         }
6189     }
6190 }
6191 
ScaleBarDraw(ocpnDC & dc)6192 void ChartCanvas::ScaleBarDraw( ocpnDC& dc )
6193 {
6194     if(0 /*!g_bsimplifiedScalebar*/){
6195         double blat, blon, tlat, tlon;
6196         wxPoint r;
6197 
6198         int x_origin = m_bDisplayGrid ? 60 : 20;
6199         int y_origin = m_canvas_height - 50;
6200 
6201         float dist;
6202         int count;
6203         wxPen pen1, pen2;
6204 
6205         if( GetVP().chart_scale > 80000 )        // Draw 10 mile scale as SCALEB11
6206         {
6207             dist = 10.0;
6208             count = 5;
6209             pen1 = wxPen( GetGlobalColor( _T ( "SNDG2" ) ), 3, wxPENSTYLE_SOLID );
6210             pen2 = wxPen( GetGlobalColor( _T ( "SNDG1" ) ), 3, wxPENSTYLE_SOLID );
6211         } else                                // Draw 1 mile scale as SCALEB10
6212         {
6213             dist = 1.0;
6214             count = 10;
6215             pen1 = wxPen( GetGlobalColor( _T ( "SCLBR" ) ), 3, wxPENSTYLE_SOLID );
6216             pen2 = wxPen( GetGlobalColor( _T ( "CHGRD" ) ), 3, wxPENSTYLE_SOLID );
6217         }
6218 
6219         GetCanvasPixPoint( x_origin, y_origin, blat, blon );
6220         double rotation = -VPoint.rotation;
6221 
6222         ll_gc_ll( blat, blon, rotation * 180 / PI, dist, &tlat, &tlon );
6223         GetCanvasPointPix( tlat, tlon, &r );
6224         int l1 = ( y_origin - r.y ) / count;
6225 
6226         for( int i = 0; i < count; i++ ) {
6227             int y = l1 * i;
6228             if( i & 1 ) dc.SetPen( pen1 );
6229             else
6230                 dc.SetPen( pen2 );
6231 
6232             dc.DrawLine( x_origin, y_origin - y, x_origin, y_origin - ( y + l1 ) );
6233         }
6234     }
6235     else {
6236         double blat, blon, tlat, tlon;
6237 
6238         int x_origin = 5.0 * GetPixPerMM();
6239         int chartbar_height = GetChartbarHeight();
6240 //         ocpnStyle::Style* style = g_StyleManager->GetCurrentStyle();
6241 //         if (style->chartStatusWindowTransparent)
6242 //             chartbar_height = 0;
6243         int y_origin = m_canvas_height - chartbar_height - 5;
6244 
6245         GetCanvasPixPoint( x_origin, y_origin, blat, blon );
6246         GetCanvasPixPoint( x_origin + m_canvas_width, y_origin, tlat, tlon );
6247 
6248         double d;
6249         ll_gc_ll_reverse(blat, blon, tlat, tlon, 0, &d);
6250         d /= 2;
6251 
6252         int unit = g_iDistanceFormat;
6253         if( d < .5 && ( unit == DISTANCE_KM || unit == DISTANCE_MI || unit == DISTANCE_NMI ) )
6254             unit = ( unit == DISTANCE_MI ) ? DISTANCE_FT : DISTANCE_M;
6255 
6256         // nice number
6257         float dist = toUsrDistance( d, unit ), logdist = log(dist) / log(10.F);
6258         float places = floor(logdist), rem = logdist - places;
6259         dist = pow(10, places);
6260 
6261         if(rem < .2)
6262             dist /= 5;
6263         else if(rem < .5)
6264             dist /= 2;
6265 
6266         wxString s = wxString::Format(_T("%g "), dist) + getUsrDistanceUnit( unit );
6267         wxPen pen1 = wxPen( GetGlobalColor( _T ( "UBLCK" ) ), 3, wxPENSTYLE_SOLID );
6268         double rotation = -VPoint.rotation;
6269 
6270         ll_gc_ll( blat, blon, rotation * 180 / PI + 90, fromUsrDistance(dist, unit), &tlat, &tlon );
6271         wxPoint r;
6272         GetCanvasPointPix( tlat, tlon, &r );
6273         int l1 = r.x - x_origin;
6274 
6275         m_scaleBarRect = wxRect(x_origin, y_origin- 12, l1, 12);        // Store this for later reference
6276 
6277         dc.SetPen(pen1);
6278 
6279         dc.DrawLine( x_origin, y_origin, x_origin, y_origin - 12);
6280         dc.DrawLine( x_origin, y_origin, x_origin + l1, y_origin);
6281         dc.DrawLine( x_origin + l1, y_origin, x_origin + l1, y_origin - 12);
6282 
6283         dc.SetFont( *m_pgridFont );
6284         dc.SetTextForeground( GetGlobalColor( _T ( "UBLCK" ) ) );
6285         int w, h;
6286         dc.GetTextExtent(s, &w, &h);
6287         dc.DrawText( s, x_origin + l1/2 - w/2, y_origin - h - 1 );
6288     }
6289 }
6290 
JaggyCircle(ocpnDC & dc,wxPen pen,int x,int y,int radius)6291 void ChartCanvas::JaggyCircle( ocpnDC &dc, wxPen pen, int x, int y, int radius )
6292 {
6293     //    Constants?
6294     double da_min = 2.;
6295     double da_max = 6.;
6296     double ra_min = 0.;
6297     double ra_max = 40.;
6298 
6299     wxPen pen_save = dc.GetPen();
6300 
6301     wxDateTime now = wxDateTime::Now();
6302 
6303     dc.SetPen( pen );
6304 
6305     int x0, y0, x1, y1;
6306 
6307     x0 = x1 = x + radius;                    // Start point
6308     y0 = y1 = y;
6309     double angle = 0.;
6310     int i = 0;
6311 
6312     while( angle < 360. ) {
6313         double da = da_min + ( ( (double) rand() / RAND_MAX ) * ( da_max - da_min ) );
6314         angle += da;
6315 
6316         if( angle > 360. ) angle = 360.;
6317 
6318         double ra = ra_min + ( ( (double) rand() / RAND_MAX ) * ( ra_max - ra_min ) );
6319 
6320         double r;
6321         if( i & 1 ) r = radius + ra;
6322         else
6323             r = radius - ra;
6324 
6325         x1 = (int) ( x + cos( angle * PI / 180. ) * r );
6326         y1 = (int) ( y + sin( angle * PI / 180. ) * r );
6327 
6328         dc.DrawLine( x0, y0, x1, y1 );
6329 
6330         x0 = x1;
6331         y0 = y1;
6332 
6333         i++;
6334 
6335     }
6336 
6337     dc.DrawLine( x + radius, y, x1, y1 );             // closure
6338 
6339     dc.SetPen( pen_save );
6340 }
6341 
6342 static bool bAnchorSoundPlaying = false;
6343 
onSoundFinished(void * ptr)6344 static void onSoundFinished( void* ptr )
6345 {
6346     bAnchorSoundPlaying = false;
6347 }
6348 
6349 
AlertDraw(ocpnDC & dc)6350 void ChartCanvas::AlertDraw( ocpnDC& dc )
6351 {
6352 // Just for prototyping, visual alert for anchorwatch goes here
6353     bool play_sound = false;
6354     if( pAnchorWatchPoint1 && AnchorAlertOn1 ) {
6355         if( AnchorAlertOn1 ) {
6356             wxPoint TargetPoint;
6357             GetCanvasPointPix( pAnchorWatchPoint1->m_lat, pAnchorWatchPoint1->m_lon, &TargetPoint );
6358             JaggyCircle( dc, wxPen( GetGlobalColor( _T("URED") ), 2 ), TargetPoint.x, TargetPoint.y,
6359                          100 );
6360             play_sound = true;
6361         }
6362     } else
6363         AnchorAlertOn1 = false;
6364 
6365     if( pAnchorWatchPoint2 && AnchorAlertOn2 ) {
6366         if( AnchorAlertOn2 ) {
6367             wxPoint TargetPoint;
6368             GetCanvasPointPix( pAnchorWatchPoint2->m_lat, pAnchorWatchPoint2->m_lon, &TargetPoint );
6369             JaggyCircle( dc, wxPen( GetGlobalColor( _T("URED") ), 2 ), TargetPoint.x, TargetPoint.y,
6370                          100 );
6371             play_sound = true;
6372         }
6373     } else
6374         AnchorAlertOn2 = false;
6375 
6376     if( play_sound && !bAnchorSoundPlaying) {
6377         g_anchorwatch_sound->SetCmd( g_CmdSoundString.mb_str( wxConvUTF8) );
6378         g_anchorwatch_sound->Load( g_sAIS_Alert_Sound_File );
6379         if ( g_anchorwatch_sound->IsOk( ) ) {
6380             bAnchorSoundPlaying = true;
6381             g_anchorwatch_sound->SetFinishedCallback( onSoundFinished, NULL );
6382             g_anchorwatch_sound->Play( );
6383         }
6384     } else if( g_anchorwatch_sound->IsOk() ) {
6385         g_anchorwatch_sound->Stop();
6386     }
6387 
6388 }
6389 // End of prototype anchor watch alerting-----------------------
6390 
UpdateShips()6391 void ChartCanvas::UpdateShips()
6392 {
6393     //  Get the rectangle in the current dc which bounds the "ownship" symbol
6394 
6395     wxClientDC dc( this );
6396     if( !dc.IsOk() ) return;
6397 
6398     wxBitmap test_bitmap( dc.GetSize().x, dc.GetSize().y );
6399     wxMemoryDC temp_dc( test_bitmap );
6400 
6401     temp_dc.ResetBoundingBox();
6402     temp_dc.DestroyClippingRegion();
6403     temp_dc.SetClippingRegion( 0, 0, dc.GetSize().x, dc.GetSize().y );
6404 
6405     // Draw the ownship on the temp_dc
6406     ocpnDC ocpndc = ocpnDC( temp_dc );
6407     ShipDraw( ocpndc );
6408 
6409     if( g_pActiveTrack && g_pActiveTrack->IsRunning() ) {
6410         TrackPoint* p = g_pActiveTrack->GetLastPoint();
6411         if( p ) {
6412             wxPoint px;
6413             GetCanvasPointPix( p->m_lat, p->m_lon, &px );
6414             ocpndc.CalcBoundingBox( px.x, px.y );
6415         }
6416     }
6417 
6418     ship_draw_rect = wxRect( temp_dc.MinX(), temp_dc.MinY(),
6419             temp_dc.MaxX() - temp_dc.MinX(),
6420             temp_dc.MaxY() - temp_dc.MinY() );
6421 
6422     wxRect own_ship_update_rect = ship_draw_rect;
6423 
6424     if( !own_ship_update_rect.IsEmpty() ) {
6425         //  The required invalidate rectangle is the union of the last drawn rectangle
6426         //  and this drawn rectangle
6427         own_ship_update_rect.Union( ship_draw_last_rect );
6428         own_ship_update_rect.Inflate( 2 );
6429     }
6430 
6431     if( !own_ship_update_rect.IsEmpty() ) RefreshRect( own_ship_update_rect, false );
6432 
6433     ship_draw_last_rect = ship_draw_rect;
6434 
6435     temp_dc.SelectObject( wxNullBitmap );
6436 }
6437 
UpdateAlerts()6438 void ChartCanvas::UpdateAlerts()
6439 {
6440     //  Get the rectangle in the current dc which bounds the detected Alert targets
6441 
6442     //  Use this dc
6443     wxClientDC dc( this );
6444 
6445     // Get dc boundary
6446     int sx, sy;
6447     dc.GetSize( &sx, &sy );
6448 
6449     //  Need a bitmap
6450     wxBitmap test_bitmap( sx, sy, -1 );
6451 
6452     // Create a memory DC
6453     wxMemoryDC temp_dc;
6454     temp_dc.SelectObject( test_bitmap );
6455 
6456     temp_dc.ResetBoundingBox();
6457     temp_dc.DestroyClippingRegion();
6458     temp_dc.SetClippingRegion( wxRect( 0, 0, sx, sy ) );
6459 
6460     // Draw the Alert Targets on the temp_dc
6461     ocpnDC ocpndc = ocpnDC( temp_dc );
6462     AlertDraw( ocpndc );
6463 
6464     //  Retrieve the drawing extents
6465     wxRect alert_rect( temp_dc.MinX(), temp_dc.MinY(), temp_dc.MaxX() - temp_dc.MinX(),
6466                        temp_dc.MaxY() - temp_dc.MinY() );
6467 
6468     if( !alert_rect.IsEmpty() ) alert_rect.Inflate( 2 );              // clear all drawing artifacts
6469 
6470     if( !alert_rect.IsEmpty() || !alert_draw_rect.IsEmpty() ) {
6471         //  The required invalidate rectangle is the union of the last drawn rectangle
6472         //  and this drawn rectangle
6473         wxRect alert_update_rect = alert_draw_rect;
6474         alert_update_rect.Union( alert_rect );
6475 
6476         //  Invalidate the rectangular region
6477         RefreshRect( alert_update_rect, false );
6478     }
6479 
6480     //  Save this rectangle for next time
6481     alert_draw_rect = alert_rect;
6482 
6483     temp_dc.SelectObject( wxNullBitmap );      // clean up
6484 }
6485 
UpdateAIS()6486 void ChartCanvas::UpdateAIS()
6487 {
6488     if(!g_pAIS) return;
6489 
6490     //  Get the rectangle in the current dc which bounds the detected AIS targets
6491 
6492     //  Use this dc
6493     wxClientDC dc( this );
6494 
6495     // Get dc boundary
6496     int sx, sy;
6497     dc.GetSize( &sx, &sy );
6498 
6499     wxRect ais_rect;
6500 
6501     //  How many targets are there?
6502 
6503     //  If more than "some number", it will be cheaper to refresh the entire screen
6504     //  than to build update rectangles for each target.
6505     AIS_Target_Hash *current_targets = g_pAIS->GetTargetList();
6506     if( current_targets->size() > 10 ) {
6507         ais_rect = wxRect( 0, 0, sx, sy );            // full screen
6508     } else {
6509         //  Need a bitmap
6510         wxBitmap test_bitmap( sx, sy, -1 );
6511 
6512         // Create a memory DC
6513         wxMemoryDC temp_dc;
6514         temp_dc.SelectObject( test_bitmap );
6515 
6516         temp_dc.ResetBoundingBox();
6517         temp_dc.DestroyClippingRegion();
6518         temp_dc.SetClippingRegion( wxRect( 0, 0, sx, sy ) );
6519 
6520         // Draw the AIS Targets on the temp_dc
6521         ocpnDC ocpndc = ocpnDC( temp_dc );
6522         AISDraw( ocpndc, GetVP(), this );
6523         AISDrawAreaNotices( ocpndc, GetVP(), this );
6524 
6525         //  Retrieve the drawing extents
6526         ais_rect = wxRect( temp_dc.MinX(), temp_dc.MinY(), temp_dc.MaxX() - temp_dc.MinX(),
6527                            temp_dc.MaxY() - temp_dc.MinY() );
6528 
6529         if( !ais_rect.IsEmpty() ) ais_rect.Inflate( 2 );              // clear all drawing artifacts
6530 
6531         temp_dc.SelectObject( wxNullBitmap );      // clean up
6532 
6533     }
6534 
6535     if( !ais_rect.IsEmpty() || !ais_draw_rect.IsEmpty() ) {
6536         //  The required invalidate rectangle is the union of the last drawn rectangle
6537         //  and this drawn rectangle
6538         wxRect ais_update_rect = ais_draw_rect;
6539         ais_update_rect.Union( ais_rect );
6540 
6541         //  Invalidate the rectangular region
6542         RefreshRect( ais_update_rect, false );
6543     }
6544 
6545     //  Save this rectangle for next time
6546     ais_draw_rect = ais_rect;
6547 
6548 }
6549 
OnActivate(wxActivateEvent & event)6550 void ChartCanvas::OnActivate( wxActivateEvent& event )
6551 {
6552     ReloadVP();
6553 }
6554 
OnSize(wxSizeEvent & event)6555 void ChartCanvas::OnSize( wxSizeEvent& event )
6556 {
6557 
6558     GetClientSize( &m_canvas_width, &m_canvas_height );
6559 
6560 //    Get some canvas metrics
6561 
6562 //          Rescale to current value, in order to rebuild VPoint data structures
6563 //          for new canvas size
6564     SetVPScale( GetVPScale() );
6565 
6566     m_absolute_min_scale_ppm = m_canvas_width / ( 1.2 * WGS84_semimajor_axis_meters * PI ); // something like 180 degrees
6567 
6568     //  Inform the parent Frame that I am being resized...
6569     gFrame->ProcessCanvasResize();
6570 
6571     //  Adjust the toolbar, if necessary
6572     if( m_toolBar ) {
6573         m_toolBar->RePosition();
6574         m_toolBar->SetGeometry(m_Compass->IsShown(), m_Compass->GetRect());
6575         m_toolBar->Realize();
6576         m_toolBar->RePosition();
6577     }
6578 
6579     //  if MUIBar is active, size the bar
6580 //     if(g_useMUI && !m_muiBar){                          // rebuild if necessary
6581 //         m_muiBar = new MUIBar(this, wxHORIZONTAL);
6582 //         m_muiBarHOSize = m_muiBar->GetSize();
6583 //     }
6584 
6585 
6586     if(m_muiBar){
6587         SetMUIBarPosition();
6588         UpdateFollowButtonState();
6589         m_muiBar->SetCanvasENCAvailable( m_bENCGroup );
6590         m_muiBar->Raise();
6591     }
6592 
6593 
6594 
6595 
6596 //    Set up the scroll margins
6597     xr_margin = m_canvas_width * 95 / 100;
6598     xl_margin = m_canvas_width * 5 / 100;
6599     yt_margin = m_canvas_height * 5 / 100;
6600     yb_margin = m_canvas_height * 95 / 100;
6601 
6602     if( m_pQuilt ) m_pQuilt->SetQuiltParameters( m_canvas_scale_factor, m_canvas_width );
6603 
6604 //    Resize the current viewport
6605 
6606     VPoint.pix_width = m_canvas_width;
6607     VPoint.pix_height = m_canvas_height;
6608 
6609     // Resize the scratch BM
6610     delete pscratch_bm;
6611     pscratch_bm = new wxBitmap( VPoint.pix_width, VPoint.pix_height, -1 );
6612     m_brepaint_piano = true;
6613 
6614     // Resize the Route Calculation BM
6615     m_dc_route.SelectObject( wxNullBitmap );
6616     delete proute_bm;
6617     proute_bm = new wxBitmap( VPoint.pix_width, VPoint.pix_height, -1 );
6618     m_dc_route.SelectObject( *proute_bm );
6619 
6620     //  Resize the saved Bitmap
6621     m_cached_chart_bm.Create( VPoint.pix_width, VPoint.pix_height, -1 );
6622 
6623     //  Resize the working Bitmap
6624     m_working_bm.Create( VPoint.pix_width, VPoint.pix_height, -1 );
6625 
6626     //  Rescale again, to capture all the changes for new canvas size
6627     SetVPScale( GetVPScale() );
6628 
6629 #ifdef ocpnUSE_GL
6630     if( /*g_bopengl &&*/ m_glcc ) {
6631         m_glcc->OnSize( event );
6632     }
6633 #endif
6634 
6635     FormatPianoKeys();
6636     //  Invalidate the whole window
6637     ReloadVP();
6638 }
6639 
ProcessNewGUIScale()6640 void ChartCanvas::ProcessNewGUIScale()
6641 {
6642     m_muiBar->Hide();
6643     delete m_muiBar;
6644     m_muiBar = 0;
6645 
6646     CreateMUIBar();
6647 }
6648 
6649 
CreateMUIBar()6650 void ChartCanvas::CreateMUIBar()
6651 {
6652     if(g_useMUI && !m_muiBar){                          // rebuild if necessary
6653 
6654         // We need to update the m_bENCGroup flag, at least for the initial creation of a MUIBar
6655         if(ChartData)
6656             m_bENCGroup = ChartData->IsENCInGroup( m_groupIndex );
6657 
6658         m_muiBar = new MUIBar(this, wxHORIZONTAL, g_toolbar_scalefactor);
6659         m_muiBar->SetColorScheme( m_cs );
6660         m_muiBarHOSize = m_muiBar->GetSize();
6661     }
6662 
6663 
6664     if(m_muiBar){
6665         SetMUIBarPosition();
6666         UpdateFollowButtonState();
6667         m_muiBar->UpdateDynamicValues();
6668         m_muiBar->SetCanvasENCAvailable( m_bENCGroup );
6669         m_muiBar->Raise();
6670     }
6671 
6672 }
6673 
6674 
SetMUIBarPosition()6675 void ChartCanvas::SetMUIBarPosition()
6676 {
6677     //  if MUIBar is active, size the bar
6678     if(m_muiBar){
6679         // We precalculate the piano width based on the canvas width
6680         int pianoWidth = GetClientSize().x * (g_btouch ? 0.7f : 0.6f);
6681 //        if(m_Piano)
6682 //            pianoWidth = m_Piano->GetWidth();
6683 
6684         if((m_muiBar->GetOrientation() == wxHORIZONTAL)){
6685             if(m_muiBarHOSize.x > (GetClientSize().x - pianoWidth)){
6686                 delete m_muiBar;
6687                 m_muiBar = new MUIBar(this, wxVERTICAL, g_toolbar_scalefactor);
6688             }
6689         }
6690 
6691         if((m_muiBar->GetOrientation() == wxVERTICAL)){
6692             if(m_muiBarHOSize.x < (GetClientSize().x - pianoWidth)){
6693                 delete m_muiBar;
6694                 m_muiBar = new MUIBar(this, wxHORIZONTAL, g_toolbar_scalefactor);
6695             }
6696         }
6697 
6698         m_muiBar->SetBestPosition();
6699     }
6700 }
6701 
DestroyMuiBar()6702 void ChartCanvas::DestroyMuiBar()
6703 {
6704     if(m_muiBar){
6705         m_muiBar->Destroy();
6706         m_muiBar = NULL;
6707     }
6708 }
6709 
ShowChartInfoWindow(int x,int dbIndex)6710 void ChartCanvas::ShowChartInfoWindow( int x, int dbIndex )
6711 {
6712     if( dbIndex >= 0 ) {
6713         if( NULL == m_pCIWin ) {
6714             m_pCIWin = new ChInfoWin( this );
6715             m_pCIWin->Hide();
6716         }
6717 
6718         if( !m_pCIWin->IsShown() || (m_pCIWin->dbIndex != dbIndex) ) {
6719             wxString s;
6720             ChartBase *pc = NULL;
6721 
6722             // TOCTOU race but worst case will reload chart.
6723             // need to lock it or the background spooler may evict charts in
6724             // OpenChartFromDBAndLock
6725             if( ( ChartData->IsChartInCache( dbIndex ) ) && ChartData->IsValid() )
6726                 pc = ChartData->OpenChartFromDBAndLock( dbIndex, FULL_INIT );   // this must come from cache
6727 
6728             int char_width, char_height;
6729             s = ChartData->GetFullChartInfo( pc, dbIndex, &char_width, &char_height );
6730             if (pc)
6731                 ChartData->UnLockCacheChart(dbIndex);
6732 
6733             m_pCIWin->SetString( s );
6734             m_pCIWin->FitToChars( char_width, char_height );
6735 
6736             wxPoint p;
6737             p.x = x;
6738              if( ( p.x + m_pCIWin->GetWinSize().x ) > m_canvas_width )
6739                  p.x = (m_canvas_width - m_pCIWin->GetWinSize().x)/2;    // centered
6740 
6741             p.y = m_canvas_height - m_Piano->GetHeight() - 4 - m_pCIWin->GetWinSize().y;
6742 
6743             m_pCIWin->dbIndex = dbIndex;
6744             m_pCIWin->SetPosition( p );
6745             m_pCIWin->SetBitmap();
6746             m_pCIWin->Refresh();
6747             m_pCIWin->Show();
6748         }
6749     } else {
6750         HideChartInfoWindow();
6751     }
6752 }
6753 
HideChartInfoWindow(void)6754 void ChartCanvas::HideChartInfoWindow( void )
6755 {
6756     if( m_pCIWin /*&& m_pCIWin->IsShown()*/ ){
6757         m_pCIWin->Hide();
6758         m_pCIWin->Destroy();
6759         m_pCIWin = NULL;
6760 
6761 #ifdef __OCPN__ANDROID__
6762         androidForceFullRepaint();
6763 #endif
6764 
6765     }
6766 }
6767 
PanTimerEvent(wxTimerEvent & event)6768 void ChartCanvas::PanTimerEvent( wxTimerEvent& event )
6769 {
6770     wxMouseEvent ev( wxEVT_MOTION );
6771     ev.m_x = mouse_x;
6772     ev.m_y = mouse_y;
6773     ev.m_leftDown = mouse_leftisdown;
6774 
6775     wxEvtHandler *evthp = GetEventHandler();
6776 
6777     ::wxPostEvent( evthp, ev );
6778 
6779 }
6780 
MovementTimerEvent(wxTimerEvent &)6781 void ChartCanvas::MovementTimerEvent( wxTimerEvent& )
6782 {
6783     DoTimedMovement();
6784 }
6785 
MovementStopTimerEvent(wxTimerEvent &)6786 void ChartCanvas::MovementStopTimerEvent( wxTimerEvent& )
6787 {
6788     StopMovement( );
6789 }
6790 
CheckEdgePan(int x,int y,bool bdragging,int margin,int delta)6791 bool ChartCanvas::CheckEdgePan( int x, int y, bool bdragging, int margin, int delta )
6792 {
6793     if(m_disable_edge_pan)
6794         return false;
6795 
6796     bool bft = false;
6797     int pan_margin = m_canvas_width * margin / 100;
6798     int pan_timer_set = 200;
6799     double pan_delta = GetVP().pix_width * delta / 100;
6800     int pan_x = 0;
6801     int pan_y = 0;
6802 
6803     if( x > m_canvas_width - pan_margin ) {
6804         bft = true;
6805         pan_x = pan_delta;
6806     }
6807 
6808     else if( x < pan_margin ) {
6809         bft = true;
6810         pan_x = -pan_delta;
6811     }
6812 
6813     if( y < pan_margin ) {
6814         bft = true;
6815         pan_y = -pan_delta;
6816     }
6817 
6818     else if( y > m_canvas_height - pan_margin ) {
6819         bft = true;
6820         pan_y = pan_delta;
6821     }
6822 
6823     //    Of course, if dragging, and the mouse left button is not down, we must stop the event injection
6824     if( bdragging ) {
6825         if( !g_btouch )
6826         {
6827             wxMouseState state = ::wxGetMouseState();
6828 #if  wxCHECK_VERSION(3,0,0)
6829             if( !state.LeftIsDown() )
6830 #else
6831             if( !state.LeftDown() )
6832 #endif
6833                 bft = false;
6834         }
6835     }
6836     if( ( bft ) && !pPanTimer->IsRunning() ) {
6837         PanCanvas( pan_x, pan_y );
6838         pPanTimer->Start( pan_timer_set, wxTIMER_ONE_SHOT );
6839         return true;
6840     }
6841 
6842     //    This mouse event must not be due to pan timer event injector
6843     //    Mouse is out of the pan zone, so prevent any orphan event injection
6844     if( ( !bft ) && pPanTimer->IsRunning() ) {
6845         pPanTimer->Stop();
6846     }
6847 
6848     return ( false );
6849 }
6850 
6851 // Look for waypoints at the current position.
6852 // Used to determine what a mouse event should act on.
6853 
FindRoutePointsAtCursor(float selectRadius,bool setBeingEdited)6854 void ChartCanvas::FindRoutePointsAtCursor( float selectRadius, bool setBeingEdited )
6855 {
6856     m_lastRoutePointEditTarget = m_pRoutePointEditTarget;       // save a copy
6857     m_pRoutePointEditTarget = NULL;
6858     m_pFoundPoint = NULL;
6859 
6860     SelectItem *pFind = NULL;
6861     SelectableItemList SelList = pSelect->FindSelectionList( this, m_cursor_lat, m_cursor_lon, SELTYPE_ROUTEPOINT );
6862     wxSelectableItemListNode *node = SelList.GetFirst();
6863     while( node ) {
6864         pFind = node->GetData();
6865 
6866         RoutePoint *frp = (RoutePoint *) pFind->m_pData1;
6867 
6868         //    Get an array of all routes using this point
6869         m_pEditRouteArray = g_pRouteMan->GetRouteArrayContaining( frp );
6870 
6871         // Use route array to determine actual visibility for the point
6872         bool brp_viz = false;
6873         if( m_pEditRouteArray ) {
6874             for( unsigned int ir = 0; ir < m_pEditRouteArray->GetCount(); ir++ ) {
6875                 Route *pr = (Route *) m_pEditRouteArray->Item( ir );
6876                 if( pr->IsVisible() ) {
6877                     brp_viz = true;
6878                     break;
6879                 }
6880             }
6881         } else
6882             brp_viz = frp->IsVisible();               // isolated point
6883 
6884         if( brp_viz ) {
6885             //    Use route array to rubberband all affected routes
6886             if( m_pEditRouteArray )                 // Editing Waypoint as part of route
6887             {
6888                 for( unsigned int ir = 0; ir < m_pEditRouteArray->GetCount(); ir++ ) {
6889                     Route *pr = (Route *) m_pEditRouteArray->Item( ir );
6890                     pr->m_bIsBeingEdited = setBeingEdited;
6891                 }
6892                 m_bRouteEditing = setBeingEdited;
6893             } else                                      // editing Mark
6894             {
6895                 frp->m_bRPIsBeingEdited = setBeingEdited;
6896                 m_bMarkEditing = setBeingEdited;
6897             }
6898 
6899             m_pRoutePointEditTarget = frp;
6900             m_pFoundPoint = pFind;
6901             break;            // out of the while(node)
6902         }
6903 
6904         node = node->GetNext();
6905     }       // while (node)
6906 }
6907 
MouseTimedEvent(wxTimerEvent & event)6908 void ChartCanvas::MouseTimedEvent( wxTimerEvent& event )
6909 {
6910     if( singleClickEventIsValid ) MouseEvent( singleClickEvent );
6911     singleClickEventIsValid = false;
6912     m_DoubleClickTimer->Stop();
6913 }
6914 
6915 bool leftIsDown;
6916 
6917 
MouseEventOverlayWindows(wxMouseEvent & event)6918 bool ChartCanvas::MouseEventOverlayWindows( wxMouseEvent& event )
6919 {
6920     if (!m_bChartDragging && !m_bDrawingRoute) {
6921         if(m_Compass && m_Compass->IsShown() && m_Compass->GetRect().Contains(event.GetPosition())) {
6922             if (m_Compass->MouseEvent( event )) {
6923                 cursor_region = CENTER;
6924                 if( !g_btouch )
6925                     SetCanvasCursor( event );
6926                 return true;
6927             }
6928         }
6929 
6930         if(MouseEventChartBar( event ))
6931             return true;
6932     }
6933     return false;
6934 }
6935 
6936 
MouseEventChartBar(wxMouseEvent & event)6937 bool ChartCanvas::MouseEventChartBar( wxMouseEvent& event )
6938 {
6939     if(!g_bShowChartBar)
6940         return false;
6941 
6942     if (! m_Piano->MouseEvent(event) )
6943         return false;
6944 
6945     cursor_region = CENTER;
6946     if( !g_btouch )
6947         SetCanvasCursor( event );
6948     return true;
6949 }
6950 
MouseEventSetup(wxMouseEvent & event,bool b_handle_dclick)6951 bool ChartCanvas::MouseEventSetup( wxMouseEvent& event,  bool b_handle_dclick )
6952 {
6953     int x, y;
6954 
6955     bool bret = false;
6956 
6957     event.GetPosition( &x, &y );
6958 
6959     m_MouseDragging = event.Dragging();
6960 
6961     //  Some systems produce null drag events, where the pointer position has not changed from the previous value.
6962     //  Detect this case, and abort further processing (FS#1748)
6963 #ifdef __WXMSW__
6964     if(event.Dragging()){
6965         if((x == mouse_x) && (y == mouse_y))
6966             return true;
6967     }
6968 #endif
6969 
6970     mouse_x = x;
6971     mouse_y = y;
6972     mouse_leftisdown = event.LeftDown();
6973     GetCanvasPixPoint( x, y, m_cursor_lat, m_cursor_lon );
6974 
6975     //  Establish the event region
6976     cursor_region = CENTER;
6977 
6978     int chartbar_height = GetChartbarHeight();
6979 
6980     if( m_Compass && m_Compass->IsShown() &&
6981         m_Compass->GetRect().Contains(event.GetPosition())) {
6982         cursor_region = CENTER;
6983     } else if( x > xr_margin ) {
6984         cursor_region = MID_RIGHT;
6985     } else if( x < xl_margin ) {
6986         cursor_region = MID_LEFT;
6987     } else if( y > yb_margin - chartbar_height &&
6988                y < m_canvas_height - chartbar_height) {
6989         cursor_region = MID_TOP;
6990     } else if( y < yt_margin ) {
6991         cursor_region = MID_BOT;
6992     } else {
6993         cursor_region = CENTER;
6994     }
6995 
6996 
6997     if( !g_btouch )
6998         SetCanvasCursor( event );
6999 
7000 
7001     // Protect from leftUp's coming from event handlers in child
7002     // windows who return focus to the canvas.
7003     leftIsDown = event.LeftDown();
7004 
7005 
7006 #ifndef __WXOSX__
7007     if (event.LeftDown()) {
7008         if ( g_bShowMenuBar == false && g_bTempShowMenuBar == true ) {
7009             // The menu bar is temporarily visible due to alt having been pressed.
7010             // Clicking will hide it, and do nothing else.
7011             g_bTempShowMenuBar = false;
7012             parent_frame->ApplyGlobalSettings(false);
7013             return(true);
7014         }
7015     }
7016 #endif
7017 
7018 // Update modifiers here; some window managers never send the key event
7019     m_modkeys = 0;
7020     if(event.ControlDown())
7021         m_modkeys |= wxMOD_CONTROL;
7022     if(event.AltDown())
7023         m_modkeys |= wxMOD_ALT;
7024 
7025 #ifdef __WXMSW__
7026     //TODO Test carefully in other platforms, remove ifdef....
7027     if( event.ButtonDown() && !HasCapture() ) CaptureMouse();
7028     if( event.ButtonUp() && HasCapture() ) ReleaseMouse();
7029 #endif
7030 
7031     if(g_pi_manager)
7032         if(g_pi_manager->SendMouseEventToPlugins( event ))
7033             return(true);                     // PlugIn did something, and does not want the canvas to do anything else
7034 
7035 
7036         // Capture LeftUp's and time them, unless it already came from the timer.
7037 
7038     if( b_handle_dclick && event.LeftUp() && !singleClickEventIsValid ) {
7039 
7040         // Ignore the second LeftUp after the DClick.
7041         if( m_DoubleClickTimer->IsRunning() ) {
7042             m_DoubleClickTimer->Stop();
7043             return(true);
7044         }
7045 
7046         // Save the event for later running if there is no DClick.
7047         m_DoubleClickTimer->Start( 350, wxTIMER_ONE_SHOT );
7048         singleClickEvent = event;
7049         singleClickEventIsValid = true;
7050         return(true);
7051     }
7052 
7053     //  This logic is necessary on MSW to handle the case where
7054     //  a context (right-click) menu is dismissed without action
7055     //  by clicking on the chart surface.
7056     //  We need to avoid an unintentional pan by eating some clicks...
7057 #ifdef __WXMSW__
7058     if( event.LeftDown() || event.LeftUp() || event.Dragging() ) {
7059         if( g_click_stop > 0 ) {
7060             g_click_stop--;
7061             return(true);
7062         }
7063     }
7064 #endif
7065 
7066 
7067 
7068     //  Kick off the Rotation control timer
7069     if( GetUpMode() == COURSE_UP_MODE ) {
7070         m_b_rot_hidef = false;
7071         pRotDefTimer->Start( 500, wxTIMER_ONE_SHOT );
7072     } else
7073         pRotDefTimer->Stop();
7074 
7075 
7076 //      Retrigger the route leg / AIS target popup timer
7077     bool bRoll = !g_btouch;
7078 #ifdef __OCPN__ANDROID__
7079     bRoll = g_bRollover;
7080 #endif
7081     if( bRoll )
7082     {
7083        if( (m_pRouteRolloverWin && m_pRouteRolloverWin->IsActive()) ||
7084 	   (m_pTrackRolloverWin && m_pTrackRolloverWin->IsActive()) ||
7085 	   (m_pAISRolloverWin && m_pAISRolloverWin->IsActive()) )
7086             m_RolloverPopupTimer.Start( 10, wxTIMER_ONE_SHOT );               // faster response while the rollover is turned on
7087         else
7088             m_RolloverPopupTimer.Start( m_rollover_popup_timer_msec, wxTIMER_ONE_SHOT );
7089     }
7090 
7091 //  Retrigger the cursor tracking timer
7092     pCurTrackTimer->Start( m_curtrack_timer_msec, wxTIMER_ONE_SHOT );
7093 
7094 //      Show cursor position on Status Bar, if present
7095 //      except for GTK, under which status bar updates are very slow
7096 //      due to Update() call.
7097 //      In this case, as a workaround, update the status window
7098 //      after an interval timer (pCurTrackTimer) pops, which will happen
7099 //      whenever the mouse has stopped moving for specified interval.
7100 //      See the method OnCursorTrackTimerEvent()
7101 #if !defined(__WXGTK__) && !defined(__WXQT__)
7102     SetCursorStatus(m_cursor_lat, m_cursor_lon);
7103 #endif
7104 
7105     //  Send the current cursor lat/lon to all PlugIns requesting it
7106     if( g_pi_manager ){
7107         //  Occasionally, MSW will produce nonsense events on right click....
7108         //  This results in an error in cursor geo position, so we skip this case
7109         if( (x >= 0) && (y >= 0) )
7110             g_pi_manager->SendCursorLatLonToAllPlugIns( m_cursor_lat, m_cursor_lon );
7111     }
7112 
7113 
7114     if(!g_btouch){
7115         if( ( m_bMeasure_Active && ( m_nMeasureState >= 2 ) ) || ( m_routeState > 1 ) )
7116         {
7117             wxPoint p = ClientToScreen( wxPoint( x, y ) );
7118 
7119             // Submerge the toolbar if necessary
7120             if( m_toolBar ) {
7121                 wxRect rect = m_toolBar->GetScreenRect();
7122                 rect.Inflate( 20 );
7123                 if( rect.Contains( p.x, p.y ) )
7124                     m_toolBar->Submerge();
7125             }
7126         }
7127     }
7128 
7129     if(1/*!g_btouch*/ ){
7130         //    Route Creation Rubber Banding
7131         if( m_routeState >= 2 ) {
7132             r_rband.x = x;
7133             r_rband.y = y;
7134             m_bDrawingRoute = true;
7135 
7136             if(!g_btouch )
7137                 CheckEdgePan( x, y, event.Dragging(), 5, 2 );
7138             Refresh( false );
7139         }
7140 
7141 
7142         //    Measure Tool Rubber Banding
7143         if( m_bMeasure_Active && ( m_nMeasureState >= 2 ) ) {
7144             r_rband.x = x;
7145             r_rband.y = y;
7146             m_bDrawingRoute = true;
7147 
7148             if(!g_btouch )
7149                 CheckEdgePan( x, y, event.Dragging(), 5, 2 );
7150             Refresh( false );
7151         }
7152     }
7153     return bret;
7154 
7155 }
7156 
CallPopupMenu(int x,int y)7157 void ChartCanvas::CallPopupMenu(int x, int y)
7158 {
7159     int mx, my;
7160     mx = x;
7161     my = y;
7162 
7163     last_drag.x = mx;
7164     last_drag.y = my;
7165     if( m_routeState ) {                    // creating route?
7166         InvokeCanvasMenu(x, y, SELTYPE_ROUTECREATE);
7167         return;
7168     }
7169     // General Right Click
7170     // Look for selectable objects
7171     double slat, slon;
7172     slat = m_cursor_lat;
7173     slon = m_cursor_lon;
7174 
7175     #if defined(__WXMAC__) || defined(__OCPN__ANDROID__)
7176     wxScreenDC sdc;
7177     ocpnDC dc( sdc );
7178     #else
7179     wxClientDC cdc( GetParent() );
7180     ocpnDC dc( cdc );
7181     #endif
7182 
7183     SelectItem *pFindAIS;
7184     SelectItem *pFindRP;
7185     SelectItem *pFindRouteSeg;
7186     SelectItem *pFindTrackSeg;
7187     SelectItem *pFindCurrent = NULL;
7188     SelectItem *pFindTide = NULL;
7189 
7190     //    Deselect any current objects
7191     if( m_pSelectedRoute ) {
7192         m_pSelectedRoute->m_bRtIsSelected = false;        // Only one selection at a time
7193         m_pSelectedRoute->DeSelectRoute();
7194         #ifdef ocpnUSE_GL
7195         if(g_bopengl && m_glcc){
7196             InvalidateGL();
7197             Update();
7198         }
7199         else
7200             #endif
7201             m_pSelectedRoute->Draw( dc, this, GetVP().GetBBox() );
7202     }
7203 
7204     if( m_pFoundRoutePoint ) {
7205         m_pFoundRoutePoint->m_bPtIsSelected = false;
7206         m_pFoundRoutePoint->Draw( dc, this );
7207         RefreshRect( m_pFoundRoutePoint->CurrentRect_in_DC );
7208     }
7209 
7210 	/**in touch mode a route point could have been selected and draghandle icon shown so clear the selection*/
7211 	if (g_btouch && m_pRoutePointEditTarget) {
7212 		m_pRoutePointEditTarget->m_bRPIsBeingEdited = false;
7213 		m_pRoutePointEditTarget->m_bPtIsSelected = false;
7214 		m_pRoutePointEditTarget->EnableDragHandle(false);
7215 	}
7216 
7217     //      Get all the selectable things at the cursor
7218     pFindAIS = pSelectAIS->FindSelection( this, slat, slon, SELTYPE_AISTARGET );
7219     pFindRP = pSelect->FindSelection( this, slat, slon, SELTYPE_ROUTEPOINT );
7220     pFindRouteSeg = pSelect->FindSelection( this, slat, slon, SELTYPE_ROUTESEGMENT );
7221     pFindTrackSeg = pSelect->FindSelection( this, slat, slon, SELTYPE_TRACKSEGMENT );
7222 
7223     if( m_bShowCurrent ) pFindCurrent = pSelectTC->FindSelection( this, slat, slon, SELTYPE_CURRENTPOINT );
7224 
7225     if( m_bShowTide )                                // look for tide stations
7226         pFindTide = pSelectTC->FindSelection( this, slat, slon, SELTYPE_TIDEPOINT );
7227 
7228     int seltype = 0;
7229 
7230     //    Try for AIS targets first
7231     if( pFindAIS ) {
7232         m_FoundAIS_MMSI = pFindAIS->GetUserData();
7233 
7234         //      Make sure the target data is available
7235         if( g_pAIS->Get_Target_Data_From_MMSI( m_FoundAIS_MMSI ) ) seltype |=
7236             SELTYPE_AISTARGET;
7237     }
7238 
7239     //    Now the various Route Parts
7240 
7241     m_pFoundRoutePoint = NULL;
7242     if( pFindRP ) {
7243         RoutePoint *pFirstVizPoint = NULL;
7244         RoutePoint *pFoundActiveRoutePoint = NULL;
7245         RoutePoint *pFoundVizRoutePoint = NULL;
7246         Route *pSelectedActiveRoute = NULL;
7247         Route *pSelectedVizRoute = NULL;
7248 
7249         //There is at least one routepoint, so get the whole list
7250         SelectableItemList SelList = pSelect->FindSelectionList( this, slat, slon, SELTYPE_ROUTEPOINT );
7251         wxSelectableItemListNode *node = SelList.GetFirst();
7252         while( node ) {
7253             SelectItem *pFindSel = node->GetData();
7254 
7255             RoutePoint *prp = (RoutePoint *) pFindSel->m_pData1;        //candidate
7256 
7257             //    Get an array of all routes using this point
7258             wxArrayPtrVoid *proute_array = g_pRouteMan->GetRouteArrayContaining( prp );
7259 
7260             // Use route array (if any) to determine actual visibility for this point
7261             bool brp_viz = false;
7262             if( proute_array ) {
7263                 for( unsigned int ir = 0; ir < proute_array->GetCount(); ir++ ) {
7264                     Route *pr = (Route *) proute_array->Item( ir );
7265                     if( pr->IsVisible() ) {
7266                         brp_viz = true;
7267                         break;
7268                     }
7269                 }
7270                 if( !brp_viz  && prp->m_bKeepXRoute)    // is not visible as part of route, but still exists as a waypoint
7271                     brp_viz = prp->IsVisible();         //  so treat as isolated point
7272 
7273             } else
7274                 brp_viz = prp->IsVisible();               // isolated point
7275 
7276             if( ( NULL == pFirstVizPoint ) && brp_viz ) pFirstVizPoint = prp;
7277 
7278             // Use route array to choose the appropriate route
7279             // Give preference to any active route, otherwise select the first visible route in the array for this point
7280             m_pSelectedRoute = NULL;
7281             if( proute_array ) {
7282                 for( unsigned int ir = 0; ir < proute_array->GetCount(); ir++ ) {
7283                     Route *pr = (Route *) proute_array->Item( ir );
7284                     if( pr->m_bRtIsActive ) {
7285                         pSelectedActiveRoute = pr;
7286                         pFoundActiveRoutePoint = prp;
7287                         break;
7288                     }
7289                 }
7290 
7291                 if( NULL == pSelectedVizRoute ) {
7292                     for( unsigned int ir = 0; ir < proute_array->GetCount(); ir++ ) {
7293                         Route *pr = (Route *) proute_array->Item( ir );
7294                         if( pr->IsVisible() ) {
7295                             pSelectedVizRoute = pr;
7296                             pFoundVizRoutePoint = prp;
7297                             break;
7298                         }
7299                     }
7300                 }
7301 
7302                 delete proute_array;
7303             }
7304 
7305             node = node->GetNext();
7306         }
7307 
7308         //      Now choose the "best" selections
7309         if( pFoundActiveRoutePoint ) {
7310             m_pFoundRoutePoint = pFoundActiveRoutePoint;
7311             m_pSelectedRoute = pSelectedActiveRoute;
7312         } else if( pFoundVizRoutePoint ) {
7313             m_pFoundRoutePoint = pFoundVizRoutePoint;
7314             m_pSelectedRoute = pSelectedVizRoute;
7315         } else
7316             // default is first visible point in list
7317             m_pFoundRoutePoint = pFirstVizPoint;
7318 
7319         if( m_pSelectedRoute ) {
7320             if( m_pSelectedRoute->IsVisible() ) seltype |= SELTYPE_ROUTEPOINT;
7321         } else if( m_pFoundRoutePoint ) seltype |= SELTYPE_MARKPOINT;
7322 
7323         //      Highlite the selected point, to verify the proper right click selection
7324         if( m_pFoundRoutePoint) {
7325             m_pFoundRoutePoint->m_bPtIsSelected = true;
7326             wxRect wp_rect;
7327             m_pFoundRoutePoint->CalculateDCRect( m_dc_route, this, &wp_rect );
7328             RefreshRect( wp_rect, true );
7329         }
7330 
7331     }
7332 
7333     // Note here that we use SELTYPE_ROUTESEGMENT to select tracks as well as routes
7334     // But call the popup handler with identifier appropriate to the type
7335     if( pFindRouteSeg )                  // there is at least one select item
7336     {
7337         SelectableItemList SelList = pSelect->FindSelectionList( this, slat, slon, SELTYPE_ROUTESEGMENT );
7338 
7339         if( NULL == m_pSelectedRoute )  // the case where a segment only is selected
7340         {
7341             //  Choose the first visible route containing segment in the list
7342             wxSelectableItemListNode *node = SelList.GetFirst();
7343             while( node ) {
7344                 SelectItem *pFindSel = node->GetData();
7345 
7346                 Route *pr = (Route *) pFindSel->m_pData3;
7347                 if( pr->IsVisible() ) {
7348                     m_pSelectedRoute = pr;
7349                     break;
7350                 }
7351                 node = node->GetNext();
7352             }
7353         }
7354 
7355         if( m_pSelectedRoute ) {
7356             if( NULL == m_pFoundRoutePoint )
7357                 m_pFoundRoutePoint =   (RoutePoint *) pFindRouteSeg->m_pData1;
7358 
7359             m_pSelectedRoute->m_bRtIsSelected = !(seltype & SELTYPE_ROUTEPOINT);
7360             if( m_pSelectedRoute->m_bRtIsSelected ){
7361                 #ifdef ocpnUSE_GL
7362                 if(g_bopengl && m_glcc){
7363                     InvalidateGL();
7364                     Update();
7365                 }
7366                 else
7367                     #endif
7368                     m_pSelectedRoute->Draw( dc, this, GetVP().GetBBox() );
7369             }
7370 
7371             seltype |= SELTYPE_ROUTESEGMENT;
7372         }
7373 
7374     }
7375 
7376     if( pFindTrackSeg ) {
7377         m_pSelectedTrack = NULL;
7378         SelectableItemList SelList = pSelect->FindSelectionList( this, slat, slon, SELTYPE_TRACKSEGMENT );
7379 
7380         //  Choose the first visible track containing segment in the list
7381         wxSelectableItemListNode *node = SelList.GetFirst();
7382         while( node ) {
7383             SelectItem *pFindSel = node->GetData();
7384 
7385             Track *pt = (Track *) pFindSel->m_pData3;
7386             if( pt->IsVisible() ) {
7387                 m_pSelectedTrack = pt;
7388                 break;
7389             }
7390             node = node->GetNext();
7391         }
7392 
7393         if( m_pSelectedTrack ) seltype |= SELTYPE_TRACKSEGMENT;
7394     }
7395 
7396     bool bseltc = false;
7397     //                      if(0 == seltype)
7398     {
7399         if( pFindCurrent ) {
7400             // There may be multiple current entries at the same point.
7401             // For example, there often is a current substation (with directions specified)
7402             // co-located with its master.  We want to select the substation, so that
7403             // the direction will be properly indicated on the graphic.
7404             // So, we search the select list looking for IDX_type == 'c' (i.e substation)
7405             IDX_entry *pIDX_best_candidate;
7406 
7407             SelectItem *pFind = NULL;
7408             SelectableItemList SelList = pSelectTC->FindSelectionList( this, m_cursor_lat, m_cursor_lon, SELTYPE_CURRENTPOINT );
7409 
7410             //      Default is first entry
7411             wxSelectableItemListNode *node = SelList.GetFirst();
7412             pFind = node->GetData();
7413             pIDX_best_candidate = (IDX_entry *) ( pFind->m_pData1 );
7414 
7415             if( SelList.GetCount() > 1 ) {
7416                 node = node->GetNext();
7417                 while( node ) {
7418                     pFind = node->GetData();
7419                     IDX_entry *pIDX_candidate = (IDX_entry *) ( pFind->m_pData1 );
7420                     if( pIDX_candidate->IDX_type == 'c' ) {
7421                         pIDX_best_candidate = pIDX_candidate;
7422                         break;
7423                     }
7424 
7425                     node = node->GetNext();
7426                 }       // while (node)
7427             } else {
7428                 wxSelectableItemListNode *node = SelList.GetFirst();
7429                 pFind = node->GetData();
7430                 pIDX_best_candidate = (IDX_entry *) ( pFind->m_pData1 );
7431             }
7432 
7433             m_pIDXCandidate = pIDX_best_candidate;
7434 
7435             if( 0 == seltype ) {
7436                 DrawTCWindow( x, y, (void *) pIDX_best_candidate );
7437                 Refresh( false );
7438                 bseltc = true;
7439             } else
7440                 seltype |= SELTYPE_CURRENTPOINT;
7441         }
7442 
7443         else if( pFindTide ) {
7444             m_pIDXCandidate = (IDX_entry *) pFindTide->m_pData1;
7445 
7446             if( 0 == seltype ) {
7447                 DrawTCWindow( x, y, (void *) pFindTide->m_pData1 );
7448                 Refresh( false );
7449                 bseltc = true;
7450             } else
7451                 seltype |= SELTYPE_TIDEPOINT;
7452         }
7453     }
7454 
7455     if( 0 == seltype )
7456         seltype |= SELTYPE_UNKNOWN;
7457 
7458     if( !bseltc ){
7459         InvokeCanvasMenu(x, y, seltype);
7460 
7461         // Clean up if not deleted in InvokeCanvasMenu
7462         if( m_pSelectedRoute && g_pRouteMan->IsRouteValid(m_pSelectedRoute) ) {
7463             m_pSelectedRoute->m_bRtIsSelected = false;
7464         }
7465 
7466         m_pSelectedRoute = NULL;
7467 
7468         if( m_pFoundRoutePoint ) {
7469             if (pSelect->IsSelectableRoutePointValid(m_pFoundRoutePoint))
7470                 m_pFoundRoutePoint->m_bPtIsSelected = false;
7471         }
7472         m_pFoundRoutePoint = NULL;
7473 
7474         Refresh( true );
7475 
7476     }
7477 
7478     // Seth: Is this refresh needed?
7479     Refresh( false );            // needed for MSW, not GTK  Why??
7480 }
7481 
MouseEventProcessObjects(wxMouseEvent & event)7482 bool ChartCanvas::MouseEventProcessObjects( wxMouseEvent& event )
7483 {
7484     // For now just bail out completely if the point clicked is not on the chart
7485     if(std::isnan(m_cursor_lat))
7486         return false;
7487 
7488     //          Mouse Clicks
7489     bool ret = false;        // return true if processed
7490 
7491     int x, y, mx, my;
7492     event.GetPosition( &x, &y );
7493     mx = x;
7494     my = y;
7495 
7496     //    Calculate meaningful SelectRadius
7497     float SelectRadius;
7498     SelectRadius = g_Platform->GetSelectRadiusPix() / ( m_true_scale_ppm * 1852 * 60 );  // Degrees, approximately
7499 
7500 ///
7501     // We start with Double Click processing. The first left click just starts a timer and
7502     // is remembered, then we actually do something if there is a LeftDClick.
7503     // If there is, the two single clicks are ignored.
7504 
7505     if( event.LeftDClick() && ( cursor_region == CENTER ) ) {
7506 
7507         m_DoubleClickTimer->Start();
7508         singleClickEventIsValid = false;
7509 
7510         double zlat, zlon;
7511         GetCanvasPixPoint( x, y, zlat, zlon );
7512 
7513         if(m_bShowAIS){
7514             SelectItem *pFindAIS;
7515             pFindAIS = pSelectAIS->FindSelection( this, zlat, zlon, SELTYPE_AISTARGET );
7516 
7517             if( pFindAIS ) {
7518                 m_FoundAIS_MMSI = pFindAIS->GetUserData();
7519                 if( g_pAIS->Get_Target_Data_From_MMSI( m_FoundAIS_MMSI ) ) {
7520                     wxWindow *pwin = wxDynamicCast(this, wxWindow);
7521                     ShowAISTargetQueryDialog( pwin, m_FoundAIS_MMSI );
7522                 }
7523                 return true;
7524             }
7525         }
7526 
7527         SelectableItemList rpSelList = pSelect->FindSelectionList( this, zlat, zlon, SELTYPE_ROUTEPOINT );
7528         wxSelectableItemListNode *node = rpSelList.GetFirst();
7529         bool b_onRPtarget = false;
7530         while( node ) {
7531             SelectItem *pFind = node->GetData();
7532             RoutePoint *frp = (RoutePoint *) pFind->m_pData1;
7533             if(m_pRoutePointEditTarget && (frp == m_pRoutePointEditTarget) ){
7534                 b_onRPtarget = true;
7535                 break;
7536             }
7537             node = node->GetNext();
7538         }
7539 
7540         //      Double tap with selected RoutePoint or Mark
7541 
7542         if(m_pRoutePointEditTarget){
7543             if( b_onRPtarget ) {
7544                 ShowMarkPropertiesDialog( m_pRoutePointEditTarget );
7545                 return true;
7546             }
7547             else {
7548                 m_pRoutePointEditTarget->m_bRPIsBeingEdited = false;
7549                 m_pRoutePointEditTarget->m_bPtIsSelected = false;
7550 				if (g_btouch)
7551 					m_pRoutePointEditTarget->EnableDragHandle(false);
7552                 wxRect wp_rect;
7553                 m_pRoutePointEditTarget->CalculateDCRect( m_dc_route, this, &wp_rect );
7554                 m_pRoutePointEditTarget = NULL;         //cancel selection
7555                 RefreshRect( wp_rect, true );
7556                 return true;
7557             }
7558         }
7559         else{
7560             node = rpSelList.GetFirst();
7561             if( node ) {
7562                 SelectItem *pFind = node->GetData();
7563                 RoutePoint *frp = (RoutePoint *) pFind->m_pData1;
7564                 if( frp ){
7565                     wxArrayPtrVoid *proute_array = g_pRouteMan->GetRouteArrayContaining( frp );
7566 
7567                     // Use route array (if any) to determine actual visibility for this point
7568                     bool brp_viz = false;
7569                     if( proute_array ){
7570                         for( unsigned int ir = 0; ir < proute_array->GetCount(); ir++ )
7571                         {
7572                             Route *pr = (Route *) proute_array->Item( ir );
7573                             if( pr->IsVisible() )
7574                             {
7575                                 brp_viz = true;
7576                                 break;
7577                             }
7578                         }
7579                         if( !brp_viz && frp->m_bKeepXRoute ) // is not visible as part of route, but still exists as a waypoint
7580                             brp_viz = frp->IsVisible(); // so treat as isolated point
7581                         } else
7582                             brp_viz = frp->IsVisible(); // isolated point
7583 
7584                     if( brp_viz ){
7585                         ShowMarkPropertiesDialog( frp );
7586                         return true;
7587                     }
7588                 }
7589             }
7590         }
7591 
7592 
7593 
7594         SelectItem* cursorItem;
7595         cursorItem = pSelect->FindSelection( this, zlat, zlon, SELTYPE_ROUTESEGMENT );
7596 
7597         if( cursorItem ) {
7598             Route *pr = (Route *) cursorItem->m_pData3;
7599             if( pr->IsVisible() ) {
7600                 ShowRoutePropertiesDialog( _("Route Properties"), pr );
7601                 return true;
7602             }
7603         }
7604 
7605         cursorItem = pSelect->FindSelection( this, zlat, zlon, SELTYPE_TRACKSEGMENT );
7606 
7607         if( cursorItem ) {
7608             Track *pt = (Track *) cursorItem->m_pData3;
7609             if( pt->IsVisible() ) {
7610                 ShowTrackPropertiesDialog( pt );
7611                 return true;
7612             }
7613         }
7614 
7615         // Found no object to act on, so show chart info.
7616 
7617         ShowObjectQueryWindow( x, y, zlat, zlon );
7618         return true;
7619     }
7620 
7621 
7622 
7623 ///
7624     if( event.LeftDown() ) {
7625         //  This really should not be needed, but....
7626         //  on Windows, when using wxAUIManager, sometimes the focus is lost
7627         //  when clicking into another pane, e.g.the AIS target list, and then back to this pane.
7628         //  Oddly, some mouse events are not lost, however.  Like this one....
7629         SetFocus();
7630 
7631         last_drag.x = mx;
7632         last_drag.y = my;
7633         leftIsDown = true;
7634 
7635         if(!g_btouch){
7636             if( m_routeState )                  // creating route?
7637             {
7638                 double rlat, rlon;
7639 
7640                 SetCursor( *pCursorPencil );
7641                 rlat = m_cursor_lat;
7642                 rlon = m_cursor_lon;
7643 
7644                 m_bRouteEditing = true;
7645 
7646                 if( m_routeState == 1 ) {
7647                     m_pMouseRoute = new Route();
7648                     pRouteList->Append( m_pMouseRoute );
7649                     r_rband.x = x;
7650                     r_rband.y = y;
7651                 }
7652 
7653                 //    Check to see if there is a nearby point which may be reused
7654                 RoutePoint *pMousePoint = NULL;
7655 
7656                 //    Calculate meaningful SelectRadius
7657                 double nearby_radius_meters = g_Platform->GetSelectRadiusPix() / m_true_scale_ppm;
7658 
7659                 RoutePoint *pNearbyPoint = pWayPointMan->GetNearbyWaypoint( rlat, rlon,
7660                                                                             nearby_radius_meters );
7661                 if( pNearbyPoint && ( pNearbyPoint != m_prev_pMousePoint )
7662                     && !pNearbyPoint->m_bIsInLayer && pNearbyPoint->IsVisible() )
7663                 {
7664                     wxArrayPtrVoid *proute_array = g_pRouteMan->GetRouteArrayContaining( pNearbyPoint );
7665 
7666                     // Use route array (if any) to determine actual visibility for this point
7667                     bool brp_viz = false;
7668                     if( proute_array ){
7669                         for( unsigned int ir = 0; ir < proute_array->GetCount(); ir++ ){
7670                             Route *pr = (Route *) proute_array->Item( ir );
7671                             if( pr->IsVisible() ) {
7672                                 brp_viz = true;
7673                                 break;
7674                             }
7675                         }
7676 
7677                         if( !brp_viz && pNearbyPoint->m_bKeepXRoute ) // is not visible as part of route, but still exists as a waypoint
7678                             brp_viz = pNearbyPoint->IsVisible(); // so treat as isolated point
7679                     }
7680                     else
7681                         brp_viz = pNearbyPoint->IsVisible(); // isolated point
7682 
7683 
7684                     if( brp_viz ){
7685                         m_FinishRouteOnKillFocus = false;              // Avoid route finish on focus change for message dialog
7686                         int dlg_return = OCPNMessageBox( this, _("Use nearby waypoint?"),
7687                                                  _("OpenCPN Route Create"),
7688                                                    (long) wxYES_NO | wxCANCEL | wxYES_DEFAULT );
7689                         m_FinishRouteOnKillFocus = true;
7690 
7691                         if( dlg_return == wxID_YES ) {
7692                             pMousePoint = pNearbyPoint;
7693 
7694                             // Using existing waypoint, so nothing to delete for undo.
7695                             if( m_routeState > 1 )
7696                                 undo->BeforeUndoableAction( Undo_AppendWaypoint, pMousePoint, Undo_HasParent, NULL );
7697 
7698                             // check all other routes to see if this point appears in any other route
7699                             // If it appears in NO other route, then it should e considered an isolated mark
7700                             if( !g_pRouteMan->FindRouteContainingWaypoint( pMousePoint ) )
7701                                 pMousePoint->m_bKeepXRoute = true;
7702                         }
7703                     }
7704                 }
7705 
7706                 if( NULL == pMousePoint ) {                 // need a new point
7707                     pMousePoint = new RoutePoint( rlat, rlon, g_default_routepoint_icon, _T(""), wxEmptyString );
7708                     pMousePoint->SetNameShown( false );
7709 
7710                     pConfig->AddNewWayPoint( pMousePoint, -1 );    // use auto next num
7711                     pSelect->AddSelectableRoutePoint( rlat, rlon, pMousePoint );
7712 
7713                     if( m_routeState > 1 )
7714                         undo->BeforeUndoableAction( Undo_AppendWaypoint, pMousePoint, Undo_IsOrphanded, NULL );
7715                 }
7716 
7717                 if(m_pMouseRoute){
7718                     if( m_routeState == 1 ) {
7719                         // First point in the route.
7720                         m_pMouseRoute->AddPoint( pMousePoint );
7721                     } else {
7722                         if( m_pMouseRoute->m_NextLegGreatCircle ) {
7723                             double rhumbBearing, rhumbDist, gcBearing, gcDist;
7724                             DistanceBearingMercator( rlat, rlon, m_prev_rlat, m_prev_rlon, &rhumbBearing, &rhumbDist );
7725                             Geodesic::GreatCircleDistBear( m_prev_rlon, m_prev_rlat, rlon, rlat, &gcDist, &gcBearing, NULL );
7726                             double gcDistNM = gcDist / 1852.0;
7727 
7728                             // Empirically found expression to get reasonable route segments.
7729                             int segmentCount = (3.0 + (rhumbDist - gcDistNM)) / pow(rhumbDist-gcDistNM-1, 0.5 );
7730 
7731                             wxString msg;
7732                             msg << _("For this leg the Great Circle route is ")
7733                             << FormatDistanceAdaptive( rhumbDist - gcDistNM ) << _(" shorter than rhumbline.\n\n")
7734                             << _("Would you like include the Great Circle routing points for this leg?");
7735 
7736                             m_FinishRouteOnKillFocus = false;
7737                             m_disable_edge_pan = true;  // This helps on OS X if MessageBox does not fully capture mouse
7738 
7739                             int answer = OCPNMessageBox( this, msg, _("OpenCPN Route Create"), wxYES_NO | wxNO_DEFAULT );
7740 
7741                             m_disable_edge_pan = false;
7742                             m_FinishRouteOnKillFocus = true;
7743 
7744                             if( answer == wxID_YES ) {
7745                                 RoutePoint* gcPoint;
7746                                 RoutePoint* prevGcPoint = m_prev_pMousePoint;
7747                                 wxRealPoint gcCoord;
7748 
7749                                 for( int i = 1; i <= segmentCount; i++ ) {
7750                                     double fraction = (double) i * ( 1.0 / (double) segmentCount );
7751                                     Geodesic::GreatCircleTravel( m_prev_rlon, m_prev_rlat, gcDist * fraction,
7752                                                                  gcBearing, &gcCoord.x, &gcCoord.y, NULL );
7753 
7754                                     if( i < segmentCount ) {
7755                                         gcPoint = new RoutePoint( gcCoord.y, gcCoord.x, _T("xmblue"), _T(""),
7756                                                                   wxEmptyString );
7757                                         gcPoint->SetNameShown( false );
7758                                         pConfig->AddNewWayPoint( gcPoint, -1 );
7759                                         pSelect->AddSelectableRoutePoint( gcCoord.y, gcCoord.x, gcPoint );
7760                                     } else {
7761                                         gcPoint = pMousePoint; // Last point, previously exsisting!
7762                                     }
7763 
7764                                     m_pMouseRoute->AddPoint( gcPoint );
7765                                     pSelect->AddSelectableRouteSegment( prevGcPoint->m_lat, prevGcPoint->m_lon,
7766                                                                         gcPoint->m_lat, gcPoint->m_lon, prevGcPoint, gcPoint, m_pMouseRoute );
7767                                     prevGcPoint = gcPoint;
7768                                 }
7769 
7770                                 undo->CancelUndoableAction( true );
7771 
7772                             } else {
7773                                 m_pMouseRoute->AddPoint( pMousePoint );
7774                                 pSelect->AddSelectableRouteSegment( m_prev_rlat, m_prev_rlon,
7775                                                                     rlat, rlon, m_prev_pMousePoint, pMousePoint, m_pMouseRoute );
7776                                 undo->AfterUndoableAction( m_pMouseRoute );
7777                             }
7778                         } else {
7779                             // Ordinary rhumblinesegment.
7780                             m_pMouseRoute->AddPoint( pMousePoint );
7781                             pSelect->AddSelectableRouteSegment( m_prev_rlat, m_prev_rlon,
7782                                                                 rlat, rlon, m_prev_pMousePoint, pMousePoint, m_pMouseRoute );
7783                             undo->AfterUndoableAction( m_pMouseRoute );
7784                         }
7785                     }
7786                 }
7787 
7788                 m_prev_rlat = rlat;
7789                 m_prev_rlon = rlon;
7790                 m_prev_pMousePoint = pMousePoint;
7791                 if(m_pMouseRoute)
7792                     m_pMouseRoute->m_lastMousePointIndex = m_pMouseRoute->GetnPoints();
7793 
7794                 m_routeState++;
7795                 gFrame->RefreshAllCanvas();
7796                 ret = true;
7797             }
7798 
7799             else if( m_bMeasure_Active && m_nMeasureState )   // measure tool?
7800             {
7801                 SetCursor( *pCursorPencil );
7802 
7803                 if( m_nMeasureState == 1 ) {
7804                     m_pMeasureRoute = new Route();
7805                     pRouteList->Append( m_pMeasureRoute );
7806                     r_rband.x = x;
7807                     r_rband.y = y;
7808                 }
7809 
7810                 RoutePoint *pMousePoint = new RoutePoint( m_cursor_lat, m_cursor_lon,
7811                                                           wxString( _T ( "circle" ) ), wxEmptyString, wxEmptyString );
7812                 pMousePoint->m_bShowName = false;
7813 
7814                 m_pMeasureRoute->AddPoint( pMousePoint );
7815 
7816                 m_prev_rlat = m_cursor_lat;
7817                 m_prev_rlon = m_cursor_lon;
7818                 m_prev_pMousePoint = pMousePoint;
7819                 m_pMeasureRoute->m_lastMousePointIndex = m_pMeasureRoute->GetnPoints();
7820 
7821                 m_nMeasureState++;
7822                 gFrame->RefreshAllCanvas();
7823                 ret = true;
7824             }
7825 
7826             else {
7827                 FindRoutePointsAtCursor( SelectRadius, true );    // Not creating Route
7828             }
7829         }  // !g_btouch
7830         else {                  // g_btouch
7831 
7832            if(( m_bMeasure_Active && m_nMeasureState ) || ( m_routeState )){
7833 
7834                // if near screen edge, pan with injection
7835                //                if( CheckEdgePan( x, y, true, 5, 10 ) ) {
7836                                 //                    return;
7837                                 //                }
7838 
7839            }
7840         }
7841 
7842         if(ret)
7843             return true;
7844     }
7845 
7846     if( event.Dragging() ) {
7847 
7848         //in touch screen mode ensure the finger/cursor is on the selected point's radius to allow dragging
7849         if( g_btouch ) {
7850             if( m_pRoutePointEditTarget && !m_bIsInRadius ) {
7851                 SelectItem *pFind = NULL;
7852                 SelectableItemList SelList = pSelect->FindSelectionList( this, m_cursor_lat, m_cursor_lon, SELTYPE_ROUTEPOINT );
7853                 wxSelectableItemListNode *node = SelList.GetFirst();
7854                 while( node ) {
7855                     pFind = node->GetData();
7856                     RoutePoint *frp = (RoutePoint *) pFind->m_pData1;
7857                     if( m_pRoutePointEditTarget == frp )
7858                         m_bIsInRadius = true;
7859                     node = node->GetNext();
7860                 }
7861             }
7862 
7863             // Check for use of dragHandle
7864             if(m_pRoutePointEditTarget && m_pRoutePointEditTarget->IsDragHandleEnabled()){
7865                 SelectItem *pFind = NULL;
7866                 SelectableItemList SelList = pSelect->FindSelectionList( this, m_cursor_lat, m_cursor_lon, SELTYPE_DRAGHANDLE );
7867                 wxSelectableItemListNode *node = SelList.GetFirst();
7868                 while( node ) {
7869                     pFind = node->GetData();
7870                     RoutePoint *frp = (RoutePoint *) pFind->m_pData1;
7871                     if( m_pRoutePointEditTarget == frp ){
7872                         m_bIsInRadius = true;
7873                         break;
7874                     }
7875                     node = node->GetNext();
7876                 }
7877 
7878                 if(!m_dragoffsetSet){
7879                     m_pRoutePointEditTarget->PresetDragOffset(this, mouse_x, mouse_y);
7880                     m_dragoffsetSet = true;
7881                 }
7882             }
7883 
7884         }
7885 
7886 
7887         if( m_bRouteEditing && m_pRoutePointEditTarget ) {
7888 
7889             bool DraggingAllowed = g_btouch ? m_bIsInRadius : true;
7890 
7891             if( NULL == g_pMarkInfoDialog ) {
7892                 if( g_bWayPointPreventDragging ) DraggingAllowed = false;
7893             } else if( !g_pMarkInfoDialog->IsShown() && g_bWayPointPreventDragging )
7894                 DraggingAllowed = false;
7895 
7896             if( m_pRoutePointEditTarget && ( m_pRoutePointEditTarget->GetIconName() == _T("mob") ) )
7897                 DraggingAllowed = false;
7898 
7899             if( m_pRoutePointEditTarget->m_bIsInLayer )
7900                 DraggingAllowed = false;
7901 
7902             if( DraggingAllowed ) {
7903 
7904                 if( !undo->InUndoableAction() ) {
7905                     undo->BeforeUndoableAction( Undo_MoveWaypoint, m_pRoutePointEditTarget,
7906                                                 Undo_NeedsCopy, m_pFoundPoint );
7907                 }
7908 
7909                 // Get the update rectangle for the union of the un-edited routes
7910                 wxRect pre_rect;
7911 
7912                 if( !g_bopengl && m_pEditRouteArray ) {
7913                     for( unsigned int ir = 0; ir < m_pEditRouteArray->GetCount(); ir++ ) {
7914                         Route *pr = (Route *) m_pEditRouteArray->Item( ir );
7915                         //      Need to validate route pointer
7916                         //      Route may be gone due to drgging close to ownship with
7917                         //      "Delete On Arrival" state set, as in the case of
7918                         //      navigating to an isolated waypoint on a temporary route
7919                         if( g_pRouteMan->IsRouteValid(pr) ) {
7920                             wxRect route_rect;
7921                             pr->CalculateDCRect( m_dc_route, this, &route_rect );
7922                             pre_rect.Union( route_rect );
7923                         }
7924                     }
7925                 }
7926 
7927                 double new_cursor_lat = m_cursor_lat;
7928                 double new_cursor_lon = m_cursor_lon;
7929 
7930                 if( CheckEdgePan( x, y, true, 5, 2 ) )
7931                     GetCanvasPixPoint( x, y, new_cursor_lat, new_cursor_lon );
7932 
7933                 // update the point itself
7934                 if( g_btouch ) {
7935                     //m_pRoutePointEditTarget->SetPointFromDraghandlePoint(VPoint, new_cursor_lat, new_cursor_lon);
7936                     m_pRoutePointEditTarget->SetPointFromDraghandlePoint(this, mouse_x, mouse_y);
7937                     // update the Drag Handle entry in the pSelect list
7938                     pSelect->ModifySelectablePoint( new_cursor_lat, new_cursor_lon, m_pRoutePointEditTarget, SELTYPE_DRAGHANDLE );
7939                     m_pFoundPoint->m_slat = m_pRoutePointEditTarget->m_lat;             // update the SelectList entry
7940                     m_pFoundPoint->m_slon = m_pRoutePointEditTarget->m_lon;
7941                 }
7942                 else{
7943                     m_pRoutePointEditTarget->m_lat = new_cursor_lat;    // update the RoutePoint entry
7944                     m_pRoutePointEditTarget->m_lon = new_cursor_lon;
7945                     m_pRoutePointEditTarget->m_wpBBox.Invalidate();
7946                     m_pFoundPoint->m_slat = new_cursor_lat;             // update the SelectList entry
7947                     m_pFoundPoint->m_slon = new_cursor_lon;
7948                 }
7949 
7950 
7951                 //    Update the MarkProperties Dialog, if currently shown
7952                 if( ( NULL != g_pMarkInfoDialog ) && ( g_pMarkInfoDialog->IsShown() ) ) {
7953                     if( m_pRoutePointEditTarget == g_pMarkInfoDialog->GetRoutePoint() ) g_pMarkInfoDialog->UpdateProperties( true );
7954                 }
7955 
7956                 if(g_bopengl) {
7957                     //InvalidateGL();
7958                     Refresh( false );
7959                 } else {
7960                     // Get the update rectangle for the edited route
7961                     wxRect post_rect;
7962 
7963                     if( m_pEditRouteArray ) {
7964                         for( unsigned int ir = 0; ir < m_pEditRouteArray->GetCount(); ir++ ) {
7965                             Route *pr = (Route *) m_pEditRouteArray->Item( ir );
7966                             if( g_pRouteMan->IsRouteValid(pr) ) {
7967                                 wxRect route_rect;
7968                                 pr->CalculateDCRect( m_dc_route, this, &route_rect );
7969                                 post_rect.Union( route_rect );
7970                             }
7971                         }
7972                     }
7973 
7974                     //    Invalidate the union region
7975                     pre_rect.Union( post_rect );
7976                     RefreshRect( pre_rect, false );
7977                 }
7978                 gFrame->RefreshCanvasOther( this );
7979                 m_bRoutePoinDragging = true;
7980             }
7981             ret = true;
7982         }     // if Route Editing
7983 
7984         else if( m_bMarkEditing && m_pRoutePointEditTarget ) {
7985 
7986             bool DraggingAllowed = g_btouch ? m_bIsInRadius : true;
7987 
7988             if( NULL == g_pMarkInfoDialog ) {
7989                 if( g_bWayPointPreventDragging )
7990                     DraggingAllowed = false;
7991             } else if( !g_pMarkInfoDialog->IsShown() && g_bWayPointPreventDragging )
7992                 DraggingAllowed = false;
7993 
7994             if( m_pRoutePointEditTarget
7995                 && ( m_pRoutePointEditTarget->GetIconName() == _T("mob") ) )
7996                 DraggingAllowed = false;
7997 
7998             if( m_pRoutePointEditTarget->m_bIsInLayer )
7999                 DraggingAllowed = false;
8000 
8001             if( DraggingAllowed ) {
8002                         if( !undo->InUndoableAction() ) {
8003                             undo->BeforeUndoableAction( Undo_MoveWaypoint, m_pRoutePointEditTarget,
8004                                                         Undo_NeedsCopy, m_pFoundPoint );
8005                         }
8006 
8007                         //      The mark may be an anchorwatch
8008                         double lpp1 = 0.;
8009                         double lpp2 = 0.;
8010                         double lppmax;
8011 
8012                         if( pAnchorWatchPoint1 == m_pRoutePointEditTarget ) {
8013                             lpp1 = fabs( GetAnchorWatchRadiusPixels( pAnchorWatchPoint1 ) );
8014 
8015                         }
8016                         if( pAnchorWatchPoint2 == m_pRoutePointEditTarget ) {
8017                             lpp2 = fabs( GetAnchorWatchRadiusPixels( pAnchorWatchPoint2 ) );
8018                         }
8019                         lppmax = wxMax(lpp1 + 10, lpp2 + 10);         // allow for cruft
8020 
8021                         // Get the update rectangle for the un-edited mark
8022                         wxRect pre_rect;
8023                         if(!g_bopengl) {
8024                             m_pRoutePointEditTarget->CalculateDCRect( m_dc_route, this, &pre_rect );
8025                             if( ( lppmax > pre_rect.width / 2 ) || ( lppmax > pre_rect.height / 2 ) )
8026                                 pre_rect.Inflate( (int) ( lppmax - ( pre_rect.width / 2 ) ), (int) ( lppmax - ( pre_rect.height / 2 ) ) );
8027                         }
8028 
8029                         // update the point itself
8030                         if( g_btouch ) {
8031 //                            m_pRoutePointEditTarget->SetPointFromDraghandlePoint(VPoint, m_cursor_lat, m_cursor_lon);
8032                             m_pRoutePointEditTarget->SetPointFromDraghandlePoint(this, mouse_x, mouse_y);
8033                             // update the Drag Handle entry in the pSelect list
8034                             pSelect->ModifySelectablePoint( m_cursor_lat, m_cursor_lon, m_pRoutePointEditTarget, SELTYPE_DRAGHANDLE );
8035                             m_pFoundPoint->m_slat = m_pRoutePointEditTarget->m_lat;             // update the SelectList entry
8036                             m_pFoundPoint->m_slon = m_pRoutePointEditTarget->m_lon;
8037                         }
8038                         else{
8039                             m_pRoutePointEditTarget->m_lat = m_cursor_lat;    // update the RoutePoint entry
8040                             m_pRoutePointEditTarget->m_lon = m_cursor_lon;
8041                             m_pRoutePointEditTarget->m_wpBBox.Invalidate();
8042                             m_pFoundPoint->m_slat = m_cursor_lat;             // update the SelectList entry
8043                             m_pFoundPoint->m_slon = m_cursor_lon;
8044                         }
8045 
8046 
8047 
8048                         //    Update the MarkProperties Dialog, if currently shown
8049                         if( ( NULL != g_pMarkInfoDialog ) && ( g_pMarkInfoDialog->IsShown() ) ) {
8050                             if( m_pRoutePointEditTarget == g_pMarkInfoDialog->GetRoutePoint() )
8051                                 g_pMarkInfoDialog->UpdateProperties( true );
8052                         }
8053 
8054                         //    Invalidate the union region
8055                         if(g_bopengl) {
8056                             if(!g_btouch)
8057                                 InvalidateGL();
8058                             Refresh( false );
8059                         } else {
8060                             // Get the update rectangle for the edited mark
8061                             wxRect post_rect;
8062                             m_pRoutePointEditTarget->CalculateDCRect( m_dc_route, this, &post_rect );
8063                             if( ( lppmax > post_rect.width / 2 ) || ( lppmax > post_rect.height / 2 ) )
8064                                 post_rect.Inflate((int) ( lppmax - ( post_rect.width / 2 ) ),
8065                                                   (int) ( lppmax - ( post_rect.height / 2 ) ) );
8066 
8067                             //    Invalidate the union region
8068                             pre_rect.Union( post_rect );
8069                             RefreshRect( pre_rect, false );
8070                         }
8071                         gFrame->RefreshCanvasOther( this );
8072                         m_bRoutePoinDragging = true;
8073                     }
8074                     ret = true;
8075 
8076         }
8077 
8078         if(ret)
8079             return true;
8080     }       //dragging
8081 
8082     if( event.LeftUp() ) {
8083         bool b_startedit_route = false;
8084         m_dragoffsetSet = false;
8085 
8086         if(g_btouch) {
8087             m_bChartDragging = false;
8088             m_bIsInRadius = false;
8089 
8090             if( m_routeState )                  // creating route?
8091             {
8092                 if(m_bedge_pan){
8093                     m_bedge_pan = false;
8094                     return false;
8095                 }
8096 
8097                 double rlat, rlon;
8098 
8099                 rlat = m_cursor_lat;
8100                 rlon = m_cursor_lon;
8101 
8102                 if( m_pRoutePointEditTarget) {
8103                     m_pRoutePointEditTarget->m_bRPIsBeingEdited = false;
8104                     m_pRoutePointEditTarget->m_bPtIsSelected = false;
8105                     wxRect wp_rect;
8106                     m_pRoutePointEditTarget->CalculateDCRect( m_dc_route, this, &wp_rect );
8107                     RefreshRect( wp_rect, true );
8108                     m_pRoutePointEditTarget = NULL;
8109                 }
8110                 m_bRouteEditing = true;
8111 
8112                 if( m_routeState == 1 ) {
8113                     m_pMouseRoute = new Route();
8114                     m_pMouseRoute->SetHiLite(50);
8115                     pRouteList->Append( m_pMouseRoute );
8116                     r_rband.x = x;
8117                     r_rband.y = y;
8118                 }
8119 
8120 
8121                 //    Check to see if there is a nearby point which may be reused
8122                 RoutePoint *pMousePoint = NULL;
8123 
8124                 //    Calculate meaningful SelectRadius
8125                 double nearby_radius_meters = g_Platform->GetSelectRadiusPix() / m_true_scale_ppm;
8126 
8127                 RoutePoint *pNearbyPoint = pWayPointMan->GetNearbyWaypoint( rlat, rlon,
8128                                                                             nearby_radius_meters );
8129                 if( pNearbyPoint && ( pNearbyPoint != m_prev_pMousePoint )
8130                     && !pNearbyPoint->m_bIsInLayer && pNearbyPoint->IsVisible() )
8131                 {
8132                     int dlg_return;
8133                     #ifndef __WXOSX__
8134                     m_FinishRouteOnKillFocus = false;  // Avoid route finish on focus change for message dialog
8135                     dlg_return = OCPNMessageBox( this, _("Use nearby waypoint?"),
8136                                              _("OpenCPN Route Create"),
8137                                                (long) wxYES_NO | wxCANCEL | wxYES_DEFAULT );
8138                     m_FinishRouteOnKillFocus = true;
8139                                                  #else
8140                                                  dlg_return = wxID_YES;
8141                                                  #endif
8142                                                  if( dlg_return == wxID_YES ) {
8143                                                      pMousePoint = pNearbyPoint;
8144 
8145                                                      // Using existing waypoint, so nothing to delete for undo.
8146                                                      if( m_routeState > 1 )
8147                                                          undo->BeforeUndoableAction( Undo_AppendWaypoint, pMousePoint, Undo_HasParent, NULL );
8148 
8149                                                      // check all other routes to see if this point appears in any other route
8150                                                          // If it appears in NO other route, then it should e considered an isolated mark
8151                                                          if( !g_pRouteMan->FindRouteContainingWaypoint( pMousePoint ) ) pMousePoint->m_bKeepXRoute =
8152                                                              true;
8153                                                  }
8154                 }
8155 
8156                 if( NULL == pMousePoint ) {                 // need a new point
8157                     pMousePoint = new RoutePoint( rlat, rlon, g_default_routepoint_icon, _T(""), wxEmptyString );
8158                     pMousePoint->SetNameShown( false );
8159 
8160                     pConfig->AddNewWayPoint( pMousePoint, -1 );    // use auto next num
8161                     pSelect->AddSelectableRoutePoint( rlat, rlon, pMousePoint );
8162 
8163                     if( m_routeState > 1 )
8164                         undo->BeforeUndoableAction( Undo_AppendWaypoint, pMousePoint, Undo_IsOrphanded, NULL );
8165                 }
8166 
8167                 if( m_routeState == 1 ) {
8168                     // First point in the route.
8169                     m_pMouseRoute->AddPoint( pMousePoint );
8170                 } else {
8171                     if( m_pMouseRoute->m_NextLegGreatCircle ) {
8172                         double rhumbBearing, rhumbDist, gcBearing, gcDist;
8173                         DistanceBearingMercator( rlat, rlon, m_prev_rlat, m_prev_rlon, &rhumbBearing, &rhumbDist );
8174                         Geodesic::GreatCircleDistBear( m_prev_rlon, m_prev_rlat, rlon, rlat, &gcDist, &gcBearing, NULL );
8175                         double gcDistNM = gcDist / 1852.0;
8176 
8177                         // Empirically found expression to get reasonable route segments.
8178                         int segmentCount = (3.0 + (rhumbDist - gcDistNM)) / pow(rhumbDist-gcDistNM-1, 0.5 );
8179 
8180                         wxString msg;
8181                         msg << _("For this leg the Great Circle route is ")
8182                         << FormatDistanceAdaptive( rhumbDist - gcDistNM ) << _(" shorter than rhumbline.\n\n")
8183                         << _("Would you like include the Great Circle routing points for this leg?");
8184 
8185                         #ifndef __WXOSX__
8186                         m_FinishRouteOnKillFocus = false;
8187                         int answer = OCPNMessageBox( this, msg, _("OpenCPN Route Create"), wxYES_NO | wxNO_DEFAULT );
8188                         m_FinishRouteOnKillFocus = true;
8189                         #else
8190                         int answer = wxID_NO;
8191                         #endif
8192 
8193                         if( answer == wxID_YES ) {
8194                             RoutePoint* gcPoint;
8195                             RoutePoint* prevGcPoint = m_prev_pMousePoint;
8196                             wxRealPoint gcCoord;
8197 
8198                             for( int i = 1; i <= segmentCount; i++ ) {
8199                                 double fraction = (double) i * ( 1.0 / (double) segmentCount );
8200                                 Geodesic::GreatCircleTravel( m_prev_rlon, m_prev_rlat, gcDist * fraction,
8201                                                              gcBearing, &gcCoord.x, &gcCoord.y, NULL );
8202 
8203                                 if( i < segmentCount ) {
8204                                     gcPoint = new RoutePoint( gcCoord.y, gcCoord.x, _T("xmblue"), _T(""),
8205                                                               wxEmptyString );
8206                                     gcPoint->SetNameShown( false );
8207                                     pConfig->AddNewWayPoint( gcPoint, -1 );
8208                                     pSelect->AddSelectableRoutePoint( gcCoord.y, gcCoord.x, gcPoint );
8209                                 } else {
8210                                     gcPoint = pMousePoint; // Last point, previously exsisting!
8211                                 }
8212 
8213                                 m_pMouseRoute->AddPoint( gcPoint );
8214                                 pSelect->AddSelectableRouteSegment( prevGcPoint->m_lat, prevGcPoint->m_lon,
8215                                                                     gcPoint->m_lat, gcPoint->m_lon, prevGcPoint, gcPoint, m_pMouseRoute );
8216                                 prevGcPoint = gcPoint;
8217                             }
8218 
8219                             undo->CancelUndoableAction( true );
8220 
8221                         } else {
8222                             m_pMouseRoute->AddPoint( pMousePoint );
8223                             pSelect->AddSelectableRouteSegment( m_prev_rlat, m_prev_rlon,
8224                                                                 rlat, rlon, m_prev_pMousePoint, pMousePoint, m_pMouseRoute );
8225                             undo->AfterUndoableAction( m_pMouseRoute );
8226                         }
8227                     } else {
8228                         // Ordinary rhumblinesegment.
8229                         m_pMouseRoute->AddPoint( pMousePoint );
8230                         pSelect->AddSelectableRouteSegment( m_prev_rlat, m_prev_rlon,
8231                                                             rlat, rlon, m_prev_pMousePoint, pMousePoint, m_pMouseRoute );
8232                         undo->AfterUndoableAction( m_pMouseRoute );
8233                     }
8234                 }
8235 
8236                 m_prev_rlat = rlat;
8237                 m_prev_rlon = rlon;
8238                 m_prev_pMousePoint = pMousePoint;
8239                 m_pMouseRoute->m_lastMousePointIndex = m_pMouseRoute->GetnPoints();
8240 
8241                 m_routeState++;
8242                 Refresh( true );
8243                 ret = true;
8244             }
8245             else if( m_bMeasure_Active && m_nMeasureState )   // measure tool?
8246             {
8247                 if(m_bedge_pan){
8248                     m_bedge_pan = false;
8249                     return false;
8250                 }
8251 
8252                 if( m_nMeasureState == 1 ) {
8253                     m_pMeasureRoute = new Route();
8254                     pRouteList->Append( m_pMeasureRoute );
8255                     r_rband.x = x;
8256                     r_rband.y = y;
8257                 }
8258 
8259 
8260                 RoutePoint *pMousePoint = new RoutePoint( m_cursor_lat, m_cursor_lon,
8261                                                           wxString( _T ( "circle" ) ), wxEmptyString, wxEmptyString );
8262                                                           pMousePoint->m_bShowName = false;
8263 
8264                 m_pMeasureRoute->AddPoint( pMousePoint );
8265 
8266                 m_prev_rlat = m_cursor_lat;
8267                 m_prev_rlon = m_cursor_lon;
8268                 m_prev_pMousePoint = pMousePoint;
8269                 m_pMeasureRoute->m_lastMousePointIndex = m_pMeasureRoute->GetnPoints();
8270 
8271                 m_nMeasureState++;
8272 
8273                 Refresh( true );
8274                 ret = true;
8275             }
8276             else {
8277 
8278                 bool bSelectAllowed = true;
8279                 if( NULL == g_pMarkInfoDialog ) {
8280                     if( g_bWayPointPreventDragging ) bSelectAllowed = false;
8281                 } else if( !g_pMarkInfoDialog->IsShown() && g_bWayPointPreventDragging )
8282                     bSelectAllowed = false;
8283 
8284 				/*if this left up happens at the end of a route point dragging and if the cursor/thumb is on the
8285 				draghandle icon, not on the point iself a new selection will select nothing and the drag will never
8286 				be ended, so the legs around this point never selectable. At this step we don't need a new selection,
8287 				just keep the previoulsly selected and dragged point */
8288 				if (m_bRoutePoinDragging)
8289 					bSelectAllowed = false;
8290 
8291                 if(bSelectAllowed){
8292 
8293                 bool b_was_editing_mark = m_bMarkEditing;
8294                 bool b_was_editing_route = m_bRouteEditing;
8295                 FindRoutePointsAtCursor( SelectRadius, true );    // Possibly selecting a point in a route for later dragging
8296 
8297 				/*route and a mark points in layer can't be dragged so should't be selected and no draghandle icon*/
8298 				if (m_pRoutePointEditTarget && m_pRoutePointEditTarget->m_bIsInLayer)
8299 					m_pRoutePointEditTarget = NULL;
8300 
8301                 if( !b_was_editing_route ) {
8302                     if( m_pEditRouteArray ) {
8303                         b_startedit_route = true;
8304 
8305 
8306                         //  Hide the track and route rollover during route point edit, not needed, and may be confusing
8307                         if( m_pTrackRolloverWin && m_pTrackRolloverWin->IsActive()  ) {
8308                             m_pTrackRolloverWin->IsActive( false );
8309                         }
8310                         if( m_pRouteRolloverWin && m_pRouteRolloverWin->IsActive()  ) {
8311                             m_pRouteRolloverWin->IsActive( false );
8312                         }
8313 
8314                         wxRect pre_rect;
8315                         for( unsigned int ir = 0; ir < m_pEditRouteArray->GetCount(); ir++ ) {
8316                             Route *pr = (Route *) m_pEditRouteArray->Item( ir );
8317                             //      Need to validate route pointer
8318                             //      Route may be gone due to drgging close to ownship with
8319                             //      "Delete On Arrival" state set, as in the case of
8320                             //      navigating to an isolated waypoint on a temporary route
8321                             if( g_pRouteMan->IsRouteValid(pr) ) {
8322                                 //                                pr->SetHiLite(50);
8323                                 wxRect route_rect;
8324                                 pr->CalculateDCRect( m_dc_route, this, &route_rect );
8325                                 pre_rect.Union( route_rect );
8326                             }
8327                         }
8328                         RefreshRect( pre_rect, true );
8329                     }
8330                 }
8331                 else {
8332                     b_startedit_route = false;
8333                 }
8334 
8335 
8336                 //  Mark editing
8337                 if( m_pRoutePointEditTarget ) {
8338 
8339                     if(b_was_editing_mark || b_was_editing_route) {            // kill previous hilight
8340                         if( m_lastRoutePointEditTarget) {
8341                             m_lastRoutePointEditTarget->m_bRPIsBeingEdited = false;
8342                             m_lastRoutePointEditTarget->m_bPtIsSelected = false;
8343                             m_lastRoutePointEditTarget->EnableDragHandle( false );
8344                             pSelect->DeleteSelectablePoint( m_lastRoutePointEditTarget, SELTYPE_DRAGHANDLE );
8345 
8346                         }
8347                     }
8348 
8349                     if( m_pRoutePointEditTarget) {
8350                         m_pRoutePointEditTarget->m_bRPIsBeingEdited = true;
8351                         m_pRoutePointEditTarget->m_bPtIsSelected = true;
8352                         m_pRoutePointEditTarget->EnableDragHandle( true );
8353                         wxPoint2DDouble dragHandlePoint = m_pRoutePointEditTarget->GetDragHandlePoint(this);
8354                         pSelect->AddSelectablePoint(dragHandlePoint.m_y, dragHandlePoint.m_x, m_pRoutePointEditTarget, SELTYPE_DRAGHANDLE);
8355 
8356                     }
8357                 }
8358                 else {                  // Deselect everything
8359                     if( m_lastRoutePointEditTarget) {
8360                         m_lastRoutePointEditTarget->m_bRPIsBeingEdited = false;
8361                         m_lastRoutePointEditTarget->m_bPtIsSelected = false;
8362                         m_lastRoutePointEditTarget->EnableDragHandle( false );
8363                         pSelect->DeleteSelectablePoint( m_lastRoutePointEditTarget, SELTYPE_DRAGHANDLE );
8364 
8365                         //  Clear any routes being edited, probably orphans
8366                         wxArrayPtrVoid *lastEditRouteArray = g_pRouteMan->GetRouteArrayContaining( m_lastRoutePointEditTarget );
8367                         if( lastEditRouteArray ) {
8368                             for( unsigned int ir = 0; ir < lastEditRouteArray->GetCount(); ir++ ) {
8369                                 Route *pr = (Route *) lastEditRouteArray->Item( ir );
8370                                 if( g_pRouteMan->IsRouteValid(pr) ) {
8371                                     pr->m_bIsBeingEdited = false;
8372                                 }
8373                             }
8374                         }
8375                     }
8376                 }
8377 
8378                 //  Do the refresh
8379 
8380                 if(g_bopengl) {
8381                     InvalidateGL();
8382                     Refresh( false );
8383                 } else {
8384                     if( m_lastRoutePointEditTarget) {
8385                         wxRect wp_rect;
8386                         m_lastRoutePointEditTarget->CalculateDCRect( m_dc_route, this, &wp_rect );
8387                         RefreshRect( wp_rect, true );
8388                     }
8389 
8390                         if( m_pRoutePointEditTarget) {
8391                         wxRect wp_rect;
8392                         m_pRoutePointEditTarget->CalculateDCRect( m_dc_route, this, &wp_rect );
8393                         RefreshRect( wp_rect, true );
8394                     }
8395                 }
8396             }
8397             }       //  bSelectAllowed
8398 
8399             //      Check to see if there is a route or AIS target under the cursor
8400             //      If so, start the rollover timer which creates the popup
8401             bool b_start_rollover = false;
8402             if( g_pAIS && g_pAIS->GetNumTargets() && m_bShowAIS ) {
8403                 SelectItem *pFind = pSelectAIS->FindSelection( this, m_cursor_lat, m_cursor_lon, SELTYPE_AISTARGET );
8404                 if( pFind )
8405                     b_start_rollover = true;
8406             }
8407 
8408             if(!b_start_rollover && !b_startedit_route){
8409                 SelectableItemList SelList = pSelect->FindSelectionList( this, m_cursor_lat, m_cursor_lon, SELTYPE_ROUTESEGMENT );
8410                 wxSelectableItemListNode *node = SelList.GetFirst();
8411                 while( node ) {
8412                     SelectItem *pFindSel = node->GetData();
8413 
8414                     Route *pr = (Route *) pFindSel->m_pData3;        //candidate
8415 
8416                     if( pr && pr->IsVisible() ){
8417                         b_start_rollover = true;
8418                         break;
8419                     }
8420                     node = node->GetNext();
8421                 }       // while
8422             }
8423 
8424             if(!b_start_rollover && !b_startedit_route){
8425                 SelectableItemList SelList = pSelect->FindSelectionList( this, m_cursor_lat, m_cursor_lon, SELTYPE_TRACKSEGMENT );
8426                 wxSelectableItemListNode *node = SelList.GetFirst();
8427                 while( node ) {
8428                     SelectItem *pFindSel = node->GetData();
8429 
8430                     Track *tr = (Track *) pFindSel->m_pData3;        //candidate
8431 
8432                     if( tr && tr->IsVisible() ){
8433                         b_start_rollover = true;
8434                         break;
8435                     }
8436                     node = node->GetNext();
8437                 }       // while
8438             }
8439 
8440             if( b_start_rollover )
8441                 m_RolloverPopupTimer.Start( m_rollover_popup_timer_msec, wxTIMER_ONE_SHOT );
8442 
8443 
8444             if( m_bRouteEditing/* && !b_startedit_route*/) {            // End of RoutePoint drag
8445             if( m_pRoutePointEditTarget ) {
8446                 pSelect->UpdateSelectableRouteSegments( m_pRoutePointEditTarget );
8447 
8448                 if( m_pEditRouteArray ) {
8449                     for( unsigned int ir = 0; ir < m_pEditRouteArray->GetCount(); ir++ ) {
8450                         Route *pr = (Route *) m_pEditRouteArray->Item( ir );
8451                         if( g_pRouteMan->IsRouteValid(pr) ) {
8452                             pr->FinalizeForRendering();
8453                             pr->UpdateSegmentDistances();
8454                             if( m_bRoutePoinDragging ) pConfig->UpdateRoute( pr );
8455                         }
8456                     }
8457                 }
8458 
8459                 //    Update the RouteProperties Dialog, if currently shown
8460                 if( pRoutePropDialog && pRoutePropDialog->IsShown() ) {
8461                     if( m_pEditRouteArray ) {
8462                         for( unsigned int ir = 0; ir < m_pEditRouteArray->GetCount(); ir++ ) {
8463                             Route *pr = (Route *) m_pEditRouteArray->Item( ir );
8464                             if( g_pRouteMan->IsRouteValid(pr) ) {
8465                                 if( pRoutePropDialog->GetRoute() == pr ) {
8466                                     pRoutePropDialog->SetRouteAndUpdate( pr, true );
8467                                 }
8468 /* cannot edit track points anyway
8469                                 else if ( ( NULL != pTrackPropDialog ) && ( pTrackPropDialog->IsShown() ) && pTrackPropDialog->m_pTrack == pr ) {
8470                                     pTrackPropDialog->SetTrackAndUpdate( pr );
8471                                 }
8472 */
8473                             }
8474                         }
8475                     }
8476                 }
8477 
8478             }
8479             }
8480 
8481 			else if(  m_bMarkEditing ) {				// End of way point drag
8482 				if( m_pRoutePointEditTarget )
8483 					if( m_bRoutePoinDragging ) pConfig->UpdateWayPoint( m_pRoutePointEditTarget );
8484 			}
8485 
8486 			if( m_pRoutePointEditTarget )
8487                 undo->AfterUndoableAction( m_pRoutePointEditTarget );
8488 
8489             if(!m_pRoutePointEditTarget){
8490                 delete m_pEditRouteArray;
8491                 m_pEditRouteArray = NULL;
8492                 m_bRouteEditing = false;
8493             }
8494             m_bRoutePoinDragging = false;
8495         }       // g_btouch
8496 
8497 
8498         else{                   // !g_btouch
8499         if( m_bRouteEditing ) {            // End of RoutePoint drag
8500             if( m_pRoutePointEditTarget ) {
8501                 pSelect->UpdateSelectableRouteSegments( m_pRoutePointEditTarget );
8502                 m_pRoutePointEditTarget->m_bBlink = false;
8503 
8504                 if( m_pEditRouteArray ) {
8505                     for( unsigned int ir = 0; ir < m_pEditRouteArray->GetCount(); ir++ ) {
8506                         Route *pr = (Route *) m_pEditRouteArray->Item( ir );
8507                         if( g_pRouteMan->IsRouteValid(pr) ) {
8508                             pr->FinalizeForRendering();
8509                             pr->UpdateSegmentDistances();
8510                             pr->m_bIsBeingEdited = false;
8511 
8512                             if( m_bRoutePoinDragging ) pConfig->UpdateRoute( pr );
8513 
8514                             pr->SetHiLite( 0 );
8515                         }
8516                     }
8517                     Refresh( false );
8518                 }
8519 
8520                 //    Update the RouteProperties Dialog, if currently shown
8521                 if( pRoutePropDialog && pRoutePropDialog->IsShown() ) {
8522                     if( m_pEditRouteArray ) {
8523                         for( unsigned int ir = 0; ir < m_pEditRouteArray->GetCount(); ir++ ) {
8524                             Route *pr = (Route *) m_pEditRouteArray->Item( ir );
8525                             if( g_pRouteMan->IsRouteValid(pr) ) {
8526                                 if( pRoutePropDialog->GetRoute() == pr ) {
8527                                     pRoutePropDialog->SetRouteAndUpdate( pr, true );
8528                                 }
8529                             }
8530                         }
8531                     }
8532                 }
8533 
8534                 m_pRoutePointEditTarget->m_bPtIsSelected = false;
8535                 m_pRoutePointEditTarget->m_bRPIsBeingEdited = false;
8536 
8537                 delete m_pEditRouteArray;
8538                 m_pEditRouteArray = NULL;
8539                 undo->AfterUndoableAction( m_pRoutePointEditTarget );
8540             }
8541 
8542             InvalidateGL();
8543             m_bRouteEditing = false;
8544             m_pRoutePointEditTarget = NULL;
8545 
8546             if( m_toolBar && !m_toolBar->IsToolbarShown())
8547                 SurfaceToolbar();
8548             ret = true;
8549         }
8550 
8551         else if( m_bMarkEditing) {         // end of Waypoint drag
8552             if( m_pRoutePointEditTarget ) {
8553                 if( m_bRoutePoinDragging ) pConfig->UpdateWayPoint( m_pRoutePointEditTarget );
8554                 undo->AfterUndoableAction( m_pRoutePointEditTarget );
8555                 m_pRoutePointEditTarget->m_bRPIsBeingEdited = false;
8556                 wxRect wp_rect;
8557                 m_pRoutePointEditTarget->CalculateDCRect( m_dc_route, this, &wp_rect );
8558                 m_pRoutePointEditTarget->m_bPtIsSelected = false;
8559                 RefreshRect( wp_rect, true );
8560 
8561             }
8562             m_pRoutePointEditTarget = NULL;
8563             m_bMarkEditing = false;
8564             if( m_toolBar && !m_toolBar->IsToolbarShown())
8565                 SurfaceToolbar();
8566             ret = true;
8567         }
8568 
8569         else if( leftIsDown ) {  // left click for chart center
8570             leftIsDown = false;
8571             ret = false;
8572 
8573             if( !g_btouch ){
8574                 if( !m_bChartDragging && !m_bMeasure_Active ) {
8575                  } else {
8576                     m_bChartDragging = false;
8577                 }
8578             }
8579 
8580         }
8581 		 m_bRoutePoinDragging = false;
8582         }       // !btouch
8583 
8584         if(ret)
8585             return true;
8586     }           // left up
8587 
8588     if( event.RightDown() ) {
8589         SetFocus();           //  This is to let a plugin know which canvas is right-clicked
8590         last_drag.x = mx;
8591         last_drag.y = my;
8592 
8593         if(g_btouch ){
8594 //            if( m_pRoutePointEditTarget )
8595 //                return false;
8596         }
8597 
8598         ret = true;
8599         m_FinishRouteOnKillFocus = false;
8600         CallPopupMenu(mx , my);
8601         m_FinishRouteOnKillFocus = true;
8602     }   //Right down
8603 
8604     return ret;
8605 
8606 }
8607 
8608 bool panleftIsDown;
8609 
MouseEventProcessCanvas(wxMouseEvent & event)8610 bool ChartCanvas::MouseEventProcessCanvas( wxMouseEvent& event )
8611 {
8612     int x, y;
8613     event.GetPosition( &x, &y );
8614 
8615     //        Check for wheel rotation
8616     // ideally, should be just longer than the time between
8617     // processing accumulated mouse events from the event queue
8618     // as would happen during screen redraws.
8619     int wheel_dir = event.GetWheelRotation();
8620 
8621     if( wheel_dir ) {
8622         int mouse_wheel_oneshot = abs(wheel_dir)*4;                  //msec
8623         wheel_dir = wheel_dir > 0 ? 1 : -1; // normalize
8624 
8625         double factor = 2.0;
8626         if(wheel_dir < 0)
8627             factor = 1/factor;
8628 
8629         if(g_bsmoothpanzoom){
8630             if( (m_wheelstopwatch.Time() < m_wheelzoom_stop_oneshot) ) {
8631                 if( wheel_dir == m_last_wheel_dir ) {
8632                     m_wheelzoom_stop_oneshot += mouse_wheel_oneshot;
8633                     //                    m_zoom_target /= factor;
8634                 }
8635                 else
8636                     StopMovement( );
8637             }
8638             else {
8639                 m_wheelzoom_stop_oneshot = mouse_wheel_oneshot;
8640                 m_wheelstopwatch.Start(0);
8641                 //                m_zoom_target =  VPoint.chart_scale / factor;
8642             }
8643         }
8644 
8645         m_last_wheel_dir = wheel_dir;
8646 
8647 
8648         ZoomCanvas( factor, true, false );
8649 
8650     }
8651 
8652     if( event.LeftDown() ) {
8653         // Skip the first left click if it will cause a canvas focus shift
8654         if( (GetCanvasCount() > 1) &&  (this != g_focusCanvas) ){
8655             //printf("focus shift\n");
8656             return false;
8657         }
8658 
8659         last_drag.x = x, last_drag.y = y;
8660         panleftIsDown = true;
8661     }
8662 
8663     if( event.LeftUp() ) {
8664         if( panleftIsDown ) {  // leftUp for chart center, but only after a leftDown seen here.
8665             panleftIsDown = false;
8666 
8667             if( !g_btouch ){
8668                 if( !m_bChartDragging && !m_bMeasure_Active ) {
8669                     switch( cursor_region ){
8670                         case MID_RIGHT: {
8671                             PanCanvas( 100, 0 );
8672                             break;
8673                         }
8674 
8675                         case MID_LEFT: {
8676                             PanCanvas( -100, 0 );
8677                             break;
8678                         }
8679 
8680                         case MID_TOP: {
8681                             PanCanvas( 0, 100 );
8682                             break;
8683                         }
8684 
8685                         case MID_BOT: {
8686                             PanCanvas( 0, -100 );
8687                             break;
8688                         }
8689 
8690                         case CENTER: {
8691                             PanCanvas( x - GetVP().pix_width / 2, y - GetVP().pix_height / 2 );
8692                             break;
8693                         }
8694                     }
8695                 } else {
8696                     m_bChartDragging = false;
8697                 }
8698             }
8699         }
8700     }
8701 
8702     if( event.Dragging() && event.LeftIsDown()){
8703             /*
8704              * fixed dragging.
8705              * On my Surface Pro 3 running Arch Linux there is no mouse down event before the drag event.
8706              * Hence, as there is no mouse down event, last_drag is not reset before the drag.
8707              * And that results in one single drag session, meaning you cannot drag the map a few miles
8708              * north, lift your finger, and the go even further north. Instead, the map resets itself
8709              * always to the very first drag start (since there is not reset of last_drag).
8710              *
8711              * Besides, should not left down and dragging be enough of a situation to start a drag procedure?
8712              *
8713              * Anyways, guarded it to be active in touch situations only.
8714              */
8715             if( g_btouch ) {
8716                 if(false == m_bChartDragging) {
8717                     last_drag.x = x, last_drag.y = y;
8718                     m_bChartDragging = true;
8719                 }
8720             }
8721 
8722 
8723             if( ( last_drag.x != x ) || ( last_drag.y != y ) ) {
8724                 m_bChartDragging = true;
8725                 StartTimedMovement();
8726                 m_pan_drag.x += last_drag.x - x;
8727                 m_pan_drag.y += last_drag.y - y;
8728 
8729                 last_drag.x = x, last_drag.y = y;
8730 
8731                 if( g_btouch ) {
8732                     if(( m_bMeasure_Active && m_nMeasureState ) || ( m_routeState )){
8733                         //deactivate next LeftUp to ovoid creating an unexpected point
8734                         m_DoubleClickTimer->Start();
8735                         singleClickEventIsValid = false;
8736                     }
8737                 }
8738 
8739             }
8740     }
8741 
8742 
8743 
8744     return true;
8745 
8746 
8747 }
8748 
MouseEvent(wxMouseEvent & event)8749 void ChartCanvas::MouseEvent( wxMouseEvent& event )
8750 {
8751     if (MouseEventOverlayWindows( event ))
8752         return;
8753 
8754     if(MouseEventSetup( event ))
8755         return;              // handled, no further action required
8756 
8757     if(!MouseEventProcessObjects( event ))
8758          MouseEventProcessCanvas( event );
8759 }
8760 
8761 
SetCanvasCursor(wxMouseEvent & event)8762 void ChartCanvas::SetCanvasCursor( wxMouseEvent& event )
8763 {
8764     //    Switch to the appropriate cursor on mouse movement
8765 
8766     wxCursor *ptarget_cursor = pCursorArrow;
8767     if( !pPlugIn_Cursor ) {
8768         ptarget_cursor = pCursorArrow;
8769         if( ( !m_routeState )
8770             && ( !m_bMeasure_Active ) /*&& ( !m_bCM93MeasureOffset_Active )*/) {
8771 
8772             if( cursor_region == MID_RIGHT ) {
8773                 ptarget_cursor = pCursorRight;
8774             } else if( cursor_region == MID_LEFT ) {
8775                 ptarget_cursor = pCursorLeft;
8776             } else if( cursor_region == MID_TOP ) {
8777                 ptarget_cursor = pCursorDown;
8778             } else if( cursor_region == MID_BOT ) {
8779                 ptarget_cursor = pCursorUp;
8780             } else {
8781                 ptarget_cursor = pCursorArrow;
8782             }
8783             } else if( m_bMeasure_Active || m_routeState ) // If Measure tool use Pencil Cursor
8784                 ptarget_cursor = pCursorPencil;
8785     }
8786     else {
8787         ptarget_cursor = pPlugIn_Cursor;
8788     }
8789 
8790 
8791         SetCursor( *ptarget_cursor );
8792 
8793 }
8794 
8795 
8796 
8797 
8798 
LostMouseCapture(wxMouseCaptureLostEvent & event)8799 void ChartCanvas::LostMouseCapture( wxMouseCaptureLostEvent& event )
8800 {
8801     SetCursor( *pCursorArrow );
8802 }
8803 
8804 
8805 
ShowObjectQueryWindow(int x,int y,float zlat,float zlon)8806 void ChartCanvas::ShowObjectQueryWindow( int x, int y, float zlat, float zlon )
8807 {
8808 
8809     ChartPlugInWrapper *target_plugin_chart = NULL;
8810     s57chart *Chs57 = NULL;
8811 
8812     ChartBase *target_chart = GetChartAtCursor();
8813     if( target_chart ){
8814         if( (target_chart->GetChartType() == CHART_TYPE_PLUGIN) && (target_chart->GetChartFamily() == CHART_FAMILY_VECTOR) )
8815             target_plugin_chart = dynamic_cast<ChartPlugInWrapper *>(target_chart);
8816         else
8817             Chs57 = dynamic_cast<s57chart*>( target_chart );
8818     }
8819 
8820     std::vector<Ais8_001_22*> area_notices;
8821 
8822     if( g_pAIS && m_bShowAIS && g_bShowAreaNotices ) {
8823         AIS_Target_Hash* an_sources = g_pAIS->GetAreaNoticeSourcesList();
8824 
8825         float vp_scale = GetVPScale();
8826 
8827         for( AIS_Target_Hash::iterator target = an_sources->begin(); target != an_sources->end(); ++target ) {
8828             AIS_Target_Data* target_data = target->second;
8829             if( !target_data->area_notices.empty() ) {
8830                 for( AIS_Area_Notice_Hash::iterator ani = target_data->area_notices.begin(); ani != target_data->area_notices.end(); ++ani ) {
8831                     Ais8_001_22& area_notice = ani->second;
8832 
8833                     wxBoundingBox bbox;
8834 
8835                     for( Ais8_001_22_SubAreaList::iterator sa = area_notice.sub_areas.begin(); sa != area_notice.sub_areas.end(); ++sa ) {
8836                         switch( sa->shape ) {
8837                             case AIS8_001_22_SHAPE_CIRCLE: {
8838                                 wxPoint target_point;
8839                                 GetCanvasPointPix( sa->latitude, sa->longitude, &target_point );
8840                                 bbox.Expand( target_point );
8841                                 if( sa->radius_m > 0.0 )
8842                                     bbox.EnLarge( sa->radius_m * vp_scale );
8843                                 break;
8844                             }
8845                             case AIS8_001_22_SHAPE_POLYGON:
8846                             case AIS8_001_22_SHAPE_POLYLINE: {
8847                                 for( int i = 0; i < 4; ++i ) {
8848                                     double lat = sa->latitude;
8849                                     double lon = sa->longitude;
8850                                     ll_gc_ll( lat, lon, sa->angles[i], sa->dists_m[i] / 1852.0,
8851                                               &lat, &lon );
8852                                     wxPoint target_point;
8853                                     GetCanvasPointPix( lat, lon, &target_point );
8854                                     bbox.Expand( target_point );
8855                                 }
8856                             }
8857                         }
8858                     }
8859 
8860                     if( bbox.PointInBox( x, y ) ) {
8861                         area_notices.push_back( &area_notice );
8862                     }
8863                 }
8864             }
8865         }
8866     }
8867 
8868 
8869     if( target_plugin_chart || Chs57 || !area_notices.empty() ) {
8870         // Go get the array of all objects at the cursor lat/lon
8871         int sel_rad_pix = 5;
8872         float SelectRadius = sel_rad_pix / ( GetVP().view_scale_ppm * 1852 * 60 );
8873 
8874         // Make sure we always get the lights from an object, even if we are currently
8875         // not displaying lights on the chart.
8876 
8877         SetCursor( wxCURSOR_WAIT );
8878         bool lightsVis = m_encShowLights; //gFrame->ToggleLights( false );
8879         if( !lightsVis ) SetShowENCLights( true );
8880 ;
8881 
8882         ListOfObjRazRules* rule_list = NULL;
8883         ListOfPI_S57Obj* pi_rule_list = NULL;
8884         if( Chs57 )
8885             rule_list = Chs57->GetObjRuleListAtLatLon( zlat, zlon, SelectRadius, &GetVP() );
8886         else if( target_plugin_chart )
8887             pi_rule_list = g_pi_manager->GetPlugInObjRuleListAtLatLon( target_plugin_chart, zlat, zlon, SelectRadius, GetVP() );
8888 
8889         ListOfObjRazRules* overlay_rule_list = NULL;
8890         ChartBase *overlay_chart = GetOverlayChartAtCursor();
8891         s57chart *CHs57_Overlay = dynamic_cast<s57chart*>( overlay_chart );
8892 
8893         if( CHs57_Overlay ) {
8894             overlay_rule_list =
8895                 CHs57_Overlay->GetObjRuleListAtLatLon( zlat, zlon, SelectRadius, &GetVP() );
8896         }
8897 
8898         if( !lightsVis ) SetShowENCLights( false );
8899 
8900         wxString objText;
8901         wxFont *dFont = FontMgr::Get().GetFont( _("ObjectQuery") );
8902         wxString face = dFont->GetFaceName();
8903 
8904         if( NULL == g_pObjectQueryDialog ) {
8905             g_pObjectQueryDialog = new S57QueryDialog(this, -1, _( "Object Query" ), wxDefaultPosition, wxSize( g_S57_dialog_sx, g_S57_dialog_sy ));
8906         }
8907 
8908         wxColor bg = g_pObjectQueryDialog->GetBackgroundColour();
8909         wxColor fg = FontMgr::Get().GetFontColor( _("ObjectQuery") );
8910 
8911         objText.Printf( _T("<html><body bgcolor=#%02x%02x%02x><font color=#%02x%02x%02x>"),
8912                        bg.Red(), bg.Green(), bg.Blue(), fg.Red(), fg.Green(), fg.Blue() );
8913 
8914 #ifdef __WXOSX__
8915         int points = dFont->GetPointSize();
8916 #else
8917         int points = dFont->GetPointSize() + 1;
8918 #endif
8919 
8920         int sizes[7];
8921         for ( int i=-2; i<5; i++ ) {
8922             sizes[i+2] = points + i + (i>0?i:0);
8923         }
8924         g_pObjectQueryDialog->m_phtml->SetFonts(face, face, sizes);
8925 
8926         if(wxFONTSTYLE_ITALIC == dFont->GetStyle())
8927             objText += _T("<i>");
8928 
8929         if( overlay_rule_list && CHs57_Overlay) {
8930             objText << CHs57_Overlay->CreateObjDescriptions( overlay_rule_list );
8931             objText << _T("<hr noshade>");
8932         }
8933 
8934         for( std::vector< Ais8_001_22* >::iterator an = area_notices.begin(); an != area_notices.end(); ++an ) {
8935             objText << _T( "<b>AIS Area Notice:</b> " );
8936             objText << ais8_001_22_notice_names[( *an )->notice_type];
8937             for( std::vector< Ais8_001_22_SubArea >::iterator sa = ( *an )->sub_areas.begin(); sa != ( *an )->sub_areas.end(); ++sa )
8938                 if( !sa->text.empty() )
8939                     objText << sa->text;
8940             objText << _T( "<br>expires: " ) << ( *an )->expiry_time.Format();
8941             objText << _T( "<hr noshade>" );
8942         }
8943 
8944         if( Chs57 )
8945             objText << Chs57->CreateObjDescriptions( rule_list );
8946         else if( target_plugin_chart )
8947             objText << g_pi_manager->CreateObjDescriptions( target_plugin_chart, pi_rule_list );
8948 
8949         objText << _T("</font>");
8950         if(wxFONTSTYLE_ITALIC == dFont->GetStyle())
8951             objText << _T("</i>");
8952 
8953         objText << _T("</body></html>");
8954 
8955         g_pObjectQueryDialog->SetHTMLPage( objText );
8956 
8957         g_pObjectQueryDialog->Show();
8958 
8959         if( rule_list )
8960             rule_list->Clear();
8961         delete rule_list;
8962 
8963         if( overlay_rule_list )
8964             overlay_rule_list->Clear();
8965         delete overlay_rule_list;
8966 
8967         if( pi_rule_list )
8968             pi_rule_list->Clear();
8969         delete pi_rule_list;
8970 
8971         SetCursor( wxCURSOR_ARROW );
8972     }
8973 }
8974 
8975 
ShowMarkPropertiesDialog(RoutePoint * markPoint)8976 void ChartCanvas::ShowMarkPropertiesDialog( RoutePoint* markPoint ) {
8977     bool bNew = false;
8978     if ( !g_pMarkInfoDialog ){    // There is one global instance of the MarkProp Dialog
8979         g_pMarkInfoDialog = new MarkInfoDlg(this);
8980         bNew = true;
8981     }
8982 
8983     if( 1/*g_bresponsive*/ ) {
8984         wxSize canvas_size = GetSize();
8985 
8986         g_pMarkInfoDialog->SetMinSize(wxSize(-1, wxMin(600, canvas_size.y)));
8987 
8988         g_pMarkInfoDialog->Layout();
8989 
8990         wxPoint canvas_pos = GetPosition();
8991         wxSize fitted_size = g_pMarkInfoDialog->GetSize();;
8992 
8993         bool newFit = false;
8994         if(canvas_size.x < fitted_size.x){
8995             fitted_size.x = canvas_size.x - 40;
8996             if(canvas_size.y < fitted_size.y)
8997                 fitted_size.y -= 40;                // scrollbar added
8998         }
8999         if(canvas_size.y < fitted_size.y){
9000             fitted_size.y = canvas_size.y - 40;
9001             if(canvas_size.x < fitted_size.x)
9002                 fitted_size.x -= 40;                // scrollbar added
9003         }
9004 
9005         if(newFit){
9006             g_pMarkInfoDialog->SetSize( fitted_size );
9007             g_pMarkInfoDialog->Centre();
9008         }
9009     }
9010 
9011     markPoint->m_bRPIsBeingEdited = false;
9012 
9013     g_pMarkInfoDialog->SetRoutePoint( markPoint );
9014     g_pMarkInfoDialog->UpdateProperties();
9015     if( markPoint->m_bIsInLayer ) {
9016         wxString caption( wxString::Format( _T("%s, %s: %s"), _("Waypoint Properties"), _("Layer"), GetLayerName( markPoint->m_LayerID ) ) );
9017         g_pMarkInfoDialog->SetDialogTitle( caption );
9018     } else
9019         g_pMarkInfoDialog->SetDialogTitle( _("Waypoint Properties") );
9020 
9021     g_pMarkInfoDialog->Show();
9022     g_pMarkInfoDialog->Raise();
9023     g_pMarkInfoDialog->InitialFocus();
9024     if(bNew)
9025         g_pMarkInfoDialog->CenterOnScreen();
9026 }
9027 
ShowRoutePropertiesDialog(wxString title,Route * selected)9028 void ChartCanvas::ShowRoutePropertiesDialog(wxString title, Route* selected)
9029 {
9030     pRoutePropDialog = RoutePropDlgImpl::getInstance( this );
9031     pRoutePropDialog->SetRouteAndUpdate( selected );
9032     //pNew->UpdateProperties();
9033     pRoutePropDialog->Show();
9034     pRoutePropDialog->Raise();
9035     return;
9036     pRoutePropDialog = RoutePropDlgImpl::getInstance( this ); // There is one global instance of the RouteProp Dialog
9037 
9038     if( g_bresponsive ) {
9039 
9040         wxSize canvas_size = GetSize();
9041         wxPoint canvas_pos = GetPosition();
9042         wxSize fitted_size = pRoutePropDialog->GetSize();;
9043 
9044         if(canvas_size.x < fitted_size.x){
9045             fitted_size.x = canvas_size.x;
9046             if(canvas_size.y < fitted_size.y)
9047                 fitted_size.y -= 20;                // scrollbar added
9048         }
9049         if(canvas_size.y < fitted_size.y){
9050             fitted_size.y = canvas_size.y;
9051             if(canvas_size.x < fitted_size.x)
9052                 fitted_size.x -= 20;                // scrollbar added
9053         }
9054 
9055 
9056         pRoutePropDialog->SetSize( fitted_size );
9057         pRoutePropDialog->Centre();
9058 
9059 //        int xp = (canvas_size.x - fitted_size.x)/2;
9060 //        int yp = (canvas_size.y - fitted_size.y)/2;
9061 
9062         wxPoint xxp = ClientToScreen(canvas_pos);
9063 //        pRoutePropDialog->Move(xxp.x + xp, xxp.y + yp);
9064 
9065     }
9066 
9067 
9068     pRoutePropDialog->SetRouteAndUpdate( selected );
9069 
9070     pRoutePropDialog->Show();
9071 
9072     Refresh( false );
9073 }
9074 
ShowTrackPropertiesDialog(Track * selected)9075 void ChartCanvas::ShowTrackPropertiesDialog( Track* selected )
9076 {
9077     pTrackPropDialog = TrackPropDlg::getInstance( this );    // There is one global instance of the RouteProp Dialog
9078 
9079     pTrackPropDialog->SetTrackAndUpdate( selected );
9080     pTrackPropDialog->UpdateProperties();
9081 
9082     pTrackPropDialog->Show();
9083 
9084     Refresh( false );
9085 }
9086 
pupHandler_PasteWaypoint()9087 void pupHandler_PasteWaypoint() {
9088     Kml kml;
9089 
9090     int pasteBuffer = kml.ParsePasteBuffer();
9091     RoutePoint* pasted = kml.GetParsedRoutePoint();
9092     if( ! pasted ) return;
9093 
9094     double nearby_radius_meters = g_Platform->GetSelectRadiusPix() / gFrame->GetPrimaryCanvas()->GetCanvasTrueScale();
9095 
9096     RoutePoint *nearPoint = pWayPointMan->GetNearbyWaypoint( pasted->m_lat, pasted->m_lon,
9097                                nearby_radius_meters );
9098 
9099     int answer = wxID_NO;
9100     if( nearPoint && !nearPoint->m_bIsInLayer ) {
9101         wxString msg;
9102         msg << _("There is an existing waypoint at the same location as the one you are pasting. Would you like to merge the pasted data with it?\n\n");
9103         msg << _("Answering 'No' will create a new waypoint at the same location.");
9104         answer = OCPNMessageBox( NULL, msg, _("Merge waypoint?"), (long) wxYES_NO | wxCANCEL | wxNO_DEFAULT );
9105     }
9106 
9107     if( answer == wxID_YES ) {
9108         nearPoint->SetName( pasted->GetName() );
9109         nearPoint->m_MarkDescription = pasted->m_MarkDescription;
9110         if( pRouteManagerDialog && pRouteManagerDialog->IsShown() ) pRouteManagerDialog->UpdateWptListCtrl();
9111     }
9112 
9113     if( answer == wxID_NO ) {
9114         RoutePoint* newPoint = new RoutePoint( pasted );
9115         newPoint->m_bIsolatedMark = true;
9116         pSelect->AddSelectableRoutePoint( newPoint->m_lat, newPoint->m_lon, newPoint );
9117         pConfig->AddNewWayPoint( newPoint, -1 );
9118         pWayPointMan->AddRoutePoint( newPoint );
9119         if( pRouteManagerDialog && pRouteManagerDialog->IsShown() ) pRouteManagerDialog->UpdateWptListCtrl();
9120         if( newPoint->IsVisibleSelectable(g_focusCanvas) ) newPoint->ShowScaleWarningMessage(g_focusCanvas);
9121     }
9122 
9123     gFrame->InvalidateAllGL();
9124     gFrame->RefreshAllCanvas( false );
9125 }
9126 
pupHandler_PasteRoute()9127 void pupHandler_PasteRoute() {
9128     Kml kml;
9129 
9130     int pasteBuffer = kml.ParsePasteBuffer();
9131     Route* pasted = kml.GetParsedRoute();
9132     if( ! pasted ) return;
9133 
9134     double nearby_radius_meters = g_Platform->GetSelectRadiusPix() / gFrame->GetPrimaryCanvas()->GetCanvasTrueScale();
9135 
9136     RoutePoint* curPoint;
9137     RoutePoint* nearPoint;
9138     RoutePoint* prevPoint = NULL;
9139 
9140     bool mergepoints = false;
9141     bool createNewRoute = true;
9142     int existingWaypointCounter = 0;
9143 
9144     for( int i = 1; i <= pasted->GetnPoints(); i++ ) {
9145 		curPoint = pasted->GetPoint( i ); // NB! n starts at 1 !
9146         nearPoint = pWayPointMan->GetNearbyWaypoint( curPoint->m_lat,
9147                 curPoint->m_lon, nearby_radius_meters );
9148         if( nearPoint ) {
9149             mergepoints = true;
9150             existingWaypointCounter++;
9151             // Small hack here to avoid both extending RoutePoint and repeating all the GetNearbyWaypoint
9152             // calculations. Use existin data field in RoutePoint as temporary storage.
9153             curPoint->m_bPtIsSelected = true;
9154         }
9155     }
9156 
9157     int answer = wxID_NO;
9158     if( mergepoints ) {
9159         wxString msg;
9160         msg << _("There are existing waypoints at the same location as some of the ones you are pasting. Would you like to just merge the pasted data into them?\n\n");
9161         msg << _("Answering 'No' will create all new waypoints for this route.");
9162         answer = OCPNMessageBox( NULL, msg, _("Merge waypoints?"), (long) wxYES_NO | wxCANCEL | wxYES_DEFAULT );
9163 
9164         if( answer == wxID_CANCEL ) {
9165             return;
9166         }
9167     }
9168 
9169     // If all waypoints exist since before, and a route with the same name, we don't create a new route.
9170     if( mergepoints && answer==wxID_YES && existingWaypointCounter==pasted->GetnPoints() ) {
9171 
9172         wxRouteListNode *route_node = pRouteList->GetFirst();
9173         while( route_node ) {
9174             Route *proute = route_node->GetData();
9175 
9176             if( pasted->m_RouteNameString == proute->m_RouteNameString ) {
9177                 createNewRoute = false;
9178                 break;
9179             }
9180             route_node = route_node->GetNext();
9181         }
9182     }
9183 
9184     Route* newRoute = 0;
9185     RoutePoint* newPoint = 0;
9186 
9187     if( createNewRoute ) {
9188         newRoute = new Route();
9189         newRoute->m_RouteNameString = pasted->m_RouteNameString;
9190     }
9191 
9192     for( int i = 1; i <= pasted->GetnPoints(); i++ ) {
9193         curPoint = pasted->GetPoint( i );
9194         if( answer == wxID_YES && curPoint->m_bPtIsSelected ) {
9195             curPoint->m_bPtIsSelected = false;
9196             newPoint = pWayPointMan->GetNearbyWaypoint( curPoint->m_lat, curPoint->m_lon,
9197                     nearby_radius_meters );
9198             newPoint->SetName( curPoint->GetName() );
9199             newPoint->m_MarkDescription = curPoint->m_MarkDescription;
9200 
9201             if( createNewRoute ) newRoute->AddPoint( newPoint );
9202         } else {
9203             curPoint->m_bPtIsSelected = false;
9204 
9205             newPoint = new RoutePoint( curPoint );
9206             newPoint->m_bIsolatedMark = false;
9207             newPoint->SetIconName( _T("circle") );
9208             newPoint->m_bIsVisible = true;
9209             newPoint->m_bShowName = false;
9210             newPoint->m_bKeepXRoute = false;
9211 
9212             newRoute->AddPoint( newPoint );
9213             pSelect->AddSelectableRoutePoint( newPoint->m_lat, newPoint->m_lon, newPoint );
9214             pConfig->AddNewWayPoint( newPoint, -1 );
9215             pWayPointMan->AddRoutePoint( newPoint );
9216         }
9217         if( i > 1 && createNewRoute ) pSelect->AddSelectableRouteSegment( prevPoint->m_lat,
9218                 prevPoint->m_lon, curPoint->m_lat, curPoint->m_lon, prevPoint, newPoint, newRoute );
9219         prevPoint = newPoint;
9220     }
9221 
9222     if( createNewRoute ) {
9223         pRouteList->Append( newRoute );
9224         pConfig->AddNewRoute( newRoute );    // use auto next num
9225 
9226         if( pRoutePropDialog && pRoutePropDialog->IsShown() ) {
9227             pRoutePropDialog->SetRouteAndUpdate( newRoute );
9228         }
9229 
9230         if( pRouteManagerDialog && pRouteManagerDialog->IsShown() ) {
9231             pRouteManagerDialog->UpdateRouteListCtrl();
9232             pRouteManagerDialog->UpdateWptListCtrl();
9233         }
9234         gFrame->InvalidateAllGL();
9235         gFrame->RefreshAllCanvas( false );
9236     }
9237     if( newPoint->IsVisibleSelectable(g_focusCanvas )) newPoint->ShowScaleWarningMessage(g_focusCanvas);
9238 }
9239 
pupHandler_PasteTrack()9240 void pupHandler_PasteTrack() {
9241     Kml kml;
9242 
9243     int pasteBuffer = kml.ParsePasteBuffer();
9244     Track* pasted = kml.GetParsedTrack();
9245     if( ! pasted ) return;
9246 
9247     TrackPoint* curPoint;
9248 
9249     Track* newTrack = new Track();
9250     TrackPoint* newPoint;
9251     TrackPoint* prevPoint = NULL;
9252 
9253     newTrack->SetName(pasted->GetName());
9254 
9255     for( int i = 0; i < pasted->GetnPoints(); i++ ) {
9256         curPoint = pasted->GetPoint( i );
9257 
9258         newPoint = new TrackPoint( curPoint );
9259 
9260         wxDateTime now = wxDateTime::Now();
9261         newPoint->SetCreateTime(curPoint->GetCreateTime());
9262 
9263         newTrack->AddPoint( newPoint );
9264 
9265         if( prevPoint )
9266             pSelect->AddSelectableTrackSegment(
9267                 prevPoint->m_lat, prevPoint->m_lon,
9268                 newPoint->m_lat, newPoint->m_lon,
9269                 prevPoint, newPoint, newTrack );
9270 
9271         prevPoint = newPoint;
9272     }
9273 
9274     pTrackList->Append( newTrack );
9275     pConfig->AddNewTrack( newTrack );
9276 
9277     gFrame->InvalidateAllGL();
9278     gFrame->RefreshAllCanvas( false );
9279 }
9280 
InvokeCanvasMenu(int x,int y,int seltype)9281 bool ChartCanvas::InvokeCanvasMenu(int x, int y, int seltype)
9282 {
9283     m_canvasMenu = new CanvasMenuHandler(this, m_pSelectedRoute, m_pSelectedTrack,
9284                                          m_pFoundRoutePoint, m_FoundAIS_MMSI, m_pIDXCandidate);
9285 
9286     Connect(  wxEVT_COMMAND_MENU_SELECTED,(wxObjectEventFunction) (wxEventFunction) &ChartCanvas::PopupMenuHandler );
9287 
9288     m_canvasMenu->CanvasPopupMenu( x, y, seltype );
9289 
9290     Disconnect(  wxEVT_COMMAND_MENU_SELECTED,(wxObjectEventFunction) (wxEventFunction) &ChartCanvas::PopupMenuHandler );
9291 
9292     delete m_canvasMenu;
9293     m_canvasMenu = NULL;
9294 
9295 #ifdef __WXQT__
9296     //gFrame->SurfaceToolbar();
9297     //g_MainToolbar->Raise();
9298 #endif
9299 
9300     return true;
9301 }
9302 
PopupMenuHandler(wxCommandEvent & event)9303 void ChartCanvas::PopupMenuHandler( wxCommandEvent& event )
9304 {
9305     //  Pass menu events from the canvas to the menu handler
9306     //  This is necessarily in ChartCanvas since that is the menu's parent.
9307     if(m_canvasMenu){
9308         m_canvasMenu->PopupMenuHandler( event );
9309     }
9310     return;
9311 
9312 }
9313 
StartRoute(void)9314 void ChartCanvas::StartRoute( void )
9315 {
9316     // Do not allow more than one canvas to create a route at one time.
9317     if(g_brouteCreating)
9318         return;
9319 
9320     if(g_MainToolbar)
9321         g_MainToolbar->DisableTooltips();
9322 
9323     g_brouteCreating = true;
9324     m_routeState = 1;
9325     m_bDrawingRoute = false;
9326     SetCursor( *pCursorPencil );
9327     SetCanvasToolbarItemState( ID_ROUTE, true );
9328     gFrame->SetMasterToolbarItemState( ID_MENU_ROUTE_NEW, true );
9329 
9330     HideGlobalToolbar();
9331 
9332 #ifdef __OCPN__ANDROID__
9333     androidSetRouteAnnunciator(true);
9334 #endif
9335 
9336 }
9337 
FinishRoute(void)9338 void ChartCanvas::FinishRoute( void )
9339 {
9340     m_routeState = 0;
9341     m_prev_pMousePoint = NULL;
9342     m_bDrawingRoute = false;
9343 
9344     SetCanvasToolbarItemState( ID_ROUTE, false );
9345     gFrame->SetMasterToolbarItemState( ID_MENU_ROUTE_NEW, false );
9346 #ifdef __OCPN__ANDROID__
9347     androidSetRouteAnnunciator(false);
9348 #endif
9349 
9350     SetCursor( *pCursorArrow );
9351 
9352     if( m_pMouseRoute ) {
9353         if( m_bAppendingRoute )
9354             pConfig->UpdateRoute( m_pMouseRoute );
9355         else {
9356             if( m_pMouseRoute->GetnPoints() > 1 ) {
9357                 pConfig->AddNewRoute( m_pMouseRoute );
9358             } else {
9359                 g_pRouteMan->DeleteRoute( m_pMouseRoute );
9360                 m_pMouseRoute = NULL;
9361             }
9362         }
9363         if( m_pMouseRoute )
9364             m_pMouseRoute->SetHiLite(0);
9365 
9366         if( RoutePropDlgImpl::getInstanceFlag() && pRoutePropDialog && ( pRoutePropDialog->IsShown() ) ) {
9367             pRoutePropDialog->SetRouteAndUpdate( m_pMouseRoute, true );
9368         }
9369 
9370         if(RouteManagerDialog::getInstanceFlag() && pRouteManagerDialog){
9371         if( pRouteManagerDialog && pRouteManagerDialog->IsShown() )
9372             pRouteManagerDialog->UpdateRouteListCtrl();
9373         }
9374 
9375     }
9376     m_bAppendingRoute = false;
9377     m_pMouseRoute = NULL;
9378 
9379     m_pSelectedRoute = NULL;
9380 
9381     undo->InvalidateUndo();
9382     gFrame->RefreshAllCanvas( true );
9383 
9384     if(g_MainToolbar)
9385         g_MainToolbar->EnableTooltips();
9386 
9387     ShowGlobalToolbar();
9388 
9389     g_brouteCreating = false;
9390 }
9391 
HideGlobalToolbar()9392 void ChartCanvas::HideGlobalToolbar()
9393 {
9394     if(m_canvasIndex == 0){
9395         m_last_TBviz = gFrame->SetGlobalToolbarViz( false );
9396     }
9397 }
9398 
ShowGlobalToolbar()9399 void ChartCanvas::ShowGlobalToolbar()
9400 {
9401     if(m_canvasIndex == 0){
9402         if(m_last_TBviz)
9403             gFrame->SetGlobalToolbarViz( true );
9404     }
9405 }
9406 
9407 
9408 
9409 
ShowAISTargetList(void)9410 void ChartCanvas::ShowAISTargetList( void )
9411 {
9412     if( NULL == g_pAISTargetList ) {         // There is one global instance of the Dialog
9413         g_pAISTargetList = new AISTargetListDialog( parent_frame, g_pauimgr, g_pAIS );
9414     }
9415 
9416     g_pAISTargetList->UpdateAISTargetList();
9417 
9418 }
9419 
RenderAllChartOutlines(ocpnDC & dc,ViewPort & vp)9420 void ChartCanvas::RenderAllChartOutlines( ocpnDC &dc, ViewPort& vp )
9421 {
9422     if( !m_bShowOutlines ) return;
9423 
9424     if(!ChartData)
9425         return;
9426 
9427     int nEntry = ChartData->GetChartTableEntries();
9428 
9429     for( int i = 0; i < nEntry; i++ ) {
9430         ChartTableEntry *pt = (ChartTableEntry *) &ChartData->GetChartTableEntry( i );
9431 
9432         //    Check to see if the candidate chart is in the currently active group
9433         bool b_group_draw = false;
9434         if( m_groupIndex > 0 ) {
9435             for( unsigned int ig = 0; ig < pt->GetGroupArray().size(); ig++ ) {
9436                 int index = pt->GetGroupArray()[ig];
9437                 if( m_groupIndex == index ) {
9438                     b_group_draw = true;
9439                     break;
9440                 }
9441             }
9442         } else
9443             b_group_draw = true;
9444 
9445         if( b_group_draw ) RenderChartOutline( dc, i, vp );
9446     }
9447 
9448     //        On CM93 Composite Charts, draw the outlines of the next smaller scale cell
9449     cm93compchart *pcm93 = NULL;
9450     if( VPoint.b_quilt ) {
9451         for(ChartBase *pch = GetFirstQuiltChart(); pch; pch = GetNextQuiltChart())
9452             if( pch->GetChartType() == CHART_TYPE_CM93COMP ) {
9453                 pcm93 = (cm93compchart *)pch;
9454                 break;
9455             }
9456     } else
9457         if ( m_singleChart && ( m_singleChart->GetChartType() == CHART_TYPE_CM93COMP ) )
9458             pcm93 = (cm93compchart *) m_singleChart;
9459 
9460     if( pcm93 ) {
9461         double chart_native_ppm = m_canvas_scale_factor / pcm93->GetNativeScale();
9462         double zoom_factor = GetVP().view_scale_ppm / chart_native_ppm;
9463 
9464         if( zoom_factor > 8.0 ) {
9465             wxPen mPen( GetGlobalColor( _T("UINFM") ), 2, wxPENSTYLE_SHORT_DASH );
9466             dc.SetPen( mPen );
9467         } else {
9468             wxPen mPen( GetGlobalColor( _T("UINFM") ), 1, wxPENSTYLE_SOLID );
9469             dc.SetPen( mPen );
9470         }
9471 
9472         pcm93->RenderNextSmallerCellOutlines( dc, vp, this );
9473     }
9474 }
9475 
RenderChartOutline(ocpnDC & dc,int dbIndex,ViewPort & vp)9476 void ChartCanvas::RenderChartOutline( ocpnDC &dc, int dbIndex, ViewPort& vp )
9477 {
9478 #ifdef ocpnUSE_GL
9479     if(g_bopengl && m_glcc) {
9480         /* opengl version specially optimized */
9481         m_glcc->RenderChartOutline(dc, dbIndex, vp);
9482         return;
9483     }
9484 #endif
9485 
9486     if( ChartData->GetDBChartType( dbIndex ) == CHART_TYPE_PLUGIN ){
9487         if( !ChartData->IsChartAvailable( dbIndex ) )
9488             return;
9489     }
9490 
9491     float plylat, plylon;
9492     float plylat1, plylon1;
9493 
9494     int pixx, pixy, pixx1, pixy1;
9495 
9496     LLBBox box;
9497     ChartData->GetDBBoundingBox( dbIndex, box );
9498 
9499     // Don't draw an outline in the case where the chart covers the entire world */
9500     if(box.GetLonRange() == 360)
9501         return;
9502 
9503     double lon_bias = 0;
9504     // chart is outside of viewport lat/lon bounding box
9505     if( box.IntersectOutGetBias( vp.GetBBox(), lon_bias ) )
9506         return;
9507 
9508     int nPly = ChartData->GetDBPlyPoint( dbIndex, 0, &plylat, &plylon );
9509 
9510     if( ChartData->GetDBChartType( dbIndex ) == CHART_TYPE_CM93 )
9511         dc.SetPen( wxPen( GetGlobalColor( _T ( "YELO1" ) ), 1, wxPENSTYLE_SOLID ) );
9512 
9513     else if( ChartData->GetDBChartFamily( dbIndex ) == CHART_FAMILY_VECTOR )
9514         dc.SetPen( wxPen( GetGlobalColor( _T ( "UINFG" ) ), 1, wxPENSTYLE_SOLID ) );
9515 
9516     else
9517         dc.SetPen( wxPen( GetGlobalColor( _T ( "UINFR" ) ), 1, wxPENSTYLE_SOLID ) );
9518 
9519     //        Are there any aux ply entries?
9520     int nAuxPlyEntries = ChartData->GetnAuxPlyEntries( dbIndex );
9521     if( 0 == nAuxPlyEntries )                 // There are no aux Ply Point entries
9522     {
9523         wxPoint r, r1;
9524 
9525         ChartData->GetDBPlyPoint( dbIndex, 0, &plylat, &plylon );
9526         plylon += lon_bias;
9527 
9528         GetCanvasPointPix( plylat, plylon, &r );
9529         pixx = r.x;
9530         pixy = r.y;
9531 
9532         for( int i = 0; i < nPly - 1; i++ ) {
9533             ChartData->GetDBPlyPoint( dbIndex, i + 1, &plylat1, &plylon1 );
9534             plylon1 += lon_bias;
9535 
9536             GetCanvasPointPix( plylat1, plylon1, &r1 );
9537             pixx1 = r1.x;
9538             pixy1 = r1.y;
9539 
9540             int pixxs1 = pixx1;
9541             int pixys1 = pixy1;
9542 
9543             bool b_skip = false;
9544 
9545             if( vp.chart_scale > 5e7 ) {
9546                 //    calculate projected distance between these two points in meters
9547                 double dist = sqrt( pow( (double) (pixx1 - pixx), 2 ) +
9548                                     pow( (double) (pixy1 - pixy), 2 ) ) / vp.view_scale_ppm;
9549 
9550                 if(dist > 0.0){
9551                     //    calculate GC distance between these two points in meters
9552                     double distgc = DistGreatCircle( plylat, plylon, plylat1, plylon1 ) * 1852.;
9553 
9554                     //    If the distances are nonsense, it means that the scale is very small and the segment wrapped the world
9555                     //    So skip it....
9556                     //    TODO improve this to draw two segments
9557                     if( fabs( dist - distgc ) > 10000. * 1852. )          //lotsa miles
9558                         b_skip = true;
9559                 }
9560                 else
9561                     b_skip = true;
9562             }
9563 
9564             ClipResult res = cohen_sutherland_line_clip_i( &pixx, &pixy, &pixx1, &pixy1, 0,
9565                              vp.pix_width, 0, vp.pix_height );
9566             if( res != Invisible && !b_skip ) dc.DrawLine( pixx, pixy, pixx1, pixy1, false );
9567 
9568             plylat = plylat1;
9569             plylon = plylon1;
9570             pixx = pixxs1;
9571             pixy = pixys1;
9572         }
9573 
9574         ChartData->GetDBPlyPoint( dbIndex, 0, &plylat1, &plylon1 );
9575         plylon1 += lon_bias;
9576 
9577         GetCanvasPointPix( plylat1, plylon1, &r1 );
9578         pixx1 = r1.x;
9579         pixy1 = r1.y;
9580 
9581         ClipResult res = cohen_sutherland_line_clip_i( &pixx, &pixy, &pixx1, &pixy1, 0,
9582                          vp.pix_width, 0, vp.pix_height );
9583         if( res != Invisible ) dc.DrawLine( pixx, pixy, pixx1, pixy1, false );
9584     }
9585 
9586     else                              // Use Aux PlyPoints
9587     {
9588         wxPoint r, r1;
9589 
9590         int nAuxPlyEntries = ChartData->GetnAuxPlyEntries( dbIndex );
9591         for( int j = 0; j < nAuxPlyEntries; j++ ) {
9592 
9593             int nAuxPly = ChartData->GetDBAuxPlyPoint( dbIndex, 0, j, &plylat, &plylon );
9594             GetCanvasPointPix( plylat, plylon, &r );
9595             pixx = r.x;
9596             pixy = r.y;
9597 
9598             for( int i = 0; i < nAuxPly - 1; i++ ) {
9599                 ChartData->GetDBAuxPlyPoint( dbIndex, i + 1, j, &plylat1, &plylon1 );
9600 
9601                 GetCanvasPointPix( plylat1, plylon1, &r1 );
9602                 pixx1 = r1.x;
9603                 pixy1 = r1.y;
9604 
9605                 int pixxs1 = pixx1;
9606                 int pixys1 = pixy1;
9607 
9608                 bool b_skip = false;
9609 
9610                 if( vp.chart_scale > 5e7 ) {
9611                     //    calculate projected distance between these two points in meters
9612                     double dist = sqrt(
9613                                       (double) ( ( pixx1 - pixx ) * ( pixx1 - pixx ) )
9614                                       + ( ( pixy1 - pixy ) * ( pixy1 - pixy ) ) ) / vp.view_scale_ppm;
9615                    if(dist > 0.0){
9616                    //    calculate GC distance between these two points in meters
9617                         double distgc = DistGreatCircle( plylat, plylon, plylat1, plylon1 ) * 1852.;
9618 
9619                     //    If the distances are nonsense, it means that the scale is very small and the segment wrapped the world
9620                     //    So skip it....
9621                     //    TODO improve this to draw two segments
9622                         if( fabs( dist - distgc ) > 10000. * 1852. )          //lotsa miles
9623                             b_skip = true;
9624                     }
9625                     else
9626                         b_skip = true;
9627                 }
9628 
9629                 ClipResult res = cohen_sutherland_line_clip_i( &pixx, &pixy, &pixx1, &pixy1, 0,
9630                                  vp.pix_width, 0, vp.pix_height );
9631                 if( res != Invisible && !b_skip ) dc.DrawLine( pixx, pixy, pixx1, pixy1 );
9632 
9633                 plylat = plylat1;
9634                 plylon = plylon1;
9635                 pixx = pixxs1;
9636                 pixy = pixys1;
9637             }
9638 
9639             ChartData->GetDBAuxPlyPoint( dbIndex, 0, j, &plylat1, &plylon1 );
9640             GetCanvasPointPix( plylat1, plylon1, &r1 );
9641             pixx1 = r1.x;
9642             pixy1 = r1.y;
9643 
9644             ClipResult res = cohen_sutherland_line_clip_i( &pixx, &pixy, &pixx1, &pixy1, 0,
9645                              vp.pix_width, 0, vp.pix_height );
9646             if( res != Invisible ) dc.DrawLine( pixx, pixy, pixx1, pixy1, false );
9647         }
9648     }
9649 
9650 }
9651 
RouteLegInfo(ocpnDC & dc,wxPoint ref_point,const wxString & first,const wxString & second)9652 static void RouteLegInfo( ocpnDC &dc, wxPoint ref_point, const wxString &first, const wxString &second )
9653 {
9654     wxFont *dFont = FontMgr::Get().GetFont( _("RouteLegInfoRollover") );
9655     dc.SetFont( *dFont );
9656 
9657     int w1, h1;
9658     int w2 = 0;
9659     int h2 = 0;
9660     int h, w;
9661 
9662     int xp, yp;
9663     int hilite_offset = 3;
9664 #ifdef __WXMAC__
9665     wxScreenDC sdc;
9666     sdc.GetTextExtent(first, &w1, &h1, NULL, NULL, dFont);
9667     if(second.Len())
9668         sdc.GetTextExtent(second, &w2, &h2, NULL, NULL, dFont);
9669 #else
9670     dc.GetTextExtent( first, &w1, &h1 );
9671     if(second.Len())
9672         dc.GetTextExtent( second, &w2, &h2 );
9673 #endif
9674 
9675     w = wxMax(w1, w2);
9676     h = h1 + h2;
9677 
9678     xp = ref_point.x - w;
9679     yp = ref_point.y;
9680     yp += hilite_offset;
9681 
9682     AlphaBlending( dc, xp, yp, w, h, 0.0, GetGlobalColor( _T ( "YELO1" ) ), 172 );
9683 
9684     dc.SetPen( wxPen( GetGlobalColor( _T ( "UBLCK" ) ) ) );
9685     dc.SetTextForeground( FontMgr::Get().GetFontColor( _("RouteLegInfoRollover") ) );
9686 
9687     dc.DrawText( first, xp, yp );
9688     if(second.Len())
9689         dc.DrawText( second, xp, yp + h1 );
9690 }
9691 
RenderRouteLegs(ocpnDC & dc)9692 void ChartCanvas::RenderRouteLegs( ocpnDC &dc )
9693 {
9694         Route* route = 0;
9695     if( m_routeState >= 2)
9696         route = m_pMouseRoute;
9697     if(m_pMeasureRoute && m_bMeasure_Active && ( m_nMeasureState >= 2 ) )
9698             route = m_pMeasureRoute;
9699 
9700         if(!route)
9701             return;
9702 
9703         double render_lat = m_cursor_lat;
9704         double render_lon = m_cursor_lon;
9705 
9706             int np = route->GetnPoints();
9707             if(np){
9708                 if(g_btouch && (np > 1))
9709                     np --;
9710                 RoutePoint rp = route->GetPoint(np);
9711                 render_lat = rp.m_lat;
9712                 render_lon = rp.m_lon;
9713             }
9714 
9715     double rhumbBearing, rhumbDist;
9716         DistanceBearingMercator( m_cursor_lat, m_cursor_lon, render_lat, render_lon, &rhumbBearing, &rhumbDist );
9717     double brg = rhumbBearing;
9718     double dist = rhumbDist;
9719 
9720     // Skip GreatCircle rubberbanding on touch devices.
9721     if(!g_btouch){
9722         double gcBearing, gcBearing2, gcDist;
9723         Geodesic::GreatCircleDistBear( render_lon, render_lat, m_cursor_lon, m_cursor_lat, &gcDist, &gcBearing, &gcBearing2);
9724         double gcDistm = gcDist / 1852.0;
9725 
9726         if( ( render_lat == m_cursor_lat ) && ( render_lon == m_cursor_lon ) ) rhumbBearing = 90.;
9727 
9728         wxPoint destPoint, lastPoint;
9729 
9730         route->m_NextLegGreatCircle = false;
9731         int milesDiff = rhumbDist - gcDistm;
9732         if( milesDiff > 1 ) {
9733             brg = gcBearing;
9734             dist = gcDistm;
9735             route->m_NextLegGreatCircle = true;
9736         }
9737 
9738             route->DrawPointWhich( dc, this, route->m_lastMousePointIndex, &lastPoint );
9739 
9740             if( route->m_NextLegGreatCircle ) {
9741                 for( int i=1; i<=milesDiff; i++ ) {
9742                     double p = (double)i * (1.0/(double)milesDiff);
9743                     double pLat, pLon;
9744                     Geodesic::GreatCircleTravel( render_lon, render_lat, gcDist*p, brg, &pLon, &pLat, &gcBearing2 );
9745                     destPoint = VPoint.GetPixFromLL( pLat, pLon );
9746                     route->DrawSegment( dc, this, &lastPoint, &destPoint, GetVP(), false );
9747                     lastPoint = destPoint;
9748                 }
9749             }
9750             else {
9751                 if (r_rband.x && r_rband.y) {    // RubberBand disabled?
9752                     route->DrawSegment(dc, this, &lastPoint, &r_rband, GetVP(), false);
9753 
9754                     if (m_bMeasure_DistCircle) {
9755                         double distanceRad = sqrtf(powf((float)(r_rband.x - lastPoint.x), 2) +
9756                             powf((float)(r_rband.y - lastPoint.y), 2));
9757 
9758                         dc.SetPen(*g_pRouteMan->GetRoutePen());
9759                         dc.SetBrush(*wxTRANSPARENT_BRUSH);
9760                         dc.StrokeCircle(lastPoint.x, lastPoint.y, distanceRad);
9761                     }
9762                 }
9763             }
9764         }
9765 
9766         wxString routeInfo;
9767         if( g_bShowTrue )
9768             routeInfo << wxString::Format( wxString("%03d°  ", wxConvUTF8 ), (int)brg );
9769         if( g_bShowMag ){
9770             double latAverage = (m_cursor_lat + render_lat)/2;
9771             double lonAverage = (m_cursor_lon + render_lon)/2;
9772             double varBrg = gFrame->GetMag( brg, latAverage, lonAverage);
9773 
9774             routeInfo << wxString::Format( wxString("%03d°(M)  ", wxConvUTF8 ), (int)varBrg );
9775         }
9776 
9777         routeInfo << _T(" ") << FormatDistanceAdaptive( dist );
9778 
9779         wxString s0;
9780         if( !route->m_bIsInLayer )
9781             s0.Append( _("Route") + _T(": ") );
9782         else
9783             s0.Append( _("Layer Route: ") );
9784 
9785         double disp_length = route->m_route_length;
9786         if( !g_btouch)
9787             disp_length += dist;                // Add in the to-be-created leg.
9788         s0 += FormatDistanceAdaptive( disp_length );
9789 
9790         RouteLegInfo( dc, r_rband, routeInfo, s0 );
9791 
9792         m_brepaint_piano = true;
9793 }
9794 
WarpPointerDeferred(int x,int y)9795 void ChartCanvas::WarpPointerDeferred( int x, int y )
9796 {
9797     warp_x = x;
9798     warp_y = y;
9799     warp_flag = true;
9800 }
9801 
9802 
9803 int s_msg;
9804 
UpdateCanvasS52PLIBConfig()9805 void ChartCanvas::UpdateCanvasS52PLIBConfig()
9806 {
9807     if(!ps52plib)
9808         return;
9809 
9810     if( VPoint.b_quilt ){          // quilted
9811         if( !m_pQuilt || !m_pQuilt->IsComposed() )
9812             return;  // not ready
9813 
9814         if(m_pQuilt->IsQuiltVector()){
9815             if(ps52plib->GetStateHash() != m_s52StateHash){
9816                 UpdateS52State();
9817                 m_s52StateHash = ps52plib->GetStateHash();
9818             }
9819         }
9820     }
9821     else{
9822         if(ps52plib->GetStateHash() != m_s52StateHash){
9823             UpdateS52State();
9824             m_s52StateHash = ps52plib->GetStateHash();
9825         }
9826     }
9827 
9828     // Plugin charts
9829     bool bSendPlibState = true;
9830     if( VPoint.b_quilt ){          // quilted
9831         if(!m_pQuilt->DoesQuiltContainPlugins())
9832             bSendPlibState = false;
9833     }
9834 
9835     if(bSendPlibState){
9836         wxJSONValue v;
9837         v[_T("OpenCPN Version Major")] = VERSION_MAJOR;
9838         v[_T("OpenCPN Version Minor")] = VERSION_MINOR;
9839         v[_T("OpenCPN Version Patch")] = VERSION_PATCH;
9840         v[_T("OpenCPN Version Date")] = VERSION_DATE;
9841         v[_T("OpenCPN Version Full")] = VERSION_FULL;
9842 
9843         //  S52PLIB state
9844         v[_T("OpenCPN S52PLIB ShowText")] = GetShowENCText();
9845         v[_T("OpenCPN S52PLIB ShowSoundings")] = GetShowENCDepth();
9846         v[_T("OpenCPN S52PLIB ShowLights")] = GetShowENCLights();
9847         v[_T("OpenCPN S52PLIB ShowAnchorConditions")] = m_encShowAnchor; //ps52plib->GetAnchorOn();
9848         v[_T("OpenCPN S52PLIB ShowQualityOfData")] = GetShowENCDataQual(); //ps52plib->GetQualityOfDataOn();
9849         v[_T("OpenCPN S52PLIB ShowATONLabel")] = GetShowENCBuoyLabels();
9850         v[_T("OpenCPN S52PLIB ShowLightDescription")] = GetShowENCLightDesc();
9851 
9852         v[_T("OpenCPN S52PLIB DisplayCategory")] = GetENCDisplayCategory();
9853 
9854         v[_T("OpenCPN S52PLIB SoundingsFactor")] = g_ENCSoundingScaleFactor;
9855 
9856         // Global options
9857 /*
9858         v[_T("OpenCPN S52PLIB MetaDisplay")] = ps52plib->m_bShowMeta;
9859         v[_T("OpenCPN S52PLIB DeclutterText")] = ps52plib->m_bDeClutterText;
9860         v[_T("OpenCPN S52PLIB ShowNationalText")] = ps52plib->m_bShowNationalTexts;
9861         v[_T("OpenCPN S52PLIB ShowImportantTextOnly")] = ps52plib->m_bShowS57ImportantTextOnly;
9862         v[_T("OpenCPN S52PLIB UseSCAMIN")] = ps52plib->m_bUseSCAMIN;
9863         v[_T("OpenCPN S52PLIB SymbolStyle")] = ps52plib->m_nSymbolStyle;
9864         v[_T("OpenCPN S52PLIB BoundaryStyle")] = ps52plib->m_nBoundaryStyle;
9865         v[_T("OpenCPN S52PLIB ColorShades")] = S52_getMarinerParam( S52_MAR_TWO_SHADES );
9866 */
9867         wxJSONWriter w;
9868         wxString out;
9869         w.Write(v, out);
9870 
9871         if(!g_lastPluginMessage.IsSameAs(out)){
9872             //printf("message %d  %d\n", s_msg++, m_canvasIndex);
9873             g_pi_manager->SendMessageToAllPlugins(wxString(_T("OpenCPN Config")), out);
9874         }
9875     }
9876 }
9877 int spaint;
9878 int s_in_update;
OnPaint(wxPaintEvent & event)9879 void ChartCanvas::OnPaint( wxPaintEvent& event )
9880 {
9881     wxPaintDC dc( this );
9882 
9883     //GetToolbar()->Show( m_bToolbarEnable );
9884 
9885     //  Paint updates may have been externally disabled (temporarily, to avoid Yield() recursion performance loss)
9886     //  It is important that the wxPaintDC is built, even if we elect to not process this paint message.
9887     //  Otherwise, the paint message may not be removed from the message queue, esp on Windows. (FS#1213)
9888     //  This would lead to a deadlock condition in ::wxYield()
9889 
9890     if(!m_b_paint_enable){
9891         return;
9892     }
9893 
9894 
9895     //  If necessary, reconfigure the S52 PLIB
9896     UpdateCanvasS52PLIBConfig();
9897 
9898 
9899 #ifdef ocpnUSE_GL
9900     if( !g_bdisable_opengl && m_glcc )
9901         m_glcc->Show( g_bopengl );
9902 
9903     if( m_glcc && g_bopengl ) {
9904         if( !s_in_update ) {          // no recursion allowed, seen on lo-spec Mac
9905             s_in_update++;
9906             m_glcc->Update();
9907             s_in_update--;
9908         }
9909 
9910         return;
9911     }
9912 #endif
9913 
9914     if( ( GetVP().pix_width == 0 ) || ( GetVP().pix_height == 0 ) ) return;
9915 
9916     wxRegion ru = GetUpdateRegion();
9917 
9918     int rx, ry, rwidth, rheight;
9919     ru.GetBox( rx, ry, rwidth, rheight );
9920         //printf("%d Onpaint update region box: %d %d %d %d\n", spaint++, rx, ry, rwidth, rheight);
9921 
9922 #ifdef ocpnUSE_DIBSECTION
9923     ocpnMemDC temp_dc;
9924 #else
9925     wxMemoryDC temp_dc;
9926 #endif
9927 
9928     long height = GetVP().pix_height;
9929 
9930 #ifdef __WXMAC__
9931     //On OS X we have to explicitly extend the region for the piano area
9932     ocpnStyle::Style* style = g_StyleManager->GetCurrentStyle();
9933     if(!style->chartStatusWindowTransparent && g_bShowChartBar)
9934         height += m_Piano->GetHeight();
9935 #endif // __WXMAC__
9936     wxRegion rgn_chart( 0, 0, GetVP().pix_width, height );
9937 
9938 //    In case Thumbnail is shown, set up dc clipper and blt iterator regions
9939     if( pthumbwin ) {
9940         int thumbx, thumby, thumbsx, thumbsy;
9941         pthumbwin->GetPosition( &thumbx, &thumby );
9942         pthumbwin->GetSize( &thumbsx, &thumbsy );
9943         wxRegion rgn_thumbwin( thumbx, thumby, thumbsx - 1, thumbsy - 1 );
9944 
9945         if( pthumbwin->IsShown() ) {
9946             rgn_chart.Subtract( rgn_thumbwin );
9947             ru.Subtract( rgn_thumbwin );
9948         }
9949     }
9950 
9951     // subtract the chart bar if it isn't transparent, and determine if we need to paint it
9952     wxRegion rgn_blit = ru;
9953     if(g_bShowChartBar) {
9954         wxRect chart_bar_rect(0, GetClientSize().y - m_Piano->GetHeight(),
9955                               GetClientSize().x, m_Piano->GetHeight());
9956 
9957         ocpnStyle::Style* style = g_StyleManager->GetCurrentStyle();
9958         if(ru.Contains(chart_bar_rect) != wxOutRegion) {
9959             if(style->chartStatusWindowTransparent)
9960                 m_brepaint_piano = true;
9961             else
9962                 ru.Subtract(chart_bar_rect);
9963         }
9964     }
9965 
9966     if(m_Compass && m_Compass->IsShown()){
9967         wxRect compassRect = m_Compass->GetRect();
9968         if(ru.Contains(compassRect) != wxOutRegion) {
9969             ru.Subtract(compassRect);
9970         }
9971     }
9972 
9973 
9974 
9975     //  Is this viewpoint the same as the previously painted one?
9976     bool b_newview = true;
9977 
9978     if( ( m_cache_vp.view_scale_ppm == VPoint.view_scale_ppm )
9979             && ( m_cache_vp.rotation == VPoint.rotation ) && ( m_cache_vp.clat == VPoint.clat )
9980             && ( m_cache_vp.clon == VPoint.clon ) && m_cache_vp.IsValid() ) {
9981         b_newview = false;
9982     }
9983 
9984     //  If the ViewPort is skewed or rotated, we may be able to use the cached rotated bitmap.
9985     bool b_rcache_ok = false;
9986     if( fabs( VPoint.skew ) > 0.01 || fabs( VPoint.rotation ) > 0.01)
9987         b_rcache_ok = !b_newview;
9988 
9989     //  Make a special VP
9990     if( VPoint.b_MercatorProjectionOverride ) VPoint.SetProjectionType( PROJECTION_MERCATOR );
9991     ViewPort svp = VPoint;
9992 
9993     svp.pix_width = svp.rv_rect.width;
9994     svp.pix_height = svp.rv_rect.height;
9995 
9996 //        printf("Onpaint pix %d %d\n", VPoint.pix_width, VPoint.pix_height);
9997 //        printf("OnPaint rv_rect %d %d\n", VPoint.rv_rect.width, VPoint.rv_rect.height);
9998 
9999     OCPNRegion chart_get_region( wxRect( 0, 0, svp.pix_width, svp.pix_height ) );
10000 
10001     //  If we are going to use the cached rotated image, there is no need to fetch any chart data
10002     //  and this will do it...
10003     if( b_rcache_ok ) chart_get_region.Clear();
10004 
10005     //  Blit pan acceleration
10006     if( VPoint.b_quilt )          // quilted
10007     {
10008         if( !m_pQuilt || !m_pQuilt->IsComposed() )
10009             return;  // not ready
10010 
10011         bool bvectorQuilt = m_pQuilt->IsQuiltVector();
10012 
10013         bool busy = false;
10014         if( bvectorQuilt &&
10015             ( m_cache_vp.view_scale_ppm != VPoint.view_scale_ppm || m_cache_vp.rotation != VPoint.rotation))
10016         {
10017             OCPNPlatform::ShowBusySpinner();
10018             busy = true;
10019         }
10020 
10021         if( ( m_working_bm.GetWidth() != svp.pix_width )
10022                 || ( m_working_bm.GetHeight() != svp.pix_height ) ) m_working_bm.Create(
10023                         svp.pix_width, svp.pix_height, -1 ); // make sure the target is big enoug
10024 
10025         if( fabs( VPoint.rotation ) < 0.01 ) {
10026             bool b_save = true;
10027 
10028             if(g_SencThreadManager){
10029                 if(g_SencThreadManager->GetJobCount()){
10030                     b_save = false;
10031                     m_cache_vp.Invalidate();
10032                 }
10033             }
10034 
10035             //  If the saved wxBitmap from last OnPaint is useable
10036             //  calculate the blit parameters
10037 
10038             //  We can only do screen blit painting if subsequent ViewPorts differ by whole pixels
10039             //  So, in small scale bFollow mode, force the full screen render.
10040             //  This seems a hack....There may be better logic here.....
10041 
10042 //                  if(m_bFollow)
10043 //                        b_save = false;
10044 
10045             if( m_bm_cache_vp.IsValid() && m_cache_vp.IsValid() /*&& !m_bFollow*/) {
10046                 if( b_newview ) {
10047                     wxPoint c_old = VPoint.GetPixFromLL( VPoint.clat, VPoint.clon );
10048                     wxPoint c_new = m_bm_cache_vp.GetPixFromLL( VPoint.clat, VPoint.clon );
10049 
10050                     int dy = c_new.y - c_old.y;
10051                     int dx = c_new.x - c_old.x;
10052 
10053 //                              printf("In OnPaint Trying Blit dx: %d  dy:%d\n\n", dx, dy);
10054 
10055                     if( m_pQuilt->IsVPBlittable( VPoint, dx, dy, true ) ) {
10056                         if( dx || dy ) {
10057                             //  Blit the reuseable portion of the cached wxBitmap to a working bitmap
10058                             temp_dc.SelectObject( m_working_bm );
10059 
10060                             wxMemoryDC cache_dc;
10061                             cache_dc.SelectObject( m_cached_chart_bm );
10062 
10063                             if( dy > 0 ) {
10064                                 if( dx > 0 ) {
10065                                     temp_dc.Blit( 0, 0, VPoint.pix_width - dx,
10066                                                                VPoint.pix_height - dy, &cache_dc, dx, dy );
10067                                 }
10068                                 else {
10069                                     temp_dc.Blit( -dx, 0, VPoint.pix_width + dx,
10070                                                   VPoint.pix_height - dy, &cache_dc, 0, dy );
10071                                 }
10072 
10073                             } else {
10074                                 if( dx > 0 ) {
10075                                     temp_dc.Blit( 0, -dy, VPoint.pix_width - dx,
10076                                                                VPoint.pix_height + dy, &cache_dc, dx, 0 );
10077                                 }
10078                                 else {
10079                                     temp_dc.Blit( -dx, -dy, VPoint.pix_width + dx,
10080                                                   VPoint.pix_height + dy, &cache_dc, 0, 0 );
10081                                 }
10082                             }
10083 
10084                             OCPNRegion update_region;
10085                             if( dy ) {
10086                                 if( dy > 0 ) update_region.Union(
10087                                         wxRect( 0, VPoint.pix_height - dy, VPoint.pix_width, dy ) );
10088                                 else
10089                                     update_region.Union( wxRect( 0, 0, VPoint.pix_width, -dy ) );
10090                             }
10091 
10092                             if( dx ) {
10093                                 if( dx > 0 ) update_region.Union(
10094                                         wxRect( VPoint.pix_width - dx, 0, dx, VPoint.pix_height ) );
10095                                 else
10096                                     update_region.Union( wxRect( 0, 0, -dx, VPoint.pix_height ) );
10097                             }
10098 
10099                             //  Render the new region
10100                             m_pQuilt->RenderQuiltRegionViewOnDCNoText( temp_dc, svp, update_region );
10101                             cache_dc.SelectObject( wxNullBitmap );
10102                         } else {
10103                             //    No sensible (dx, dy) change in the view, so use the cached member bitmap
10104                             temp_dc.SelectObject( m_cached_chart_bm );
10105                             b_save = false;
10106 
10107                         }
10108                         m_pQuilt->ComputeRenderRegion( svp, chart_get_region );
10109 
10110                     } else              // not blitable
10111                     {
10112                         temp_dc.SelectObject( m_working_bm );
10113                         m_pQuilt->RenderQuiltRegionViewOnDCNoText( temp_dc, svp, chart_get_region );
10114                     }
10115                 } else {
10116                     //    No change in the view, so use the cached member bitmap2
10117                     temp_dc.SelectObject( m_cached_chart_bm );
10118                     b_save = false;
10119                 }
10120             } else      //cached bitmap is not yet valid
10121             {
10122                 temp_dc.SelectObject( m_working_bm );
10123                 m_pQuilt->RenderQuiltRegionViewOnDCNoText( temp_dc, svp, chart_get_region );
10124             }
10125 
10126             //  Save the fully rendered quilt image as a wxBitmap member of this class
10127             if( b_save ) {
10128 //                        if((m_cached_chart_bm.GetWidth() != svp.pix_width) || (m_cached_chart_bm.GetHeight() != svp.pix_height))
10129 //                              m_cached_chart_bm.Create(svp.pix_width, svp.pix_height, -1); // target wxBitmap is big enough
10130                 wxMemoryDC scratch_dc_0;
10131                 scratch_dc_0.SelectObject( m_cached_chart_bm );
10132                 scratch_dc_0.Blit( 0, 0, svp.pix_width, svp.pix_height, &temp_dc, 0, 0 );
10133 
10134                 scratch_dc_0.SelectObject( wxNullBitmap );
10135 
10136                 m_bm_cache_vp = VPoint;     // save the ViewPort associated with the cached wxBitmap
10137             }
10138         }
10139 
10140         else            // quilted, rotated
10141         {
10142             temp_dc.SelectObject( m_working_bm );
10143             OCPNRegion chart_get_all_region( wxRect( 0, 0, svp.pix_width, svp.pix_height ) );
10144             m_pQuilt->RenderQuiltRegionViewOnDCNoText( temp_dc, svp, chart_get_all_region );
10145         }
10146 
10147         if(busy)
10148             OCPNPlatform::HideBusySpinner();
10149 
10150     }
10151 
10152     else                  // not quilted
10153     {
10154         if( !m_singleChart ) {
10155             dc.SetBackground( wxBrush( *wxLIGHT_GREY ) );
10156             dc.Clear();
10157             return;
10158         }
10159 
10160         if(!chart_get_region.IsEmpty()){
10161             m_singleChart->RenderRegionViewOnDC( temp_dc, svp, chart_get_region );
10162         }
10163     }
10164 
10165     if(temp_dc.IsOk() ) {
10166 
10167 //    Arrange to render the World Chart vector data behind the rendered current chart
10168 //    so that uncovered canvas areas show at least the world chart.
10169         OCPNRegion chartValidRegion;
10170         if( !VPoint.b_quilt ) {
10171             // Make a region covering the current chart on the canvas
10172 
10173             if(m_singleChart->GetChartFamily() == CHART_FAMILY_VECTOR)
10174                 m_singleChart->GetValidCanvasRegion( svp, &chartValidRegion );
10175             else {
10176                 // The raster calculations  in ChartBaseBSB::ComputeSourceRectangle
10177                 // require that the viewport passed here have pix_width and pix_height
10178                 // set to the actual display, not the virtual (rv_rect) sizes
10179                 // (the vector calculations require the virtual sizes in svp)
10180 
10181                 m_singleChart->GetValidCanvasRegion( VPoint, &chartValidRegion );
10182                 chartValidRegion.Offset(-VPoint.rv_rect.x, -VPoint.rv_rect.y);
10183             }
10184         }
10185         else
10186             chartValidRegion = m_pQuilt->GetFullQuiltRenderedRegion();
10187 
10188         temp_dc.DestroyClippingRegion();
10189 
10190         //    Copy current chart region
10191         OCPNRegion backgroundRegion( wxRect(0, 0, svp.pix_width, svp.pix_height) );
10192 
10193         if( chartValidRegion.IsOk() )
10194             backgroundRegion.Subtract( chartValidRegion );
10195 
10196         if( ! backgroundRegion.IsEmpty() ) {
10197 
10198             //    Draw the Background Chart only in the areas NOT covered by the current chart view
10199 
10200             /* unfortunately wxDC::DrawRectangle and wxDC::Clear do not respect
10201                clipping regions with more than 1 rectangle so... */
10202             wxColour water = pWorldBackgroundChart->water;
10203             if(water.IsOk()){
10204                 temp_dc.SetPen( *wxTRANSPARENT_PEN );
10205                 temp_dc.SetBrush( wxBrush( water ) );
10206                 OCPNRegionIterator upd( backgroundRegion ); // get the update rect list
10207                 while( upd.HaveRects() ) {
10208                     wxRect rect = upd.GetRect();
10209                     temp_dc.DrawRectangle(rect);
10210                     upd.NextRect();
10211                 }
10212             }
10213             //    Associate with temp_dc
10214             wxRegion *clip_region = backgroundRegion.GetNew_wxRegion();
10215             temp_dc.SetDeviceClippingRegion( *clip_region );
10216             delete clip_region;
10217 
10218             ocpnDC bgdc( temp_dc );
10219             double r = VPoint.rotation;
10220             SetVPRotation(VPoint.skew);
10221 
10222             pWorldBackgroundChart->RenderViewOnDC( bgdc, VPoint );
10223             SetVPRotation( r );
10224         }
10225     } // temp_dc.IsOk();
10226 
10227     wxMemoryDC *pChartDC = &temp_dc;
10228     wxMemoryDC rotd_dc;
10229 
10230     if( ( ( fabs( GetVP().rotation ) > 0.01 ) )
10231         ||   ( fabs( GetVP().skew ) > 0.01 ) )  {
10232 
10233         //  Can we use the current rotated image cache?
10234         if( !b_rcache_ok ) {
10235 #ifdef __WXMSW__
10236             wxMemoryDC tbase_dc;
10237             wxBitmap bm_base( svp.pix_width, svp.pix_height, -1 );
10238             tbase_dc.SelectObject( bm_base );
10239             tbase_dc.Blit( 0, 0, svp.pix_width, svp.pix_height, &temp_dc, 0, 0 );
10240             tbase_dc.SelectObject( wxNullBitmap );
10241 #else
10242             const wxBitmap &bm_base = temp_dc.GetSelectedBitmap();
10243 #endif
10244 
10245             wxImage base_image;
10246             if( bm_base.IsOk() ) base_image = bm_base.ConvertToImage();
10247 
10248             //    Use a local static image rotator to improve wxWidgets code profile
10249             //    Especially, on GTK the wxRound and wxRealPoint functions are very expensive.....
10250 
10251             double angle = GetVP().skew - GetVP().rotation;
10252             wxImage ri;
10253             bool b_rot_ok = false;
10254             if( base_image.IsOk() ) {
10255                 ViewPort rot_vp = GetVP();
10256 
10257                 m_b_rot_hidef = false;
10258 
10259                 ri = Image_Rotate( base_image, angle,
10260                                    wxPoint( GetVP().rv_rect.width / 2, GetVP().rv_rect.height / 2 ),
10261                                    m_b_rot_hidef, &m_roffset );
10262 
10263                 if( ( rot_vp.view_scale_ppm == VPoint.view_scale_ppm )
10264                         && ( rot_vp.rotation == VPoint.rotation ) && ( rot_vp.clat == VPoint.clat )
10265                         && ( rot_vp.clon == VPoint.clon ) && rot_vp.IsValid() && ( ri.IsOk() ) ) {
10266                     b_rot_ok = true;
10267                 }
10268             }
10269 
10270             if( b_rot_ok ) {
10271                 delete m_prot_bm;
10272                 m_prot_bm = new wxBitmap( ri );
10273 
10274             }
10275 
10276             m_roffset.x += VPoint.rv_rect.x;
10277             m_roffset.y += VPoint.rv_rect.y;
10278 
10279         }
10280 
10281         if( m_prot_bm && m_prot_bm->IsOk() ) {
10282             rotd_dc.SelectObject( *m_prot_bm );
10283             pChartDC = &rotd_dc;
10284         } else {
10285             pChartDC = &temp_dc;
10286             m_roffset = wxPoint( 0, 0 );
10287         }
10288     } else {            // unrotated
10289         pChartDC = &temp_dc;
10290         m_roffset = wxPoint( 0, 0 );
10291     }
10292 
10293     wxPoint offset = m_roffset;
10294 
10295     //        Save the PixelCache viewpoint for next time
10296     m_cache_vp = VPoint;
10297 
10298 //    Set up a scratch DC for overlay objects
10299     wxMemoryDC mscratch_dc;
10300     mscratch_dc.SelectObject( *pscratch_bm );
10301 
10302     mscratch_dc.ResetBoundingBox();
10303     mscratch_dc.DestroyClippingRegion();
10304     mscratch_dc.SetDeviceClippingRegion( rgn_chart );
10305 
10306     //    Blit the externally invalidated areas of the chart onto the scratch dc
10307     wxRegionIterator upd( rgn_blit ); // get the update rect list
10308     while( upd ) {
10309         wxRect rect = upd.GetRect();
10310 
10311         mscratch_dc.Blit( rect.x, rect.y, rect.width, rect.height, pChartDC, rect.x - offset.x,
10312                           rect.y - offset.y );
10313         upd++;
10314     }
10315 
10316     // If multi-canvas, indicate which canvas has keyboard focus
10317     // by drawing a simple blue bar at the top.
10318     if(g_canvasConfig != 0){             // multi-canvas?
10319         if( this == wxWindow::FindFocus()){
10320             g_focusCanvas = this;
10321 
10322             wxColour colour = GetGlobalColor(_T("BLUE4"));
10323             mscratch_dc.SetPen(wxPen(colour));
10324             mscratch_dc.SetBrush(wxBrush(colour));
10325 
10326             wxRect activeRect(0, 0, GetClientSize().x, m_focus_indicator_pix);
10327             mscratch_dc.DrawRectangle(activeRect);
10328         }
10329     }
10330 
10331 
10332 // Any MBtiles?
10333     std::vector<int> stackIndexArray = m_pQuilt->GetExtendedStackIndexArray();
10334     unsigned int im = stackIndexArray.size();
10335     if( VPoint.b_quilt && im > 0 ) {
10336 
10337         std::vector<int> tiles_to_show;
10338         for( unsigned int is = 0; is < im; is++ ) {
10339             const ChartTableEntry &cte = ChartData->GetChartTableEntry( stackIndexArray[is] );
10340             if(IsTileOverlayIndexInNoShow(stackIndexArray[is])){
10341                 continue;
10342             }
10343             if(cte.GetChartType() == CHART_TYPE_MBTILES){
10344                 tiles_to_show.push_back(stackIndexArray[is]);
10345             }
10346         }
10347 
10348         if(tiles_to_show.size())
10349             SetAlertString( _("MBTile requires OpenGL to be enabled"));
10350     }
10351 
10352 //    Draw the rest of the overlay objects directly on the scratch dc
10353     ocpnDC scratch_dc( mscratch_dc );
10354     DrawOverlayObjects( scratch_dc, ru );
10355 
10356     if( m_bShowTide ){
10357         RebuildTideSelectList( GetVP().GetBBox() );
10358         DrawAllTidesInBBox( scratch_dc, GetVP().GetBBox() );
10359     }
10360 
10361     if( m_bShowCurrent ){
10362         RebuildCurrentSelectList( GetVP().GetBBox() );
10363         DrawAllCurrentsInBBox( scratch_dc, GetVP().GetBBox() );
10364     }
10365 
10366     if( m_brepaint_piano && g_bShowChartBar ) {
10367         m_Piano->Paint(GetClientSize().y - m_Piano->GetHeight(), mscratch_dc);
10368         //m_brepaint_piano = false;
10369     }
10370 
10371     if(m_Compass)
10372         m_Compass->Paint(scratch_dc);
10373 
10374     RenderAlertMessage( mscratch_dc, GetVP());
10375 
10376     //quiting?
10377     if( g_bquiting ) {
10378 #ifdef ocpnUSE_DIBSECTION
10379         ocpnMemDC q_dc;
10380 #else
10381         wxMemoryDC q_dc;
10382 #endif
10383         wxBitmap qbm( GetVP().pix_width, GetVP().pix_height );
10384         q_dc.SelectObject( qbm );
10385 
10386         // Get a copy of the screen
10387         q_dc.Blit( 0, 0, GetVP().pix_width, GetVP().pix_height, &mscratch_dc, 0, 0 );
10388 
10389         //  Draw a rectangle over the screen with a stipple brush
10390         wxBrush qbr( *wxBLACK, wxBRUSHSTYLE_FDIAGONAL_HATCH );
10391         q_dc.SetBrush( qbr );
10392         q_dc.DrawRectangle( 0, 0, GetVP().pix_width, GetVP().pix_height );
10393 
10394         // Blit back into source
10395         mscratch_dc.Blit( 0, 0, GetVP().pix_width, GetVP().pix_height, &q_dc, 0, 0, wxCOPY );
10396 
10397         q_dc.SelectObject( wxNullBitmap );
10398 
10399     }
10400 #if 0
10401     //  It is possible that this two-step method may be reuired for some platforms.
10402     //  So, retain in the code base to aid recovery if necessary
10403 
10404     // Create and Render the Vector quilt decluttered text overlay, omitting CM93 composite
10405     if( VPoint.b_quilt ) {
10406         if(m_pQuilt->IsQuiltVector() && ps52plib && ps52plib->GetShowS57Text()){
10407             ChartBase *chart = m_pQuilt->GetRefChart();
10408             if( chart && chart->GetChartType() != CHART_TYPE_CM93COMP){
10409 
10410                 //        Clear the text Global declutter list
10411                 ChartPlugInWrapper *ChPI = dynamic_cast<ChartPlugInWrapper*>( chart );
10412                 if(ChPI)
10413                     ChPI->ClearPLIBTextList();
10414                 else{
10415                     if(ps52plib)
10416                         ps52plib->ClearTextList();
10417                 }
10418 
10419                 wxMemoryDC t_dc;
10420                 wxBitmap qbm(  GetVP().pix_width, GetVP().pix_height );
10421 
10422                 wxColor maskBackground = wxColour(1,0,0);
10423                 t_dc.SelectObject( qbm );
10424                 t_dc.SetBackground(wxBrush(maskBackground));
10425                 t_dc.Clear();
10426 
10427                 //  Copy the scratch DC into the new bitmap
10428                 t_dc.Blit( 0, 0, GetVP().pix_width, GetVP().pix_height, scratch_dc.GetDC(), 0, 0, wxCOPY );
10429 
10430                 //  Render the text to the new bitmap
10431                 OCPNRegion chart_all_text_region( wxRect( 0, 0, GetVP().pix_width, GetVP().pix_height ) );
10432                 m_pQuilt->RenderQuiltRegionViewOnDCTextOnly( t_dc, svp, chart_all_text_region );
10433 
10434                 //  Copy the new bitmap back to the scratch dc
10435                 wxRegionIterator upd_final( ru );
10436                 while( upd_final ) {
10437                     wxRect rect = upd_final.GetRect();
10438                     scratch_dc.GetDC()->Blit( rect.x, rect.y, rect.width, rect.height, &t_dc, rect.x, rect.y, wxCOPY, true );
10439                     upd_final++;
10440                 }
10441 
10442                 t_dc.SelectObject( wxNullBitmap );
10443             }
10444         }
10445     }
10446 #endif
10447     // Direct rendering model...
10448     if( VPoint.b_quilt ) {
10449         if(m_pQuilt->IsQuiltVector() && ps52plib && ps52plib->GetShowS57Text()){
10450             ChartBase *chart = m_pQuilt->GetRefChart();
10451             if( chart && chart->GetChartType() != CHART_TYPE_CM93COMP){
10452 
10453                 //        Clear the text Global declutter list
10454                 ChartPlugInWrapper *ChPI = dynamic_cast<ChartPlugInWrapper*>( chart );
10455                 if(ChPI)
10456                     ChPI->ClearPLIBTextList();
10457                 else{
10458                     if(ps52plib)
10459                         ps52plib->ClearTextList();
10460                 }
10461 
10462                 //  Render the text directly to the scratch bitmap
10463                 OCPNRegion chart_all_text_region( wxRect( 0, 0, GetVP().pix_width, GetVP().pix_height ) );
10464 
10465                 if(g_bShowChartBar && m_Piano) {
10466                     wxRect chart_bar_rect(0, GetVP().pix_height - m_Piano->GetHeight(),
10467                                           GetVP().pix_width, m_Piano->GetHeight());
10468 
10469                     ocpnStyle::Style* style = g_StyleManager->GetCurrentStyle();
10470                     if(!style->chartStatusWindowTransparent)
10471                         chart_all_text_region.Subtract(chart_bar_rect);
10472                 }
10473 
10474                 if(m_Compass && m_Compass->IsShown()){
10475                     wxRect compassRect = m_Compass->GetRect();
10476                     if(chart_all_text_region.Contains(compassRect) != wxOutRegion) {
10477                         chart_all_text_region.Subtract(compassRect);
10478                     }
10479                 }
10480 
10481 
10482                 mscratch_dc.DestroyClippingRegion();
10483 
10484                 m_pQuilt->RenderQuiltRegionViewOnDCTextOnly( mscratch_dc, svp, chart_all_text_region );
10485 
10486             }
10487         }
10488     }
10489 
10490 
10491 //    And finally, blit the scratch dc onto the physical dc
10492     wxRegionIterator upd_final( rgn_blit );
10493     while( upd_final ) {
10494         wxRect rect = upd_final.GetRect();
10495         dc.Blit( rect.x, rect.y, rect.width, rect.height, &mscratch_dc, rect.x, rect.y );
10496         upd_final++;
10497     }
10498 
10499     //  Test code to validate the dc drawing rectangle....
10500 /*
10501     wxRegionIterator upd_ru ( rgn_blit ); // get the update rect list
10502      while ( upd_ru )
10503      {
10504      wxRect rect = upd_ru.GetRect();
10505 
10506      dc.SetPen(wxPen(*wxRED));
10507      dc.SetBrush(wxBrush(*wxRED, wxTRANSPARENT));
10508      dc.DrawRectangle(rect);
10509      upd_ru ++ ;
10510      }
10511 */
10512 
10513 
10514 //    Deselect the chart bitmap from the temp_dc, so that it will not be destroyed in the temp_dc dtor
10515     temp_dc.SelectObject( wxNullBitmap );
10516 //    And for the scratch bitmap
10517     mscratch_dc.SelectObject( wxNullBitmap );
10518 
10519     dc.DestroyClippingRegion();
10520 
10521 
10522     if(m_muiBar){
10523         m_muiBar->Refresh();
10524     }
10525 
10526     PaintCleanup();
10527 
10528 }
10529 
PaintCleanup()10530 void ChartCanvas::PaintCleanup()
10531 {
10532 //    Handle the current graphic window, if present
10533 
10534     if( pCwin ) {
10535         pCwin->Show();
10536         if( m_bTCupdate ) {
10537             pCwin->Refresh();
10538             pCwin->Update();
10539         }
10540     }
10541 
10542 //    And set flags for next time
10543     m_bTCupdate = false;
10544 
10545 //    Handle deferred WarpPointer
10546     if( warp_flag ) {
10547         WarpPointer( warp_x, warp_y );
10548         warp_flag = false;
10549     }
10550 
10551     // Start movement timer, this runs nearly immediately.
10552     // the reason we cannot simply call it directly is the
10553     // refresh events it emits may be blocked from this paint event
10554     pMovementTimer->Start( 1, wxTIMER_ONE_SHOT );
10555 }
10556 
10557 #if 0
10558 wxColour GetErrorGraphicColor(double val)
10559 {
10560     /*
10561      double valm = wxMin(val_max, val);
10562 
10563      unsigned char green = (unsigned char)(255 * (1 - (valm/val_max)));
10564      unsigned char red   = (unsigned char)(255 * (valm/val_max));
10565 
10566      wxImage::HSVValue hv = wxImage::RGBtoHSV(wxImage::RGBValue(red, green, 0));
10567 
10568      hv.saturation = 1.0;
10569      hv.value = 1.0;
10570 
10571      wxImage::RGBValue rv = wxImage::HSVtoRGB(hv);
10572      return wxColour(rv.red, rv.green, rv.blue);
10573      */
10574 
10575     //    HTML colors taken from NOAA WW3 Web representation
10576     wxColour c;
10577     if((val > 0) && (val < 1)) c.Set(_T("#002ad9"));
10578     else if((val >= 1) && (val < 2)) c.Set(_T("#006ed9"));
10579     else if((val >= 2) && (val < 3)) c.Set(_T("#00b2d9"));
10580     else if((val >= 3) && (val < 4)) c.Set(_T("#00d4d4"));
10581     else if((val >= 4) && (val < 5)) c.Set(_T("#00d9a6"));
10582     else if((val >= 5) && (val < 7)) c.Set(_T("#00d900"));
10583     else if((val >= 7) && (val < 9)) c.Set(_T("#95d900"));
10584     else if((val >= 9) && (val < 12)) c.Set(_T("#d9d900"));
10585     else if((val >= 12) && (val < 15)) c.Set(_T("#d9ae00"));
10586     else if((val >= 15) && (val < 18)) c.Set(_T("#d98300"));
10587     else if((val >= 18) && (val < 21)) c.Set(_T("#d95700"));
10588     else if((val >= 21) && (val < 24)) c.Set(_T("#d90000"));
10589     else if((val >= 24) && (val < 27)) c.Set(_T("#ae0000"));
10590     else if((val >= 27) && (val < 30)) c.Set(_T("#8c0000"));
10591     else if((val >= 30) && (val < 36)) c.Set(_T("#870000"));
10592     else if((val >= 36) && (val < 42)) c.Set(_T("#690000"));
10593     else if((val >= 42) && (val < 48)) c.Set(_T("#550000"));
10594     else if( val >= 48) c.Set(_T("#410000"));
10595 
10596     return c;
10597 }
10598 
10599 void ChartCanvas::RenderGeorefErrorMap( wxMemoryDC *pmdc, ViewPort *vp)
10600 {
10601     wxImage gr_image(vp->pix_width, vp->pix_height);
10602     gr_image.InitAlpha();
10603 
10604     double maxval = -10000;
10605     double minval = 10000;
10606 
10607     double rlat, rlon;
10608     double glat, glon;
10609 
10610     GetCanvasPixPoint(0, 0, rlat, rlon);
10611 
10612     for(int i=1; i < vp->pix_height-1; i++)
10613     {
10614         for(int j=0; j < vp->pix_width; j++)
10615         {
10616             // Reference mercator value
10617 //                  vp->GetMercatorLLFromPix(wxPoint(j, i), &rlat, &rlon);
10618 
10619             // Georef value
10620             GetCanvasPixPoint(j, i, glat, glon);
10621 
10622             maxval = wxMax(maxval, (glat - rlat));
10623             minval = wxMin(minval, (glat - rlat));
10624 
10625         }
10626         rlat = glat;
10627     }
10628 
10629     GetCanvasPixPoint(0, 0, rlat, rlon);
10630     for(int i=1; i < vp->pix_height-1; i++)
10631     {
10632         for(int j=0; j < vp->pix_width; j++)
10633         {
10634             // Reference mercator value
10635 //                  vp->GetMercatorLLFromPix(wxPoint(j, i), &rlat, &rlon);
10636 
10637             // Georef value
10638             GetCanvasPixPoint(j, i, glat, glon);
10639 
10640             double f = ((glat - rlat)-minval)/(maxval - minval);
10641 
10642             double dy = (f * 40);
10643 
10644             wxColour c = GetErrorGraphicColor(dy);
10645             unsigned char r = c.Red();
10646             unsigned char g = c.Green();
10647             unsigned char b = c.Blue();
10648 
10649             gr_image.SetRGB(j, i, r,g,b);
10650             if((glat - rlat )!= 0)
10651                 gr_image.SetAlpha(j, i, 128);
10652             else
10653                 gr_image.SetAlpha(j, i, 255);
10654 
10655         }
10656         rlat = glat;
10657     }
10658 
10659     //    Create a Bitmap
10660     wxBitmap *pbm = new wxBitmap(gr_image);
10661     wxMask *gr_mask = new wxMask(*pbm, wxColour(0,0,0));
10662     pbm->SetMask(gr_mask);
10663 
10664     pmdc->DrawBitmap(*pbm, 0,0);
10665 
10666     delete pbm;
10667 
10668 }
10669 
10670 #endif
10671 
CancelMouseRoute()10672 void ChartCanvas::CancelMouseRoute()
10673 {
10674     m_routeState = 0;
10675     m_pMouseRoute = NULL;
10676     m_bDrawingRoute = false;
10677 }
10678 
GetNextContextMenuId()10679 int ChartCanvas::GetNextContextMenuId()
10680 {
10681     return CanvasMenuHandler::GetNextContextMenuId();
10682 }
10683 
SetCursor(const wxCursor & c)10684 bool ChartCanvas::SetCursor( const wxCursor &c )
10685 {
10686 #ifdef ocpnUSE_GL
10687     if( g_bopengl && m_glcc )
10688         return m_glcc->SetCursor( c );
10689     else
10690 #endif
10691         return wxWindow::SetCursor( c );
10692 }
10693 
Refresh(bool eraseBackground,const wxRect * rect)10694 void ChartCanvas::Refresh( bool eraseBackground, const wxRect *rect )
10695 {
10696     if( g_bquiting )
10697         return;
10698     //  Keep the mouse position members up to date
10699     GetCanvasPixPoint( mouse_x, mouse_y, m_cursor_lat, m_cursor_lon );
10700 
10701     //      Retrigger the route leg popup timer
10702     //      This handles the case when the chart is moving in auto-follow mode, but no user mouse input is made.
10703     //      The timer handler may Hide() the popup if the chart moved enough
10704     //      n.b.  We use slightly longer oneshot value to allow this method's Refresh() to complete before
10705     //      potentially getting another Refresh() in the popup timer handler.
10706     if( !m_RolloverPopupTimer.IsRunning() &&
10707         ((m_pRouteRolloverWin && m_pRouteRolloverWin->IsActive()) ||
10708 	(m_pTrackRolloverWin && m_pTrackRolloverWin->IsActive()) ||
10709 	(m_pAISRolloverWin && m_pAISRolloverWin->IsActive())) )
10710         m_RolloverPopupTimer.Start( 500, wxTIMER_ONE_SHOT );
10711 
10712 #ifdef ocpnUSE_GL
10713     if( m_glcc && g_bopengl ) {
10714 
10715         //      We need to invalidate the FBO cache to ensure repaint of "grounded" overlay objects.
10716         if( eraseBackground && m_glcc->UsingFBO() )
10717             m_glcc->Invalidate();
10718 
10719 
10720         m_glcc->Refresh( eraseBackground, NULL ); // We always are going to render the entire screen anyway, so make
10721         // sure that the window managers understand the invalid area
10722         // is actually the entire client area.
10723 
10724         //  We need to selectively Refresh some child windows, if they are visible.
10725         //  Note that some children are refreshed elsewhere on timer ticks, so don't need attention here.
10726 
10727         //      Thumbnail chart
10728         if( pthumbwin && pthumbwin->IsShown() ) {
10729             pthumbwin->Raise();
10730             pthumbwin->Refresh( false );
10731         }
10732 
10733         //      ChartInfo window
10734         if( m_pCIWin && m_pCIWin->IsShown() ) {
10735             m_pCIWin->Raise();
10736             m_pCIWin->Refresh( false );
10737         }
10738 
10739 //        if(g_MainToolbar)
10740 //            g_MainToolbar->UpdateRecoveryWindow(g_bshowToolbar);
10741 
10742     } else
10743 #endif
10744         wxWindow::Refresh( eraseBackground, rect );
10745 
10746 }
10747 
Update()10748 void ChartCanvas::Update()
10749 {
10750     if( m_glcc && g_bopengl ) {
10751 #ifdef ocpnUSE_GL
10752         m_glcc->Update();
10753 #endif
10754     } else
10755         wxWindow::Update();
10756 }
10757 
DrawEmboss(ocpnDC & dc,emboss_data * pemboss)10758 void ChartCanvas::DrawEmboss( ocpnDC &dc, emboss_data *pemboss)
10759 {
10760     if( !pemboss ) return;
10761     int x = pemboss->x, y = pemboss->y;
10762     const double factor = 200;
10763 
10764     wxASSERT_MSG( dc.GetDC(), wxT ( "DrawEmboss has no dc (opengl?)" ) );
10765     wxMemoryDC *pmdc = dynamic_cast<wxMemoryDC*>( dc.GetDC() );
10766     wxASSERT_MSG ( pmdc, wxT ( "dc to EmbossCanvas not a memory dc" ) );
10767 
10768     //Grab a snipped image out of the chart
10769     wxMemoryDC snip_dc;
10770     wxBitmap snip_bmp( pemboss->width, pemboss->height, -1 );
10771     snip_dc.SelectObject( snip_bmp );
10772 
10773     snip_dc.Blit( 0, 0, pemboss->width, pemboss->height, pmdc, x, y );
10774     snip_dc.SelectObject( wxNullBitmap );
10775 
10776     wxImage snip_img = snip_bmp.ConvertToImage();
10777 
10778     //  Apply Emboss map to the snip image
10779     unsigned char* pdata = snip_img.GetData();
10780     if( pdata ) {
10781         for( int y = 0; y < pemboss->height; y++ ) {
10782             int map_index = ( y * pemboss->width );
10783             for( int x = 0; x < pemboss->width; x++ ) {
10784                 double val = ( pemboss->pmap[map_index] * factor ) / 256.;
10785 
10786                 int nred = (int) ( ( *pdata ) + val );
10787                 nred = nred > 255 ? 255 : ( nred < 0 ? 0 : nred );
10788                 *pdata++ = (unsigned char) nred;
10789 
10790                 int ngreen = (int) ( ( *pdata ) + val );
10791                 ngreen = ngreen > 255 ? 255 : ( ngreen < 0 ? 0 : ngreen );
10792                 *pdata++ = (unsigned char) ngreen;
10793 
10794                 int nblue = (int) ( ( *pdata ) + val );
10795                 nblue = nblue > 255 ? 255 : ( nblue < 0 ? 0 : nblue );
10796                 *pdata++ = (unsigned char) nblue;
10797 
10798                 map_index++;
10799             }
10800         }
10801     }
10802 
10803     //  Convert embossed snip to a bitmap
10804     wxBitmap emb_bmp( snip_img );
10805 
10806     //  Map to another memoryDC
10807     wxMemoryDC result_dc;
10808     result_dc.SelectObject( emb_bmp );
10809 
10810     //  Blit to target
10811     pmdc->Blit( x, y, pemboss->width, pemboss->height, &result_dc, 0, 0 );
10812 
10813     result_dc.SelectObject( wxNullBitmap );
10814 }
10815 
EmbossOverzoomIndicator(ocpnDC & dc)10816 emboss_data *ChartCanvas::EmbossOverzoomIndicator( ocpnDC &dc )
10817 {
10818     double zoom_factor = GetVP().ref_scale / GetVP().chart_scale;
10819 
10820     if( GetQuiltMode() ) {
10821 
10822         // disable Overzoom indicator for MBTiles
10823         int refIndex = GetQuiltRefChartdbIndex();
10824         if(refIndex >= 0){
10825             const ChartTableEntry &cte = ChartData->GetChartTableEntry( refIndex );
10826             ChartTypeEnum current_type = (ChartTypeEnum) cte.GetChartType();
10827             if( current_type == CHART_TYPE_MBTILES){
10828                 ChartBase *pChart = m_pQuilt->GetRefChart();
10829                 ChartMBTiles *ptc = dynamic_cast<ChartMBTiles *>( pChart );
10830                 if(ptc){
10831                     zoom_factor = ptc->GetZoomFactor();
10832                 }
10833             }
10834         }
10835 
10836         if( zoom_factor <= 3.9 )
10837             return NULL;
10838     } else {
10839         if( m_singleChart ) {
10840             if( zoom_factor <= 3.9 )
10841                 return NULL;
10842         }
10843         else
10844             return NULL;
10845     }
10846 
10847     if(m_pEM_OverZoom){
10848         m_pEM_OverZoom->x = 4;
10849         m_pEM_OverZoom->y = 0;
10850         if(g_MainToolbar && IsPrimaryCanvas()){
10851             wxRect masterToolbarRect = g_MainToolbar->GetRect();
10852             m_pEM_OverZoom->x = masterToolbarRect.width + 4;
10853         }
10854     }
10855     return m_pEM_OverZoom;
10856 }
10857 
DrawOverlayObjects(ocpnDC & dc,const wxRegion & ru)10858 void ChartCanvas::DrawOverlayObjects( ocpnDC &dc, const wxRegion& ru )
10859 {
10860     GridDraw( dc );
10861 
10862 //     bool pluginOverlayRender = true;
10863 //
10864 //     if(g_canvasConfig > 0){     // Multi canvas
10865 //         if(IsPrimaryCanvas())
10866 //             pluginOverlayRender = false;
10867 //     }
10868 
10869     g_overlayCanvas = this;
10870 
10871     if( g_pi_manager ) {
10872         g_pi_manager->SendViewPortToRequestingPlugIns( GetVP() );
10873         g_pi_manager->RenderAllCanvasOverlayPlugIns( dc, GetVP(), m_canvasIndex);
10874     }
10875 
10876     AISDrawAreaNotices( dc, GetVP(), this);
10877     DrawEmboss( dc, EmbossDepthScale( ) );
10878     DrawEmboss( dc, EmbossOverzoomIndicator( dc ) );
10879 
10880     wxDC *pdc = dc.GetDC();
10881     if( pdc ) {
10882         pdc->DestroyClippingRegion();
10883         wxDCClipper( *pdc, ru );
10884     }
10885 
10886     if( m_bShowNavobjects ) {
10887         DrawAllTracksInBBox( dc, GetVP().GetBBox() );
10888         DrawAllRoutesInBBox( dc, GetVP().GetBBox() );
10889         DrawAllWaypointsInBBox( dc, GetVP().GetBBox() );
10890         DrawAnchorWatchPoints( dc );
10891     } else {
10892         DrawActiveTrackInBBox( dc, GetVP().GetBBox() );
10893         DrawActiveRouteInBBox( dc, GetVP().GetBBox() );
10894     }
10895 
10896     AISDraw( dc, GetVP(), this );
10897     ShipDraw( dc );
10898     AlertDraw( dc );
10899 
10900     RenderAllChartOutlines( dc, GetVP() );
10901     RenderRouteLegs( dc );
10902     ScaleBarDraw( dc );
10903     s57_DrawExtendedLightSectors( dc, VPoint, extendedSectorLegs );
10904 
10905     if( m_pTrackRolloverWin ) {
10906         m_pTrackRolloverWin->Draw(dc);
10907         m_brepaint_piano = true;
10908     }
10909 
10910     if( m_pRouteRolloverWin ) {
10911         m_pRouteRolloverWin->Draw(dc);
10912         m_brepaint_piano = true;
10913     }
10914 
10915     if( m_pAISRolloverWin ) {
10916         m_pAISRolloverWin->Draw(dc);
10917         m_brepaint_piano = true;
10918     }
10919 }
10920 
EmbossDepthScale()10921 emboss_data *ChartCanvas::EmbossDepthScale()
10922 {
10923     if( !m_bShowDepthUnits ) return NULL;
10924 
10925     int depth_unit_type = DEPTH_UNIT_UNKNOWN;
10926 
10927     if( GetQuiltMode() ) {
10928         wxString s = m_pQuilt->GetQuiltDepthUnit();
10929         s.MakeUpper();
10930         if( s == _T("FEET") ) depth_unit_type = DEPTH_UNIT_FEET;
10931         else if( s.StartsWith( _T("FATHOMS") ) ) depth_unit_type = DEPTH_UNIT_FATHOMS;
10932         else if( s.StartsWith( _T("METERS") ) ) depth_unit_type = DEPTH_UNIT_METERS;
10933         else if( s.StartsWith( _T("METRES") ) ) depth_unit_type = DEPTH_UNIT_METERS;
10934         else if( s.StartsWith( _T("METRIC") ) ) depth_unit_type = DEPTH_UNIT_METERS;
10935         else if( s.StartsWith( _T("METER") ) ) depth_unit_type = DEPTH_UNIT_METERS;
10936 
10937     } else {
10938         if( m_singleChart ) {
10939             depth_unit_type = m_singleChart->GetDepthUnitType();
10940             if( m_singleChart->GetChartFamily() == CHART_FAMILY_VECTOR ) depth_unit_type =
10941                     ps52plib->m_nDepthUnitDisplay + 1;
10942         }
10943     }
10944 
10945     emboss_data *ped = NULL;
10946     switch( depth_unit_type ) {
10947     case DEPTH_UNIT_FEET:
10948         ped = m_pEM_Feet;
10949         break;
10950     case DEPTH_UNIT_METERS:
10951         ped = m_pEM_Meters;
10952         break;
10953     case DEPTH_UNIT_FATHOMS:
10954         ped = m_pEM_Fathoms;
10955         break;
10956     default:
10957         return NULL;
10958     }
10959 
10960     ped->x = ( GetVP().pix_width - ped->width );
10961 
10962     if(m_Compass && m_bShowCompassWin){
10963         wxRect r = m_Compass->GetRect();
10964         ped->y = r.y + r.height;
10965      }
10966      else{
10967         ped->y = 40;
10968     }
10969     return ped;
10970 }
10971 
CreateDepthUnitEmbossMaps(ColorScheme cs)10972 void ChartCanvas::CreateDepthUnitEmbossMaps( ColorScheme cs )
10973 {
10974     ocpnStyle::Style* style = g_StyleManager->GetCurrentStyle();
10975     wxFont font;
10976     if( style->embossFont == wxEmptyString ){
10977         wxFont *dFont = FontMgr::Get().GetFont( _("Dialog"), 0 );
10978         font = *dFont;
10979         font.SetPointSize(60);
10980         font.SetWeight(wxFONTWEIGHT_BOLD);
10981     }
10982     else
10983         font = wxFont( style->embossHeight, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, style->embossFont );
10984 
10985     int emboss_width = 500;
10986     int emboss_height = 200;
10987 
10988 // Free any existing emboss maps
10989     delete m_pEM_Feet;
10990     delete m_pEM_Meters;
10991     delete m_pEM_Fathoms;
10992 
10993 // Create the 3 DepthUnit emboss map structures
10994     m_pEM_Feet = CreateEmbossMapData( font, emboss_width, emboss_height, _("Feet"), cs );
10995     m_pEM_Meters = CreateEmbossMapData( font, emboss_width, emboss_height, _("Meters"), cs );
10996     m_pEM_Fathoms = CreateEmbossMapData( font, emboss_width, emboss_height, _("Fathoms"), cs );
10997 }
10998 
10999 #define OVERZOOM_TEXT _("OverZoom")
11000 
SetOverzoomFont()11001 void ChartCanvas::SetOverzoomFont()
11002 {
11003     ocpnStyle::Style* style = g_StyleManager->GetCurrentStyle();
11004     int w, h;
11005 
11006     wxFont font;
11007     if( style->embossFont == wxEmptyString ){
11008         wxFont *dFont = FontMgr::Get().GetFont( _("Dialog"), 0 );
11009         font = *dFont;
11010         font.SetPointSize(40);
11011         font.SetWeight(wxFONTWEIGHT_BOLD);
11012     }
11013     else
11014         font = wxFont( style->embossHeight, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, false, style->embossFont );
11015 
11016     wxClientDC dc( this );
11017     dc.SetFont( font );
11018     dc.GetTextExtent( OVERZOOM_TEXT, &w, &h );
11019 
11020     while( font.GetPointSize() > 10 && (w > 500 || h > 100) )
11021     {
11022         font.SetPointSize( font.GetPointSize() - 1 );
11023         dc.SetFont( font );
11024         dc.GetTextExtent( OVERZOOM_TEXT, &w, &h );
11025     }
11026     m_overzoomFont = font;
11027     m_overzoomTextWidth = w;
11028     m_overzoomTextHeight = h;
11029 }
11030 
CreateOZEmbossMapData(ColorScheme cs)11031 void ChartCanvas::CreateOZEmbossMapData( ColorScheme cs )
11032 {
11033     delete m_pEM_OverZoom;
11034 
11035     if( m_overzoomTextWidth > 0 && m_overzoomTextHeight > 0 )
11036         m_pEM_OverZoom = CreateEmbossMapData( m_overzoomFont, m_overzoomTextWidth + 10, m_overzoomTextHeight + 10, OVERZOOM_TEXT, cs );
11037 }
11038 
CreateEmbossMapData(wxFont & font,int width,int height,const wxString & str,ColorScheme cs)11039 emboss_data *ChartCanvas::CreateEmbossMapData( wxFont &font, int width, int height,
11040         const wxString &str, ColorScheme cs )
11041 {
11042     int *pmap;
11043 
11044     //  Create a temporary bitmap
11045     wxBitmap bmp( width, height, -1 );
11046 
11047     // Create a memory DC
11048     wxMemoryDC temp_dc;
11049     temp_dc.SelectObject( bmp );
11050 
11051     //  Paint on it
11052     temp_dc.SetBackground( *wxWHITE_BRUSH );
11053     temp_dc.SetTextBackground( *wxWHITE );
11054     temp_dc.SetTextForeground( *wxBLACK );
11055 
11056     temp_dc.Clear();
11057 
11058     temp_dc.SetFont( font );
11059 
11060     int str_w, str_h;
11061     temp_dc.GetTextExtent( str, &str_w, &str_h );
11062 //    temp_dc.DrawText( str, width - str_w - 10, 10 );
11063     temp_dc.DrawText( str, 1, 1 );
11064 
11065     //  Deselect the bitmap
11066     temp_dc.SelectObject( wxNullBitmap );
11067 
11068     //  Convert bitmap the wxImage for manipulation
11069     wxImage img = bmp.ConvertToImage();
11070 
11071     int image_width = str_w * 105 / 100;
11072     int image_height = str_h * 105 / 100;
11073     wxRect r(0,0, wxMin(image_width, img.GetWidth()), wxMin(image_height, img.GetHeight()));
11074     wxImage imgs = img.GetSubImage(r);
11075 
11076     double val_factor;
11077     switch( cs ) {
11078     case GLOBAL_COLOR_SCHEME_DAY:
11079     default:
11080         val_factor = 1;
11081         break;
11082     case GLOBAL_COLOR_SCHEME_DUSK:
11083         val_factor = .5;
11084         break;
11085     case GLOBAL_COLOR_SCHEME_NIGHT:
11086         val_factor = .25;
11087         break;
11088     }
11089 
11090     int val;
11091     int index;
11092     const int w = imgs.GetWidth();
11093     const int h = imgs.GetHeight();
11094     pmap = (int *) calloc( w *  h * sizeof(int), 1 );
11095     //  Create emboss map by differentiating the emboss image
11096     //  and storing integer results in pmap
11097     //  n.b. since the image is B/W, it is sufficient to check
11098     //  one channel (i.e. red) only
11099     for( int y = 1; y < h - 1; y++ ) {
11100         for( int x = 1; x < w - 1; x++ ) {
11101             val = img.GetRed( x + 1, y + 1 ) - img.GetRed( x - 1, y - 1 );  // range +/- 256
11102             val = (int) ( val * val_factor );
11103             index = ( y * w ) + x;
11104             pmap[index] = val;
11105 
11106         }
11107     }
11108 
11109     emboss_data *pret = new emboss_data;
11110     pret->pmap = pmap;
11111     pret->width = w;
11112     pret->height = h;
11113 
11114     return pret;
11115 }
11116 
11117 
DrawAllTracksInBBox(ocpnDC & dc,LLBBox & BltBBox)11118 void ChartCanvas::DrawAllTracksInBBox( ocpnDC& dc, LLBBox& BltBBox )
11119 {
11120     Track *active_track = NULL;
11121     for(wxTrackListNode *node = pTrackList->GetFirst();
11122         node; node = node->GetNext()) {
11123         Track *pTrackDraw = node->GetData();
11124 
11125         if( g_pActiveTrack == pTrackDraw ) {
11126             active_track = pTrackDraw;
11127             continue;
11128         }
11129 
11130         pTrackDraw->Draw( this, dc, GetVP(), BltBBox );
11131     }
11132 
11133     if( active_track )
11134         active_track->Draw( this, dc, GetVP(), BltBBox );
11135 }
11136 
11137 
DrawActiveTrackInBBox(ocpnDC & dc,LLBBox & BltBBox)11138 void ChartCanvas::DrawActiveTrackInBBox( ocpnDC& dc, LLBBox& BltBBox )
11139 {
11140     Track *active_track = NULL;
11141     for(wxTrackListNode *node = pTrackList->GetFirst();
11142         node; node = node->GetNext()) {
11143         Track *pTrackDraw = node->GetData();
11144 
11145         if( g_pActiveTrack == pTrackDraw ) {
11146             active_track = pTrackDraw;
11147             break;
11148         }
11149     }
11150     if( active_track )
11151         active_track->Draw( this, dc, GetVP(), BltBBox );
11152 }
11153 
11154 
DrawAllRoutesInBBox(ocpnDC & dc,LLBBox & BltBBox)11155 void ChartCanvas::DrawAllRoutesInBBox( ocpnDC& dc, LLBBox& BltBBox )
11156 {
11157     Route *active_route = NULL;
11158 
11159     for(wxRouteListNode *node = pRouteList->GetFirst();
11160         node; node = node->GetNext()) {
11161 
11162         Route *pRouteDraw = node->GetData();
11163         if( pRouteDraw->IsActive() || pRouteDraw->IsSelected() ) {
11164             active_route = pRouteDraw;
11165             continue;
11166         }
11167 
11168 //        if(m_canvasIndex == 1)
11169         pRouteDraw->Draw( dc, this, BltBBox );
11170     }
11171 
11172     //  Draw any active or selected route (or track) last, so that is is always on top
11173     if( active_route )
11174         active_route->Draw( dc, this, BltBBox );
11175 }
11176 
DrawActiveRouteInBBox(ocpnDC & dc,LLBBox & BltBBox)11177 void ChartCanvas::DrawActiveRouteInBBox( ocpnDC& dc, LLBBox& BltBBox )
11178 {
11179     Route *active_route = NULL;
11180 
11181     for(wxRouteListNode *node = pRouteList->GetFirst();
11182         node; node = node->GetNext()) {
11183         Route *pRouteDraw = node->GetData();
11184         if( pRouteDraw->IsActive() || pRouteDraw->IsSelected() ) {
11185             active_route = pRouteDraw;
11186             break;
11187         }
11188 
11189     }
11190     if( active_route )
11191         active_route->Draw( dc, this, BltBBox );
11192 }
11193 
DrawAllWaypointsInBBox(ocpnDC & dc,LLBBox & BltBBox)11194 void ChartCanvas::DrawAllWaypointsInBBox( ocpnDC& dc, LLBBox& BltBBox )
11195 {
11196     if(!pWayPointMan)
11197         return;
11198 
11199     wxRoutePointListNode *node = pWayPointMan->GetWaypointList()->GetFirst();
11200 
11201     while( node ) {
11202         RoutePoint *pWP = node->GetData();
11203         if( pWP ) {
11204             if( pWP->m_bIsInRoute ) {
11205                 node = node->GetNext();
11206                 continue;
11207             }
11208 
11209             /* technically incorrect... waypoint has bounding box */
11210             if( BltBBox.Contains( pWP->m_lat, pWP->m_lon ) )
11211                 pWP->Draw( dc, this, NULL );
11212             else{
11213                 // Are Range Rings enabled?
11214                 if(pWP->GetShowWaypointRangeRings() && (pWP->GetWaypointRangeRingsNumber() > 0)){
11215                     double factor = 1.00;
11216                     if( pWP->GetWaypointRangeRingsStepUnits() == 1 )          // convert kilometers to NMi
11217                         factor = 1 / 1.852;
11218 
11219                     double radius = factor * pWP->GetWaypointRangeRingsNumber() * pWP->GetWaypointRangeRingsStep()  / 60.;
11220                     radius *= 2;                // Fudge factor
11221 
11222                     LLBBox radar_box;
11223                     radar_box.Set(pWP->m_lat-radius, pWP->m_lon-radius, pWP->m_lat+radius, pWP->m_lon+radius);
11224                     if( !BltBBox.IntersectOut( radar_box ) ){
11225                         pWP->Draw( dc, this, NULL );
11226                     }
11227                 }
11228             }
11229         }
11230 
11231         node = node->GetNext();
11232     }
11233 }
11234 
DrawBlinkObjects(void)11235 void ChartCanvas::DrawBlinkObjects( void )
11236 {
11237     //  All RoutePoints
11238     wxRect update_rect;
11239 
11240     if(!pWayPointMan)
11241         return;
11242 
11243     wxRoutePointListNode *node = pWayPointMan->GetWaypointList()->GetFirst();
11244 
11245     while( node ) {
11246         RoutePoint *pWP = node->GetData();
11247         if( pWP ) {
11248             if( pWP->m_bBlink ) {
11249                 update_rect.Union( pWP->CurrentRect_in_DC ) ;
11250             }
11251         }
11252 
11253         node = node->GetNext();
11254     }
11255     if( !update_rect.IsEmpty() )
11256         RefreshRect(update_rect);
11257 }
11258 
11259 
11260 
11261 
DrawAnchorWatchPoints(ocpnDC & dc)11262 void ChartCanvas::DrawAnchorWatchPoints( ocpnDC& dc )
11263 {
11264     // draw anchor watch rings, if activated
11265 
11266     if( pAnchorWatchPoint1 || pAnchorWatchPoint2 ) {
11267         wxPoint r1, r2;
11268         wxPoint lAnchorPoint1, lAnchorPoint2;
11269         double lpp1 = 0.0;
11270         double lpp2 = 0.0;
11271         if( pAnchorWatchPoint1 ) {
11272             lpp1 = GetAnchorWatchRadiusPixels( pAnchorWatchPoint1 );
11273             GetCanvasPointPix( pAnchorWatchPoint1->m_lat, pAnchorWatchPoint1->m_lon,
11274                                &lAnchorPoint1 );
11275 
11276         }
11277         if( pAnchorWatchPoint2 ) {
11278             lpp2 = GetAnchorWatchRadiusPixels( pAnchorWatchPoint2 );
11279             GetCanvasPointPix( pAnchorWatchPoint2->m_lat, pAnchorWatchPoint2->m_lon,
11280                                &lAnchorPoint2 );
11281 
11282         }
11283 
11284         wxPen ppPeng( GetGlobalColor( _T ( "UGREN" ) ), 2 );
11285         wxPen ppPenr( GetGlobalColor( _T ( "URED" ) ), 2 );
11286 
11287         wxBrush *ppBrush = wxTheBrushList->FindOrCreateBrush( wxColour( 0, 0, 0 ), wxBRUSHSTYLE_TRANSPARENT );
11288         dc.SetBrush( *ppBrush );
11289 
11290         if( lpp1 > 0 ) {
11291             dc.SetPen( ppPeng );
11292             dc.StrokeCircle( lAnchorPoint1.x, lAnchorPoint1.y, fabs( lpp1 ) );
11293         }
11294 
11295         if( lpp2 > 0 ) {
11296             dc.SetPen( ppPeng );
11297             dc.StrokeCircle( lAnchorPoint2.x, lAnchorPoint2.y, fabs( lpp2 ) );
11298         }
11299 
11300         if( lpp1 < 0 ) {
11301             dc.SetPen( ppPenr );
11302             dc.StrokeCircle( lAnchorPoint1.x, lAnchorPoint1.y, fabs( lpp1 ) );
11303         }
11304 
11305         if( lpp2 < 0 ) {
11306             dc.SetPen( ppPenr );
11307             dc.StrokeCircle( lAnchorPoint2.x, lAnchorPoint2.y, fabs( lpp2 ) );
11308         }
11309     }
11310 }
11311 
GetAnchorWatchRadiusPixels(RoutePoint * pAnchorWatchPoint)11312 double ChartCanvas::GetAnchorWatchRadiusPixels( RoutePoint *pAnchorWatchPoint )
11313 {
11314     double lpp = 0.;
11315     wxPoint r1;
11316     wxPoint lAnchorPoint;
11317     double d1 = 0.0;
11318     double dabs;
11319     double tlat1, tlon1;
11320 
11321     if( pAnchorWatchPoint ) {
11322         ( pAnchorWatchPoint->GetName() ).ToDouble( &d1 );
11323         d1 = AnchorDistFix( d1, AnchorPointMinDist, g_nAWMax );
11324         dabs = fabs( d1 / 1852. );
11325         ll_gc_ll( pAnchorWatchPoint->m_lat, pAnchorWatchPoint->m_lon, 0, dabs, &tlat1, &tlon1 );
11326         GetCanvasPointPix( tlat1, tlon1, &r1 );
11327         GetCanvasPointPix( pAnchorWatchPoint->m_lat, pAnchorWatchPoint->m_lon, &lAnchorPoint );
11328         lpp = sqrt( pow( (double) (lAnchorPoint.x - r1.x), 2) +
11329                     pow( (double) (lAnchorPoint.y - r1.y), 2) );
11330 
11331         //    This is an entry watch
11332         if( d1 < 0 ) lpp = -lpp;
11333     }
11334     return lpp;
11335 }
11336 
11337 //------------------------------------------------------------------------------------------
11338 //    Tides Support
11339 //------------------------------------------------------------------------------------------
RebuildTideSelectList(LLBBox & BBox)11340 void ChartCanvas::RebuildTideSelectList( LLBBox& BBox )
11341 {
11342     if(!ptcmgr)
11343         return;
11344 
11345     pSelectTC->DeleteAllSelectableTypePoints( SELTYPE_TIDEPOINT );
11346 
11347     for( int i = 1; i < ptcmgr->Get_max_IDX() + 1; i++ ) {
11348         const IDX_entry *pIDX = ptcmgr->GetIDX_entry( i );
11349         double lon = pIDX->IDX_lon;
11350         double lat = pIDX->IDX_lat;
11351 
11352         char type = pIDX->IDX_type;             // Entry "TCtcIUu" identifier
11353         if( ( type == 't' ) || ( type == 'T' ) ) {
11354 
11355             if( BBox.Contains( lat, lon ) ) {
11356 
11357                 //    Manage the point selection list
11358                 pSelectTC->AddSelectablePoint( lat, lon, pIDX, SELTYPE_TIDEPOINT );
11359             }
11360         }
11361     }
11362 }
11363 
11364 extern wxDateTime gTimeSource;
11365 
DrawAllTidesInBBox(ocpnDC & dc,LLBBox & BBox)11366 void ChartCanvas::DrawAllTidesInBBox( ocpnDC& dc, LLBBox& BBox )
11367 {
11368     if(!ptcmgr)
11369         return;
11370 
11371     wxDateTime this_now = gTimeSource;
11372     bool cur_time = !gTimeSource.IsValid();
11373     if (cur_time)
11374         this_now = wxDateTime::Now();
11375     time_t t_this_now = this_now.GetTicks();
11376 
11377     wxPen *pblack_pen = wxThePenList->FindOrCreatePen( GetGlobalColor( _T ( "UINFD" ) ), 1,
11378                         wxPENSTYLE_SOLID );
11379     wxPen *pyelo_pen = wxThePenList->FindOrCreatePen( GetGlobalColor( cur_time?_T ( "YELO1" ): _T ( "YELO2" )), 1,
11380                        wxPENSTYLE_SOLID );
11381     wxPen *pblue_pen = wxThePenList->FindOrCreatePen( GetGlobalColor( cur_time?_T ( "BLUE2" ):_T ( "BLUE3" ) ), 1,
11382                        wxPENSTYLE_SOLID );
11383 
11384     wxBrush *pgreen_brush = wxTheBrushList->FindOrCreateBrush( GetGlobalColor( _T ( "GREEN1" ) ),
11385                             wxBRUSHSTYLE_SOLID );
11386 //        wxBrush *pblack_brush = wxTheBrushList->FindOrCreateBrush ( GetGlobalColor ( _T ( "UINFD" ) ), wxSOLID );
11387     wxBrush *pblue_brush = wxTheBrushList->FindOrCreateBrush( GetGlobalColor(cur_time? _T ( "BLUE2" ):_T ( "BLUE3" ) ), wxBRUSHSTYLE_SOLID );
11388     wxBrush *pyelo_brush = wxTheBrushList->FindOrCreateBrush( GetGlobalColor(cur_time? _T ( "YELO1" ): _T ( "YELO2" ) ), wxBRUSHSTYLE_SOLID );
11389 
11390     wxFont *dFont = FontMgr::Get().GetFont( _("ExtendedTideIcon") );
11391     dc.SetTextForeground( FontMgr::Get().GetFontColor( _("ExtendedTideIcon") ) );
11392     int font_size = wxMax(8, dFont->GetPointSize());
11393     wxFont *plabelFont = FontMgr::Get().FindOrCreateFont( font_size, dFont->GetFamily(),
11394                          dFont->GetStyle(), dFont->GetWeight() );
11395 
11396     dc.SetPen( *pblack_pen );
11397     dc.SetBrush( *pgreen_brush );
11398 
11399     wxBitmap bm;
11400     switch( m_cs ) {
11401     case GLOBAL_COLOR_SCHEME_DAY:
11402         bm = m_bmTideDay;
11403         break;
11404     case GLOBAL_COLOR_SCHEME_DUSK:
11405         bm = m_bmTideDusk;
11406         break;
11407     case GLOBAL_COLOR_SCHEME_NIGHT:
11408         bm = m_bmTideNight;
11409         break;
11410     default:
11411         bm = m_bmTideDay;
11412         break;
11413     }
11414 
11415     int bmw = bm.GetWidth();
11416     int bmh = bm.GetHeight();
11417 
11418     float scale_factor = 1.0;
11419 
11420     //  Set the onscreen size of the symbol
11421     //  Compensate for various display resolutions
11422     float icon_pixelRefDim = 45;
11423 
11424 #if 0
11425     float nominal_icon_size_mm = g_Platform->GetDisplaySizeMM() *25 / 1000; // Intended physical rendered size onscreen
11426     nominal_icon_size_mm = wxMax(nominal_icon_size_mm, 8);
11427     nominal_icon_size_mm = wxMin(nominal_icon_size_mm, 15);
11428     float nominal_icon_size_pixels = wxMax(4.0, floor(g_Platform->GetDisplayDPmm() * nominal_icon_size_mm));  // nominal size, but not less than 4 pixel
11429 #endif
11430 
11431 #ifndef __OCPN__ANDROID__
11432     // another method is simply to declare that the icon shall be x times the size of a raster symbol (e.g.BOYLAT)
11433     //  This is a bit of a hack that will suffice until until we get fully scalable ENC symbol sets
11434     float nominal_icon_size_pixels = 48;  // 3 x 16
11435     float pix_factor = nominal_icon_size_pixels / icon_pixelRefDim;
11436 
11437 #else
11438     //  Yet another method goes like this:
11439     //  Set the onscreen size of the symbol
11440     //  Compensate for various display resolutions
11441     //  Develop empirically, making a symbol about 16 mm tall
11442     double symHeight = icon_pixelRefDim / GetPixPerMM();           // from draw instructions, symbol is xx pix high
11443     double targetHeight0 = 16.0;
11444 
11445     // But we want to scale the size down for smaller displays
11446     double displaySize = m_display_size_mm;
11447     displaySize = wxMax(displaySize, 100);
11448 
11449     float targetHeight = wxMin(targetHeight0, displaySize / 15);
11450 
11451     double pix_factor = targetHeight / symHeight;
11452 #endif
11453 
11454     scale_factor *= pix_factor;
11455 
11456     float user_scale_factor = g_ChartScaleFactorExp;
11457     if( g_ChartScaleFactorExp > 1.0 )
11458         user_scale_factor = (log(g_ChartScaleFactorExp) + 1.0) * 1.2;   // soften the scale factor a bit
11459 
11460     scale_factor *= user_scale_factor;
11461 
11462     {
11463 
11464         double lon_last = 0.;
11465         double lat_last = 0.;
11466         double marge = 0.05;
11467         for( int i = 1; i < ptcmgr->Get_max_IDX() + 1; i++ ) {
11468             const IDX_entry *pIDX = ptcmgr->GetIDX_entry( i );
11469 
11470             char type = pIDX->IDX_type;             // Entry "TCtcIUu" identifier
11471             if( ( type == 't' ) || ( type == 'T' ) )  // only Tides
11472             {
11473                 double lon = pIDX->IDX_lon;
11474                 double lat = pIDX->IDX_lat;
11475 
11476                 if( BBox.ContainsMarge( lat, lon, marge ) &&
11477 //try to eliminate double entry , but the only good way is to clean the file!
11478                     ( lat != lat_last ) && ( lon != lon_last ) ) {
11479 
11480                     wxPoint r;
11481                     GetCanvasPointPix( lat, lon, &r );
11482 //draw standard icons
11483                     if( GetVP().chart_scale > 500000 ) {
11484                         dc.DrawBitmap( bm, r.x - bmw / 2, r.y - bmh / 2, true );
11485                     }
11486 //draw "extended" icons
11487                     else {
11488                         dc.SetFont( *plabelFont );
11489                          {
11490                             {
11491                                 float val, nowlev;
11492                                 float ltleve = 0.;
11493                                 float htleve = 0.;
11494                                 time_t tctime;
11495                                 time_t lttime = 0;
11496                                 time_t httime = 0;
11497                                 bool wt;
11498                                 //define if flood or ebb in the last ten minutes and verify if data are useable
11499                                 if( ptcmgr->GetTideFlowSens( t_this_now, BACKWARD_TEN_MINUTES_STEP,
11500                                                              pIDX->IDX_rec_num, nowlev, val, wt ) ) {
11501 
11502                                     //search forward the first HW or LW near "now" ( starting at "now" - ten minutes )
11503                                     ptcmgr->GetHightOrLowTide(
11504                                         t_this_now + BACKWARD_TEN_MINUTES_STEP,
11505                                         FORWARD_TEN_MINUTES_STEP, FORWARD_ONE_MINUTES_STEP, val,
11506                                         wt, pIDX->IDX_rec_num, val, tctime );
11507                                     if( wt ) {
11508                                         httime = tctime;
11509                                         htleve = val;
11510                                     } else {
11511                                         lttime = tctime;
11512                                         ltleve = val;
11513                                     }
11514                                     wt = !wt;
11515 
11516                                     //then search opposite tide near "now"
11517                                     if( tctime > t_this_now )          // search backward
11518                                         ptcmgr->GetHightOrLowTide( t_this_now,
11519                                                                    BACKWARD_TEN_MINUTES_STEP, BACKWARD_ONE_MINUTES_STEP,
11520                                                                    nowlev, wt, pIDX->IDX_rec_num, val, tctime );
11521                                     else
11522                                         // or search forward
11523                                         ptcmgr->GetHightOrLowTide( t_this_now,
11524                                                                    FORWARD_TEN_MINUTES_STEP, FORWARD_ONE_MINUTES_STEP,
11525                                                                    nowlev, wt, pIDX->IDX_rec_num, val, tctime );
11526                                     if( wt ) {
11527                                         httime = tctime;
11528                                         htleve = val;
11529                                     } else {
11530                                         lttime = tctime;
11531                                         ltleve = val;
11532                                     }
11533 
11534 				    // draw the tide rectangle:
11535 
11536 				    // tide icon rectangle has default pre-scaled width = 12 , height = 45
11537 				    int width = (int) (12 * scale_factor + 0.5);
11538 				    int height = (int) (45 * scale_factor + 0.5);
11539 				    int linew = wxMax(1, (int) (scale_factor));
11540                                     int xDraw = r.x - (width / 2);
11541                                     int yDraw = r.y - (height / 2);
11542 
11543 
11544                                     //process tide state  ( %height and flow sens )
11545                                     float ts = 1 - ( ( nowlev - ltleve ) / ( htleve - ltleve ) );
11546                                     int hs = ( httime > lttime ) ? -4 : 4;
11547 				    hs *= (int) (scale_factor + 0.5);
11548                                     if( ts > 0.995 || ts < 0.005 ) hs = 0;
11549                                     int ht_y = (int) ( height * ts );
11550 
11551 				    //draw yellow tide rectangle outlined in black
11552 				    pblack_pen->SetWidth(linew);
11553                                     dc.SetPen( *pblack_pen );
11554                                     dc.SetBrush( *pyelo_brush );
11555                                     dc.DrawRectangle( xDraw, yDraw, width, height );
11556 
11557                                     //draw blue rectangle as water height, smaller in width than yellow rectangle
11558                                     dc.SetPen( *pblue_pen );
11559                                     dc.SetBrush( *pblue_brush );
11560                                     dc.DrawRectangle( (xDraw + 2*linew), yDraw + ht_y,
11561 						      (width - (4*linew)), height - ht_y );
11562 
11563                                     //draw sens arrows (ensure they are not "under-drawn" by top line of blue rectangle )
11564                                     int hl;
11565                                     wxPoint arrow[3];
11566                                     arrow[0].x = xDraw + 2*linew;
11567                                     arrow[1].x = xDraw + width / 2;
11568                                     arrow[2].x = xDraw + width - 2*linew;
11569 				    pyelo_pen->SetWidth(linew);
11570 				    pblue_pen->SetWidth(linew);
11571                                     if( ts > 0.35 || ts < 0.15 )      // one arrow at 3/4 hight tide
11572                                     {
11573                                         hl = (int) ( height * 0.25 ) + yDraw;
11574                                         arrow[0].y = hl;
11575                                         arrow[1].y = hl + hs;
11576                                         arrow[2].y = hl;
11577                                         if( ts < 0.15 )
11578 					  dc.SetPen( *pyelo_pen );
11579                                         else
11580 					  dc.SetPen( *pblue_pen );
11581                                         dc.DrawLines( 3, arrow );
11582                                     }
11583                                     if( ts > 0.60 || ts < 0.40 )       //one arrow at 1/2 hight tide
11584                                     {
11585                                         hl = (int) ( height * 0.5 ) + yDraw;
11586                                         arrow[0].y = hl;
11587                                         arrow[1].y = hl + hs;
11588                                         arrow[2].y = hl;
11589                                         if( ts < 0.40 )
11590 					  dc.SetPen( *pyelo_pen );
11591                                         else
11592 					  dc.SetPen( *pblue_pen );
11593                                         dc.DrawLines( 3, arrow );
11594                                     }
11595                                     if( ts < 0.65 || ts > 0.85 )       //one arrow at 1/4 Hight tide
11596                                     {
11597                                         hl = (int) ( height * 0.75 ) + yDraw;
11598                                         arrow[0].y = hl;
11599                                         arrow[1].y = hl + hs;
11600                                         arrow[2].y = hl;
11601                                         if( ts < 0.65 )
11602 					  dc.SetPen( *pyelo_pen );
11603                                         else
11604 					  dc.SetPen( *pblue_pen );
11605                                         dc.DrawLines( 3, arrow );
11606                                     }
11607                                     //draw tide level text
11608                                     wxString s;
11609                                     s.Printf( _T("%3.1f"), nowlev );
11610                                     Station_Data *pmsd = pIDX->pref_sta_data;           //write unit
11611                                     if( pmsd ) s.Append(
11612                                             wxString( pmsd->units_abbrv, wxConvUTF8 ) );
11613                                     int wx1;
11614                                     dc.GetTextExtent( s, &wx1, NULL );
11615                                     dc.DrawText( s, r.x - ( wx1 / 2 ), yDraw + height );
11616                                 }
11617                             }
11618                         }
11619                     }
11620                 }
11621                 lon_last = lon;
11622                 lat_last = lat;
11623             }
11624         }
11625     }
11626 }
11627 
11628 //------------------------------------------------------------------------------------------
11629 //    Currents Support
11630 //------------------------------------------------------------------------------------------
11631 
RebuildCurrentSelectList(LLBBox & BBox)11632 void ChartCanvas::RebuildCurrentSelectList( LLBBox& BBox )
11633 {
11634     if(!ptcmgr)
11635         return;
11636 
11637     pSelectTC->DeleteAllSelectableTypePoints( SELTYPE_CURRENTPOINT );
11638 
11639     double lon_last = 0.;
11640     double lat_last = 0.;
11641     for( int i = 1; i < ptcmgr->Get_max_IDX() + 1; i++ ) {
11642         const IDX_entry *pIDX = ptcmgr->GetIDX_entry( i );
11643         double lon = pIDX->IDX_lon;
11644         double lat = pIDX->IDX_lat;
11645 
11646         char type = pIDX->IDX_type;             // Entry "TCtcIUu" identifier
11647         if( ( ( type == 'c' ) || ( type == 'C' ) ) && ( 1/*pIDX->IDX_Useable*/) ) {
11648 
11649             //  TODO This is a ---HACK---
11650             //  try to avoid double current arrows.  Select the first in the list only
11651             //  Proper fix is to correct the TCDATA index file for depth indication
11652             bool b_dup = false;
11653             if( ( type == 'c' ) && ( lat == lat_last ) && ( lon == lon_last ) )
11654                 b_dup = true;
11655 
11656             if( !b_dup && ( BBox.Contains( lat, lon ) ) ) {
11657 
11658                    //    Manage the point selection list
11659                    pSelectTC->AddSelectablePoint( lat, lon, pIDX, SELTYPE_CURRENTPOINT );
11660             }
11661         }
11662         lon_last = lon;
11663         lat_last = lat;
11664     }
11665 }
11666 
11667 
DrawAllCurrentsInBBox(ocpnDC & dc,LLBBox & BBox)11668 void ChartCanvas::DrawAllCurrentsInBBox( ocpnDC& dc, LLBBox& BBox )
11669 {
11670     if(!ptcmgr)
11671         return;
11672 
11673     float tcvalue, dir;
11674     bool bnew_val;
11675     char sbuf[20];
11676     wxFont *pTCFont;
11677     double lon_last = 0.;
11678     double lat_last = 0.;
11679     // arrow size for Raz Blanchard : 12 knots north
11680     double marge = 0.2;
11681     bool cur_time = !gTimeSource.IsValid();
11682 
11683     double true_scale_display = floor( VPoint.chart_scale / 100. ) * 100.;
11684     bDrawCurrentValues =  true_scale_display < g_Show_Target_Name_Scale;
11685 
11686     wxPen *pblack_pen = wxThePenList->FindOrCreatePen( GetGlobalColor( _T ( "UINFD" ) ), 1,
11687                         wxPENSTYLE_SOLID );
11688     wxPen *porange_pen = wxThePenList->FindOrCreatePen( GetGlobalColor(cur_time? _T ( "UINFO" ):_T ( "UINFB" ) ), 1,
11689                          wxPENSTYLE_SOLID );
11690     wxBrush *porange_brush = wxTheBrushList->FindOrCreateBrush( GetGlobalColor(cur_time? _T ( "UINFO" ): _T ( "UINFB" ) ),
11691                              wxBRUSHSTYLE_SOLID );
11692     wxBrush *pgray_brush = wxTheBrushList->FindOrCreateBrush( GetGlobalColor( _T ( "UIBDR" ) ),
11693                            wxBRUSHSTYLE_SOLID );
11694     wxBrush *pblack_brush = wxTheBrushList->FindOrCreateBrush( GetGlobalColor( _T ( "UINFD" ) ),
11695                             wxBRUSHSTYLE_SOLID );
11696 
11697     double skew_angle = GetVPRotation();
11698 
11699     pTCFont = FontMgr::Get().GetFont( _("CurrentValue") );
11700 
11701     float scale_factor = 1.0;
11702 
11703     //  Set the onscreen size of the symbol
11704     //  Compensate for various display resolutions
11705     float icon_pixelRefDim = 5;
11706 
11707 #if 0
11708     float nominal_icon_size_mm = g_Platform->GetDisplaySizeMM() *3 / 1000; // Intended physical rendered size onscreen
11709     nominal_icon_size_mm = wxMax(nominal_icon_size_mm, 2);
11710     nominal_icon_size_mm = wxMin(nominal_icon_size_mm, 4);
11711     float nominal_icon_size_pixels = wxMax(4.0, floor(g_Platform->GetDisplayDPmm() * nominal_icon_size_mm));  // nominal size, but not less than 4 pixel
11712 #endif
11713 
11714 #if 0
11715     // another method is simply to declare that the icon shall be x times the size of a raster symbol (e.g.BOYLAT)
11716     //  This is a bit of a hack that will suffice until until we get fully scalable ENC symbol sets
11717     float nominal_icon_size_pixels = 6;  // 16 / 3
11718     float pix_factor = nominal_icon_size_pixels / icon_pixelRefDim;
11719 #endif
11720 
11721     //  Yet another method goes like this:
11722     //  Set the onscreen size of the symbol
11723     //  Compensate for various display resolutions
11724     //  Develop empirically, making a symbol about 16 mm tall
11725     double symHeight = icon_pixelRefDim / GetPixPerMM();           // from draw instructions, symbol is xx pix high
11726     double targetHeight0 = 2.0;
11727 
11728     // But we want to scale the size down for smaller displays
11729     double displaySize = m_display_size_mm;
11730     displaySize = wxMax(displaySize, 100);
11731 
11732     float targetHeight = wxMin(targetHeight0, displaySize / 50);
11733     double pix_factor = targetHeight / symHeight;
11734 
11735     scale_factor *= pix_factor;
11736 
11737     float user_scale_factor = g_ChartScaleFactorExp;
11738     if( g_ChartScaleFactorExp > 1.0 )
11739         user_scale_factor = (log(g_ChartScaleFactorExp) + 1.0) * 1.2;   // soften the scale factor a bit
11740 
11741     scale_factor *= user_scale_factor;
11742 
11743 
11744     {
11745 
11746         for( int i = 1; i < ptcmgr->Get_max_IDX() + 1; i++ ) {
11747             const IDX_entry *pIDX = ptcmgr->GetIDX_entry( i );
11748             double lon = pIDX->IDX_lon;
11749             double lat = pIDX->IDX_lat;
11750 
11751             char type = pIDX->IDX_type;             // Entry "TCtcIUu" identifier
11752             if( ( ( type == 'c' ) || ( type == 'C' ) ) && ( 1/*pIDX->IDX_Useable*/) ) {
11753 
11754 //  TODO This is a ---HACK---
11755 //  try to avoid double current arrows.  Select the first in the list only
11756 //  Proper fix is to correct the TCDATA index file for depth indication
11757                 bool b_dup = false;
11758                 if( ( type == 'c' ) && ( lat == lat_last ) && ( lon == lon_last ) )
11759                     b_dup = true;
11760 
11761                 if( !b_dup && ( BBox.ContainsMarge( lat, lon, marge ) ) ) {
11762 
11763                     wxPoint r;
11764                     GetCanvasPointPix( lat, lon, &r );
11765 
11766                     wxPoint d[4]; // points of a diamond at the current station location
11767                     int dd = (int) (5.0 * scale_factor + 0.5);
11768                     d[0].x = r.x;
11769                     d[0].y = r.y + dd;
11770                     d[1].x = r.x + dd;
11771                     d[1].y = r.y;
11772                     d[2].x = r.x;
11773                     d[2].y = r.y - dd;
11774                     d[3].x = r.x - dd;
11775                     d[3].y = r.y;
11776 
11777 
11778                     if( 1 ) {
11779 		        pblack_pen->SetWidth( wxMax(2, (int) (scale_factor + 0.5)) );
11780                         dc.SetPen( *pblack_pen );
11781                         dc.SetBrush( *porange_brush );
11782                         dc.DrawPolygon( 4, d );
11783 
11784                         if( type == 'C' ) {
11785                             dc.SetBrush( *pblack_brush );
11786                             dc.DrawCircle( r.x, r.y, (int)(2*scale_factor) );
11787                         }
11788 
11789                         if( GetVP().chart_scale < 1000000 ){
11790                             if(!ptcmgr->GetTideOrCurrent15( 0 , i, tcvalue, dir, bnew_val ))
11791                                 continue;
11792                         }
11793                         else
11794                             continue;
11795 
11796                         if( type == 'c' )
11797                         {
11798                             {
11799 
11800 //    Get the display pixel location of the current station
11801                                 int pixxc, pixyc;
11802                                 pixxc = r.x;
11803                                 pixyc = r.y;
11804 
11805 //    Adjust drawing size using logarithmic scale. tcvalue is current in knots
11806                                 double a1 = fabs( tcvalue ) * 10.;
11807 				// Current values <= 0.1 knot will have no arrow
11808                                 a1 = wxMax(1.0, a1);
11809                                 double a2 = log10( a1 );
11810 
11811                                 float cscale = scale_factor * a2 * 0.4;
11812 
11813                                 porange_pen->SetWidth( wxMax(2, (int) (scale_factor + 0.5)) );
11814                                 dc.SetPen( *porange_pen );
11815                                 DrawArrow( dc, pixxc, pixyc,
11816                                            dir - 90 + ( skew_angle * 180. / PI ), cscale );
11817 // Draw text, if enabled
11818 
11819                                 if( bDrawCurrentValues ) {
11820                                     dc.SetFont( *pTCFont );
11821                                     snprintf( sbuf, 19, "%3.1f", fabs( tcvalue ) );
11822                                     dc.DrawText( wxString( sbuf, wxConvUTF8 ), pixxc, pixyc );
11823                                 }
11824                             }
11825                         }           // scale
11826                     }
11827                     /*          This is useful for debugging the TC database
11828                      else
11829                      {
11830                      dc.SetPen ( *porange_pen );
11831                      dc.SetBrush ( *pgray_brush );
11832                      dc.DrawPolygon ( 4, d );
11833                      }
11834                      */
11835 
11836                 }
11837                 lon_last = lon;
11838                 lat_last = lat;
11839 
11840             }
11841         }
11842     }
11843 }
11844 
DrawTCWindow(int x,int y,void * pvIDX)11845 void ChartCanvas::DrawTCWindow( int x, int y, void *pvIDX )
11846 {
11847     pCwin = new TCWin( this, x, y, pvIDX );
11848 }
11849 
11850 #define NUM_CURRENT_ARROW_POINTS 9
11851 static wxPoint CurrentArrowArray[NUM_CURRENT_ARROW_POINTS] = { wxPoint( 0, 0 ), wxPoint( 0, -10 ),
11852         wxPoint( 55, -10 ), wxPoint( 55, -25 ), wxPoint( 100, 0 ), wxPoint( 55, 25 ), wxPoint( 55,
11853                 10 ), wxPoint( 0, 10 ), wxPoint( 0, 0 )
11854                                                              };
11855 
DrawArrow(ocpnDC & dc,int x,int y,double rot_angle,double scale)11856 void ChartCanvas::DrawArrow( ocpnDC& dc, int x, int y, double rot_angle, double scale )
11857 {
11858     if( scale > 1e-2 ) {
11859 
11860         float sin_rot = sin( rot_angle * PI / 180. );
11861         float cos_rot = cos( rot_angle * PI / 180. );
11862 
11863         // Move to the first point
11864 
11865         float xt = CurrentArrowArray[0].x;
11866         float yt = CurrentArrowArray[0].y;
11867 
11868         float xp = ( xt * cos_rot ) - ( yt * sin_rot );
11869         float yp = ( xt * sin_rot ) + ( yt * cos_rot );
11870         int x1 = (int) ( xp * scale );
11871         int y1 = (int) ( yp * scale );
11872 
11873         // Walk thru the point list
11874         for( int ip = 1; ip < NUM_CURRENT_ARROW_POINTS; ip++ ) {
11875             xt = CurrentArrowArray[ip].x;
11876             yt = CurrentArrowArray[ip].y;
11877 
11878             float xp = ( xt * cos_rot ) - ( yt * sin_rot );
11879             float yp = ( xt * sin_rot ) + ( yt * cos_rot );
11880             int x2 = (int) ( xp * scale );
11881             int y2 = (int) ( yp * scale );
11882 
11883             dc.DrawLine( x1 + x, y1 + y, x2 + x, y2 + y );
11884 
11885             x1 = x2;
11886             y1 = y2;
11887         }
11888     }
11889 }
11890 
FindValidUploadPort()11891 wxString ChartCanvas::FindValidUploadPort()
11892 {
11893     wxString port;
11894     //  Try to use the saved persistent upload port first
11895     if( !g_uploadConnection.IsEmpty() &&  g_uploadConnection.StartsWith(_T("Serial") ) ) {
11896             port = g_uploadConnection;
11897     }
11898 
11899     else if( g_pConnectionParams ) {
11900     // If there is no persistent upload port recorded (yet)
11901             // then use the first available serial connection which has output defined.
11902             for( size_t i = 0; i < g_pConnectionParams->Count(); i++ ) {
11903                 ConnectionParams *cp = g_pConnectionParams->Item( i );
11904                 if( (cp->IOSelect != DS_TYPE_INPUT) && cp->Type == SERIAL )
11905                     port << _T("Serial:") << cp->Port;
11906             }
11907     }
11908 
11909     return port;
11910 }
11911 
ShowAISTargetQueryDialog(wxWindow * win,int mmsi)11912 void ShowAISTargetQueryDialog( wxWindow *win, int mmsi )
11913 {
11914     if( !win ) return;
11915 
11916     if( NULL == g_pais_query_dialog_active ) {
11917         int pos_x = g_ais_query_dialog_x;
11918         int pos_y = g_ais_query_dialog_y;
11919 
11920         if( g_pais_query_dialog_active ) {
11921             delete g_pais_query_dialog_active;
11922             g_pais_query_dialog_active = new AISTargetQueryDialog();
11923         }
11924         else {
11925             g_pais_query_dialog_active = new AISTargetQueryDialog();
11926         }
11927 
11928         g_pais_query_dialog_active->Create( win, -1, _( "AIS Target Query" ),
11929                                             wxPoint( pos_x, pos_y ) );
11930 
11931         g_pais_query_dialog_active->SetAutoCentre( g_btouch );
11932         g_pais_query_dialog_active->SetAutoSize( g_bresponsive );
11933         g_pais_query_dialog_active->SetMMSI( mmsi );
11934         g_pais_query_dialog_active->UpdateText();
11935         wxSize sz = g_pais_query_dialog_active->GetSize();
11936 
11937         bool b_reset_pos = false;
11938 #ifdef __WXMSW__
11939         //  Support MultiMonitor setups which an allow negative window positions.
11940         //  If the requested window title bar does not intersect any installed monitor,
11941         //  then default to simple primary monitor positioning.
11942         RECT frame_title_rect;
11943         frame_title_rect.left = pos_x;
11944         frame_title_rect.top = pos_y;
11945         frame_title_rect.right = pos_x + sz.x;
11946         frame_title_rect.bottom = pos_y + 30;
11947 
11948         if( NULL == MonitorFromRect( &frame_title_rect, MONITOR_DEFAULTTONULL ) ) b_reset_pos =
11949                 true;
11950 #else
11951 
11952         //    Make sure drag bar (title bar) of window intersects wxClient Area of screen, with a little slop...
11953         wxRect window_title_rect;// conservative estimate
11954         window_title_rect.x = pos_x;
11955         window_title_rect.y = pos_y;
11956         window_title_rect.width = sz.x;
11957         window_title_rect.height = 30;
11958 
11959         wxRect ClientRect = wxGetClientDisplayRect();
11960         ClientRect.Deflate(60, 60);// Prevent the new window from being too close to the edge
11961         if(!ClientRect.Intersects(window_title_rect))
11962             b_reset_pos = true;
11963 
11964 #endif
11965 
11966         if( b_reset_pos )
11967             g_pais_query_dialog_active->Move( 50, 200 );
11968 
11969 
11970     } else {
11971         g_pais_query_dialog_active->SetMMSI( mmsi );
11972         g_pais_query_dialog_active->UpdateText();
11973     }
11974 
11975     g_pais_query_dialog_active->Show();
11976 }
11977 
ToggleCanvasQuiltMode(void)11978 void ChartCanvas::ToggleCanvasQuiltMode( void )
11979 {
11980         bool cur_mode = GetQuiltMode();
11981 
11982         if( !GetQuiltMode() )
11983             SetQuiltMode( true );
11984         else
11985             if( GetQuiltMode() ) {
11986                 SetQuiltMode( false );
11987                 g_sticky_chart = GetQuiltReferenceChartIndex();
11988             }
11989 
11990 
11991         if( cur_mode != GetQuiltMode() ){
11992             SetupCanvasQuiltMode();
11993             DoCanvasUpdate();
11994             InvalidateGL();
11995             Refresh();
11996         }
11997             //  TODO What to do about this?
11998             //g_bQuiltEnable = GetQuiltMode();
11999 
12000             // Recycle the S52 PLIB so that vector charts will flush caches and re-render
12001         if(ps52plib)
12002             ps52plib->GenerateStateHash();
12003 
12004         if( GetMUIBar() && GetMUIBar()->GetCanvasOptions())
12005             GetMUIBar()->GetCanvasOptions()->RefreshControlValues();
12006 }
12007 
DoCanvasStackDelta(int direction)12008 void ChartCanvas::DoCanvasStackDelta( int direction )
12009 {
12010     if( !GetQuiltMode() ) {
12011         int current_stack_index = GetpCurrentStack()->CurrentStackEntry;
12012         if( (current_stack_index + direction) >= GetpCurrentStack()->nEntry )
12013             return;
12014         if( (current_stack_index + direction) < 0 )
12015             return;
12016 
12017         if( m_bpersistent_quilt /*&& g_bQuiltEnable*/ ) {
12018             int new_dbIndex = GetpCurrentStack()->GetDBIndex(current_stack_index + direction );
12019 
12020             if( IsChartQuiltableRef( new_dbIndex ) ) {
12021                 ToggleCanvasQuiltMode();
12022                 SelectQuiltRefdbChart( new_dbIndex );
12023                 m_bpersistent_quilt = false;
12024             }
12025         }
12026         else {
12027             SelectChartFromStack( current_stack_index + direction );
12028         }
12029     } else {
12030         std::vector<int>  piano_chart_index_array = GetQuiltExtendedStackdbIndexArray();
12031         int refdb = GetQuiltRefChartdbIndex();
12032 
12033         //      Find the ref chart in the stack
12034         int current_index = -1;
12035         for(unsigned int i=0 ; i < piano_chart_index_array.size() ; i++){
12036             if(refdb == piano_chart_index_array[i]){
12037                 current_index = i;
12038                 break;
12039             }
12040         }
12041         if(current_index == -1)
12042             return;
12043 
12044         const ChartTableEntry &ctet = ChartData->GetChartTableEntry( refdb );
12045         int target_family= ctet.GetChartFamily();
12046 
12047         int new_index = -1;
12048         int check_index = current_index + direction;
12049         bool found = false;
12050         int check_dbIndex = -1;
12051         int new_dbIndex = -1;
12052 
12053         //      When quilted. switch within the same chart family
12054         while(!found && (unsigned int)check_index < piano_chart_index_array.size() && (check_index >= 0)){
12055             check_dbIndex = piano_chart_index_array[check_index];
12056             const ChartTableEntry &cte = ChartData->GetChartTableEntry( check_dbIndex );
12057             if(target_family == cte.GetChartFamily()){
12058                 found = true;
12059                 new_index = check_index;
12060                 new_dbIndex = check_dbIndex;
12061                 break;
12062             }
12063 
12064             check_index += direction;
12065         }
12066 
12067         if(!found)
12068             return;
12069 
12070 
12071         if( !IsChartQuiltableRef( new_dbIndex ) ) {
12072             ToggleCanvasQuiltMode();
12073             SelectdbChart( new_dbIndex );
12074             m_bpersistent_quilt = true;
12075         } else {
12076             SelectQuiltRefChart( new_index );
12077         }
12078     }
12079 
12080     gFrame->UpdateGlobalMenuItems(); // update the state of the menu items (checkmarks etc)
12081     SetQuiltChartHiLiteIndex( -1 );
12082 
12083     ReloadVP();
12084 }
12085 
12086 
12087 //--------------------------------------------------------------------------------------------------------
12088 //
12089 //      Toolbar support
12090 //
12091 //--------------------------------------------------------------------------------------------------------
12092 
OnToolLeftClick(wxCommandEvent & event)12093 void ChartCanvas::OnToolLeftClick( wxCommandEvent& event )
12094 {
12095     //  Handle the per-canvas toolbar clicks here
12096 
12097     switch( event.GetId() ){
12098 
12099         case ID_ZOOMIN: {
12100             ZoomCanvasSimple( g_plus_minus_zoom_factor );
12101             break;
12102         }
12103 
12104         case ID_ZOOMOUT: {
12105             ZoomCanvasSimple( 1.0 / g_plus_minus_zoom_factor );
12106             break;
12107         }
12108 
12109         case ID_STKUP:
12110             DoCanvasStackDelta( 1 );
12111             DoCanvasUpdate();
12112             break;
12113 
12114         case ID_STKDN:
12115             DoCanvasStackDelta( -1 );
12116             DoCanvasUpdate();
12117             break;
12118 
12119         case ID_FOLLOW: {
12120             TogglebFollow();
12121             break;
12122         }
12123 
12124         case ID_CURRENT: {
12125             ShowCurrents( !GetbShowCurrent() );
12126             ReloadVP();
12127             Refresh( false );
12128             break;
12129 
12130         }
12131 
12132         case ID_TIDE: {
12133             ShowTides( !GetbShowTide() );
12134             ReloadVP();
12135             Refresh( false );
12136             break;
12137 
12138         }
12139 
12140         case ID_ROUTE: {
12141             if( 0 == m_routeState ){
12142                 StartRoute();
12143             }
12144             else {
12145                 FinishRoute();
12146             }
12147 
12148 #ifdef __OCPN__ANDROID__
12149             androidSetRouteAnnunciator(m_routeState == 1);
12150 #endif
12151             break;
12152         }
12153 
12154         case ID_AIS: {
12155             SetAISCanvasDisplayStyle(-1);
12156             break;
12157         }
12158 
12159 
12160         default:
12161             break;
12162     }
12163 
12164     //  And then let  gFrame handle the rest....
12165     event.Skip();
12166 }
12167 
12168 
SetToolbarPosition(wxPoint position)12169 void ChartCanvas::SetToolbarPosition( wxPoint position )
12170 {
12171     m_toolbarPosition = position;
12172 }
12173 
SetToolbarOrientation(long orient)12174 void ChartCanvas::SetToolbarOrientation( long orient )
12175 {
12176     m_toolbarOrientation = orient;
12177 }
12178 
GetToolbarPosition()12179 wxPoint ChartCanvas::GetToolbarPosition()
12180 {
12181     if(m_toolBar){
12182         wxPoint tbp = m_toolBar->GetPosition();  //toolbar is a TLW, so this is screen coordinates.
12183         return ScreenToClient( tbp );
12184     }
12185     else
12186         return wxPoint(0,0);
12187 }
12188 
GetToolbarOrientation()12189 long ChartCanvas::GetToolbarOrientation()
12190 {
12191     if(m_toolBar)
12192         return m_toolBar->GetOrient();
12193     else
12194         return 0;
12195 }
12196 
SubmergeToolbar(void)12197 void ChartCanvas::SubmergeToolbar( void )
12198 {
12199     if( m_toolBar )
12200         m_toolBar->Submerge();
12201 }
12202 
SurfaceToolbar(void)12203 void ChartCanvas::SurfaceToolbar( void )
12204 {
12205     if( m_toolBar )
12206         m_toolBar->Surface();
12207 }
12208 
IsToolbarShown()12209 bool ChartCanvas::IsToolbarShown()
12210 {
12211     bool rv = false;
12212     if(m_toolBar)
12213         rv = m_toolBar->IsShown();
12214     return rv;
12215 }
12216 
ToggleToolbar(bool b_smooth)12217 void ChartCanvas::ToggleToolbar( bool b_smooth )
12218 {
12219     if( m_toolBar ) {
12220         if( m_toolBar->IsShown() ){
12221             SubmergeToolbar();
12222         }
12223         else{
12224             SurfaceToolbar();
12225             m_toolBar->Raise();
12226         }
12227     }
12228 }
12229 
DestroyToolbar()12230 void ChartCanvas::DestroyToolbar()
12231 {
12232     if( m_toolBar )
12233         m_toolBar->DestroyToolBar();
12234 }
12235 
12236 
RequestNewCanvasToolbar(bool bforcenew)12237 ocpnFloatingToolbarDialog *ChartCanvas::RequestNewCanvasToolbar(bool bforcenew)
12238 {
12239     bool toolbar_scale_tools_shown = m_pCurrentStack && m_pCurrentStack->b_valid && ( m_pCurrentStack->nEntry > 1 );
12240 
12241     bool b_reshow = true;
12242     if( m_toolBar ) {
12243         b_reshow = m_toolBar->IsShown();
12244         float ff = fabs(m_toolBar->GetScaleFactor() - m_toolbar_scalefactor);
12245         if((ff > 0.01f) || bforcenew){
12246             m_toolBar->DestroyToolBar();
12247             delete m_toolBar;
12248             m_toolBar = NULL;
12249         }
12250     }
12251 
12252     if( !m_toolBar ) {
12253         m_toolBar = new ocpnFloatingToolbarDialog( this, m_toolbarPosition, m_toolbarOrientation, m_toolbar_scalefactor );
12254         m_toolBar->CreateConfigMenu();
12255 
12256         if(g_bDeferredInitDone){
12257             m_toolBar->SetAutoHide(g_bAutoHideToolbar);
12258             m_toolBar->SetAutoHideTimer(g_nAutoHideToolbar);
12259 
12260         }
12261         // Adjust toolbar position if necessary
12262         if(g_MainToolbar && IsPrimaryCanvas()){
12263             wxRect masterToolbarRect = g_MainToolbar->GetRect();
12264             m_toolBar->SetULDockPosition(wxPoint(masterToolbarRect.width + 8, -1));
12265         }
12266 
12267      }
12268 
12269     if( m_toolBar ) {
12270         if( m_toolBar->IsToolbarShown() )
12271             m_toolBar->DestroyToolBar();
12272 
12273 
12274         m_toolBar->m_toolbar_scale_tools_shown = toolbar_scale_tools_shown;
12275 
12276         m_toolBar->CreateMyToolbar();
12277         if (m_toolBar->isSubmergedToGrabber()) {
12278             m_toolBar->SubmergeToGrabber();
12279         } else {
12280             m_toolBar->RePosition();
12281             m_toolBar->SetColorScheme(global_color_scheme);
12282             m_toolBar->Show(b_reshow && m_bToolbarEnable);
12283         }
12284     }
12285 
12286     return m_toolBar;
12287 
12288 }
12289 
12290 //      Update inplace the current toolbar with bitmaps corresponding to the current color scheme
UpdateToolbarColorScheme(ColorScheme cs)12291 void ChartCanvas::UpdateToolbarColorScheme( ColorScheme cs )
12292 {
12293     if( !m_toolBar )
12294         return;
12295 
12296     if( m_toolBar ) {
12297         if(m_toolBar->GetColorScheme() != cs){
12298             m_toolBar->SetColorScheme( cs );
12299 
12300             if( m_toolBar->IsToolbarShown() ) {
12301                 m_toolBar->DestroyToolBar();
12302                 m_toolBar->CreateMyToolbar();
12303                 if (m_toolBar->isSubmergedToGrabber())
12304                     m_toolBar->SubmergeToGrabber(); //Surface(); //SubmergeToGrabber();
12305 
12306             }
12307         }
12308     }
12309 
12310 
12311     if( m_toolBar->GetToolbar() ) {
12312         //  Re-establish toggle states
12313         m_toolBar->GetToolbar()->ToggleTool( ID_FOLLOW, m_bFollow );
12314         m_toolBar->GetToolbar()->ToggleTool( ID_CURRENT, GetbShowCurrent() );
12315         m_toolBar->GetToolbar()->ToggleTool( ID_TIDE, GetbShowTide() );
12316     }
12317 
12318     return;
12319 }
12320 
SetCanvasToolbarItemState(int tool_id,bool state)12321 void ChartCanvas::SetCanvasToolbarItemState( int tool_id, bool state )
12322 {
12323     if( GetToolbar() && GetToolbar()->GetToolbar() )
12324         GetToolbar()->GetToolbar()->ToggleTool( tool_id, state );
12325 }
12326 
12327 
12328 extern bool    g_bAllowShowScaled;
12329 
SetShowAIS(bool show)12330 void ChartCanvas::SetShowAIS( bool show )
12331 {
12332     m_bShowAIS = show;
12333     if( GetMUIBar() && GetMUIBar()->GetCanvasOptions())
12334         GetMUIBar()->GetCanvasOptions()->RefreshControlValues();
12335 }
12336 
SetAttenAIS(bool show)12337 void ChartCanvas::SetAttenAIS( bool show )
12338 {
12339     m_bShowAISScaled = show;
12340     if( GetMUIBar() && GetMUIBar()->GetCanvasOptions())
12341         GetMUIBar()->GetCanvasOptions()->RefreshControlValues();
12342 }
12343 
12344 
SetAISCanvasDisplayStyle(int StyleIndx)12345 void ChartCanvas::SetAISCanvasDisplayStyle(int StyleIndx)
12346 {
12347     // make some arrays to hold the dfferences between cycle steps
12348     //show all, scaled, hide all
12349     bool bShowAIS_Array[3] = {true, true, false};
12350     bool bShowScaled_Array[3] = {false, true, true};
12351     wxString ToolShortHelp_Array[3] = { _("Show all AIS Targets"),
12352                                         _("Attenuate less critical AIS targets"),
12353                                         _("Hide AIS Targets") };
12354     wxString iconName_Array[3] = { _T("AIS"),  _T("AIS_Suppressed"), _T("AIS_Disabled")};
12355     int ArraySize = 3;
12356     int AIS_Toolbar_Switch = 0;
12357     if (StyleIndx == -1){// -1 means coming from toolbar button
12358         //find current state of switch
12359         for ( int i = 1; i < ArraySize; i++){
12360             if( (bShowAIS_Array[i] == m_bShowAIS) && (bShowScaled_Array[i] == m_bShowAISScaled) )
12361                 AIS_Toolbar_Switch = i;
12362         }
12363         AIS_Toolbar_Switch++; // we did click so continu with next item
12364         if ( (!g_bAllowShowScaled) && (AIS_Toolbar_Switch == 1) )
12365             AIS_Toolbar_Switch++;
12366 
12367     }
12368     else { // coming from menu bar.
12369         AIS_Toolbar_Switch = StyleIndx;
12370     }
12371      //make sure we are not above array
12372     if (AIS_Toolbar_Switch >= ArraySize )
12373         AIS_Toolbar_Switch=0;
12374 
12375     int AIS_Toolbar_Switch_Next = AIS_Toolbar_Switch+1; //Find out what will happen at next click
12376     if ( (!g_bAllowShowScaled) && (AIS_Toolbar_Switch_Next == 1) )
12377         AIS_Toolbar_Switch_Next++;
12378     if (AIS_Toolbar_Switch_Next >= ArraySize )
12379         AIS_Toolbar_Switch_Next=0; // If at end of cycle start at 0
12380 
12381     //Set found values to global and member variables
12382     m_bShowAIS = bShowAIS_Array[AIS_Toolbar_Switch];
12383     m_bShowAISScaled = bShowScaled_Array[AIS_Toolbar_Switch];
12384     if( m_toolBar ) {
12385         m_toolBar->SetToolShortHelp( ID_AIS, ToolShortHelp_Array[AIS_Toolbar_Switch_Next] );
12386         if( m_toolBar->m_pTBAISTool ) {
12387             m_toolBar->GetToolbar()->SetToolNormalBitmapEx( m_toolBar->m_pTBAISTool, iconName_Array[AIS_Toolbar_Switch] );
12388             m_toolBar->GetToolbar()->Refresh();
12389             m_toolBar->m_tblastAISiconName = iconName_Array[AIS_Toolbar_Switch];
12390         }
12391     }
12392 
12393 }
12394 
TouchAISToolActive(void)12395 void ChartCanvas::TouchAISToolActive( void )
12396 {
12397     if(!m_toolBar)
12398         return;
12399 
12400     ocpnStyle::Style* style = g_StyleManager->GetCurrentStyle();
12401 
12402     if( m_toolBar->m_pTBAISTool ) {
12403         if( ( !g_pAIS->IsAISSuppressed() ) && ( !g_pAIS->IsAISAlertGeneral() ) ) {
12404             g_nAIS_activity_timer = 5;                // seconds
12405 
12406             wxString iconName = _T("AIS_Normal_Active");
12407             if( g_pAIS->IsAISAlertGeneral() ) iconName = _T("AIS_AlertGeneral_Active");
12408             if( g_pAIS->IsAISSuppressed() ) iconName = _T("AIS_Suppressed_Active");
12409             if( !m_bShowAIS ) iconName = _T("AIS_Disabled");
12410 
12411             if( m_toolBar->m_tblastAISiconName != iconName ) {
12412                 if( m_toolBar->GetToolbar()) {
12413                     m_toolBar->GetToolbar()->SetToolNormalBitmapEx( m_toolBar->m_pTBAISTool, iconName );
12414                     m_toolBar->GetToolbar()->Refresh();
12415                     m_toolBar->m_tblastAISiconName = iconName;
12416                 }
12417             }
12418         }
12419     }
12420 }
12421 
UpdateAISTBTool(void)12422 void ChartCanvas::UpdateAISTBTool( void )
12423 {
12424     if(!g_pAIS) return;
12425     if(!m_toolBar) return;
12426 
12427     ocpnStyle::Style* style = g_StyleManager->GetCurrentStyle();
12428 
12429     wxString iconName;
12430 
12431     if( m_toolBar->m_pTBAISTool ) {
12432         bool b_update = false;
12433 
12434         iconName = _T("AIS");
12435         if( g_pAIS->IsAISSuppressed() )
12436             iconName = _T("AIS_Suppressed");
12437         if( g_pAIS->IsAISAlertGeneral() )
12438             iconName = _T("AIS_AlertGeneral");
12439         if( !m_bShowAIS )
12440             iconName = _T("AIS_Disabled");
12441 
12442         //  Manage timeout for AIS activity indicator
12443         if( g_nAIS_activity_timer ) {
12444                 g_nAIS_activity_timer--;
12445 
12446                 if( 0 == g_nAIS_activity_timer ) b_update = true;
12447                 else {
12448                     iconName = _T("AIS_Normal_Active");
12449                     if( g_pAIS->IsAISSuppressed() )
12450                         iconName = _T("AIS_Suppressed_Active");
12451                     if( g_pAIS->IsAISAlertGeneral() )
12452                         iconName = _T("AIS_AlertGeneral_Active");
12453                     if( !m_bShowAIS )
12454                         iconName = _T("AIS_Disabled");
12455                 }
12456             }
12457 
12458             if( ( m_toolBar->m_tblastAISiconName != iconName ) ) b_update = true;
12459 
12460             if( b_update && m_toolBar->GetToolbar()) {
12461                 m_toolBar->GetToolbar()->SetToolNormalBitmapEx( m_toolBar->m_pTBAISTool, iconName );
12462                 m_toolBar->GetToolbar()->Refresh();
12463                 m_toolBar->m_tblastAISiconName = iconName;
12464         }
12465     }
12466 }
12467 
12468 //---------------------------------------------------------------------------------
12469 //
12470 //      Compass/GPS status icon support
12471 //
12472 //---------------------------------------------------------------------------------
12473 
UpdateGPSCompassStatusBox(bool b_force_new)12474 void ChartCanvas::UpdateGPSCompassStatusBox( bool b_force_new )
12475 {
12476     //    Look for change in overlap or positions
12477     bool b_update = false;
12478     int cc1_edge_comp = 2;
12479     wxRect rect = m_Compass->GetRect();
12480     wxSize parent_size = GetSize();
12481 
12482     // check to see if it would overlap if it was in its home position (upper right)
12483     wxPoint tentative_pt(parent_size.x - rect.width - cc1_edge_comp, g_StyleManager->GetCurrentStyle()->GetCompassYOffset());
12484     wxRect tentative_rect( tentative_pt, rect.GetSize() );
12485 
12486     if( m_toolBar ) {
12487 
12488         //  If the toolbar location has changed, or the proposed compassDialog location has changed
12489         if( m_toolBar->GetScreenRect() != m_mainlast_tb_rect || b_force_new) {
12490 
12491             wxRect tb_rect = m_toolBar->GetScreenRect();
12492             wxPoint tentative_pt_in_screen(ClientToScreen(tentative_pt));
12493             wxRect tentative_rect_in_screen(tentative_pt_in_screen.x, tentative_pt_in_screen.y,
12494                                             rect.width, rect.height);
12495 
12496             //    if they would not intersect, go ahead and move it to the upper right
12497             //      Else it has to be on lower right
12498             if( !tb_rect.Intersects( tentative_rect_in_screen ) )
12499                 m_Compass->Move( tentative_pt );
12500             else
12501                 m_Compass->Move( wxPoint( GetSize().x - rect.width - cc1_edge_comp,
12502                                           GetSize().y - ( rect.height + cc1_edge_comp ) ) );
12503 
12504             if (rect != m_Compass->GetRect()) {
12505                 Refresh(true);
12506                 m_brepaint_piano = true;
12507                 b_update = true;
12508             }
12509             m_mainlast_tb_rect = tb_rect;
12510 
12511         }
12512     }
12513     else{               // No toolbar, so just place compass in upper right.
12514         m_Compass->Move( tentative_pt );
12515     }
12516 
12517 
12518     if( m_Compass && m_Compass->IsShown())
12519         m_Compass->UpdateStatus( b_force_new | b_update );
12520 
12521     if( b_force_new | b_update )
12522         Refresh();
12523 
12524 }
12525 
12526 
12527 
SelectChartFromStack(int index,bool bDir,ChartTypeEnum New_Type,ChartFamilyEnum New_Family)12528 void ChartCanvas::SelectChartFromStack( int index, bool bDir, ChartTypeEnum New_Type,
12529                                     ChartFamilyEnum New_Family )
12530 {
12531     if( !GetpCurrentStack() ) return;
12532     if( !ChartData ) return;
12533 
12534     if( index < GetpCurrentStack()->nEntry ) {
12535         //      Open the new chart
12536         ChartBase *pTentative_Chart;
12537         pTentative_Chart = ChartData->OpenStackChartConditional( GetpCurrentStack(), index, bDir,
12538                                                                  New_Type, New_Family );
12539 
12540         if( pTentative_Chart ) {
12541             if( m_singleChart ) m_singleChart->Deactivate();
12542 
12543             m_singleChart = pTentative_Chart;
12544             m_singleChart->Activate();
12545 
12546             GetpCurrentStack()->CurrentStackEntry = ChartData->GetStackEntry( GetpCurrentStack(), m_singleChart->GetFullPath() );
12547         }
12548         //else
12549         //    SetChartThumbnail( -1 );   // need to reset thumbnail on failed chart open
12550 
12551             //      Setup the view
12552             double zLat, zLon;
12553         if( m_bFollow ) {
12554             zLat = gLat;
12555             zLon = gLon;
12556         } else {
12557             zLat = m_vLat;
12558             zLon = m_vLon;
12559         }
12560 
12561         double best_scale_ppm = GetBestVPScale( m_singleChart );
12562         double rotation = GetVPRotation();
12563         double oldskew = GetVPSkew();
12564         double newskew = m_singleChart->GetChartSkew() * PI / 180.0;
12565 
12566         if (!g_bskew_comp && (GetUpMode() == NORTH_UP_MODE) ) {
12567             if (fabs(oldskew) > 0.0001)
12568                 rotation = 0.0;
12569             if (fabs(newskew) > 0.0001)
12570                 rotation = newskew;
12571         }
12572 
12573         SetViewPoint( zLat, zLon, best_scale_ppm, newskew, rotation );
12574 
12575 
12576         UpdateGPSCompassStatusBox( true );           // Pick up the rotation
12577 
12578     }
12579 
12580     //  refresh Piano
12581     int idx = GetpCurrentStack()->GetCurrentEntrydbIndex();
12582     if (idx < 0)
12583         return;
12584 
12585     std::vector<int>  piano_active_chart_index_array;
12586     piano_active_chart_index_array.push_back( GetpCurrentStack()->GetCurrentEntrydbIndex() );
12587     m_Piano->SetActiveKeyArray( piano_active_chart_index_array );
12588 }
12589 
SelectdbChart(int dbindex)12590 void ChartCanvas::SelectdbChart( int dbindex )
12591 {
12592     if( !GetpCurrentStack() ) return;
12593     if( !ChartData ) return;
12594 
12595     if( dbindex >= 0 ) {
12596         //      Open the new chart
12597         ChartBase *pTentative_Chart;
12598         pTentative_Chart = ChartData->OpenChartFromDB( dbindex, FULL_INIT );
12599 
12600         if( pTentative_Chart ) {
12601             if( m_singleChart ) m_singleChart->Deactivate();
12602 
12603             m_singleChart = pTentative_Chart;
12604             m_singleChart->Activate();
12605 
12606             GetpCurrentStack()->CurrentStackEntry = ChartData->GetStackEntry( GetpCurrentStack(),  m_singleChart->GetFullPath() );
12607         }
12608         //else
12609         //    SetChartThumbnail( -1 );       // need to reset thumbnail on failed chart open
12610 
12611             //      Setup the view
12612         double zLat, zLon;
12613         if( m_bFollow ) {
12614             zLat = gLat;
12615             zLon = gLon;
12616         } else {
12617             zLat = m_vLat;
12618             zLon = m_vLon;
12619         }
12620 
12621         double best_scale_ppm = GetBestVPScale( m_singleChart );
12622 
12623         if( m_singleChart )
12624             SetViewPoint( zLat, zLon, best_scale_ppm, m_singleChart->GetChartSkew() * PI / 180., GetVPRotation() );
12625 
12626         //SetChartUpdatePeriod( );
12627 
12628         //UpdateGPSCompassStatusBox();           // Pick up the rotation
12629 
12630     }
12631 
12632     // TODO refresh_Piano();
12633 }
12634 
12635 
selectCanvasChartDisplay(int type,int family)12636 void ChartCanvas::selectCanvasChartDisplay( int type, int family)
12637 {
12638     double target_scale = GetVP().view_scale_ppm;
12639 
12640     if( !GetQuiltMode() ) {
12641         if(GetpCurrentStack()){
12642             int stack_index = -1;
12643             for(int i = 0; i < GetpCurrentStack()->nEntry ; i++){
12644                 int check_dbIndex = GetpCurrentStack()->GetDBIndex( i );
12645                 if (check_dbIndex < 0)
12646                     continue;
12647                 const ChartTableEntry &cte = ChartData->GetChartTableEntry( check_dbIndex );
12648                 if(type == cte.GetChartType()){
12649                     stack_index = i;
12650                     break;
12651                 }
12652                 else if(family == cte.GetChartFamily()){
12653                     stack_index = i;
12654                     break;
12655                 }
12656             }
12657 
12658             if(stack_index >= 0){
12659                 SelectChartFromStack( stack_index );
12660             }
12661         }
12662     } else {
12663         int sel_dbIndex = -1;
12664         std::vector<int>  piano_chart_index_array = GetQuiltExtendedStackdbIndexArray();
12665         for(unsigned int i = 0; i < piano_chart_index_array.size() ; i++){
12666             int check_dbIndex = piano_chart_index_array[i];
12667             const ChartTableEntry &cte = ChartData->GetChartTableEntry( check_dbIndex );
12668             if(type == cte.GetChartType()){
12669                 if( IsChartQuiltableRef( check_dbIndex ) ) {
12670                     sel_dbIndex = check_dbIndex;
12671                     break;
12672                 }
12673             }
12674             else if(family == cte.GetChartFamily()){
12675                 if( IsChartQuiltableRef( check_dbIndex ) ) {
12676                     sel_dbIndex = check_dbIndex;
12677                     break;
12678                 }
12679             }
12680         }
12681 
12682         if(sel_dbIndex >= 0){
12683             SelectQuiltRefdbChart( sel_dbIndex, false );  // no autoscale
12684             //  Re-qualify the quilt reference chart selection
12685             AdjustQuiltRefChart(  );
12686         }
12687 
12688         //  Now reset the scale to the target...
12689         SetVPScale(target_scale);
12690 
12691 
12692 
12693 
12694     }
12695 
12696     SetQuiltChartHiLiteIndex( -1 );
12697 
12698     ReloadVP();
12699 }
12700 
12701 
IsTileOverlayIndexInYesShow(int index)12702 bool ChartCanvas::IsTileOverlayIndexInYesShow( int index ){
12703     return std::find(m_tile_yesshow_index_array.begin(), m_tile_yesshow_index_array.end(), index) != m_tile_yesshow_index_array.end();
12704 }
12705 
IsTileOverlayIndexInNoShow(int index)12706 bool ChartCanvas::IsTileOverlayIndexInNoShow( int index ){
12707     return std::find(m_tile_noshow_index_array.begin(), m_tile_noshow_index_array.end(), index) != m_tile_noshow_index_array.end();
12708 }
12709 
12710 
AddTileOverlayIndexToNoShow(int index)12711 void ChartCanvas::AddTileOverlayIndexToNoShow( int index )
12712 {
12713     if(std::find(m_tile_noshow_index_array.begin(), m_tile_noshow_index_array.end(), index) == m_tile_noshow_index_array.end()) {
12714         m_tile_noshow_index_array.push_back( index );
12715     }
12716 }
12717 
12718 //-------------------------------------------------------------------------------------------------------
12719 //
12720 //      Piano support
12721 //
12722 //-------------------------------------------------------------------------------------------------------
12723 
HandlePianoClick(int selected_index,int selected_dbIndex)12724 void ChartCanvas::HandlePianoClick( int selected_index, int selected_dbIndex )
12725 {
12726     if (g_boptionsactive) return;              // Piano might be invalid due to chartset updates.
12727     if( !m_pCurrentStack ) return;
12728     if( !ChartData) return;
12729 
12730     // stop movement or on slow computer we may get something like :
12731     // zoom out with the wheel (timer is set)
12732     // quickly click and display a chart, which may zoom in
12733     // but the delayed timer fires first and it zooms out again!
12734     StopMovement();
12735 
12736     if( !GetQuiltMode() ) {
12737         if( m_bpersistent_quilt/* && g_bQuiltEnable*/ ) {
12738             if( IsChartQuiltableRef( selected_dbIndex ) ) {
12739                 ToggleCanvasQuiltMode();
12740                 SelectQuiltRefdbChart( selected_dbIndex );
12741                 m_bpersistent_quilt = false;
12742             } else {
12743                 SelectChartFromStack( selected_index );
12744             }
12745         } else {
12746             SelectChartFromStack( selected_index );
12747             g_sticky_chart = selected_dbIndex;
12748         }
12749 
12750         if( m_singleChart )
12751             GetVP().SetProjectionType(m_singleChart->GetChartProjectionType());
12752 
12753     } else {
12754 
12755         // Handle MBTiles overlays first
12756         // Left click simply toggles the noshow array index entry
12757         if( CHART_TYPE_MBTILES == ChartData->GetDBChartType( selected_dbIndex ) ){
12758            bool bfound=false;
12759            for( unsigned int i = 0; i < m_tile_noshow_index_array.size(); i++ ) {
12760                 if( m_tile_noshow_index_array[i] == selected_dbIndex ){ // chart is in the noshow list
12761                     m_tile_noshow_index_array.erase(m_tile_noshow_index_array.begin() + i );  // erase it
12762                     bfound = true;
12763                     break;
12764                 }
12765            }
12766            if(!bfound){
12767                m_tile_noshow_index_array.push_back(selected_dbIndex);
12768            }
12769 
12770            // If not already present, add this tileset to the "yes_show" array.
12771            if(!IsTileOverlayIndexInYesShow(selected_dbIndex))
12772                 m_tile_yesshow_index_array.push_back( selected_dbIndex );
12773         }
12774 
12775         else{
12776             if( IsChartQuiltableRef( selected_dbIndex ) ){
12777             //            if( ChartData ) ChartData->PurgeCache();
12778 
12779 
12780             //  If the chart is a vector chart, and of very large scale,
12781             //  then we had better set the new scale directly to avoid excessive underzoom
12782             //  on, eg, Inland ENCs
12783                 bool set_scale = false;
12784                 if( CHART_TYPE_S57 == ChartData->GetDBChartType( selected_dbIndex ) ){
12785                     if( ChartData->GetDBChartScale(selected_dbIndex) < 5000){
12786                         set_scale = true;
12787                     }
12788                 }
12789 
12790                 if(!set_scale){
12791                     SelectQuiltRefdbChart( selected_dbIndex, true );  // autoscale
12792                 }
12793                 else {
12794                     SelectQuiltRefdbChart( selected_dbIndex, false );  // no autoscale
12795 
12796 
12797                     //  Adjust scale so that the selected chart is underzoomed/overzoomed by a controlled amount
12798                     ChartBase *pc = ChartData->OpenChartFromDB( selected_dbIndex, FULL_INIT );
12799                     if( pc ) {
12800                         double proposed_scale_onscreen = GetCanvasScaleFactor() / GetVPScale();
12801 
12802                         if(g_bPreserveScaleOnX){
12803                             proposed_scale_onscreen = wxMin(proposed_scale_onscreen,
12804                                                             100 * pc->GetNormalScaleMax(GetCanvasScaleFactor(), GetCanvasWidth()));
12805                         }
12806                         else{
12807                             proposed_scale_onscreen = wxMin(proposed_scale_onscreen,
12808                                                             20 * pc->GetNormalScaleMax(GetCanvasScaleFactor(), GetCanvasWidth()));
12809 
12810                             proposed_scale_onscreen = wxMax(proposed_scale_onscreen,
12811                                                             pc->GetNormalScaleMin(GetCanvasScaleFactor(), g_b_overzoom_x));
12812                         }
12813 
12814                         SetVPScale( GetCanvasScaleFactor() / proposed_scale_onscreen );
12815                     }
12816                 }
12817             }
12818             else {
12819                 ToggleCanvasQuiltMode();
12820                 SelectdbChart( selected_dbIndex );
12821                 m_bpersistent_quilt = true;
12822             }
12823         }
12824     }
12825 
12826     SetQuiltChartHiLiteIndex( -1 );
12827     gFrame->UpdateGlobalMenuItems(); // update the state of the menu items (checkmarks etc)
12828     HideChartInfoWindow();
12829     DoCanvasUpdate();
12830     ReloadVP();                  // Pick up the new selections
12831 }
12832 
12833 
HandlePianoRClick(int x,int y,int selected_index,int selected_dbIndex)12834 void ChartCanvas::HandlePianoRClick( int x, int y, int selected_index, int selected_dbIndex )
12835 {
12836     if (g_boptionsactive) return;              // Piano might be invalid due to chartset updates.
12837     if( !GetpCurrentStack() ) return;
12838 
12839     PianoPopupMenu( x, y, selected_index, selected_dbIndex );
12840     UpdateCanvasControlBar();
12841 
12842     SetQuiltChartHiLiteIndex( -1 );
12843 }
12844 
HandlePianoRollover(int selected_index,int selected_dbIndex)12845 void ChartCanvas::HandlePianoRollover( int selected_index, int selected_dbIndex )
12846 {
12847     if (g_boptionsactive) return;              // Piano might be invalid due to chartset updates.
12848     if( !GetpCurrentStack() ) return;
12849     if( !ChartData ) return;
12850 
12851     if (ChartData->IsBusy())
12852         return;
12853 
12854     wxPoint key_location = m_Piano->GetKeyOrigin( selected_index );
12855 
12856     if( !GetQuiltMode() ) {
12857         //SetChartThumbnail( selected_index );
12858         ShowChartInfoWindow( key_location.x, selected_dbIndex );
12859     } else {
12860         std::vector<int>  piano_chart_index_array = GetQuiltExtendedStackdbIndexArray();
12861 
12862         if( ( GetpCurrentStack()->nEntry > 1 ) || ( piano_chart_index_array.size() >= 1 ) ) {
12863             ShowChartInfoWindow( key_location.x, selected_dbIndex );
12864             SetQuiltChartHiLiteIndex( selected_dbIndex );
12865 
12866             ReloadVP( false );         // no VP adjustment allowed
12867         } else if( GetpCurrentStack()->nEntry == 1 ) {
12868             const ChartTableEntry &cte = ChartData->GetChartTableEntry( GetpCurrentStack()->GetDBIndex( 0 ) );
12869             if( CHART_TYPE_CM93COMP != cte.GetChartType() ) {
12870                 ShowChartInfoWindow( key_location.x, selected_dbIndex );
12871                 ReloadVP( false );
12872             } else if( ( -1 == selected_index ) && ( -1 == selected_dbIndex ) ) {
12873                 ShowChartInfoWindow( key_location.x, selected_dbIndex );
12874             }
12875         }
12876         //SetChartThumbnail( -1 );        // hide all thumbs in quilt mode
12877     }
12878 }
12879 
12880 
UpdateCanvasControlBar(void)12881 void ChartCanvas::UpdateCanvasControlBar( void )
12882 {
12883     if(m_pianoFrozen)
12884         return;
12885 
12886     if( !GetpCurrentStack() ) return;
12887     if( !ChartData) return;
12888     if ( !g_bShowChartBar ) return;
12889 
12890     int sel_type = -1;
12891     int sel_family = -1;
12892 
12893     std::vector<int>  piano_chart_index_array;
12894     std::vector<int>  empty_piano_chart_index_array;
12895 
12896     wxString old_hash = m_Piano->GetStoredHash();
12897 
12898     if( GetQuiltMode() ) {
12899         piano_chart_index_array = GetQuiltExtendedStackdbIndexArray();
12900         m_Piano->SetKeyArray( piano_chart_index_array );
12901 
12902         std::vector<int>  piano_active_chart_index_array = GetQuiltCandidatedbIndexArray();
12903         m_Piano->SetActiveKeyArray( piano_active_chart_index_array );
12904 
12905         std::vector<int>  piano_eclipsed_chart_index_array = GetQuiltEclipsedStackdbIndexArray();
12906         m_Piano->SetEclipsedIndexArray( piano_eclipsed_chart_index_array );
12907 
12908         m_Piano->SetNoshowIndexArray( m_quilt_noshow_index_array );
12909         m_Piano->AddNoshowIndexArray( m_tile_noshow_index_array );
12910 
12911         sel_type = ChartData->GetDBChartType(GetQuiltReferenceChartIndex());
12912         sel_family = ChartData->GetDBChartFamily(GetQuiltReferenceChartIndex());
12913     } else {
12914         piano_chart_index_array = ChartData->GetCSArray( GetpCurrentStack() );
12915         m_Piano->SetKeyArray( piano_chart_index_array );
12916         // TODO refresh_Piano();
12917 
12918         if(m_singleChart){
12919             sel_type = m_singleChart->GetChartType();
12920             sel_family = m_singleChart->GetChartFamily();
12921         }
12922 
12923     }
12924 
12925     //    Set up the TMerc and Skew arrays
12926     std::vector<int>  piano_skew_chart_index_array;
12927     std::vector<int>  piano_tmerc_chart_index_array;
12928     std::vector<int>  piano_poly_chart_index_array;
12929 
12930     for( unsigned int ino = 0; ino < piano_chart_index_array.size(); ino++ ) {
12931         const ChartTableEntry &ctei = ChartData->GetChartTableEntry(
12932             piano_chart_index_array[ino] );
12933         double skew_norm = ctei.GetChartSkew();
12934         if( skew_norm > 180. ) skew_norm -= 360.;
12935 
12936         if( ctei.GetChartProjectionType() == PROJECTION_TRANSVERSE_MERCATOR )
12937             piano_tmerc_chart_index_array.push_back( piano_chart_index_array[ino] );
12938 
12939         //    Polyconic skewed charts should show as skewed
12940             else
12941                 if( ctei.GetChartProjectionType() == PROJECTION_POLYCONIC ) {
12942                     if( fabs( skew_norm ) > 1. )
12943                         piano_skew_chart_index_array.push_back(piano_chart_index_array[ino] );
12944                     else
12945                         piano_poly_chart_index_array.push_back( piano_chart_index_array[ino] );
12946                 } else
12947                     if( fabs( skew_norm ) > 1. )
12948                         piano_skew_chart_index_array.push_back(piano_chart_index_array[ino] );
12949 
12950     }
12951     m_Piano->SetSkewIndexArray( piano_skew_chart_index_array );
12952     m_Piano->SetTmercIndexArray( piano_tmerc_chart_index_array );
12953     m_Piano->SetPolyIndexArray( piano_poly_chart_index_array );
12954 
12955 
12956     wxString new_hash = m_Piano->GenerateAndStoreNewHash();
12957     if(new_hash != old_hash) {
12958         m_Piano->FormatKeys();
12959         //SetChartThumbnail( -1 );
12960         HideChartInfoWindow();
12961         m_Piano->ResetRollover();
12962         SetQuiltChartHiLiteIndex( -1 );
12963         m_brepaint_piano = true;
12964     }
12965 
12966     // Create a bitmask int that describes what Family/Type of charts are shown in the bar,
12967     // and notify the platform.
12968     int mask = 0;
12969     for( unsigned int ino = 0; ino < piano_chart_index_array.size(); ino++ ) {
12970         const ChartTableEntry &ctei = ChartData->GetChartTableEntry( piano_chart_index_array[ino] );
12971         ChartFamilyEnum e = (ChartFamilyEnum)ctei.GetChartFamily();
12972         ChartTypeEnum t = (ChartTypeEnum)ctei.GetChartType();
12973         if(e == CHART_FAMILY_RASTER)
12974             mask |= 1;
12975         if(e == CHART_FAMILY_VECTOR){
12976             if(t == CHART_TYPE_CM93COMP)
12977                 mask |= 4;
12978             else
12979                 mask |= 2;
12980         }
12981     }
12982 
12983     wxString s_indicated;
12984     if(sel_type == CHART_TYPE_CM93COMP)
12985         s_indicated = _T("cm93");
12986     else{
12987         if(sel_family == CHART_FAMILY_RASTER)
12988             s_indicated = _T("raster");
12989         else if(sel_family == CHART_FAMILY_VECTOR)
12990             s_indicated = _T("vector");
12991     }
12992 
12993     g_Platform->setChartTypeMaskSel(mask, s_indicated);
12994 
12995 }
12996 
FormatPianoKeys(void)12997 void ChartCanvas::FormatPianoKeys( void )
12998 {
12999     m_Piano->FormatKeys();
13000 
13001 }
13002 
PianoPopupMenu(int x,int y,int selected_index,int selected_dbIndex)13003 void ChartCanvas::PianoPopupMenu( int x, int y, int selected_index, int selected_dbIndex )
13004 {
13005     if( !GetpCurrentStack() ) return;
13006 
13007     //    No context menu if quilting is disabled
13008     if( !GetQuiltMode() ) return;
13009 
13010     menu_selected_dbIndex = selected_dbIndex;
13011     menu_selected_index = selected_index;
13012 
13013     m_piano_ctx_menu = new wxMenu();
13014 
13015     //    Search the no-show array
13016     bool b_is_in_noshow = false;
13017     for( unsigned int i = 0; i < m_quilt_noshow_index_array.size(); i++ ) {
13018         if( m_quilt_noshow_index_array[i] == selected_dbIndex ) // chart is in the noshow list
13019                 {
13020             b_is_in_noshow = true;
13021             break;
13022         }
13023     }
13024 
13025     if( b_is_in_noshow ) {
13026         m_piano_ctx_menu->Append( ID_PIANO_ENABLE_QUILT_CHART, _("Show This Chart") );
13027         Connect( ID_PIANO_ENABLE_QUILT_CHART, wxEVT_COMMAND_MENU_SELECTED,
13028                 wxCommandEventHandler(ChartCanvas::OnPianoMenuEnableChart) );
13029     } else
13030         if( GetpCurrentStack()->nEntry > 1 ) {
13031             m_piano_ctx_menu->Append( ID_PIANO_DISABLE_QUILT_CHART, _("Hide This Chart") );
13032             Connect( ID_PIANO_DISABLE_QUILT_CHART, wxEVT_COMMAND_MENU_SELECTED,
13033                     wxCommandEventHandler(ChartCanvas::OnPianoMenuDisableChart) );
13034         }
13035 
13036     wxPoint pos = wxPoint(x, y - 30);
13037 
13038 //        Invoke the drop-down menu
13039     if( m_piano_ctx_menu->GetMenuItems().GetCount() )
13040         PopupMenu( m_piano_ctx_menu, pos );
13041 
13042     delete m_piano_ctx_menu;
13043     m_piano_ctx_menu = NULL;
13044 
13045     HideChartInfoWindow();
13046     m_Piano->ResetRollover();
13047 
13048     SetQuiltChartHiLiteIndex( -1 );
13049 
13050     ReloadVP();
13051 }
13052 
OnPianoMenuEnableChart(wxCommandEvent & event)13053 void ChartCanvas::OnPianoMenuEnableChart( wxCommandEvent& event )
13054 {
13055     for( unsigned int i = 0; i < m_quilt_noshow_index_array.size(); i++ ) {
13056         if( m_quilt_noshow_index_array[i] == menu_selected_dbIndex ) // chart is in the noshow list
13057         {
13058             m_quilt_noshow_index_array.erase(m_quilt_noshow_index_array.begin() + i );
13059             break;
13060         }
13061     }
13062 }
13063 
OnPianoMenuDisableChart(wxCommandEvent & event)13064 void ChartCanvas::OnPianoMenuDisableChart( wxCommandEvent& event )
13065 {
13066     if( !GetpCurrentStack() ) return;
13067     if( !ChartData ) return;
13068 
13069     RemoveChartFromQuilt( menu_selected_dbIndex );
13070 
13071 //      It could happen that the chart being disabled is the reference chart....
13072     if( menu_selected_dbIndex == GetQuiltRefChartdbIndex() ) {
13073         int type = ChartData->GetDBChartType( menu_selected_dbIndex );
13074 
13075         int i = menu_selected_index + 1;          // select next smaller scale chart
13076         bool b_success = false;
13077         while( i < GetpCurrentStack()->nEntry - 1 ) {
13078             int dbIndex = GetpCurrentStack()->GetDBIndex( i );
13079             if( type == ChartData->GetDBChartType( dbIndex ) ) {
13080                 SelectQuiltRefChart( i );
13081                 b_success = true;
13082                 break;
13083             }
13084             i++;
13085         }
13086 
13087         //    If that did not work, try to select the next larger scale compatible chart
13088         if( !b_success ) {
13089             i = menu_selected_index - 1;
13090             while( i > 0 ) {
13091                 int dbIndex = GetpCurrentStack()->GetDBIndex( i );
13092                 if( type == ChartData->GetDBChartType( dbIndex ) ) {
13093                     SelectQuiltRefChart( i );
13094                     b_success = true;
13095                     break;
13096                 }
13097                 i--;
13098             }
13099         }
13100     }
13101 }
13102 
RemoveChartFromQuilt(int dbIndex)13103 void ChartCanvas::RemoveChartFromQuilt( int dbIndex )
13104 {
13105     //    Remove the item from the list (if it appears) to avoid multiple addition
13106     for( unsigned int i = 0; i < m_quilt_noshow_index_array.size(); i++ ) {
13107         if( m_quilt_noshow_index_array[i] == dbIndex ) // chart is already in the noshow list
13108         {
13109             m_quilt_noshow_index_array.erase(m_quilt_noshow_index_array.begin() + i );
13110             break;
13111         }
13112     }
13113 
13114     m_quilt_noshow_index_array.push_back( dbIndex );
13115 
13116 }
13117 
13118 
13119 
UpdateS52State()13120 bool ChartCanvas::UpdateS52State()
13121 {
13122     bool retval = false;
13123 //    printf("    update %d\n", IsPrimaryCanvas());
13124 
13125     if(ps52plib){
13126         ps52plib->SetShowS57Text( m_encShowText );
13127         ps52plib->SetDisplayCategory( (DisCat) m_encDisplayCategory );
13128         ps52plib->m_bShowSoundg = m_encShowDepth;
13129         ps52plib->m_bShowAtonText = m_encShowBuoyLabels;
13130         ps52plib->m_bShowLdisText = m_encShowLightDesc;
13131 
13132         // Lights
13133         if(!m_encShowLights)                     // On, going off
13134             ps52plib->AddObjNoshow("LIGHTS");
13135         else                                   // Off, going on
13136             ps52plib->RemoveObjNoshow("LIGHTS");
13137         ps52plib->SetLightsOff( !m_encShowLights );
13138         ps52plib->m_bExtendLightSectors = true;
13139 
13140         // TODO ps52plib->m_bShowAtons = m_encShowBuoys;
13141         ps52plib->SetAnchorOn( m_encShowAnchor );
13142         ps52plib->SetQualityOfData( m_encShowDataQual );
13143     }
13144 
13145     return retval;
13146 }
13147 
SetShowENCDataQual(bool show)13148 void ChartCanvas::SetShowENCDataQual( bool show )
13149 {
13150     m_encShowDataQual = show;
13151     if( GetMUIBar() && GetMUIBar()->GetCanvasOptions())
13152         GetMUIBar()->GetCanvasOptions()->RefreshControlValues();
13153 
13154     m_s52StateHash = 0;         // Force a S52 PLIB re-configure
13155 }
13156 
13157 
SetShowENCText(bool show)13158 void ChartCanvas::SetShowENCText( bool show )
13159 {
13160     m_encShowText = show;
13161     if( GetMUIBar() && GetMUIBar()->GetCanvasOptions())
13162         GetMUIBar()->GetCanvasOptions()->RefreshControlValues();
13163 
13164     m_s52StateHash = 0;         // Force a S52 PLIB re-configure
13165 }
13166 
SetENCDisplayCategory(int category)13167 void ChartCanvas::SetENCDisplayCategory( int category )
13168 {
13169     m_encDisplayCategory = category;
13170     m_s52StateHash = 0;         // Force a S52 PLIB re-configure
13171 }
13172 
13173 
SetShowENCDepth(bool show)13174 void ChartCanvas::SetShowENCDepth( bool show )
13175 {
13176     m_encShowDepth = show;
13177     if( GetMUIBar() && GetMUIBar()->GetCanvasOptions())
13178         GetMUIBar()->GetCanvasOptions()->RefreshControlValues();
13179 
13180     m_s52StateHash = 0;         // Force a S52 PLIB re-configure
13181 }
13182 
SetShowENCLightDesc(bool show)13183 void ChartCanvas::SetShowENCLightDesc( bool show )
13184 {
13185     m_encShowLightDesc = show;
13186     if( GetMUIBar() && GetMUIBar()->GetCanvasOptions())
13187         GetMUIBar()->GetCanvasOptions()->RefreshControlValues();
13188 
13189     m_s52StateHash = 0;         // Force a S52 PLIB re-configure
13190 }
13191 
SetShowENCBuoyLabels(bool show)13192 void ChartCanvas::SetShowENCBuoyLabels( bool show )
13193 {
13194     m_encShowBuoyLabels = show;
13195     m_s52StateHash = 0;         // Force a S52 PLIB re-configure
13196 }
13197 
SetShowENCLights(bool show)13198 void ChartCanvas::SetShowENCLights( bool show )
13199 {
13200     m_encShowLights = show;
13201     if( GetMUIBar() && GetMUIBar()->GetCanvasOptions())
13202         GetMUIBar()->GetCanvasOptions()->RefreshControlValues();
13203 
13204     m_s52StateHash = 0;         // Force a S52 PLIB re-configure
13205 }
13206 
SetShowENCAnchor(bool show)13207 void ChartCanvas::SetShowENCAnchor( bool show )
13208 {
13209     m_encShowAnchor = show;
13210     if( GetMUIBar() && GetMUIBar()->GetCanvasOptions())
13211         GetMUIBar()->GetCanvasOptions()->RefreshControlValues();
13212 
13213     m_s52StateHash = 0;         // Force a S52 PLIB re-configure
13214 }
13215 
GetMUIBarRect()13216 wxRect ChartCanvas::GetMUIBarRect()
13217 {
13218     wxRect rv;
13219     if(m_muiBar){
13220         rv = m_muiBar->GetRect();
13221     }
13222 
13223     return rv;
13224 }
13225 
RenderAlertMessage(wxDC & dc,const ViewPort & vp)13226 void ChartCanvas::RenderAlertMessage( wxDC &dc, const ViewPort &vp)
13227 {
13228     if(!GetAlertString().IsEmpty())
13229     {
13230 
13231         wxFont *pfont = wxTheFontList->FindOrCreateFont(10, wxFONTFAMILY_DEFAULT,
13232                                                         wxFONTSTYLE_NORMAL,
13233                                                         wxFONTWEIGHT_NORMAL);
13234 
13235         dc.SetFont( *pfont );
13236         dc.SetPen( *wxTRANSPARENT_PEN);
13237 
13238         dc.SetBrush( wxColour(243, 229, 47 ) );
13239         int w, h;
13240         dc.GetMultiLineTextExtent( GetAlertString(), &w, &h );
13241         h += 2;
13242         //int yp = vp.pix_height - 20 - h;
13243 
13244         wxRect sbr = GetScaleBarRect();
13245         int xp = sbr.x+sbr.width + 10;
13246         int yp = (sbr.y + sbr.height) - h;
13247 
13248         int wdraw = w + 10;
13249         dc.DrawRectangle( xp, yp, wdraw, h );
13250         dc.DrawLabel( GetAlertString(), wxRect( xp, yp, wdraw, h ), wxALIGN_CENTRE_HORIZONTAL | wxALIGN_CENTRE_VERTICAL);
13251     }
13252 }
13253 
13254 //--------------------------------------------------------------------------------------------------------
13255 //    Screen Brightness Control Support Routines
13256 //
13257 //--------------------------------------------------------------------------------------------------------
13258 
13259 #ifdef __UNIX__
13260 #define BRIGHT_XCALIB
13261 #define __OPCPN_USEICC__
13262 #endif
13263 
13264 
13265 #ifdef __OPCPN_USEICC__
13266 int CreateSimpleICCProfileFile(const char *file_name, double co_red, double co_green, double co_blue);
13267 
13268 wxString temp_file_name;
13269 #endif
13270 
13271 #if 0
13272 class ocpnCurtain: public wxDialog
13273 {
13274     DECLARE_CLASS( ocpnCurtain )
13275     DECLARE_EVENT_TABLE()
13276 
13277 public:
13278     ocpnCurtain( wxWindow *parent, wxPoint position, wxSize size, long wstyle );
13279     ~ocpnCurtain( );
13280     bool ProcessEvent(wxEvent& event);
13281 
13282 };
13283 
13284 IMPLEMENT_CLASS ( ocpnCurtain, wxDialog )
13285 
13286 BEGIN_EVENT_TABLE(ocpnCurtain, wxDialog)
13287 END_EVENT_TABLE()
13288 
13289 ocpnCurtain::ocpnCurtain( wxWindow *parent, wxPoint position, wxSize size, long wstyle )
13290 {
13291     wxDialog::Create( parent, -1, _T("ocpnCurtain"), position, size, wxNO_BORDER | wxSTAY_ON_TOP );
13292 }
13293 
13294 ocpnCurtain::~ocpnCurtain()
13295 {
13296 }
13297 
13298 bool ocpnCurtain::ProcessEvent(wxEvent& event)
13299 {
13300     GetParent()->GetEventHandler()->SetEvtHandlerEnabled(true);
13301     return GetParent()->GetEventHandler()->ProcessEvent(event);
13302 }
13303 #endif
13304 
13305 #ifdef _WIN32
13306 #include <windows.h>
13307 
13308 HMODULE hGDI32DLL;
13309 typedef BOOL (WINAPI *SetDeviceGammaRamp_ptr_type)( HDC hDC, LPVOID lpRampTable );
13310 typedef BOOL (WINAPI *GetDeviceGammaRamp_ptr_type)( HDC hDC, LPVOID lpRampTable );
13311 SetDeviceGammaRamp_ptr_type g_pSetDeviceGammaRamp;            // the API entry points in the dll
13312 GetDeviceGammaRamp_ptr_type g_pGetDeviceGammaRamp;
13313 
13314 WORD *g_pSavedGammaMap;
13315 
13316 #endif
13317 
InitScreenBrightness(void)13318 int InitScreenBrightness( void )
13319 {
13320 #ifdef _WIN32
13321     if( gFrame->GetPrimaryCanvas()->GetglCanvas() && g_bopengl ) {
13322         HDC hDC;
13323         BOOL bbr;
13324 
13325         if( NULL == hGDI32DLL ) {
13326             hGDI32DLL = LoadLibrary( TEXT("gdi32.dll") );
13327 
13328             if( NULL != hGDI32DLL ) {
13329                 //Get the entry points of the required functions
13330                 g_pSetDeviceGammaRamp = (SetDeviceGammaRamp_ptr_type) GetProcAddress( hGDI32DLL,
13331                                         "SetDeviceGammaRamp" );
13332                 g_pGetDeviceGammaRamp = (GetDeviceGammaRamp_ptr_type) GetProcAddress( hGDI32DLL,
13333                                         "GetDeviceGammaRamp" );
13334 
13335                 //    If the functions are not found, unload the DLL and return false
13336                 if( ( NULL == g_pSetDeviceGammaRamp ) || ( NULL == g_pGetDeviceGammaRamp ) ) {
13337                     FreeLibrary( hGDI32DLL );
13338                     hGDI32DLL = NULL;
13339                     return 0;
13340                 }
13341             }
13342         }
13343 
13344         //    Interface is ready, so....
13345         //    Get some storage
13346         if( !g_pSavedGammaMap ) {
13347             g_pSavedGammaMap = (WORD *) malloc( 3 * 256 * sizeof(WORD) );
13348 
13349             hDC = GetDC( NULL );                                      // Get the full screen DC
13350             bbr = g_pGetDeviceGammaRamp( hDC, g_pSavedGammaMap );    // Get the existing ramp table
13351             ReleaseDC( NULL, hDC );                                       // Release the DC
13352         }
13353 
13354         //    On Windows hosts, try to adjust the registry to allow full range setting of Gamma table
13355         //    This is an undocumented Windows hack.....
13356         wxRegKey *pRegKey =
13357             new wxRegKey(
13358             _T("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ICM") );
13359         if( !pRegKey->Exists() ) pRegKey->Create();
13360         pRegKey->SetValue( _T("GdiIcmGammaRange"), 256 );
13361 
13362         g_brightness_init = true;
13363         return 1;
13364     }
13365 
13366     else {
13367         if( NULL == g_pcurtain ) {
13368             if( gFrame->CanSetTransparent() ) {
13369                 //    Build the curtain window
13370                 g_pcurtain = new wxDialog( gFrame->GetPrimaryCanvas(), -1, _T(""), wxPoint( 0, 0 ), ::wxGetDisplaySize(),
13371                                            wxNO_BORDER | wxTRANSPARENT_WINDOW | wxSTAY_ON_TOP | wxDIALOG_NO_PARENT );
13372 
13373                 //                  g_pcurtain = new ocpnCurtain(gFrame, wxPoint(0,0),::wxGetDisplaySize(),
13374                 //                      wxNO_BORDER | wxTRANSPARENT_WINDOW |wxSTAY_ON_TOP | wxDIALOG_NO_PARENT);
13375 
13376                 g_pcurtain->Hide();
13377 
13378                 HWND hWnd = GetHwndOf(g_pcurtain);
13379                 SetWindowLong( hWnd, GWL_EXSTYLE,
13380                                GetWindowLong( hWnd, GWL_EXSTYLE ) | ~WS_EX_APPWINDOW );
13381                 g_pcurtain->SetBackgroundColour( wxColour( 0, 0, 0 ) );
13382                 g_pcurtain->SetTransparent( 0 );
13383 
13384                 g_pcurtain->Maximize();
13385                 g_pcurtain->Show();
13386 
13387                 //    All of this is obtuse, but necessary for Windows...
13388                 g_pcurtain->Enable();
13389                 g_pcurtain->Disable();
13390 
13391                 gFrame->Disable();
13392                 gFrame->Enable();
13393                 //SetFocus();
13394 
13395             }
13396         }
13397         g_brightness_init = true;
13398 
13399         return 1;
13400     }
13401 #else
13402     //    Look for "xcalib" application
13403     wxString cmd ( _T ( "xcalib -version" ) );
13404 
13405     wxArrayString output;
13406     long r = wxExecute ( cmd, output );
13407     if(0 != r)
13408         wxLogMessage(_T("   External application \"xcalib\" not found. Screen brightness not changed."));
13409 
13410     g_brightness_init = true;
13411     return 0;
13412 #endif
13413 }
13414 
RestoreScreenBrightness(void)13415 int RestoreScreenBrightness( void )
13416 {
13417 #ifdef _WIN32
13418 
13419     if( g_pSavedGammaMap ) {
13420         HDC hDC = GetDC( NULL );                                 // Get the full screen DC
13421         g_pSetDeviceGammaRamp( hDC, g_pSavedGammaMap );          // Restore the saved ramp table
13422         ReleaseDC( NULL, hDC );                                  // Release the DC
13423 
13424         free( g_pSavedGammaMap );
13425         g_pSavedGammaMap = NULL;
13426     }
13427 
13428     if( g_pcurtain ) {
13429         g_pcurtain->Close();
13430         g_pcurtain->Destroy();
13431         g_pcurtain = NULL;
13432     }
13433 
13434     g_brightness_init = false;
13435     return 1;
13436 
13437 #endif
13438 
13439 #ifdef BRIGHT_XCALIB
13440     if(g_brightness_init)
13441     {
13442         wxString cmd;
13443         cmd = _T("xcalib -clear");
13444         wxExecute(cmd, wxEXEC_ASYNC);
13445         g_brightness_init = false;
13446     }
13447 
13448     return 1;
13449 #endif
13450 
13451     return 0;
13452 }
13453 
13454 //    Set brightness. [0..100]
SetScreenBrightness(int brightness)13455 int SetScreenBrightness( int brightness )
13456 {
13457 #ifdef _WIN32
13458 
13459     //    Under Windows, we use the SetDeviceGammaRamp function which exists in some (most modern?) versions of gdi32.dll
13460     //    Load the required library dll, if not already in place
13461     if( gFrame->GetPrimaryCanvas()->GetglCanvas() && g_bopengl ) {
13462         if( g_pcurtain ) {
13463             g_pcurtain->Close();
13464             g_pcurtain->Destroy();
13465             g_pcurtain = NULL;
13466         }
13467 
13468         InitScreenBrightness();
13469 
13470         if( NULL == hGDI32DLL ) {
13471             // Unicode stuff.....
13472             wchar_t wdll_name[80];
13473             MultiByteToWideChar( 0, 0, "gdi32.dll", -1, wdll_name, 80 );
13474             LPCWSTR cstr = wdll_name;
13475 
13476             hGDI32DLL = LoadLibrary( cstr );
13477 
13478             if( NULL != hGDI32DLL ) {
13479                 //Get the entry points of the required functions
13480                 g_pSetDeviceGammaRamp = (SetDeviceGammaRamp_ptr_type) GetProcAddress( hGDI32DLL,
13481                                         "SetDeviceGammaRamp" );
13482                 g_pGetDeviceGammaRamp = (GetDeviceGammaRamp_ptr_type) GetProcAddress( hGDI32DLL,
13483                                         "GetDeviceGammaRamp" );
13484 
13485                 //    If the functions are not found, unload the DLL and return false
13486                 if( ( NULL == g_pSetDeviceGammaRamp ) || ( NULL == g_pGetDeviceGammaRamp ) ) {
13487                     FreeLibrary( hGDI32DLL );
13488                     hGDI32DLL = NULL;
13489                     return 0;
13490                 }
13491             }
13492         }
13493 
13494         HDC hDC = GetDC( NULL );                          // Get the full screen DC
13495 
13496         /*
13497          int cmcap = GetDeviceCaps(hDC, COLORMGMTCAPS);
13498          if (cmcap != CM_GAMMA_RAMP)
13499          {
13500          wxLogMessage(_T("    Video hardware does not support brightness control by gamma ramp adjustment."));
13501          return false;
13502          }
13503          */
13504 
13505         int increment = brightness * 256 / 100;
13506 
13507         // Build the Gamma Ramp table
13508         WORD GammaTable[3][256];
13509 
13510         int table_val = 0;
13511         for( int i = 0; i < 256; i++ ) {
13512 
13513             GammaTable[0][i] = r_gamma_mult * (WORD) table_val;
13514             GammaTable[1][i] = g_gamma_mult * (WORD) table_val;
13515             GammaTable[2][i] = b_gamma_mult * (WORD) table_val;
13516 
13517             table_val += increment;
13518 
13519             if( table_val > 65535 ) table_val = 65535;
13520 
13521         }
13522 
13523         g_pSetDeviceGammaRamp( hDC, GammaTable );          // Set the ramp table
13524         ReleaseDC( NULL, hDC );                                     // Release the DC
13525 
13526         return 1;
13527     } else {
13528         if( g_pSavedGammaMap ) {
13529             HDC hDC = GetDC( NULL );                                       // Get the full screen DC
13530             g_pSetDeviceGammaRamp( hDC, g_pSavedGammaMap );          // Restore the saved ramp table
13531             ReleaseDC( NULL, hDC );                                             // Release the DC
13532         }
13533 
13534         if(brightness < 100 ){
13535             if( NULL == g_pcurtain )
13536                 InitScreenBrightness();
13537 
13538             if( g_pcurtain ) {
13539                 int sbrite = wxMax(1, brightness);
13540                 sbrite = wxMin(100, sbrite);
13541 
13542                 g_pcurtain->SetTransparent( ( 100 - sbrite ) * 256 / 100 );
13543             }
13544         }
13545         else{
13546             if( g_pcurtain ) {
13547                 g_pcurtain->Close();
13548                 g_pcurtain->Destroy();
13549                 g_pcurtain = NULL;
13550             }
13551         }
13552 
13553 
13554         return 1;
13555     }
13556 
13557 #endif
13558 
13559 #ifdef BRIGHT_XCALIB
13560 
13561     if(!g_brightness_init)
13562     {
13563         last_brightness = 100;
13564         g_brightness_init = true;
13565         temp_file_name = wxFileName::CreateTempFileName(_T(""));
13566         InitScreenBrightness();
13567     }
13568 
13569 #ifdef __OPCPN_USEICC__
13570     //  Create a dead simple temporary ICC profile file, with gamma ramps set as desired,
13571     //  and then activate this temporary profile using xcalib <filename>
13572     if(!CreateSimpleICCProfileFile ( ( const char * ) temp_file_name.fn_str(),
13573                                      brightness * r_gamma_mult,
13574                                      brightness * g_gamma_mult,
13575                                      brightness * b_gamma_mult ))
13576     {
13577         wxString cmd ( _T ( "xcalib " ) );
13578         cmd += temp_file_name;
13579 
13580         wxExecute ( cmd, wxEXEC_ASYNC );
13581     }
13582 
13583 #else
13584     //    Or, use "xcalib -co" to set overall contrast value
13585     //    This is not as nice, since the -co parameter wants to be a fraction of the current contrast,
13586     //    and values greater than 100 are not allowed.  As a result, increases of contrast must do a "-clear" step
13587     //    first, which produces objectionable flashing.
13588     if(brightness > last_brightness)
13589     {
13590         wxString cmd;
13591         cmd = _T("xcalib -clear");
13592         wxExecute(cmd, wxEXEC_ASYNC);
13593 
13594         ::wxMilliSleep(10);
13595 
13596         int brite_adj = wxMax(1, brightness);
13597         cmd.Printf(_T("xcalib -co %2d -a"), brite_adj);
13598         wxExecute(cmd, wxEXEC_ASYNC);
13599     }
13600     else
13601     {
13602         int brite_adj = wxMax(1, brightness);
13603         int factor = (brite_adj * 100) / last_brightness;
13604         factor = wxMax(1, factor);
13605         wxString cmd;
13606         cmd.Printf(_T("xcalib -co %2d -a"), factor);
13607         wxExecute(cmd, wxEXEC_ASYNC);
13608     }
13609 
13610 #endif
13611 
13612     last_brightness = brightness;
13613 
13614 #endif
13615 
13616     return 0;
13617 }
13618 
13619 #ifdef __OPCPN_USEICC__
13620 
13621 #define MLUT_TAG     0x6d4c5554L
13622 #define VCGT_TAG     0x76636774L
13623 
GetIntEndian(unsigned char * s)13624 int GetIntEndian(unsigned char *s)
13625 {
13626     int ret;
13627     unsigned char *p;
13628     int i;
13629 
13630     p = (unsigned char *)&ret;
13631 
13632     if(1)
13633         for(i=sizeof(int)-1; i>-1; --i)
13634             *p++ = s[i];
13635     else
13636         for(i=0; i<(int)sizeof(int); ++i)
13637             *p++ = s[i];
13638 
13639     return ret;
13640 }
13641 
GetShortEndian(unsigned char * s)13642 unsigned short GetShortEndian(unsigned char *s)
13643 {
13644     unsigned short ret;
13645     unsigned char *p;
13646     int i;
13647 
13648     p = (unsigned char *)&ret;
13649 
13650     if(1)
13651         for(i=sizeof(unsigned short)-1; i>-1; --i)
13652             *p++ = s[i];
13653     else
13654         for(i=0; i<(int)sizeof(unsigned short); ++i)
13655             *p++ = s[i];
13656 
13657     return ret;
13658 }
13659 
13660 //    Create a very simple Gamma correction file readable by xcalib
CreateSimpleICCProfileFile(const char * file_name,double co_red,double co_green,double co_blue)13661 int CreateSimpleICCProfileFile(const char *file_name, double co_red, double co_green, double co_blue)
13662 {
13663     FILE *fp;
13664 
13665     if(file_name)
13666     {
13667         fp = fopen(file_name, "wb");
13668         if(!fp)
13669             return -1; /* file can not be created */
13670     }
13671     else
13672         return -1; /* filename char pointer not valid */
13673 
13674     //    Write header
13675     char header[128];
13676     for(int i=0; i< 128; i++)
13677         header[i] = 0;
13678 
13679     fwrite(header, 128, 1, fp);
13680 
13681     //    Num tags
13682     int numTags0 = 1;
13683     int numTags = GetIntEndian((unsigned char *)&numTags0);
13684     fwrite(&numTags, 1, 4, fp);
13685 
13686     int tagName0 = VCGT_TAG;
13687     int tagName = GetIntEndian((unsigned char *)&tagName0);
13688     fwrite(&tagName, 1, 4, fp);
13689 
13690     int tagOffset0 = 128 + 4 * sizeof(int);
13691     int tagOffset = GetIntEndian((unsigned char *)&tagOffset0);
13692     fwrite(&tagOffset, 1, 4, fp);
13693 
13694     int tagSize0 = 1;
13695     int tagSize = GetIntEndian((unsigned char *)&tagSize0);
13696     fwrite(&tagSize, 1, 4, fp);
13697 
13698     fwrite(&tagName, 1, 4, fp);// another copy of tag
13699 
13700     fwrite(&tagName, 1, 4, fp);// dummy
13701 
13702     //  Table type
13703 
13704     /* VideoCardGammaTable (The simplest type) */
13705     int gammatype0 = 0;
13706     int gammatype = GetIntEndian((unsigned char *)&gammatype0);
13707     fwrite(&gammatype, 1, 4, fp);
13708 
13709     int numChannels0 = 3;
13710     unsigned short numChannels = GetShortEndian((unsigned char *)&numChannels0);
13711     fwrite(&numChannels, 1, 2, fp);
13712 
13713     int numEntries0 = 256;
13714     unsigned short numEntries = GetShortEndian((unsigned char *)&numEntries0);
13715     fwrite(&numEntries, 1, 2, fp);
13716 
13717     int entrySize0 = 1;
13718     unsigned short entrySize = GetShortEndian((unsigned char *)&entrySize0);
13719     fwrite(&entrySize, 1, 2, fp);
13720 
13721     unsigned char ramp[256];
13722 
13723     //    Red ramp
13724     for(int i=0; i< 256; i++)
13725         ramp[i] = i * co_red/100.;
13726     fwrite(ramp, 256, 1, fp);
13727 
13728     //    Green ramp
13729     for(int i=0; i< 256; i++)
13730         ramp[i] = i * co_green/100.;
13731     fwrite(ramp, 256, 1, fp);
13732 
13733     //    Blue ramp
13734     for(int i=0; i< 256; i++)
13735         ramp[i] = i * co_blue/100.;
13736     fwrite(ramp, 256, 1, fp);
13737 
13738     fclose(fp);
13739 
13740     return 0;
13741 }
13742 #endif // __OPCPN_USEICC__
13743 
13744