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