OpenCPN Partial API docs
Loading...
Searching...
No Matches
pluginmanager.cpp
1/***************************************************************************
2 *
3 * Project: OpenCPN
4 * Purpose: PlugIn Manager Object
5 * Author: David Register
6 *
7 ***************************************************************************
8 * Copyright (C) 2010 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#include <config.h>
27
28#ifdef __MINGW32__
29#undef IPV6STRICT // mingw FTBS fix: missing struct ip_mreq
30#include <windows.h>
31#endif
32
33#include <typeinfo>
34#if defined(__linux__) && !defined(__ANDROID__)
35#include <wordexp.h>
36#endif
37#include <wx/wx.h>
38#include <wx/dir.h>
39#include <wx/event.h>
40#include <wx/filename.h>
41#include <wx/aui/aui.h>
42#include <wx/platinfo.h>
43#include <wx/popupwin.h>
44#include <wx/progdlg.h>
45#include <wx/statline.h>
46#include <wx/tokenzr.h>
47#include <wx/tooltip.h>
48#include <wx/app.h>
49#include <wx/hashset.h>
50#include <wx/hashmap.h>
51#include <wx/jsonval.h>
52#include <wx/uri.h>
53#include <wx/zipstrm.h>
54#include <wx/zstream.h>
55#include <wx/tarstrm.h>
56#include <wx/textwrapper.h>
57#include <wx/app.h>
58
59#ifndef __WXMSW__
60#include <cxxabi.h>
61#endif // __WXMSW__
62
63
64#include <algorithm>
65#include <archive.h>
66#include <cstdio>
67#include <cstdio>
68#include <errno.h>
69#include <fcntl.h>
70#include <fstream>
71#include <iostream>
72#include <iostream>
73#include <memory>
74#include <set>
75#include <sstream>
76#include <stdint.h>
77#include <string>
78#include <unordered_map>
79
80#include <archive_entry.h>
81typedef __LA_INT64_T la_int64_t; // "older" libarchive versions support
82
83#ifdef USE_LIBELF
84#include <elf.h>
85#include <libelf.h>
86#include <gelf.h>
87#endif
88
89
90#include "ais_decoder.h"
91#include "ais.h"
92#include "ais_target_data.h"
93#include "canvasMenu.h"
94#include "catalog_handler.h"
95#include "cat_settings.h"
96#include "chartbase.h" // for ChartPlugInWrapper
97#include "chartdb.h"
98#include "chartdbs.h"
99#include "chcanv.h"
100#include "comm_navmsg_bus.h"
101#include "comm_vars.h"
102#include "config.h"
103#include "config_vars.h"
104#include "downloader.h"
105#include "download_mgr.h"
106#include "dychart.h"
107#include "FontMgr.h"
108#include "georef.h"
109#include "ocpn_pixel.h"
110#include "gshhs.h"
111#include "json_event.h"
112#include "logger.h"
113#include "multiplexer.h"
114#include "mygeom.h"
115#include "nav_object_database.h"
116#include "navutil.h"
117#include "navutil_base.h"
118#include "observable_confvar.h"
119#include "observable_globvar.h"
120#include "OCPN_AUIManager.h"
121#include "ocpndc.h"
122#include "ocpn_pixel.h"
123#include "OCPNPlatform.h"
124#include "OCPNRegion.h"
125#include "ocpn_utils.h"
126#include "options.h"
127#include "piano.h"
128#include "plugin_cache.h"
129#include "plugin_handler.h"
130#include "plugin_loader.h"
131#include "pluginmanager.h"
132#include "plugin_paths.h"
133#include "route.h"
134#include "routemanagerdialog.h"
135#include "routeman.h"
136#include "routeman_gui.h"
137#include "s52plib.h"
138#include "s52utils.h"
139#include "safe_mode.h"
140#include "semantic_vers.h"
141#include "SoundFactory.h"
142#include "SystemCmdSound.h"
143#include "styles.h"
144#include "toolbar.h"
145#include "track.h"
146#include "update_mgr.h"
147#include "waypointman_gui.h"
148#include "svg_utils.h"
149#include "ocpn_frame.h"
150#include "comm_drv_registry.h"
151#include "comm_drv_n0183_serial.h"
152#include "comm_drv_n0183_net.h"
153#include "comm_drv_registry.h"
154#include "comm_drv_n2k.h"
155#include "ocpn_app.h"
156
157#ifdef __OCPN__ANDROID__
158#include <dlfcn.h>
159#include "androidUTIL.h"
160#endif
161
162#ifdef ocpnUSE_GL
163#include "glChartCanvas.h"
164#endif
165
166#ifndef __WXMSW__
167#include <signal.h>
168#include <setjmp.h>
169
170struct sigaction sa_all_PIM;
171struct sigaction sa_all_PIM_previous;
172
173sigjmp_buf env_PIM; // the context saved by sigsetjmp();
174
175void catch_signals_PIM(int signo) {
176 switch (signo) {
177 case SIGSEGV:
178 siglongjmp(env_PIM, 1); // jump back to the setjmp() point
179 break;
180
181 default:
182 break;
183 }
184}
185
186#endif
187
188extern MyConfig *pConfig;
189extern AisDecoder *g_pAIS;
190extern OCPN_AUIManager *g_pauimgr;
191
192#if wxUSE_XLOCALE || !wxCHECK_VERSION(3, 0, 0)
193extern wxLocale *plocale_def_lang;
194#endif
195
196extern OCPNPlatform *g_Platform;
197extern ChartDB *ChartData;
198extern MyFrame *gFrame;
199extern ocpnStyle::StyleManager *g_StyleManager;
200extern options *g_pOptions;
201extern Multiplexer *g_pMUX;
202extern bool g_bShowChartBar;
203extern Routeman *g_pRouteMan;
204extern WayPointman *pWayPointMan;
205extern Select *pSelect;
206extern RouteManagerDialog *pRouteManagerDialog;
207extern RouteList *pRouteList;
208extern std::vector<Track*> g_TrackList;
209extern PlugInManager *g_pi_manager;
210extern s52plib *ps52plib;
211extern wxString ChartListFileName;
212extern bool g_boptionsactive;
213extern options *g_options;
214extern ColorScheme global_color_scheme;
215extern wxArrayString g_locale_catalog_array;
216extern int g_GUIScaleFactor;
217extern int g_ChartScaleFactor;
218extern wxString g_locale;
219extern bool g_btouch;
220extern ocpnFloatingToolbarDialog *g_MainToolbar;
221
222extern int g_chart_zoom_modifier_raster;
223extern int g_chart_zoom_modifier_vector;
224extern double g_display_size_mm;
225extern bool g_bopengl;
226
227extern ChartGroupArray *g_pGroupArray;
228extern unsigned int g_canvasConfig;
229
230extern wxString g_CmdSoundString;
231
232extern int g_iSDMMFormat;
233
234unsigned int gs_plib_flags;
235wxString g_lastPluginMessage;
236extern ChartCanvas *g_focusCanvas;
237extern ChartCanvas *g_overlayCanvas;
238extern bool g_bquiting;
239extern wxString g_catalog_channel;
240extern wxString g_catalog_custom_url;
241
242WX_DEFINE_ARRAY_PTR(ChartCanvas *, arrayofCanvasPtr);
243extern arrayofCanvasPtr g_canvasArray;
244
245const char *const LINUX_LOAD_PATH = "~/.local/lib:/usr/local/lib:/usr/lib";
246const char *const FLATPAK_LOAD_PATH = "~/.var/app/org.opencpn.OpenCPN/lib";
247
248void NotifySetupOptionsPlugin(const PlugInContainer *pic);
249
250enum { CurlThreadId = wxID_HIGHEST + 1 };
251
252#include <wx/listimpl.cpp>
253WX_DEFINE_LIST(Plugin_WaypointList);
254WX_DEFINE_LIST(Plugin_HyperlinkList);
255
256wxDEFINE_EVENT(EVT_N0183_PLUGIN, ObservedEvt);
257wxDEFINE_EVENT(EVT_SIGNALK, ObservedEvt);
258
259wxDECLARE_APP(MyApp);
260
261static void SendAisJsonMessage(std::shared_ptr<const AisTargetData> pTarget) {
262 // Only send messages if someone is listening...
263 if (!g_pi_manager->GetJSONMessageTargetCount()) return;
264
265 // Do JSON message to all Plugin to inform of target
266 wxJSONValue jMsg;
267
268 wxLongLong t = ::wxGetLocalTimeMillis();
269
270 jMsg[wxS("Source")] = wxS("AisDecoder");
271 jMsg[wxT("Type")] = wxT("Information");
272 jMsg[wxT("Msg")] = wxS("AIS Target");
273 jMsg[wxT("MsgId")] = t.GetValue();
274 jMsg[wxS("lat")] = pTarget->Lat;
275 jMsg[wxS("lon")] = pTarget->Lon;
276 jMsg[wxS("sog")] = pTarget->SOG;
277 jMsg[wxS("cog")] = pTarget->COG;
278 jMsg[wxS("hdg")] = pTarget->HDG;
279 jMsg[wxS("mmsi")] = pTarget->MMSI;
280 jMsg[wxS("class")] = pTarget->Class;
281 jMsg[wxS("ownship")] = pTarget->b_OwnShip;
282 jMsg[wxS("active")] = pTarget->b_active;
283 jMsg[wxS("lost")] = pTarget->b_lost;
284 wxString l_ShipName = wxString::FromUTF8(pTarget->ShipName);
285 for (size_t i = 0; i < l_ShipName.Len(); i++) {
286 if (l_ShipName.GetChar(i) == '@') l_ShipName.SetChar(i, '\n');
287 }
288 jMsg[wxS("shipname")] = l_ShipName;
289 wxString l_CallSign = wxString::FromUTF8(pTarget->CallSign);
290 for (size_t i = 0; i < l_CallSign.Len(); i++) {
291 if (l_CallSign.GetChar(i) == '@') l_CallSign.SetChar(i, '\n');
292 }
293 jMsg[wxS("callsign")] = l_CallSign;
294 jMsg[wxS("removed")] = pTarget->b_removed;
295 g_pi_manager->SendJSONMessageToAllPlugins(wxT("AIS"), jMsg);
296}
297
298
304public:
305
306 void message(const std::string& message) {
307 if (m_defer)
308 m_deferred_messages.push_back(message);
309 else
310 show_msg(message);
311 }
312
313 void show_deferred_messages() {
314 for (auto m : m_deferred_messages) show_msg(m);
315 m_defer = false;
316 }
317
318 BlacklistUI() : m_defer(true) {};
319
320private:
321 void show_msg(wxString msg) {
322 OCPNMessageBox(NULL, msg, wxString(_("OpenCPN Info")),
323 wxICON_INFORMATION | wxOK, 10); // 10 second timeout
324 }
325
326 bool m_defer; // defer dialogs until setup completed
327 std::vector<wxString> m_deferred_messages;
328};
329
330class PanelHardBreakWrapper : public wxTextWrapper {
331public:
332 PanelHardBreakWrapper(wxWindow *win, const wxString &text, int widthMax) {
333 m_lineCount = 0;
334 Wrap(win, text, widthMax);
335 }
336 wxString const &GetWrapped() const { return m_wrapped; }
337 int const GetLineCount() const { return m_lineCount; }
338 wxArrayString GetLineArray() { return m_array; }
339
340protected:
341 virtual void OnOutputLine(const wxString &line) {
342 m_wrapped += line;
343 m_array.Add(line);
344 }
345 virtual void OnNewLine() {
346 m_wrapped += '\n';
347 m_lineCount++;
348 }
349
350private:
351 wxString m_wrapped;
352 int m_lineCount;
353 wxArrayString m_array;
354};
355
356static const std::vector<std::string> SYSTEM_PLUGINS = {
357 "chartdownloader", "wmm", "dashboard", "grib"};
358
360 template <typename T>
361 std::size_t operator()(T t) const {
362 return static_cast<std::size_t>(t);
363 }
364};
365
366wxString message_by_status(PluginStatus stat) {
367 switch (stat) {
368 case PluginStatus::System:
369 return _("Plugin is a standard system plugin");
370 case PluginStatus::Managed:
371 return _("Plugin is managed by OpenCPN");
372 case PluginStatus::Unmanaged:
373 return _("Plugin is not managed by OpenCPN");
374 case PluginStatus::Ghost:
375 return ("");
376 case PluginStatus::Unknown:
377 return _("Plugin status unknown");
378 case PluginStatus::LegacyUpdateAvailable:
379 return _("Update to managed Plugin is available");
380 case PluginStatus::ManagedInstallAvailable:
381 return _("New managed Plugin installation available");
382 case PluginStatus::ManagedInstalledUpdateAvailable:
383 return _("Update to installed Plugin is available");
384 case PluginStatus::ManagedInstalledCurrentVersion:
385 return _("Plugin is latest available");
386 case PluginStatus::ManagedInstalledDowngradeAvailable:
387 return ("");
388 case PluginStatus::PendingListRemoval:
389 return ("");
390 default:
391 return ("");
392 }
393}
394
395static std::unordered_map<PluginStatus, const char *, EnumClassHash>
396 icon_by_status(
397 {{PluginStatus::System, "emblem-system.svg"},
398 {PluginStatus::Managed, "emblem-default.svg"},
399 {PluginStatus::Unmanaged, "emblem-unmanaged.svg"},
400 {PluginStatus::Ghost, "ghost.svg"},
401 {PluginStatus::Unknown, "emblem-unmanaged.svg"},
402 {PluginStatus::LegacyUpdateAvailable, "emblem-legacy-update.svg"},
403 {PluginStatus::ManagedInstallAvailable, "emblem-default.svg"},
404 {PluginStatus::ManagedInstalledUpdateAvailable,
405 "emblem-legacy-update.svg"},
406 {PluginStatus::ManagedInstalledCurrentVersion, "emblem-default.svg"},
407 {PluginStatus::ManagedInstalledDowngradeAvailable,
408 "emblem-default.svg"},
409 {PluginStatus::PendingListRemoval, "emblem-default.svg"}
410
411 });
412
413static std::unordered_map<PluginStatus, const char *, EnumClassHash>
414 literalstatus_by_status(
415 {{PluginStatus::System, "System"},
416 {PluginStatus::Managed, "Managed"},
417 {PluginStatus::Unmanaged, "Unmanaged"},
418 {PluginStatus::Ghost, "Ghost"},
419 {PluginStatus::Unknown, "Unknown"},
420 {PluginStatus::LegacyUpdateAvailable, "LegacyUpdateAvailable"},
421 {PluginStatus::ManagedInstallAvailable, "ManagedInstallAvailable"},
422 {PluginStatus::ManagedInstalledUpdateAvailable,
423 "ManagedInstalledUpdateAvailable"},
424 {PluginStatus::ManagedInstalledCurrentVersion,
425 "ManagedInstalledCurrentVersion"},
426 {PluginStatus::ManagedInstalledDowngradeAvailable,
427 "ManagedInstalledDowngradeAvailable"},
428 {PluginStatus::PendingListRemoval, "PendingListRemoval"}
429
430 });
431
436static std::vector<PluginMetadata> getCompatiblePlugins() {
438 struct metadata_compare {
439 bool operator()(const PluginMetadata &lhs,
440 const PluginMetadata &rhs) const {
441 return lhs.key() < rhs.key();
442 }
443 };
444
445 std::vector<PluginMetadata> returnArray;
446
447 std::set<PluginMetadata, metadata_compare> unique_plugins;
448 for (auto plugin : PluginHandler::getInstance()->getAvailable()) {
449 unique_plugins.insert(plugin);
450 }
451 for (auto plugin : unique_plugins) {
452 if (PluginHandler::isCompatible(plugin)) {
453 returnArray.push_back(plugin);
454 }
455 }
456 return returnArray;
457}
458
459static SemanticVersion metadata_version(const PluginMetadata pm) {
460 return SemanticVersion::parse(pm.name);
461}
462
463// Get installed version from manifest for given plugin. For
464// older plugins this contains more detailed info then the
465// plugin API. From API level 117 the API should contain the
466// same info.
467//
468// TODO: Get version from API for api level 117+
469SemanticVersion getInstalledVersion(const std::string name) {
470 std::string installed;
471 std::string path = PluginHandler::versionPath(name);
472 if (path == "" || !wxFileName::IsFileReadable(path)) {
473 return SemanticVersion(-1, -1);
474 }
475 std::ifstream stream;
476 stream.open(path, std::ifstream::in);
477 stream >> installed;
478 return SemanticVersion::parse(installed);
479}
480
485static std::vector<PluginMetadata> getUpdates(const char *name) {
486 auto updates = getCompatiblePlugins();
487 updates.erase(
488 std::remove_if(updates.begin(), updates.end(),
489 [&](const PluginMetadata m) { return m.name != name; }),
490 updates.end());
491
492 auto inst_vers = getInstalledVersion(name);
493 if (inst_vers.major == -1) {
494 return updates;
495 }
496
497 // Drop already installed plugin, it has its own update options.
498 updates.erase(std::remove_if(updates.begin(), updates.end(),
499 [&](const PluginMetadata m) {
500 return metadata_version(m) == inst_vers;
501 }),
502 updates.end());
503 return updates;
504}
505
510static int count_files_in_dirs(const char *filename,
511 const std::vector<std::string> dirs) {
512 int count = 0;
513 for (auto dir : dirs) {
514 const std::string sep(1, wxFileName::GetPathSeparator());
515 auto path = dir + sep + filename;
516 if (ocpn::exists(path)) {
517 count += 1;
518 }
519 }
520 return count;
521}
522
523static PluginMetadata getLatestUpdate() {
524 auto updates = getCompatiblePlugins();
525 if (updates.size() == 0) {
526 PluginMetadata metadata;
527 return metadata;
528 }
529 std::sort(updates.begin(), updates.end(),
530 [](PluginMetadata m1, PluginMetadata m2) {
531 return !(m1.version < m2.version);
532 });
533 return updates[0];
534}
535
537static void gui_uninstall(PlugInContainer *pic, const char *plugin) {
538 g_Platform->ShowBusySpinner();
539 PluginLoader::getInstance()->DeactivatePlugIn(pic);
540 pic->m_bEnabled = false;
541 PluginLoader::getInstance()->UpdatePlugIns();
542
543 wxLogMessage("Uninstalling %s", plugin);
544 PluginHandler::getInstance()->uninstall(plugin);
545 PluginLoader::getInstance()->UpdatePlugIns();
546 g_Platform->HideBusySpinner();
547}
548
549static bool LoadAllPlugIns(bool load_enabled) {
550 g_Platform->ShowBusySpinner();
551 bool b = PluginLoader::getInstance()->LoadAllPlugIns(load_enabled);
552 g_Platform->HideBusySpinner();
553 return b;
554}
555
556static void run_update_dialog(PluginListPanel *parent, PlugInContainer *pic,
557 bool uninstall, const char *name = 0,
558 bool b_forceEnable = false) {
559 wxString pluginName = pic->m_common_name;
560 const char *plugin = name == 0 ? pic->m_common_name.mb_str().data() : name;
561 auto updates = getUpdates(plugin);
562 auto parent_dlg = dynamic_cast<wxScrolledWindow *>(parent);
563 wxASSERT(parent_dlg != 0);
564 UpdateDialog dialog(parent_dlg, updates);
565 auto status = dialog.ShowModal();
566 status = dialog.GetReturnCode();
567 if (status != wxID_OK) {
568 return;
569 }
570
571 auto update = dialog.GetUpdate();
572 if (!g_pi_manager->CheckBlacklistedPlugin(update)) {
573 return;
574 }
575
576 wxLogMessage("Installing %s", update.name.c_str());
577
578 auto pluginHandler = PluginHandler::getInstance();
579 auto path = ocpn::lookup_tarball(update.tarball_url.c_str());
580 if (uninstall && path != "") {
581 gui_uninstall(pic, update.name.c_str());
582 }
583 bool cacheResult = pluginHandler->installPluginFromCache(update);
584
585 if (!cacheResult) {
586 g_Platform->ShowBusySpinner(); // Will be cancelled in downloader->run()
587 wxYield();
588
589 auto downloader = new GuiDownloader(parent_dlg, update);
590 std::string tempTarballPath = downloader->run(parent_dlg, uninstall);
591
592 if (!tempTarballPath.size()) // Error, dialog already presented
593 return;
594
595 // Provisional error check
596 bool bOK = true;
597 std::string manifestPath = PluginHandler::fileListPath(update.name);
598 if (!isRegularFile(manifestPath.c_str())) {
599 wxLogMessage("Installation of %s failed", update.name.c_str());
600 PluginHandler::cleanupFiles(manifestPath, update.name);
601 bOK = false;
602 }
603
604 // On successful installation, copy the temp tarball to the local cache
605 if (bOK) {
606 wxLogMessage("Installation of %s successful", update.name.c_str());
607 wxURI uri(wxString(update.tarball_url.c_str()));
608 wxFileName fn(uri.GetPath());
609 std::string basename = fn.GetFullName().ToStdString();
610
611 if (ocpn::store_tarball(tempTarballPath.c_str(), basename.c_str())) {
612 wxLogDebug("Copied %s to local cache at %s", tempTarballPath.c_str(),
613 basename);
614 remove(tempTarballPath.c_str());
615 }
616 }
617 }
618
619 // Check the library compatibility of the subject plugin
620 // Find the plugin library file, looking for "_pi.{dll/so/dylib file}
621#ifdef __WXMSW__
622 wxString pispec = _T("_pi.dll");
623#elif defined(__WXOSX__)
624 wxString pispec = _T("_pi.dylib");
625#else
626 wxString pispec = _T("_pi.so");
627#endif
628
629 std::string manifestPath = PluginHandler::fileListPath(update.name);
630 wxTextFile manifest_file(manifestPath);
631 wxString pluginFile;
632 if (manifest_file.Open()) {
633 wxString val;
634 for (wxString str = manifest_file.GetFirstLine(); !manifest_file.Eof();
635 str = manifest_file.GetNextLine()) {
636 if (str.Contains(pispec)) {
637 if (getenv("OCPN_KEEP_PLUGINS")) {
638 // Undocumented debug hook
639 continue;
640 }
641 auto loader = PluginLoader::getInstance();
642 if (!loader->CheckPluginCompatibility(str)) {
643 wxString msg =
644 _("The plugin is not compatible with this version of OpenCPN, "
645 "and will be uninstalled.");
646 OCPNMessageBox(NULL, msg, wxString(_("OpenCPN Info")),
647 wxICON_INFORMATION | wxOK, 10);
648
649 PluginHandler::cleanupFiles(manifestPath, update.name);
650 } else {
651 pluginFile = str;
652 }
653 break;
654 }
655 }
656 }
657
658 if (b_forceEnable && pluginFile.Length()) {
659 wxString config_section = (_T ( "/PlugIns/" ));
660 wxFileName fn(pluginFile);
661 config_section += fn.GetFullName();
662 pConfig->SetPath(config_section);
663 pConfig->Write(_T ( "bEnabled" ), true);
664 }
665
666 // Reload all plugins, which will bring in the action results.
667 LoadAllPlugIns(false);
668
669 // Check to see if this plugin needs an options instance reload
670 if (g_options) {
671 bool b_newOptions = false;
672 auto loader = PluginLoader::getInstance();
673 for (unsigned int i = 0; i < loader->GetPlugInArray()->GetCount();
674 i++) {
675 PlugInContainer *pic = loader->GetPlugInArray()->Item(i);
676 wxString cname = pic->m_common_name;
677 if (pic->m_bInitState && (pluginName == cname)) {
678 if ((pic->m_cap_flag & INSTALLS_TOOLBOX_PAGE)) {
679 g_options->SetNeedNew(true);
680
681 NotifySetupOptionsPlugin(pic);
682 }
683 }
684 }
685 }
686
687 parent->ReloadPluginPanels();
688 // wxString name(plugin);
689 // g_pi_manager->GetListPanelPtr()->SelectByName(name);
690}
691
692// Some static helper funtions
693// Scope is local to this module
694
695PlugIn_ViewPort CreatePlugInViewport(const ViewPort &vp) {
696 // Create a PlugIn Viewport
697 ViewPort tvp = vp;
698 PlugIn_ViewPort pivp;
699
700 pivp.clat = tvp.clat; // center point
701 pivp.clon = tvp.clon;
702 pivp.view_scale_ppm = tvp.view_scale_ppm;
703 pivp.skew = tvp.skew;
704 pivp.rotation = tvp.rotation;
705 pivp.chart_scale = tvp.chart_scale;
706 pivp.pix_width = tvp.pix_width;
707 pivp.pix_height = tvp.pix_height;
708 pivp.rv_rect = tvp.rv_rect;
709 pivp.b_quilt = tvp.b_quilt;
710 pivp.m_projection_type = tvp.m_projection_type;
711
712 pivp.lat_min = tvp.GetBBox().GetMinLat();
713 pivp.lat_max = tvp.GetBBox().GetMaxLat();
714 pivp.lon_min = tvp.GetBBox().GetMinLon();
715 pivp.lon_max = tvp.GetBBox().GetMaxLon();
716
717 pivp.bValid = tvp.IsValid(); // This VP is valid
718
719 return pivp;
720}
721
722ViewPort CreateCompatibleViewport(const PlugIn_ViewPort &pivp) {
723 // Create a system ViewPort
724 ViewPort vp;
725
726 vp.clat = pivp.clat; // center point
727 vp.clon = pivp.clon;
728 vp.view_scale_ppm = pivp.view_scale_ppm;
729 vp.skew = pivp.skew;
730 vp.rotation = pivp.rotation;
731 vp.chart_scale = pivp.chart_scale;
732 vp.pix_width = pivp.pix_width;
733 vp.pix_height = pivp.pix_height;
734 vp.rv_rect = pivp.rv_rect;
735 vp.b_quilt = pivp.b_quilt;
736 vp.m_projection_type = pivp.m_projection_type;
737
738 if (gFrame->GetPrimaryCanvas())
739 vp.ref_scale = gFrame->GetPrimaryCanvas()->GetVP().ref_scale;
740 else
741 vp.ref_scale = vp.chart_scale;
742
743 vp.SetBoxes();
744 vp.Validate(); // This VP is valid
745
746 return vp;
747}
748
749class pluginUtilHandler : public wxEvtHandler {
750public:
753
754 void OnPluginUtilAction(wxCommandEvent &event);
755
756 DECLARE_EVENT_TABLE()
757};
758
759BEGIN_EVENT_TABLE(pluginUtilHandler, wxEvtHandler)
760EVT_BUTTON(ID_CMD_BUTTON_PERFORM_ACTION, pluginUtilHandler::OnPluginUtilAction)
761END_EVENT_TABLE()
762
764
765void pluginUtilHandler::OnPluginUtilAction(wxCommandEvent &event) {
766 auto panel = static_cast<PluginPanel *>(event.GetClientData());
767 PluginListPanel *plugin_list_panel =
768 dynamic_cast<PluginListPanel *>(panel->GetParent());
769 wxASSERT(plugin_list_panel != 0);
770
771 auto actionPIC = panel->GetPlugin();
772 wxString name = actionPIC->m_common_name;
773
774 // Perform the indicated action according to the verb...
775 switch (panel->GetAction()) {
776 case ActionVerb::UPGRADE_TO_MANAGED_VERSION: {
777 auto loader = PluginLoader::getInstance();
778
779 // capture the plugin name
780 std::string pluginName = actionPIC->m_ManagedMetadata.name;
781
782 wxLogMessage("Installing managed plugin: %s", pluginName.c_str());
783 auto downloader =
784 new GuiDownloader(plugin_list_panel, actionPIC->m_ManagedMetadata);
785 downloader->run(plugin_list_panel, false);
786
787 // Provisional error check
788 std::string manifestPath = PluginHandler::fileListPath(pluginName);
789 if (isRegularFile(manifestPath.c_str())) {
790 // dynamically deactivate the legacy plugin, making way for the upgrade.
791 for (unsigned i = 0; i < loader->GetPlugInArray()->GetCount();
792 i += 1) {
793 if (actionPIC->m_ManagedMetadata.name ==
794 loader->GetPlugInArray()
795 ->Item(i)
796 ->m_common_name.ToStdString()) {
797 loader->UnLoadPlugIn(i);
798 break;
799 }
800 }
801
802 // Reload all plugins, which will bring in the new, managed version.
803 LoadAllPlugIns(false);
804 } else {
805 PluginHandler::cleanupFiles(manifestPath,
806 actionPIC->m_ManagedMetadata.name);
807 }
808 plugin_list_panel->ReloadPluginPanels();
809 plugin_list_panel->SelectByName(name);
810
811 break;
812 }
813
814 case ActionVerb::UPGRADE_INSTALLED_MANAGED_VERSION:
815 case ActionVerb::REINSTALL_MANAGED_VERSION:
816 case ActionVerb::DOWNGRADE_INSTALLED_MANAGED_VERSION: {
817 // Grab a copy of the managed metadata
818 auto metaSave = actionPIC->m_ManagedMetadata;
819 run_update_dialog(plugin_list_panel, actionPIC, true,
820 metaSave.name.c_str());
821 break;
822 }
823
824 case ActionVerb::INSTALL_MANAGED_VERSION: {
825 wxLogMessage("Installing new managed plugin.");
826 run_update_dialog(plugin_list_panel, actionPIC, false);
827 break;
828 }
829
830 case ActionVerb::UNINSTALL_MANAGED_VERSION: {
831 PluginLoader::getInstance()->DeactivatePlugIn(actionPIC);
832
833 // Capture the confirmation dialog contents before the plugin goes away
834 wxString message;
835 message.Printf("%s %s\n", actionPIC->m_ManagedMetadata.name.c_str(),
836 actionPIC->m_ManagedMetadata.version.c_str());
837 message += _("successfully un-installed");
838
839 wxLogMessage("Uninstalling %s",
840 actionPIC->m_ManagedMetadata.name.c_str());
841
842 PluginHandler::getInstance()->uninstall(
843 actionPIC->m_ManagedMetadata.name);
844
845 // Reload all plugins, which will bring in the action results.
846 auto loader = PluginLoader::getInstance();
847 LoadAllPlugIns(false);
848 plugin_list_panel->ReloadPluginPanels();
849
850 OCPNMessageBox(gFrame, message, _("Un-Installation complete"),
851 wxICON_INFORMATION | wxOK);
852
853 break;
854 }
855
856 case ActionVerb::NOP:
857 default:
858 break;
859 }
860}
861
862#if 0
871class StatusIconPanel: public wxPanel
872{
873 public:
874 StatusIconPanel(wxWindow* parent, const PlugInContainer* pic)
875 :wxPanel(parent)
876 {
877 m_parent = wxDynamicCast(parent, PluginPanel);
878
879 m_stat = pic->m_pluginStatus; //::Unknown;
880 SetToolTip(message_by_status[m_stat]);
881 m_icon_name = icon_by_status[m_stat];
882
883 m_penWidthUnselected = g_Platform->GetDisplayDPmm() * .25;
884 m_penWidthSelected = g_Platform->GetDisplayDPmm() * .5;
885
886 //SetBackgroundColour(GetGlobalColor(_T("DILG0")));
887 //auto minsize = wxSize(GetCharWidth() * 5, GetCharWidth() * 10);
888 //SetSize(minsize);
889 SetMinSize(wxSize(GetCharWidth() * 5, -1));
890 Bind(wxEVT_PAINT, &StatusIconPanel::OnPaint, this);
891 Bind(wxEVT_LEFT_DOWN, &StatusIconPanel::OnIconSelected, this);
892
893
894 }
895
896 void OnPaint(wxPaintEvent& event)
897 {
898 auto size = GetClientSize();
899 int minsize = GetCharWidth() * 3;
900 auto offset = minsize / 4;
901
902 LoadIcon(m_icon_name.c_str(), m_bitmap, wxMax(1, minsize));
903 wxPaintDC dc(this);
904 if (!m_bitmap.IsOk()) {
905 wxLogMessage("StatusPluginPanel: bitmap is not OK!");
906 return;
907 }
908
909 int penWidth = m_penWidthUnselected;
910 wxColour border = GetDialogColor(DLG_UNSELECTED_ACCENT);
911
912 if(m_parent->GetSelected()){
913 penWidth = m_penWidthSelected;
914 border = GetDialogColor(DLG_SELECTED_ACCENT);
915 }
916
917 wxBrush b(m_parent->GetBackgroundColour(), wxSOLID);
918 dc.SetBrush(b);
919 dc.SetPen( wxPen(border, penWidth) );
920
921 dc.DrawRoundedRectangle(-20, 5, 20 + GetSize().x-10, GetSize().y-10, 5);
922 //dc.DrawRectangle(0, 0, GetSize().x, GetSize().y);
923 dc.DrawBitmap(m_bitmap, offset * 3 / 4, offset*3, true);
924
925
926 //dc.DrawText(_T("PluginStatus"), 0, 0);
927 //dc.DrawText(literalstatus_by_status[m_stat], 4 * GetCharWidth(), GetCharHeight());
928 }
929
930 void SetStatus(PluginStatus stat)
931 {
932 m_stat = stat;
933 SetToolTip(message_by_status[m_stat]);
934 m_icon_name = icon_by_status[m_stat];
935 Refresh();
936 }
937
938 void OnIconSelected( wxMouseEvent &event )
939 {
940 if(m_parent){
941 m_parent->OnPluginSelected(event);
942 }
943 }
944
945
946 protected:
947 wxBitmap m_bitmap;
948 std::string m_icon_name;
949 PluginStatus m_stat;
950 PluginPanel *m_parent;
951 int m_penWidthUnselected;
952 int m_penWidthSelected;
953
954 void LoadIcon(const char* icon_name, wxBitmap& bitmap, int size=32)
955 {
956 wxFileName path(g_Platform->GetSharedDataDir(), icon_name);
957 path.AppendDir("uidata");
958 path.AppendDir("traditional");
959 bool ok = false;
960
961
962 if (path.IsFileReadable()) {
963 bitmap = LoadSVG(path.GetFullPath(), size, size);
964 ok = bitmap.IsOk();
965 }
966
967 if (!ok) {
968 auto style = g_StyleManager->GetCurrentStyle();
969 bitmap = wxBitmap(style->GetIcon( _T("default_pi"), size, size));
970 wxLogMessage("Icon: %s not found.", path.GetFullPath());
971 }
972 }
973};
974#endif
975
976//------------------------------------------------------------------------------
977// NMEA Event Implementation
978// PlugIn Messaging scheme Event
979//------------------------------------------------------------------------------
980
981const wxEventType wxEVT_OCPN_MSG = wxNewEventType();
982
983OCPN_MsgEvent::OCPN_MsgEvent(wxEventType commandType, int id)
984 : wxEvent(id, commandType) {}
985
986OCPN_MsgEvent::~OCPN_MsgEvent() {}
987
988wxEvent *OCPN_MsgEvent::Clone() const {
989 OCPN_MsgEvent *newevent = new OCPN_MsgEvent(*this);
990 newevent->m_MessageID =
991 this->m_MessageID
992 .c_str(); // this enforces a deep copy of the string data
993 newevent->m_MessageText = this->m_MessageText.c_str();
994 return newevent;
995}
996
997//------------------------------------------------------------------------------------------------
998//
999// The PlugInToolbarToolContainer Implementation
1000//
1001//------------------------------------------------------------------------------------------------
1002PlugInToolbarToolContainer::PlugInToolbarToolContainer() {
1003 bitmap_dusk = NULL;
1004 bitmap_night = NULL;
1005 bitmap_day = NULL;
1006 bitmap_Rollover_day = NULL;
1007 bitmap_Rollover_dusk = NULL;
1008 bitmap_Rollover_night = NULL;
1009}
1010
1011PlugInToolbarToolContainer::~PlugInToolbarToolContainer() {
1012 delete bitmap_dusk;
1013 delete bitmap_night;
1014 delete bitmap_day;
1015 delete bitmap_Rollover_day;
1016 delete bitmap_Rollover_dusk;
1017 delete bitmap_Rollover_night;
1018}
1019
1020//-----------------------------------------------------------------------------------------------------
1021//
1022// The PlugIn Manager Implementation
1023//
1024//-----------------------------------------------------------------------------------------------------
1025PlugInManager *s_ppim;
1026
1027BEGIN_EVENT_TABLE(PlugInManager, wxEvtHandler)
1028#if !defined(__OCPN__ANDROID__) && defined(OCPN_USE_CURL)
1029EVT_CURL_END_PERFORM(CurlThreadId, PlugInManager::OnEndPerformCurlDownload)
1030EVT_CURL_DOWNLOAD(CurlThreadId, PlugInManager::OnCurlDownload)
1031#endif
1032END_EVENT_TABLE()
1033
1034static void event_message_box(const wxString& msg) {
1035 OCPNMessageBox(NULL, msg, wxString(_("OpenCPN Info")),
1036 wxICON_INFORMATION | wxOK, 0); // no timeout
1037}
1038
1039static void event_message_box(const wxString& msg, wxCommandEvent ev) {
1040 auto s = wxString::Format(msg, ev.GetString());
1041 event_message_box(s);
1042}
1043
1044static void OnLoadPlugin(const PlugInContainer* pic) {
1045 if (g_options) {
1046 if ((pic->m_cap_flag & INSTALLS_TOOLBOX_PAGE)) {
1047 if (!pic->m_bToolboxPanel) NotifySetupOptionsPlugin(pic);
1048 }
1049 }
1050}
1051
1052
1053PlugInManager::PlugInManager(MyFrame *parent) {
1054#if !defined(__OCPN__ANDROID__) && defined(OCPN_USE_CURL)
1055 m_pCurlThread = NULL;
1056 m_pCurl = 0;
1057#endif
1058 pParent = parent;
1059 s_ppim = this;
1060
1061 MyFrame *pFrame = GetParentFrame();
1062 if (pFrame) {
1063 m_plugin_menu_item_id_next = CanvasMenuHandler::GetNextContextMenuId();
1064 m_plugin_tool_id_next = pFrame->GetNextToolbarToolId();
1065 }
1066
1067#ifdef __OCPN__ANDROID__
1068 // Due to the oddball mixed static/dynamic linking model used in the Android
1069 // architecture, all classes used in PlugIns must be present in the core,
1070 // even if stubs.
1071 //
1072 // Here is where we do that....
1073 if (pFrame) {
1074 wxArrayString as;
1075 as.Add(_T("Item0"));
1076 wxRadioBox *box =
1077 new wxRadioBox(pFrame, -1, _T(""), wxPoint(0, 0), wxSize(-1, -1), as);
1078 delete box;
1079 }
1080
1081#endif
1082
1083#if !defined(__OCPN__ANDROID__) && defined (OCPN_USE_CURL)
1084 wxCurlBase::Init();
1085 m_last_online = false;
1086 m_last_online_chk = -1;
1087#endif
1088
1089 m_utilHandler = new pluginUtilHandler();
1090 m_listPanel = NULL;
1091 m_blacklist = blacklist_factory();
1092 m_blacklist_ui = std::unique_ptr<BlacklistUI>(new BlacklistUI());
1093
1094 wxDEFINE_EVENT(EVT_JSON_TO_ALL_PLUGINS, ObservedEvt);
1095 evt_json_to_all_plugins_listener.Listen(g_pRouteMan->json_msg, this,
1096 EVT_JSON_TO_ALL_PLUGINS);
1097 Bind(EVT_JSON_TO_ALL_PLUGINS, [&](ObservedEvt& ev) {
1098 auto json = std::static_pointer_cast<const wxJSONValue>(ev.GetSharedPtr());
1099 SendJSONMessageToAllPlugins(ev.GetString(), *json); });
1100
1101 wxDEFINE_EVENT(EVT_LEGINFO_TO_ALL_PLUGINS, ObservedEvt);
1102 evt_routeman_leginfo_listener.Listen(g_pRouteMan->json_leg_info, this,
1103 EVT_LEGINFO_TO_ALL_PLUGINS);
1104 Bind(EVT_LEGINFO_TO_ALL_PLUGINS, [&](ObservedEvt& ev) {
1105 auto ptr = UnpackEvtPointer<ActiveLegDat>(ev);
1106 SendActiveLegInfoToAllPlugIns(ptr.get()); });
1107
1108 HandlePluginLoaderEvents();
1109 InitCommListeners();
1110}
1111PlugInManager::~PlugInManager() {
1112#if !defined(__OCPN__ANDROID__) && defined (OCPN_USE_CURL)
1113 wxCurlBase::Shutdown();
1114#endif
1115}
1116
1117void PlugInManager::InitCommListeners(void) {
1118
1119 // Initialize the comm listener to support
1120 // void SetNMEASentence(wxString &sentence);
1121
1122 auto& msgbus = NavMsgBus::GetInstance();
1123
1124 m_listener_N0183_all.Listen(Nmea0183Msg::MessageKey("ALL"), this,
1125 EVT_N0183_PLUGIN);
1126 Bind(EVT_N0183_PLUGIN, [&](ObservedEvt ev) {
1127 auto ptr = ev.GetSharedPtr();
1128 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
1129 HandleN0183(n0183_msg); });
1130
1131 SignalkMsg sk_msg;
1132 m_listener_SignalK.Listen(sk_msg, this, EVT_SIGNALK);
1133
1134 Bind(EVT_SIGNALK, [&](ObservedEvt ev) {
1135 HandleSignalK(UnpackEvtPointer<SignalkMsg>(ev));
1136 });
1137
1138}
1139
1140void PlugInManager::HandleN0183( std::shared_ptr <const Nmea0183Msg> n0183_msg ) {
1141
1142 std::string s = n0183_msg->payload;
1143 wxString sentence(s.c_str());
1144
1145 if (s[0] == '$') {
1146 const auto& drivers = CommDriverRegistry::GetInstance().GetDrivers();
1147 auto target_driver = FindDriver(drivers, n0183_msg->source->iface);
1148
1149 bool bpass_input_filter = true;
1150
1151 // Get the params for the driver sending this message
1152 ConnectionParams params;
1153 auto drv_serial =
1154 std::dynamic_pointer_cast<CommDriverN0183Serial>(target_driver);
1155 if (drv_serial) {
1156 params = drv_serial->GetParams();
1157 } else {
1158 auto drv_net = std::dynamic_pointer_cast<CommDriverN0183Net>(target_driver);
1159 if (drv_net) {
1160 params = drv_net->GetParams();
1161 }
1162 }
1163
1164 // Check to see if the message passes the source's input filter
1165 bpass_input_filter = params.SentencePassesFilter(sentence,
1166 FILTER_INPUT);
1167
1168 if (bpass_input_filter)
1169 SendNMEASentenceToAllPlugIns(sentence);
1170 }
1171 else if (s[0] == '!'){
1172 //printf("AIS to all: %s", s.c_str());
1173 SendAISSentenceToAllPlugIns(sentence);
1174 }
1175}
1176
1177void PlugInManager::HandleSignalK(std::shared_ptr<const SignalkMsg> sK_msg){
1178 wxJSONReader jsonReader;
1179 wxJSONValue root;
1180
1181 std::string msgTerminated = sK_msg->raw_message;;
1182
1183 int errors = jsonReader.Parse(msgTerminated, &root);
1184 if (errors == 0)
1185 SendJSONMessageToAllPlugins(wxT("OCPN_CORE_SIGNALK"), root);
1186}
1187
1188
1194wxDEFINE_EVENT(EVT_PLUGMGR_AIS_MSG, ObservedEvt);
1195wxDEFINE_EVENT(EVT_PLUGMGR_ROUTEMAN_MSG, ObservedEvt);
1196wxDEFINE_EVENT(EVT_BLACKLISTED_PLUGIN, wxCommandEvent);
1197wxDEFINE_EVENT(EVT_DEACTIVATE_PLUGIN, wxCommandEvent);
1198wxDEFINE_EVENT(EVT_INCOMPATIBLE_PLUGIN, wxCommandEvent);
1199wxDEFINE_EVENT(EVT_LOAD_DIRECTORY, wxCommandEvent);
1200wxDEFINE_EVENT(EVT_LOAD_PLUGIN, wxCommandEvent);
1201wxDEFINE_EVENT(EVT_PLUGIN_UNLOAD, wxCommandEvent);
1202wxDEFINE_EVENT(EVT_PLUGLIST_CHANGE, wxCommandEvent);
1203wxDEFINE_EVENT(EVT_UNREADABLE_PLUGIN, wxCommandEvent);
1204wxDEFINE_EVENT(EVT_UPDATE_CHART_TYPES, wxCommandEvent);
1205wxDEFINE_EVENT(EVT_PLUGIN_LOADALL_FINALIZE, wxCommandEvent);
1206wxDEFINE_EVENT(EVT_VERSION_INCOMPATIBLE_PLUGIN, wxCommandEvent);
1207
1208
1209void PlugInManager::HandlePluginLoaderEvents() {
1210 auto loader = PluginLoader::getInstance();
1211
1212 evt_blacklisted_plugin_listener.Listen(loader->evt_blacklisted_plugin,
1213 this, EVT_BLACKLISTED_PLUGIN);
1214 Bind(EVT_BLACKLISTED_PLUGIN, [&](wxCommandEvent& ev) {
1215 m_blacklist_ui->message(ev.GetString().ToStdString()); });
1216
1217 evt_deactivate_plugin_listener.Listen(loader->evt_deactivate_plugin,
1218 this, EVT_DEACTIVATE_PLUGIN);
1219 Bind(EVT_DEACTIVATE_PLUGIN, [&](wxCommandEvent& ev) {
1220 auto pic = static_cast<const PlugInContainer*>(ev.GetClientData());
1221 OnPluginDeactivate(pic); });
1222
1223 evt_incompatible_plugin_listener.Listen(loader->evt_incompatible_plugin,
1224 this, EVT_INCOMPATIBLE_PLUGIN);
1225 Bind(EVT_INCOMPATIBLE_PLUGIN,
1226 [&](wxCommandEvent& ev) { event_message_box(ev.GetString()); });
1227
1228 evt_pluglist_change_listener.Listen(loader->evt_pluglist_change,
1229 this, EVT_PLUGLIST_CHANGE);
1230 Bind(EVT_PLUGLIST_CHANGE, [&](wxCommandEvent&) {
1231 if (m_listPanel) m_listPanel->ReloadPluginPanels();
1232 g_options->itemBoxSizerPanelPlugins->Layout(); });
1233
1234 evt_load_directory_listener.Listen(loader->evt_load_directory, this,
1235 EVT_LOAD_DIRECTORY);
1236 Bind(EVT_LOAD_DIRECTORY, [&](wxCommandEvent&) {
1237 pConfig->SetPath("/PlugIns/");
1238 SetPluginOrder(pConfig->Read("PluginOrder", wxEmptyString)); });
1239
1240 evt_load_plugin_listener.Listen(loader->evt_load_plugin, this,
1241 EVT_LOAD_PLUGIN);
1242 Bind(EVT_LOAD_PLUGIN, [&](wxCommandEvent& ev) {
1243 auto pic = static_cast<const PlugInContainer*>(ev.GetClientData());
1244 OnLoadPlugin(pic); });
1245
1246 evt_version_incompatible_plugin_listener.Listen(
1247 loader->evt_version_incompatible_plugin, this,
1248 EVT_VERSION_INCOMPATIBLE_PLUGIN);
1249 Bind(EVT_VERSION_INCOMPATIBLE_PLUGIN, [&](wxCommandEvent& ev) {
1250 static const wxString msg =
1251 _("The plugin %s is not compatible with this version "
1252 "of OpenCPN, please get an updated version.");
1253 event_message_box(msg, ev); });
1254
1255 evt_unreadable_plugin_listener.Listen(loader->evt_blacklisted_plugin,
1256 this, EVT_UNREADABLE_PLUGIN);
1257 Bind(EVT_UNREADABLE_PLUGIN, [&](wxCommandEvent& ev) {
1258 static const wxString msg =
1259 _("Unreadable Plugin library %s detected, check file permissions:\n\n");
1260 event_message_box(msg, ev); });
1261
1262 evt_incompatible_plugin_listener.Listen(loader->evt_incompatible_plugin,
1263 this, EVT_INCOMPATIBLE_PLUGIN);
1264 Bind(EVT_INCOMPATIBLE_PLUGIN,
1265 [&](wxCommandEvent& ev) { event_message_box(ev.GetString()); });
1266
1267 evt_update_chart_types_listener.Listen(loader->evt_update_chart_types,
1268 this, EVT_UPDATE_CHART_TYPES);
1269 Bind(EVT_UPDATE_CHART_TYPES,
1270 [&](wxCommandEvent& ev) { UpDateChartDataTypes(); });
1271
1272 evt_plugin_loadall_finalize_listener.Listen(
1273 loader->evt_plugin_loadall_finalize, this,
1274 EVT_PLUGIN_LOADALL_FINALIZE);
1275 Bind(EVT_PLUGIN_LOADALL_FINALIZE,
1276 [&](wxCommandEvent& ev) { FinalizePluginLoadall(); });
1277
1278 evt_ais_json_listener.Listen(g_pAIS->plugin_msg, this,
1279 EVT_PLUGMGR_AIS_MSG);
1280 evt_routeman_json_listener.Listen(g_pRouteMan->json_msg, this,
1281 EVT_PLUGMGR_ROUTEMAN_MSG);
1282 Bind(EVT_PLUGMGR_AIS_MSG, [&](ObservedEvt &ev) {
1283 auto pTarget = UnpackEvtPointer<AisTargetData>(ev);
1284 SendAisJsonMessage(pTarget); });
1285 Bind(EVT_PLUGMGR_ROUTEMAN_MSG, [&](ObservedEvt& ev) {
1286 auto msg = UnpackEvtPointer<wxJSONValue>(ev);
1287 SendJSONMessageToAllPlugins(ev.GetString(), *msg); });
1288}
1289
1294wxDEFINE_EVENT(EVT_DOWNLOAD_FAILED, wxCommandEvent);
1295wxDEFINE_EVENT(EVT_DOWNLOAD_OK, wxCommandEvent);
1296
1297void PlugInManager::HandlePluginHandlerEvents() {
1298 auto loader = PluginLoader::getInstance();
1299
1300 evt_download_failed_listener.Listen(loader->evt_update_chart_types,
1301 this, EVT_DOWNLOAD_FAILED);
1302 Bind(EVT_DOWNLOAD_FAILED, [&](wxCommandEvent& ev) {
1303 wxString message = _("Please check system log for more info.");
1304 OCPNMessageBox(gFrame, message, _("Installation error"),
1305 wxICON_ERROR | wxOK | wxCENTRE); });
1306
1307 evt_download_ok_listener.Listen(loader->evt_update_chart_types, this,
1308 EVT_DOWNLOAD_OK);
1309 Bind(EVT_DOWNLOAD_OK, [&](wxCommandEvent& ev) {
1310 wxString message(ev.GetString());
1311 message += _(" successfully installed from cache");
1312 OCPNMessageBox(gFrame, message, _("Installation complete"),
1313 wxICON_INFORMATION | wxOK | wxCENTRE); });
1314}
1315
1323static void setLoadPath() {
1324 using namespace std;
1325
1326 auto const osSystemId = wxPlatformInfo::Get().GetOperatingSystemId();
1327 vector<string> dirs = PluginPaths::getInstance()->Libdirs();
1328 if (osSystemId & wxOS_UNIX_LINUX) {
1329 string path = ocpn::join(dirs, ':');
1330 wxString envPath;
1331 if (wxGetEnv("LD_LIBRARY_PATH", &envPath)) {
1332 path = path + ":" + envPath.ToStdString();
1333 }
1334 wxLogMessage("Using LD_LIBRARY_PATH: %s", path.c_str());
1335 wxSetEnv("LD_LIBRARY_PATH", path.c_str());
1336 } else if (osSystemId & wxOS_WINDOWS) {
1337 // On windows, Libdirs() and Bindirs() are the same.
1338 string path = ocpn::join(dirs, ';');
1339 wxString envPath;
1340 if (wxGetEnv("PATH", &envPath)) {
1341 path = path + ";" + envPath.ToStdString();
1342 }
1343 wxLogMessage("Using PATH: %s", path);
1344 wxSetEnv("PATH", path);
1345 } else if (osSystemId & wxOS_MAC) {
1346 string path = ocpn::join(dirs, ':');
1347 wxString envPath;
1348 if (wxGetEnv("DYLD_LIBRARY_PATH", &envPath)) {
1349 path = path + ":" + envPath.ToStdString();
1350 }
1351 wxLogMessage("Using DYLD_LIBRARY_PATH: %s", path.c_str());
1352 wxSetEnv("DYLD_LIBRARY_PATH", path.c_str());
1353 } else {
1354 wxString os_name = wxPlatformInfo::Get().GetPortIdName();
1355 if (os_name.Contains(_T("wxQT"))) {
1356 wxLogMessage(_T("setLoadPath() using Android library path"));
1357 } else
1358 wxLogWarning("SetLoadPath: Unsupported platform.");
1359 }
1360 if (osSystemId & wxOS_MAC || osSystemId & wxOS_UNIX_LINUX) {
1361 vector<string> dirs = PluginPaths::getInstance()->Bindirs();
1362 string path = ocpn::join(dirs, ':');
1363 wxString envPath;
1364 wxGetEnv("PATH", &envPath);
1365 path = path + ":" + envPath.ToStdString();
1366 wxLogMessage("Using PATH: %s", path);
1367 wxSetEnv("PATH", path);
1368 }
1369}
1370
1371bool PlugInManager::CallLateInit(void) {
1372 bool bret = true;
1373
1374 auto plugin_array = PluginLoader::getInstance()->GetPlugInArray();
1375 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
1376 PlugInContainer *pic = (*plugin_array)[i];
1377
1378 switch (pic->m_api_version) {
1379 case 110:
1380 case 111:
1381 case 112:
1382 case 113:
1383 case 114:
1384 case 115:
1385 case 116:
1386 case 117:
1387 case 118:
1388 ProcessLateInit(pic);
1389 break;
1390 }
1391 }
1392
1393 return bret;
1394}
1395
1396void PlugInManager::ProcessLateInit(PlugInContainer *pic) {
1397 if (pic->m_cap_flag & WANTS_LATE_INIT) {
1398 wxString msg(_T("PlugInManager: Calling LateInit PlugIn: "));
1399 msg += pic->m_plugin_file;
1400 wxLogMessage(msg);
1401
1402 opencpn_plugin_110 *ppi =
1403 dynamic_cast<opencpn_plugin_110 *>(pic->m_pplugin);
1404 if (ppi) ppi->LateInit();
1405 }
1406}
1407
1408void PlugInManager::OnPluginDeactivate(const PlugInContainer* pic) {
1409 // Unload chart cache if this plugin is responsible for any charts
1410 if ((pic->m_cap_flag & INSTALLS_PLUGIN_CHART) ||
1411 (pic->m_cap_flag & INSTALLS_PLUGIN_CHART_GL)) {
1412 ChartData->PurgeCachePlugins();
1413 gFrame->InvalidateAllQuilts();
1414
1415 }
1416 // Deactivate (Remove) any ToolbarTools added by this PlugIn
1417 for (unsigned int i = 0; i < m_PlugInToolbarTools.GetCount(); i++) {
1418 PlugInToolbarToolContainer* pttc = m_PlugInToolbarTools[i];
1419
1420 if (pttc->m_pplugin == pic->m_pplugin) {
1421 m_PlugInToolbarTools.Remove(pttc);
1422 delete pttc;
1423 }
1424 }
1425
1426 // Deactivate (Remove) any ContextMenu items addded by this PlugIn
1427 for (unsigned int i = 0; i < m_PlugInMenuItems.GetCount(); i++) {
1428 PlugInMenuItemContainer* pimis = m_PlugInMenuItems[i];
1429 if (pimis->m_pplugin == pic->m_pplugin) {
1430 m_PlugInMenuItems.Remove(pimis);
1431 delete pimis;
1432 }
1433 }
1434 // *pic is a malloc'ed copy of the original *pic, owned by us.
1435 free(const_cast<PlugInContainer*>(pic));
1436}
1437
1438
1439void PlugInManager::SendVectorChartObjectInfo(const wxString &chart,
1440 const wxString &feature,
1441 const wxString &objname,
1442 double &lat, double &lon,
1443 double &scale, int &nativescale) {
1444 wxString decouple_chart(chart);
1445 wxString decouple_feature(feature);
1446 wxString decouple_objname(objname);
1447 auto plugin_array = PluginLoader::getInstance()->GetPlugInArray();
1448 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
1449 PlugInContainer *pic = (*plugin_array)[i];
1450 if (pic->m_bEnabled && pic->m_bInitState) {
1451 if (pic->m_cap_flag & WANTS_VECTOR_CHART_OBJECT_INFO) {
1452 switch (pic->m_api_version) {
1453 case 112:
1454 case 113:
1455 case 114:
1456 case 115:
1457 case 116:
1458 case 117:
1459 case 118: {
1460 opencpn_plugin_112 *ppi =
1461 dynamic_cast<opencpn_plugin_112 *>(pic->m_pplugin);
1462 if (ppi)
1463 ppi->SendVectorChartObjectInfo(decouple_chart, decouple_feature,
1464 decouple_objname, lat, lon, scale,
1465 nativescale);
1466 break;
1467 }
1468 default:
1469 break;
1470 }
1471 }
1472 }
1473 }
1474}
1475
1476bool PlugInManager::IsAnyPlugInChartEnabled() {
1477 // Is there a PlugIn installed and active that implements PlugIn Chart
1478 // type(s)?
1479 auto plugin_array = PluginLoader::getInstance()->GetPlugInArray();
1480 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
1481 PlugInContainer *pic = (*plugin_array)[i];
1482 if (pic->m_bEnabled && pic->m_bInitState) {
1483 if ((pic->m_cap_flag & INSTALLS_PLUGIN_CHART) ||
1484 (pic->m_cap_flag & INSTALLS_PLUGIN_CHART_GL))
1485 return true;
1486 }
1487 }
1488 return false;
1489}
1490
1491void PlugInManager::UpdateManagedPlugins() {
1492 PlugInContainer *pict;
1493 auto plugin_array = PluginLoader::getInstance()->GetPlugInArray();
1494 // Clear the status (to "unmanaged") on all plugins
1495 for (size_t i = 0; i < plugin_array->GetCount(); i++) {
1496 pict = plugin_array->Item(i);
1497 plugin_array->Item(i)->m_pluginStatus = PluginStatus::Unmanaged;
1498
1499 // Pre-mark the default "system" plugins
1500 auto r =
1501 std::find(SYSTEM_PLUGINS.begin(), SYSTEM_PLUGINS.end(),
1502 plugin_array->Item(i)->m_common_name.Lower().ToStdString());
1503 if (r != SYSTEM_PLUGINS.end())
1504 plugin_array->Item(i)->m_pluginStatus = PluginStatus::System;
1505 }
1506
1507 std::vector<PluginMetadata> available = getCompatiblePlugins();
1508
1509 // Traverse the list again
1510 // Remove any inactive/uninstalled managed plugins that are no longer
1511 // available in the current catalog Usually due to reverting from Alpha/Beta
1512 // catalog back to master
1513 for (size_t i = 0; i < plugin_array->GetCount(); i++) {
1514 pict = plugin_array->Item(i);
1515 if (pict->m_ManagedMetadata.name
1516 .size()) { // If metadata is good, must be a managed plugin
1517 bool bfound = false;
1518 for (auto plugin : available) {
1519 if (pict->m_common_name.IsSameAs(wxString(plugin.name.c_str()))) {
1520 bfound = true;
1521 break;
1522 }
1523 }
1524 if (!bfound) {
1525 if (!pict->m_pplugin) { // Only remove inactive plugins
1526 plugin_array->Item(i)->m_pluginStatus =
1527 PluginStatus::PendingListRemoval;
1528 }
1529 }
1530 }
1531 }
1532
1533 // Remove any list items marked
1534 size_t i = 0;
1535 while ((i >= 0) && (i < plugin_array->GetCount())) {
1536 pict = plugin_array->Item(i);
1537 if (pict->m_pluginStatus == PluginStatus::PendingListRemoval) {
1538 plugin_array->RemoveAt(i);
1539 i = 0;
1540 } else
1541 i++;
1542 }
1543
1544 for (size_t i = 0; i < plugin_array->GetCount(); i++) {
1545 pict = plugin_array->Item(i);
1546 int yyp = 4;
1547 }
1548
1549 // Now merge and update from the catalog
1550 for (auto plugin : available) {
1551 PlugInContainer *pic = NULL;
1552 // Search for an exact name match in the existing plugin array
1553 bool bfound = false;
1554 for (size_t i = 0; i < plugin_array->GetCount(); i++) {
1555 pic = plugin_array->Item(i);
1556 if (plugin_array->Item(i)->m_common_name.IsSameAs(
1557 wxString(plugin.name.c_str()))) {
1558 bfound = true;
1559 break;
1560 }
1561 }
1562
1563 // No match found, so add a container, and populate it
1564 if (!bfound) {
1565 PlugInContainer *new_pic = new PlugInContainer;
1566 new_pic->m_common_name = wxString(plugin.name.c_str());
1567 new_pic->m_pluginStatus = PluginStatus::ManagedInstallAvailable;
1568 new_pic->m_ManagedMetadata = plugin;
1569 new_pic->m_version_major = 0;
1570 new_pic->m_version_minor = 0;
1571
1572 // In safe mode, check to see if the plugin appears to be installed
1573 // If so, set the status to "ManagedInstalledCurrentVersion", thus
1574 // enabling the "uninstall" button.
1575 if (safe_mode::get_mode()) {
1576 std::string installed;
1577 if (isRegularFile(PluginHandler::fileListPath(plugin.name).c_str())) {
1578 // Get the installed version from the manifest
1579 std::string path = PluginHandler::versionPath(plugin.name);
1580 if (path != "" && wxFileName::IsFileReadable(path)) {
1581 std::ifstream stream;
1582 stream.open(path, std::ifstream::in);
1583 stream >> installed;
1584 }
1585 }
1586 if (!installed.empty())
1587 new_pic->m_pluginStatus =
1588 PluginStatus::ManagedInstalledCurrentVersion;
1589 else
1590 new_pic->m_pluginStatus = PluginStatus::Unknown;
1591 }
1592
1593 plugin_array->Add(new_pic);
1594
1595 }
1596 // Match found, so merge the info and determine the plugin status
1597 else {
1598 // If the managed plugin is installed, the fileList (manifest) will be
1599 // present
1600 if (isRegularFile(PluginHandler::fileListPath(plugin.name).c_str())) {
1601 // Get the installed version from the manifest
1602 std::string installed;
1603 std::string path = PluginHandler::versionPath(plugin.name);
1604 if (path != "" && wxFileName::IsFileReadable(path)) {
1605 std::ifstream stream;
1606 stream.open(path, std::ifstream::in);
1607 stream >> installed;
1608 }
1609 pic->m_InstalledManagedVersion = installed;
1610 auto installedVersion = SemanticVersion::parse(installed);
1611
1612 // Compare to the version reported in metadata
1613 auto metaVersion = SemanticVersion::parse(plugin.version);
1614 if (installedVersion < metaVersion)
1615 pic->m_pluginStatus = PluginStatus::ManagedInstalledUpdateAvailable;
1616 else if (installedVersion == metaVersion)
1617 pic->m_pluginStatus = PluginStatus::ManagedInstalledCurrentVersion;
1618 else if (installedVersion > metaVersion)
1619 pic->m_pluginStatus =
1620 PluginStatus::ManagedInstalledDowngradeAvailable;
1621
1622 pic->m_ManagedMetadata = plugin;
1623 }
1624
1625 // If the new plugin is not installed....
1626 else {
1627 // If the plugin is actually loaded, but the new plugin is known not to
1628 // be installed,
1629 // then there must be a legacy plugin loaded.
1630 // and the new status must be "PluginStatus::LegacyUpdateAvailable"
1631 if (pic->m_api_version) {
1632 pic->m_pluginStatus = PluginStatus::LegacyUpdateAvailable;
1633 pic->m_ManagedMetadata = plugin;
1634 }
1635 // Otherwise, this is an uninstalled managed plugin.
1636 else {
1637 pic->m_pluginStatus = PluginStatus::ManagedInstallAvailable;
1638 }
1639 }
1640 }
1641 }
1642
1643 // Sort the list
1644
1645 // Detach and hold the uninstalled, managed plugins
1646 std::map<std::string, PlugInContainer *> sortmap;
1647 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
1648 PlugInContainer *pic = plugin_array->Item(i);
1649 if (pic->m_pluginStatus == PluginStatus::ManagedInstallAvailable) {
1650 plugin_array->Remove(pic);
1651
1652 // Sort by name, lower cased.
1653 std::string name = pic->m_ManagedMetadata.name;
1654 std::transform(name.begin(), name.end(), name.begin(), ::tolower);
1655 sortmap[name] = pic;
1656 i = 0; // Restart the list
1657 }
1658 }
1659
1660 // Add the detached plugins back at the top of the list.
1661 // Later, the list will be populated in reverse order...Why??
1662 for (std::map<std::string, PlugInContainer *>::iterator i = sortmap.begin();
1663 i != sortmap.end(); i++) {
1664 PlugInContainer *pic = i->second;
1665 plugin_array->Insert(pic, 0);
1666 }
1667
1668 if (m_listPanel) m_listPanel->ReloadPluginPanels();
1669
1670 g_options->itemBoxSizerPanelPlugins->Layout();
1671}
1672
1673bool PlugInManager::UpDateChartDataTypes() {
1674 bool bret = false;
1675 if (NULL == ChartData) return bret;
1676
1677 auto plugin_array = PluginLoader::getInstance()->GetPlugInArray();
1678 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
1679 PlugInContainer *pic = plugin_array->Item(i);
1680
1681 if (pic->m_bInitState) {
1682 if ((pic->m_cap_flag & INSTALLS_PLUGIN_CHART) ||
1683 (pic->m_cap_flag & INSTALLS_PLUGIN_CHART_GL))
1684 bret = true;
1685 }
1686 }
1687
1688 if (bret) ChartData->UpdateChartClassDescriptorArray();
1689
1690 return bret;
1691}
1692
1693void PlugInManager::FinalizePluginLoadall() {
1694
1695 //FIXME
1696 // Maybe this does not need to be done for CLI instance?
1697 // Inform plugins of the current color scheme
1698 SetColorSchemeForAllPlugIns( global_color_scheme );
1699
1700 // Tell all the PlugIns about the current OCPN configuration
1701 SendBaseConfigToAllPlugIns();
1702 SendS52ConfigToAllPlugIns( true );
1703 SendSKConfigToAllPlugIns();
1704
1705 // Inform Plugins of OpenGL configuration, if enabled
1706 if(g_bopengl){
1707 if(gFrame->GetPrimaryCanvas()->GetglCanvas())
1708 gFrame->GetPrimaryCanvas()->GetglCanvas()->SendJSONConfigMessage();
1709 }
1710
1711 // And then reload all catalogs.
1712 ReloadLocale();
1713
1714}
1715
1716void PlugInManager::SetPluginOrder(wxString serialized_names) {
1717 m_plugin_order.Empty();
1718 wxStringTokenizer tokenizer(serialized_names, _T(";"));
1719 while (tokenizer.HasMoreTokens()) {
1720 m_plugin_order.Add(tokenizer.GetNextToken());
1721 }
1722}
1723
1724wxString PlugInManager::GetPluginOrder() {
1725 wxString plugins = wxEmptyString;
1726 auto plugin_array = PluginLoader::getInstance()->GetPlugInArray();
1727 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
1728 plugins.Append(plugin_array->Item(i)->m_common_name);
1729 if (i < plugin_array->GetCount() - 1) plugins.Append(';');
1730 }
1731 return plugins;
1732}
1733
1734bool PlugInManager::UpdateConfig() {
1735 // pConfig->SetPath( _T("/PlugIns/") );
1736 // pConfig->Write( _T("PluginOrder"), GetPluginOrder() );
1737
1738 auto plugin_array = PluginLoader::getInstance()->GetPlugInArray();
1739 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
1740 PlugInContainer *pic = plugin_array->Item(i);
1741
1742 if (pic) {
1743 wxString config_section = (_T ( "/PlugIns/" ));
1744 config_section += pic->m_plugin_filename;
1745 pConfig->SetPath(config_section);
1746 pConfig->Write(_T ( "bEnabled" ), pic->m_bEnabled);
1747 }
1748 }
1749
1750 return true;
1751}
1752
1753void PlugInManager::ShowDeferredBlacklistMessages() {
1754 m_blacklist_ui->show_deferred_messages();
1755}
1756
1757bool PlugInManager::CheckBlacklistedPlugin(const PluginMetadata plugin) {
1758 auto v = SemanticVersion::parse(plugin.version);
1759 return CheckBlacklistedPlugin(wxString(plugin.name), v.major, v.minor);
1760}
1761
1762bool PlugInManager::CheckBlacklistedPlugin(opencpn_plugin *plugin) {
1763 int major = plugin->GetPlugInVersionMajor();
1764 int minor = plugin->GetPlugInVersionMinor();
1765
1766#ifdef __WXMSW__
1767 wxString name = wxString::FromAscii(typeid(*plugin).name());
1768 name.Replace("class ", wxEmptyString);
1769#else
1770 const std::type_info &ti = typeid(*plugin);
1771 int status;
1772 char *realname = abi::__cxa_demangle(ti.name(), 0, 0, &status);
1773 wxString name = wxString::FromAscii(realname);
1774 free(realname);
1775#endif // __WXMSW__
1776 return CheckBlacklistedPlugin(name, major, minor);
1777}
1778
1779bool PlugInManager::CheckBlacklistedPlugin(wxString name, int major, int minor) {
1780 auto block_status = m_blacklist->get_status(name.ToStdString(), major, minor);
1781 if (block_status == plug_status::unblocked) return true;
1782 plug_data data(name.ToStdString(), major, minor);
1783 auto msg = m_blacklist->get_message(block_status, data);
1784 m_blacklist_ui->message(msg);
1785 return false;
1786}
1787
1788bool PlugInManager::RenderAllCanvasOverlayPlugIns(ocpnDC &dc,
1789 const ViewPort &vp,
1790 int canvasIndex, int priority) {
1791 auto plugin_array = PluginLoader::getInstance()->GetPlugInArray();
1792 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
1793 PlugInContainer *pic = plugin_array->Item(i);
1794 if (pic->m_bEnabled && pic->m_bInitState) {
1795 if (pic->m_cap_flag & WANTS_OVERLAY_CALLBACK) {
1796 PlugIn_ViewPort pivp = CreatePlugInViewport(vp);
1797
1798 wxDC *pdc = dc.GetDC();
1799 if (pdc) // not in OpenGL mode
1800 {
1801 switch (pic->m_api_version) {
1802 case 106: {
1803 if (priority > 0)
1804 break;
1805 opencpn_plugin_16 *ppi =
1806 dynamic_cast<opencpn_plugin_16 *>(pic->m_pplugin);
1807 if (ppi) ppi->RenderOverlay(*pdc, &pivp);
1808 break;
1809 }
1810 case 107: {
1811 if (priority > 0)
1812 break;
1813 opencpn_plugin_17 *ppi =
1814 dynamic_cast<opencpn_plugin_17 *>(pic->m_pplugin);
1815 if (ppi) ppi->RenderOverlay(*pdc, &pivp);
1816 break;
1817 }
1818 case 108:
1819 case 109:
1820 case 110:
1821 case 111:
1822 case 112:
1823 case 113:
1824 case 114:
1825 case 115: {
1826 if (priority > 0)
1827 break;
1828 opencpn_plugin_18 *ppi =
1829 dynamic_cast<opencpn_plugin_18 *>(pic->m_pplugin);
1830 if (ppi) ppi->RenderOverlay(*pdc, &pivp);
1831 break;
1832 }
1833 case 116:
1834 case 117: {
1835 if (priority > 0)
1836 break;
1837 opencpn_plugin_18 *ppi =
1838 dynamic_cast<opencpn_plugin_18 *>(pic->m_pplugin);
1839 if (ppi) {
1840 ppi->RenderOverlay(*pdc, &pivp);
1841 }
1842 opencpn_plugin_116 *ppi116 =
1843 dynamic_cast<opencpn_plugin_116 *>(pic->m_pplugin);
1844 if (ppi116)
1845 ppi116->RenderOverlayMultiCanvas(*pdc, &pivp, canvasIndex);
1846 break;
1847 }
1848 case 118: {
1849 if (priority <= 0) {
1850 opencpn_plugin_18 *ppi =
1851 dynamic_cast<opencpn_plugin_18 *>(pic->m_pplugin);
1852 if (ppi) {
1853 ppi->RenderOverlay(*pdc, &pivp);
1854 }
1855 }
1856 opencpn_plugin_118 *ppi118 =
1857 dynamic_cast<opencpn_plugin_118 *>(pic->m_pplugin);
1858 if (ppi118)
1859 ppi118->RenderOverlayMultiCanvas(*pdc, &pivp, canvasIndex, priority);
1860 break;
1861 }
1862 default:
1863 break;
1864 }
1865 } else {
1866 // If in OpenGL mode, and the PlugIn has requested OpenGL render
1867 // callbacks, then there is no need to render by wxDC here.
1868 if (pic->m_cap_flag & WANTS_OPENGL_OVERLAY_CALLBACK) continue;
1869
1870 if ((m_cached_overlay_bm.GetWidth() != vp.pix_width) ||
1871 (m_cached_overlay_bm.GetHeight() != vp.pix_height))
1872 m_cached_overlay_bm.Create(vp.pix_width, vp.pix_height, -1);
1873
1874 wxMemoryDC mdc;
1875 mdc.SelectObject(m_cached_overlay_bm);
1876 mdc.SetBackground(*wxBLACK_BRUSH);
1877 mdc.Clear();
1878
1879 bool b_rendered = false;
1880
1881 switch (pic->m_api_version) {
1882 case 106: {
1883 if (priority > 0)
1884 break;
1885 opencpn_plugin_16 *ppi =
1886 dynamic_cast<opencpn_plugin_16 *>(pic->m_pplugin);
1887 if (ppi) b_rendered = ppi->RenderOverlay(mdc, &pivp);
1888 break;
1889 }
1890 case 107: {
1891 if (priority > 0)
1892 break;
1893 opencpn_plugin_17 *ppi =
1894 dynamic_cast<opencpn_plugin_17 *>(pic->m_pplugin);
1895 if (ppi) b_rendered = ppi->RenderOverlay(mdc, &pivp);
1896 break;
1897 }
1898 case 108:
1899 case 109:
1900 case 110:
1901 case 111:
1902 case 112:
1903 case 113:
1904 case 114:
1905 case 115: {
1906 if (priority > 0)
1907 break;
1908 opencpn_plugin_18 *ppi =
1909 dynamic_cast<opencpn_plugin_18 *>(pic->m_pplugin);
1910 if (ppi) b_rendered = ppi->RenderOverlay(*pdc, &pivp);
1911 break;
1912 }
1913 case 116:
1914 case 117: {
1915 if (priority > 0)
1916 break;
1917 opencpn_plugin_18 *ppi =
1918 dynamic_cast<opencpn_plugin_18 *>(pic->m_pplugin);
1919 if (ppi) {
1920 b_rendered = ppi->RenderOverlay(*pdc, &pivp);
1921 }
1922 opencpn_plugin_116 *ppi116 =
1923 dynamic_cast<opencpn_plugin_116 *>(pic->m_pplugin);
1924 if (ppi116)
1925 b_rendered = ppi116->RenderOverlayMultiCanvas(*pdc, &pivp,
1926 g_canvasConfig);
1927 break;
1928 }
1929 case 118: {
1930 if (priority <= 0) {
1931 opencpn_plugin_18 *ppi =
1932 dynamic_cast<opencpn_plugin_18 *>(pic->m_pplugin);
1933 if (ppi) {
1934 b_rendered = ppi->RenderOverlay(*pdc, &pivp);
1935 }
1936 }
1937 opencpn_plugin_118 *ppi118 =
1938 dynamic_cast<opencpn_plugin_118 *>(pic->m_pplugin);
1939 if (ppi118)
1940 b_rendered = ppi118->RenderOverlayMultiCanvas(*pdc, &pivp,
1941 g_canvasConfig,
1942 priority);
1943 break;
1944 }
1945 default: {
1946 b_rendered = pic->m_pplugin->RenderOverlay(&mdc, &pivp);
1947 break;
1948 }
1949 }
1950
1951 mdc.SelectObject(wxNullBitmap);
1952
1953 if (b_rendered) {
1954 wxMask *p_msk = new wxMask(m_cached_overlay_bm, wxColour(0, 0, 0));
1955 m_cached_overlay_bm.SetMask(p_msk);
1956
1957 dc.DrawBitmap(m_cached_overlay_bm, 0, 0, true);
1958 }
1959 }
1960 } else if (pic->m_cap_flag & WANTS_OPENGL_OVERLAY_CALLBACK) {
1961 }
1962 }
1963 }
1964
1965 return true;
1966}
1967
1968bool PlugInManager::RenderAllGLCanvasOverlayPlugIns(wxGLContext *pcontext,
1969 const ViewPort &vp,
1970 int canvasIndex, int priority) {
1971 auto plugin_array = PluginLoader::getInstance()->GetPlugInArray();
1972 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
1973 PlugInContainer *pic = plugin_array->Item(i);
1974 if (pic->m_bEnabled && pic->m_bInitState) {
1975 if (pic->m_cap_flag & WANTS_OPENGL_OVERLAY_CALLBACK) {
1976 PlugIn_ViewPort pivp = CreatePlugInViewport(vp);
1977
1978 switch (pic->m_api_version) {
1979 case 107: {
1980 if (priority > 0)
1981 break;
1982 opencpn_plugin_17 *ppi =
1983 dynamic_cast<opencpn_plugin_17 *>(pic->m_pplugin);
1984 if (ppi) ppi->RenderGLOverlay(pcontext, &pivp);
1985 break;
1986 }
1987
1988 case 108:
1989 case 109:
1990 case 110:
1991 case 111:
1992 case 112:
1993 case 113:
1994 case 114:
1995 case 115: {
1996 if (priority > 0)
1997 break;
1998 opencpn_plugin_18 *ppi =
1999 dynamic_cast<opencpn_plugin_18 *>(pic->m_pplugin);
2000 if (ppi) ppi->RenderGLOverlay(pcontext, &pivp);
2001 break;
2002 }
2003 case 116:
2004 case 117: {
2005 if (priority > 0)
2006 break;
2007 opencpn_plugin_18 *ppi =
2008 dynamic_cast<opencpn_plugin_18 *>(pic->m_pplugin);
2009 if (ppi) {
2010 ppi->RenderGLOverlay(pcontext, &pivp);
2011 }
2012 opencpn_plugin_116 *ppi116 =
2013 dynamic_cast<opencpn_plugin_116 *>(pic->m_pplugin);
2014 if (ppi116) {
2015 ppi116->RenderGLOverlayMultiCanvas(pcontext, &pivp, canvasIndex);
2016 }
2017 break;
2018 }
2019 case 118: {
2020 if (priority <= 0) {
2021 opencpn_plugin_18 *ppi =
2022 dynamic_cast<opencpn_plugin_18 *>(pic->m_pplugin);
2023 if (ppi) {
2024 ppi->RenderGLOverlay(pcontext, &pivp);
2025 }
2026 }
2027 opencpn_plugin_118 *ppi118 =
2028 dynamic_cast<opencpn_plugin_118 *>(pic->m_pplugin);
2029 if (ppi118) {
2030 ppi118->RenderGLOverlayMultiCanvas(pcontext, &pivp, canvasIndex, priority);
2031 }
2032 break;
2033 }
2034 default:
2035 break;
2036 }
2037 }
2038 }
2039 }
2040
2041 return true;
2042}
2043
2044bool PlugInManager::SendMouseEventToPlugins(wxMouseEvent &event) {
2045 bool bret = false;
2046 auto plugin_array = PluginLoader::getInstance()->GetPlugInArray();
2047 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
2048 PlugInContainer *pic = plugin_array->Item(i);
2049 if (pic->m_bEnabled && pic->m_bInitState) {
2050 if (pic->m_cap_flag & WANTS_MOUSE_EVENTS) {
2051 switch (pic->m_api_version) {
2052 case 112:
2053 case 113:
2054 case 114:
2055 case 115:
2056 case 116:
2057 case 117:
2058 case 118: {
2059 opencpn_plugin_112 *ppi =
2060 dynamic_cast<opencpn_plugin_112 *>(pic->m_pplugin);
2061 if (ppi)
2062 if (ppi->MouseEventHook(event)) bret = true;
2063 break;
2064 }
2065 default:
2066 break;
2067 }
2068 }
2069 }
2070 }
2071
2072 return bret;
2073}
2074
2075bool PlugInManager::SendKeyEventToPlugins(wxKeyEvent &event) {
2076 bool bret = false;
2077 auto plugin_array = PluginLoader::getInstance()->GetPlugInArray();
2078 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
2079 PlugInContainer *pic = plugin_array->Item(i);
2080 if (pic->m_bEnabled && pic->m_bInitState) {
2081 if (pic->m_cap_flag & WANTS_KEYBOARD_EVENTS) {
2082 {
2083 switch (pic->m_api_version) {
2084 case 113:
2085 case 114:
2086 case 115:
2087 case 116:
2088 case 117:
2089 case 118: {
2090 opencpn_plugin_113 *ppi =
2091 dynamic_cast<opencpn_plugin_113 *>(pic->m_pplugin);
2092 if (ppi && ppi->KeyboardEventHook(event)) bret = true;
2093 break;
2094 }
2095 default:
2096 break;
2097 }
2098 }
2099 }
2100 }
2101 }
2102
2103 return bret;
2104 ;
2105}
2106
2107void PlugInManager::SendViewPortToRequestingPlugIns(ViewPort &vp) {
2108 auto plugin_array = PluginLoader::getInstance()->GetPlugInArray();
2109 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
2110 PlugInContainer *pic = plugin_array->Item(i);
2111 if (pic->m_bEnabled && pic->m_bInitState) {
2112 if (pic->m_cap_flag & WANTS_ONPAINT_VIEWPORT) {
2113 PlugIn_ViewPort pivp = CreatePlugInViewport(vp);
2114 if (pic->m_pplugin) pic->m_pplugin->SetCurrentViewPort(pivp);
2115 }
2116 }
2117 }
2118}
2119
2120void PlugInManager::SendCursorLatLonToAllPlugIns(double lat, double lon) {
2121 auto plugin_array = PluginLoader::getInstance()->GetPlugInArray();
2122 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
2123 PlugInContainer *pic = plugin_array->Item(i);
2124 if (pic->m_bEnabled && pic->m_bInitState) {
2125 if (pic->m_cap_flag & WANTS_CURSOR_LATLON)
2126 if (pic->m_pplugin) pic->m_pplugin->SetCursorLatLon(lat, lon);
2127 }
2128 }
2129}
2130
2131void NotifySetupOptionsPlugin(const PlugInContainer *pic) {
2132 if (pic->m_bEnabled && pic->m_bInitState) {
2133 if (pic->m_cap_flag & INSTALLS_TOOLBOX_PAGE) {
2134 switch (pic->m_api_version) {
2135 case 109:
2136 case 110:
2137 case 111:
2138 case 112:
2139 case 113:
2140 case 114:
2141 case 115:
2142 case 116:
2143 case 117:
2144 case 118: {
2145 opencpn_plugin_19 *ppi =
2146 dynamic_cast<opencpn_plugin_19 *>(pic->m_pplugin);
2147 if (ppi) {
2148 ppi->OnSetupOptions();
2149 const_cast<PlugInContainer*>(pic)->m_bToolboxPanel = true;
2150 }
2151 break;
2152 }
2153 default:
2154 break;
2155 }
2156 }
2157 }
2158}
2159
2160void PlugInManager::NotifySetupOptions() {
2161 auto plugin_array = PluginLoader::getInstance()->GetPlugInArray();
2162 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
2163 PlugInContainer *pic = plugin_array->Item(i);
2164 NotifySetupOptionsPlugin(pic);
2165 }
2166}
2167
2168void PlugInManager::ClosePlugInPanel(PlugInContainer *pic,
2169 int ok_apply_cancel) {
2170 if (pic->m_bEnabled && pic->m_bInitState) {
2171 if ((pic->m_cap_flag & INSTALLS_TOOLBOX_PAGE) && pic->m_bToolboxPanel) {
2172 pic->m_pplugin->OnCloseToolboxPanel(0, ok_apply_cancel);
2173 pic->m_bToolboxPanel = false;
2174 }
2175 }
2176}
2177
2178void PlugInManager::CloseAllPlugInPanels(int ok_apply_cancel) {
2179 auto plugin_array = PluginLoader::getInstance()->GetPlugInArray();
2180 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
2181 PlugInContainer *pic = plugin_array->Item(i);
2182 if (pic) {
2183 ClosePlugInPanel(pic, ok_apply_cancel);
2184 }
2185 }
2186}
2187
2188int PlugInManager::AddCanvasContextMenuItem(wxMenuItem *pitem,
2189 opencpn_plugin *pplugin,
2190 const char *name) {
2192 pmic->pmenu_item = pitem;
2193 pmic->m_pplugin = pplugin;
2194 pmic->id = pitem->GetId() == wxID_SEPARATOR ? wxID_SEPARATOR
2195 : m_plugin_menu_item_id_next;
2196 pmic->b_viz = true;
2197 pmic->b_grey = false;
2198 pmic->m_in_menu = name;
2199
2200 m_PlugInMenuItems.Add(pmic);
2201
2202 m_plugin_menu_item_id_next++;
2203
2204 return pmic->id;
2205}
2206
2207void PlugInManager::RemoveCanvasContextMenuItem(int item, const char *name) {
2208 for (unsigned int i = 0; i < m_PlugInMenuItems.GetCount(); i++) {
2209 PlugInMenuItemContainer *pimis = m_PlugInMenuItems[i];
2210 {
2211 if (pimis->id == item && !strcmp(name, pimis->m_in_menu)) {
2212 m_PlugInMenuItems.Remove(pimis);
2213 delete pimis;
2214 break;
2215 }
2216 }
2217 }
2218}
2219
2220void PlugInManager::SetCanvasContextMenuItemViz(int item, bool viz,
2221 const char *name) {
2222 for (unsigned int i = 0; i < m_PlugInMenuItems.GetCount(); i++) {
2223 PlugInMenuItemContainer *pimis = m_PlugInMenuItems[i];
2224 {
2225 if (pimis->id == item && !strcmp(name, pimis->m_in_menu)) {
2226 pimis->b_viz = viz;
2227 break;
2228 }
2229 }
2230 }
2231}
2232
2233void PlugInManager::SetCanvasContextMenuItemGrey(int item, bool grey,
2234 const char *name) {
2235 for (unsigned int i = 0; i < m_PlugInMenuItems.GetCount(); i++) {
2236 PlugInMenuItemContainer *pimis = m_PlugInMenuItems[i];
2237 {
2238 if (pimis->id == item && !strcmp(name, pimis->m_in_menu)) {
2239 pimis->b_grey = grey;
2240 break;
2241 }
2242 }
2243 }
2244}
2245
2246void PlugInManager::SendNMEASentenceToAllPlugIns(const wxString &sentence) {
2247 wxString decouple_sentence(
2248 sentence); // decouples 'const wxString &' and 'wxString &' to keep bin
2249 // compat for plugins
2250#ifndef __WXMSW__
2251 // Set up a framework to catch (some) sigsegv faults from plugins.
2252 sigaction(SIGSEGV, NULL, &sa_all_PIM_previous); // save existing
2253 // action for this signal
2254 struct sigaction temp;
2255 sigaction(SIGSEGV, NULL, &temp); // inspect existing action for this signal
2256
2257 temp.sa_handler = catch_signals_PIM; // point to my handler
2258 sigemptyset(&temp.sa_mask); // make the blocking set
2259 // empty, so that all
2260 // other signals will be
2261 // unblocked during my handler
2262 temp.sa_flags = 0;
2263 sigaction(SIGSEGV, &temp, NULL);
2264#endif
2265 auto plugin_array = PluginLoader::getInstance()->GetPlugInArray();
2266 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
2267 PlugInContainer *pic = plugin_array->Item(i);
2268 if (pic->m_bEnabled && pic->m_bInitState) {
2269 if (pic->m_cap_flag & WANTS_NMEA_SENTENCES) {
2270#ifndef __WXMSW__
2271 if (sigsetjmp(env_PIM,
2272 1)) { // Something in the "else" code block faulted.
2273
2274 // Probably safest to assume that all variables in this method are
2275 // trash.. So, simply clean up and return.
2276 sigaction(SIGSEGV, &sa_all_PIM_previous,
2277 NULL); // reset signal handler
2278 return;
2279 } else
2280#endif
2281 {
2282 // volatile int *x = 0;
2283 //*x = 0;
2284 if (pic->m_pplugin)
2285 pic->m_pplugin->SetNMEASentence(decouple_sentence);
2286 }
2287 }
2288 }
2289 }
2290
2291#ifndef __WXMSW__
2292 sigaction(SIGSEGV, &sa_all_PIM_previous, NULL); // reset signal handler
2293#endif
2294}
2295
2296int PlugInManager::GetJSONMessageTargetCount() {
2297 int rv = 0;
2298 auto plugin_array = PluginLoader::getInstance()->GetPlugInArray();
2299 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
2300 PlugInContainer *pic = plugin_array->Item(i);
2301 if (pic->m_bEnabled && pic->m_bInitState &&
2302 (pic->m_cap_flag & WANTS_PLUGIN_MESSAGING))
2303 rv++;
2304 }
2305 return rv;
2306}
2307
2308void PlugInManager::SendJSONMessageToAllPlugins(const wxString &message_id,
2309 wxJSONValue v) {
2310 wxJSONWriter w;
2311 wxString out;
2312 w.Write(v, out);
2313 SendMessageToAllPlugins(message_id, out);
2314 wxLogDebug(message_id);
2315 wxLogDebug(out);
2316}
2317
2318void PlugInManager::SendMessageToAllPlugins(const wxString &message_id,
2319 const wxString &message_body) {
2320 g_lastPluginMessage = message_body;
2321
2322 wxString decouple_message_id(
2323 message_id); // decouples 'const wxString &' and 'wxString &' to keep bin
2324 // compat for plugins
2325 wxString decouple_message_body(
2326 message_body); // decouples 'const wxString &' and 'wxString &' to keep
2327 // bin compat for plugins
2328 auto plugin_array = PluginLoader::getInstance()->GetPlugInArray();
2329 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
2330 PlugInContainer *pic = plugin_array->Item(i);
2331 if (pic->m_bEnabled && pic->m_bInitState) {
2332 if (pic->m_cap_flag & WANTS_PLUGIN_MESSAGING) {
2333 switch (pic->m_api_version) {
2334 case 106: {
2335 opencpn_plugin_16 *ppi =
2336 dynamic_cast<opencpn_plugin_16 *>(pic->m_pplugin);
2337 if (ppi)
2338 ppi->SetPluginMessage(decouple_message_id, decouple_message_body);
2339 break;
2340 }
2341 case 107: {
2342 opencpn_plugin_17 *ppi =
2343 dynamic_cast<opencpn_plugin_17 *>(pic->m_pplugin);
2344 if (ppi)
2345 ppi->SetPluginMessage(decouple_message_id, decouple_message_body);
2346 break;
2347 }
2348 case 108:
2349 case 109:
2350 case 110:
2351 case 111:
2352 case 112:
2353 case 113:
2354 case 114:
2355 case 115:
2356 case 116:
2357 case 117:
2358 case 118: {
2359 opencpn_plugin_18 *ppi =
2360 dynamic_cast<opencpn_plugin_18 *>(pic->m_pplugin);
2361 if (ppi)
2362 ppi->SetPluginMessage(decouple_message_id, decouple_message_body);
2363 break;
2364 }
2365 default:
2366 break;
2367 }
2368 }
2369 }
2370 }
2371}
2372
2373void PlugInManager::SendAISSentenceToAllPlugIns(const wxString &sentence) {
2374 wxString decouple_sentence(
2375 sentence); // decouples 'const wxString &' and 'wxString &' to keep bin
2376 // compat for plugins
2377 auto plugin_array = PluginLoader::getInstance()->GetPlugInArray();
2378 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
2379 PlugInContainer *pic = plugin_array->Item(i);
2380 if (pic->m_bEnabled && pic->m_bInitState) {
2381 if (pic->m_cap_flag & WANTS_AIS_SENTENCES)
2382 pic->m_pplugin->SetAISSentence(decouple_sentence);
2383 }
2384 }
2385}
2386
2387void PlugInManager::SendPositionFixToAllPlugIns(GenericPosDatEx *ppos) {
2388 // Send basic position fix
2390 pfix.Lat = ppos->kLat;
2391 pfix.Lon = ppos->kLon;
2392 pfix.Cog = ppos->kCog;
2393 pfix.Sog = ppos->kSog;
2394 pfix.Var = ppos->kVar;
2395 pfix.FixTime = ppos->FixTime;
2396 pfix.nSats = ppos->nSats;
2397
2398 auto plugin_array = PluginLoader::getInstance()->GetPlugInArray();
2399 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
2400 PlugInContainer *pic = plugin_array->Item(i);
2401 if (pic->m_bEnabled && pic->m_bInitState) {
2402 if (pic->m_cap_flag & WANTS_NMEA_EVENTS)
2403 if (pic->m_pplugin) pic->m_pplugin->SetPositionFix(pfix);
2404 }
2405 }
2406
2407 // Send extended position fix to PlugIns at API 108 and later
2408 PlugIn_Position_Fix_Ex pfix_ex;
2409 pfix_ex.Lat = ppos->kLat;
2410 pfix_ex.Lon = ppos->kLon;
2411 pfix_ex.Cog = ppos->kCog;
2412 pfix_ex.Sog = ppos->kSog;
2413 pfix_ex.Var = ppos->kVar;
2414 pfix_ex.FixTime = ppos->FixTime;
2415 pfix_ex.nSats = ppos->nSats;
2416 pfix_ex.Hdt = ppos->kHdt;
2417 pfix_ex.Hdm = ppos->kHdm;
2418
2419 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
2420 PlugInContainer *pic = plugin_array->Item(i);
2421 if (pic->m_bEnabled && pic->m_bInitState) {
2422 if (pic->m_cap_flag & WANTS_NMEA_EVENTS) {
2423 switch (pic->m_api_version) {
2424 case 108:
2425 case 109:
2426 case 110:
2427 case 111:
2428 case 112:
2429 case 113:
2430 case 114:
2431 case 115:
2432 case 116:
2433 case 117:
2434 case 118: {
2435 opencpn_plugin_18 *ppi =
2436 dynamic_cast<opencpn_plugin_18 *>(pic->m_pplugin);
2437 if (ppi) ppi->SetPositionFixEx(pfix_ex);
2438 break;
2439 }
2440 default:
2441 break;
2442 }
2443 }
2444 }
2445 }
2446}
2447
2448void PlugInManager::SendActiveLegInfoToAllPlugIns(const ActiveLegDat *leg_info) {
2450 leg.Btw = leg_info->Btw;
2451 leg.Dtw = leg_info->Dtw;
2452 leg.wp_name = leg_info->wp_name;
2453 leg.Xte = leg_info->Xte;
2454 leg.arrival = leg_info->arrival;
2455 auto plugin_array = PluginLoader::getInstance()->GetPlugInArray();
2456 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
2457 PlugInContainer *pic = plugin_array->Item(i);
2458 if (pic->m_bEnabled && pic->m_bInitState) {
2459 if (pic->m_cap_flag & WANTS_NMEA_EVENTS) {
2460 switch (pic->m_api_version) {
2461 case 108:
2462 case 109:
2463 case 110:
2464 case 111:
2465 case 112:
2466 case 113:
2467 case 114:
2468 case 115:
2469 case 116:
2470 break;
2471 case 117:
2472 case 118: {
2473 opencpn_plugin_117 *ppi =
2474 dynamic_cast<opencpn_plugin_117 *>(pic->m_pplugin);
2475 if (ppi) ppi->SetActiveLegInfo(leg);
2476 break;
2477 }
2478 default:
2479 break;
2480 }
2481 }
2482 }
2483 }
2484}
2485
2486void PlugInManager::SendResizeEventToAllPlugIns(int x, int y) {
2487 auto plugin_array = PluginLoader::getInstance()->GetPlugInArray();
2488 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
2489 PlugInContainer *pic = plugin_array->Item(i);
2490 if (pic->m_bEnabled && pic->m_bInitState)
2491 pic->m_pplugin->ProcessParentResize(x, y);
2492 }
2493}
2494
2495void PlugInManager::SetColorSchemeForAllPlugIns(ColorScheme cs) {
2496 auto plugin_array = PluginLoader::getInstance()->GetPlugInArray();
2497 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
2498 PlugInContainer *pic = plugin_array->Item(i);
2499 if (pic->m_bEnabled && pic->m_bInitState)
2500 pic->m_pplugin->SetColorScheme((PI_ColorScheme)cs);
2501 }
2502}
2503
2504void PlugInManager::PrepareAllPluginContextMenus() {
2505 int canvasIndex = gFrame->GetCanvasIndexUnderMouse();
2506 if (canvasIndex < 0) return;
2507
2508 auto plugin_array = PluginLoader::getInstance()->GetPlugInArray();
2509 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
2510 PlugInContainer *pic = plugin_array->Item(i);
2511 if (pic->m_bEnabled && pic->m_bInitState) {
2512 if (pic->m_cap_flag & INSTALLS_CONTEXTMENU_ITEMS) {
2513 switch (pic->m_api_version) {
2514 case 116:
2515 case 117:
2516 case 118: {
2517 opencpn_plugin_116 *ppi =
2518 dynamic_cast<opencpn_plugin_116 *>(pic->m_pplugin);
2519 if (ppi) ppi->PrepareContextMenu(canvasIndex);
2520 break;
2521 }
2522 default:
2523 break;
2524 }
2525 }
2526 }
2527 }
2528}
2529
2530
2531//FIXME (dave) unused?
2532void PlugInManager::SendSKConfigToAllPlugIns() {
2533 // Send the current ownship MMSI, encoded as sK, to all PlugIns
2534 wxJSONValue v;
2535 v[_T("self")] = g_ownshipMMSI_SK;
2536 wxJSONWriter w;
2537 wxString out;
2538 w.Write(v, out);
2539 SendMessageToAllPlugins(wxString(_T("OCPN_CORE_SIGNALK")), out);
2540}
2541
2542void PlugInManager::SendBaseConfigToAllPlugIns() {
2543 // Send the current run-time configuration to all PlugIns
2544 wxJSONValue v;
2545 v[_T("OpenCPN Version Major")] = VERSION_MAJOR;
2546 v[_T("OpenCPN Version Minor")] = VERSION_MINOR;
2547 v[_T("OpenCPN Version Patch")] = VERSION_PATCH;
2548 v[_T("OpenCPN Version Date")] = VERSION_DATE;
2549 v[_T("OpenCPN Version Full")] = VERSION_FULL;
2550
2551 // Some useful display metrics
2552 if (g_MainToolbar) {
2553 v[_T("OpenCPN Toolbar Width")] = g_MainToolbar->GetSize().x;
2554 v[_T("OpenCPN Toolbar Height")] = g_MainToolbar->GetSize().y;
2555 v[_T("OpenCPN Toolbar PosnX")] = g_MainToolbar->GetPosition().x;
2556 v[_T("OpenCPN Toolbar PosnY")] = g_MainToolbar->GetPosition().y;
2557 }
2558
2559 // Some rendering parameters
2560 v[_T("OpenCPN Zoom Mod Vector")] = g_chart_zoom_modifier_vector;
2561 v[_T("OpenCPN Zoom Mod Other")] = g_chart_zoom_modifier_raster;
2562 v[_T("OpenCPN Scale Factor Exp")] = g_Platform->GetChartScaleFactorExp(g_ChartScaleFactor);
2563 v[_T("OpenCPN Display Width")] = (int)g_display_size_mm;
2564 v[_T("OpenCPN Content Scale Factor")] = OCPN_GetDisplayContentScaleFactor();
2565 v[_T("OpenCPN Display DIP Scale Factor")] = OCPN_GetWinDIPScaleFactor();
2566
2567 wxJSONWriter w;
2568 wxString out;
2569 w.Write(v, out);
2570 SendMessageToAllPlugins(wxString(_T("OpenCPN Config")), out);
2571}
2572
2573void PlugInManager::SendS52ConfigToAllPlugIns(bool bReconfig) {
2574 // Send the current run-time configuration to all PlugIns
2575 wxJSONValue v;
2576 v[_T("OpenCPN Version Major")] = VERSION_MAJOR;
2577 v[_T("OpenCPN Version Minor")] = VERSION_MINOR;
2578 v[_T("OpenCPN Version Patch")] = VERSION_PATCH;
2579 v[_T("OpenCPN Version Date")] = VERSION_DATE;
2580 v[_T("OpenCPN Version Full")] = VERSION_FULL;
2581
2582 // S52PLIB state
2583 if (ps52plib) {
2584 // v[_T("OpenCPN S52PLIB ShowText")] = ps52plib->GetShowS57Text();
2585 // v[_T("OpenCPN S52PLIB ShowSoundings")] =
2586 // ps52plib->GetShowSoundings(); v[_T("OpenCPN S52PLIB ShowLights")]
2587 // = !ps52plib->GetLightsOff();
2588 v[_T("OpenCPN S52PLIB ShowAnchorConditions")] = ps52plib->GetAnchorOn();
2589 v[_T("OpenCPN S52PLIB ShowQualityOfData")] = ps52plib->GetQualityOfData();
2590 // v[_T("OpenCPN S52PLIB DisplayCategory")] =
2591 // ps52plib->GetDisplayCategory();
2592
2593 // Global parameters
2594 v[_T("OpenCPN S52PLIB MetaDisplay")] = ps52plib->m_bShowMeta;
2595 v[_T("OpenCPN S52PLIB DeclutterText")] = ps52plib->m_bDeClutterText;
2596 v[_T("OpenCPN S52PLIB ShowNationalText")] = ps52plib->m_bShowNationalTexts;
2597 v[_T("OpenCPN S52PLIB ShowImportantTextOnly")] =
2598 ps52plib->m_bShowS57ImportantTextOnly;
2599 v[_T("OpenCPN S52PLIB UseSCAMIN")] = ps52plib->m_bUseSCAMIN;
2600 v[_T("OpenCPN S52PLIB UseSUPER_SCAMIN")] = ps52plib->m_bUseSUPER_SCAMIN;
2601 v[_T("OpenCPN S52PLIB SymbolStyle")] = ps52plib->m_nSymbolStyle;
2602 v[_T("OpenCPN S52PLIB BoundaryStyle")] = ps52plib->m_nBoundaryStyle;
2603 v[_T("OpenCPN S52PLIB ColorShades")] = S52_getMarinerParam(S52_MAR_TWO_SHADES);
2604 v[_T("OpenCPN S52PLIB Safety Depth")] = (double)S52_getMarinerParam(S52_MAR_SAFETY_DEPTH);
2605 v[_T("OpenCPN S52PLIB Shallow Contour")] = (double)S52_getMarinerParam(S52_MAR_SHALLOW_CONTOUR);
2606 v[_T("OpenCPN S52PLIB Deep Contour")] = (double)S52_getMarinerParam(S52_MAR_DEEP_CONTOUR);
2607 }
2608
2609 // Notify plugins that S52PLIB may have reconfigured global options
2610 v[_T("OpenCPN S52PLIB GlobalReconfig")] = bReconfig;
2611
2612 wxJSONWriter w;
2613 wxString out;
2614 w.Write(v, out);
2615 SendMessageToAllPlugins(wxString(_T("OpenCPN Config")), out);
2616}
2617
2618void PlugInManager::NotifyAuiPlugIns(void) {
2619 auto plugin_array = PluginLoader::getInstance()->GetPlugInArray();
2620 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
2621 PlugInContainer *pic = plugin_array->Item(i);
2622 if (pic->m_bEnabled && pic->m_bInitState &&
2623 (pic->m_cap_flag & USES_AUI_MANAGER))
2624 pic->m_pplugin->UpdateAuiStatus();
2625 }
2626}
2627
2628int PlugInManager::AddToolbarTool(wxString label, wxBitmap *bitmap,
2629 wxBitmap *bmpRollover, wxItemKind kind,
2630 wxString shortHelp, wxString longHelp,
2631 wxObject *clientData, int position,
2632 int tool_sel, opencpn_plugin *pplugin) {
2634 pttc->label = label;
2635
2636 if (!bitmap->IsOk()) {
2637 ocpnStyle::Style *style = g_StyleManager->GetCurrentStyle();
2638 pttc->bitmap_day = new wxBitmap(style->GetIcon(_T("default_pi")));
2639 } else {
2640 // Force a non-reference copy of the bitmap from the PlugIn
2641 pttc->bitmap_day = new wxBitmap(*bitmap);
2642 pttc->bitmap_day->UnShare();
2643 }
2644
2645 if (!bmpRollover->IsOk()) {
2646 ocpnStyle::Style *style = g_StyleManager->GetCurrentStyle();
2647 pttc->bitmap_Rollover_day = new wxBitmap(style->GetIcon(_T("default_pi")));
2648 } else {
2649 // Force a non-reference copy of the bitmap from the PlugIn
2650 pttc->bitmap_Rollover_day = new wxBitmap(*bmpRollover);
2651 pttc->bitmap_Rollover_day->UnShare();
2652 }
2653
2654 pttc->bitmap_dusk = BuildDimmedToolBitmap(pttc->bitmap_day, 128);
2655 pttc->bitmap_night = BuildDimmedToolBitmap(pttc->bitmap_day, 32);
2656 pttc->bitmap_Rollover_dusk =
2657 BuildDimmedToolBitmap(pttc->bitmap_Rollover_day, 128);
2658 pttc->bitmap_Rollover_night =
2659 BuildDimmedToolBitmap(pttc->bitmap_Rollover_day, 32);
2660
2661 pttc->kind = kind;
2662 pttc->shortHelp = shortHelp;
2663 pttc->longHelp = longHelp;
2664 pttc->clientData = clientData;
2665 pttc->position = position;
2666 pttc->m_pplugin = pplugin;
2667 pttc->tool_sel = tool_sel;
2668 pttc->b_viz = true;
2669 pttc->b_toggle = false;
2670 pttc->id = m_plugin_tool_id_next;
2671
2672 m_PlugInToolbarTools.Add(pttc);
2673
2674 m_plugin_tool_id_next++;
2675
2676 return pttc->id;
2677}
2678
2679int PlugInManager::AddToolbarTool(wxString label, wxString SVGfile,
2680 wxString SVGRolloverfile,
2681 wxString SVGToggledfile, wxItemKind kind,
2682 wxString shortHelp, wxString longHelp,
2683 wxObject *clientData, int position,
2684 int tool_sel, opencpn_plugin *pplugin) {
2686 pttc->label = label;
2687
2688 pttc->pluginNormalIconSVG = SVGfile;
2689 pttc->pluginRolloverIconSVG = SVGRolloverfile;
2690 pttc->pluginToggledIconSVG = SVGToggledfile;
2691
2692 // Build a set of bitmaps based on the generic "puzzle piece" icon,
2693 // In case there is some problem with the SVG file(s) specified.
2694 ocpnStyle::Style *style = g_StyleManager->GetCurrentStyle();
2695 pttc->bitmap_day = new wxBitmap(style->GetIcon(_T("default_pi")));
2696 pttc->bitmap_Rollover_day = new wxBitmap(style->GetIcon(_T("default_pi")));
2697
2698 pttc->bitmap_dusk = BuildDimmedToolBitmap(pttc->bitmap_day, 128);
2699 pttc->bitmap_night = BuildDimmedToolBitmap(pttc->bitmap_day, 32);
2700 pttc->bitmap_Rollover_day = new wxBitmap(*pttc->bitmap_day);
2701 pttc->bitmap_Rollover_dusk =
2702 BuildDimmedToolBitmap(pttc->bitmap_Rollover_day, 128);
2703 pttc->bitmap_Rollover_night =
2704 BuildDimmedToolBitmap(pttc->bitmap_Rollover_day, 32);
2705
2706 pttc->kind = kind;
2707 pttc->shortHelp = shortHelp;
2708 pttc->longHelp = longHelp;
2709 pttc->clientData = clientData;
2710 pttc->position = position;
2711 pttc->m_pplugin = pplugin;
2712 pttc->tool_sel = tool_sel;
2713 pttc->b_viz = true;
2714 pttc->b_toggle = false;
2715 pttc->id = m_plugin_tool_id_next;
2716
2717 m_PlugInToolbarTools.Add(pttc);
2718
2719 m_plugin_tool_id_next++;
2720
2721 return pttc->id;
2722}
2723
2724void PlugInManager::RemoveToolbarTool(int tool_id) {
2725 for (unsigned int i = 0; i < m_PlugInToolbarTools.GetCount(); i++) {
2726 PlugInToolbarToolContainer *pttc = m_PlugInToolbarTools[i];
2727 {
2728 if (pttc->id == tool_id) {
2729 m_PlugInToolbarTools.Remove(pttc);
2730 delete pttc;
2731 break;
2732 }
2733 }
2734 }
2735
2736 pParent->RequestNewToolbars();
2737}
2738
2739void PlugInManager::SetToolbarToolViz(int item, bool viz) {
2740 for (unsigned int i = 0; i < m_PlugInToolbarTools.GetCount(); i++) {
2741 PlugInToolbarToolContainer *pttc = m_PlugInToolbarTools[i];
2742 {
2743 if (pttc->id == item) {
2744 pttc->b_viz = viz;
2745
2746 // Apply the change
2747 pParent->RequestNewToolbars();
2748
2749 break;
2750 }
2751 }
2752 }
2753}
2754
2755void PlugInManager::SetToolbarItemState(int item, bool toggle) {
2756 for (unsigned int i = 0; i < m_PlugInToolbarTools.GetCount(); i++) {
2757 PlugInToolbarToolContainer *pttc = m_PlugInToolbarTools[i];
2758 {
2759 if (pttc->id == item) {
2760 pttc->b_toggle = toggle;
2761 pParent->SetMasterToolbarItemState(item, toggle);
2762 break;
2763 }
2764 }
2765 }
2766}
2767
2768void PlugInManager::SetToolbarItemBitmaps(int item, wxBitmap *bitmap,
2769 wxBitmap *bmpRollover) {
2770 for (unsigned int i = 0; i < m_PlugInToolbarTools.GetCount(); i++) {
2771 PlugInToolbarToolContainer *pttc = m_PlugInToolbarTools[i];
2772 {
2773 if (pttc->id == item) {
2774 delete pttc->bitmap_day;
2775 delete pttc->bitmap_dusk;
2776 delete pttc->bitmap_night;
2777 delete pttc->bitmap_Rollover_day;
2778
2779 if (!bitmap->IsOk()) {
2780 ocpnStyle::Style *style = g_StyleManager->GetCurrentStyle();
2781 pttc->bitmap_day = new wxBitmap(style->GetIcon(_T("default_pi")));
2782 } else {
2783 // Force a non-reference copy of the bitmap from the PlugIn
2784 pttc->bitmap_day = new wxBitmap(*bitmap);
2785 pttc->bitmap_day->UnShare();
2786 }
2787
2788 if (!bmpRollover->IsOk()) {
2789 ocpnStyle::Style *style = g_StyleManager->GetCurrentStyle();
2790 pttc->bitmap_Rollover_day =
2791 new wxBitmap(style->GetIcon(_T("default_pi")));
2792 } else {
2793 // Force a non-reference copy of the bitmap from the PlugIn
2794 pttc->bitmap_Rollover_day = new wxBitmap(*bmpRollover);
2795 pttc->bitmap_Rollover_day->UnShare();
2796 }
2797
2798 pttc->bitmap_dusk = BuildDimmedToolBitmap(pttc->bitmap_day, 128);
2799 pttc->bitmap_night = BuildDimmedToolBitmap(pttc->bitmap_day, 32);
2800
2801 pParent->SetToolbarItemBitmaps(item, pttc->bitmap_day,
2802 pttc->bitmap_Rollover_day);
2803 break;
2804 }
2805 }
2806 }
2807}
2808
2809void PlugInManager::SetToolbarItemBitmaps(int item, wxString SVGfile,
2810 wxString SVGfileRollover,
2811 wxString SVGfileToggled) {
2812 for (unsigned int i = 0; i < m_PlugInToolbarTools.GetCount(); i++) {
2813 PlugInToolbarToolContainer *pttc = m_PlugInToolbarTools[i];
2814 {
2815 if (pttc->id == item) {
2816 pttc->pluginNormalIconSVG = SVGfile;
2817 pttc->pluginRolloverIconSVG = SVGfileRollover;
2818 pttc->pluginToggledIconSVG = SVGfileToggled;
2819 pParent->SetToolbarItemSVG(item, pttc->pluginNormalIconSVG,
2820 pttc->pluginRolloverIconSVG,
2821 pttc->pluginToggledIconSVG);
2822 break;
2823 }
2824 }
2825 }
2826}
2827
2828opencpn_plugin *PlugInManager::FindToolOwner(const int id) {
2829 for (unsigned int i = 0; i < m_PlugInToolbarTools.GetCount(); i++) {
2830 PlugInToolbarToolContainer *pc = m_PlugInToolbarTools[i];
2831 if (id == pc->id) return pc->m_pplugin;
2832 }
2833
2834 return NULL;
2835}
2836
2837wxString PlugInManager::GetToolOwnerCommonName(const int id) {
2838 opencpn_plugin *ppi = FindToolOwner(id);
2839 if (ppi) {
2840 auto plugin_array = PluginLoader::getInstance()->GetPlugInArray();
2841 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
2842 PlugInContainer *pic = plugin_array->Item(i);
2843 if (pic && (pic->m_pplugin == ppi)) return pic->m_common_name;
2844 }
2845 }
2846
2847 return wxEmptyString;
2848}
2849
2850wxString PlugInManager::GetLastError() { return m_last_error_string; }
2851
2852wxBitmap *PlugInManager::BuildDimmedToolBitmap(wxBitmap *pbmp_normal,
2853 unsigned char dim_ratio) {
2854 wxImage img_dup = pbmp_normal->ConvertToImage();
2855
2856 if (!img_dup.IsOk()) return NULL;
2857
2858 if (dim_ratio < 200) {
2859 // Create a dimmed version of the image/bitmap
2860 int gimg_width = img_dup.GetWidth();
2861 int gimg_height = img_dup.GetHeight();
2862
2863 double factor = (double)(dim_ratio) / 256.0;
2864
2865 for (int iy = 0; iy < gimg_height; iy++) {
2866 for (int ix = 0; ix < gimg_width; ix++) {
2867 if (!img_dup.IsTransparent(ix, iy)) {
2868 wxImage::RGBValue rgb(img_dup.GetRed(ix, iy),
2869 img_dup.GetGreen(ix, iy),
2870 img_dup.GetBlue(ix, iy));
2871 wxImage::HSVValue hsv = wxImage::RGBtoHSV(rgb);
2872 hsv.value = hsv.value * factor;
2873 wxImage::RGBValue nrgb = wxImage::HSVtoRGB(hsv);
2874 img_dup.SetRGB(ix, iy, nrgb.red, nrgb.green, nrgb.blue);
2875 }
2876 }
2877 }
2878 }
2879
2880 // Make a bitmap
2881 wxBitmap *ptoolBarBitmap;
2882
2883#ifdef __WXMSW__
2884 wxBitmap tbmp(img_dup.GetWidth(), img_dup.GetHeight(), -1);
2885 wxMemoryDC dwxdc;
2886 dwxdc.SelectObject(tbmp);
2887
2888 ptoolBarBitmap = new wxBitmap(img_dup, (wxDC &)dwxdc);
2889#else
2890 ptoolBarBitmap = new wxBitmap(img_dup);
2891#endif
2892
2893 // store it
2894 return ptoolBarBitmap;
2895}
2896
2897wxArrayString PlugInManager::GetPlugInChartClassNameArray(void) {
2898 wxArrayString array;
2899 auto plugin_array = PluginLoader::getInstance()->GetPlugInArray();
2900 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
2901 PlugInContainer *pic = plugin_array->Item(i);
2902 if (pic->m_bEnabled && pic->m_bInitState &&
2903 ((pic->m_cap_flag & INSTALLS_PLUGIN_CHART) ||
2904 (pic->m_cap_flag & INSTALLS_PLUGIN_CHART_GL))) {
2905 wxArrayString carray = pic->m_pplugin->GetDynamicChartClassNameArray();
2906
2907 for (unsigned int j = 0; j < carray.GetCount(); j++) {
2908 array.Add(carray[j]);
2909 }
2910 }
2911 }
2912
2913 // Scrub the list for duplicates
2914 // Corrects a flaw in BSB4 and NVC PlugIns
2915 unsigned int j = 0;
2916 while (j < array.GetCount()) {
2917 wxString test = array[j];
2918 unsigned int k = j + 1;
2919 while (k < array.GetCount()) {
2920 if (test == array[k]) {
2921 array.RemoveAt(k);
2922 j = -1;
2923 break;
2924 } else
2925 k++;
2926 }
2927
2928 j++;
2929 }
2930
2931 return array;
2932}
2933
2934//----------------------------------------------------------------------------------------------------------
2935// The PlugIn CallBack API Implementation
2936// The definitions of this API are found in ocpn_plugin.h
2937//----------------------------------------------------------------------------------------------------------
2938
2939int InsertPlugInTool(wxString label, wxBitmap *bitmap, wxBitmap *bmpRollover,
2940 wxItemKind kind, wxString shortHelp, wxString longHelp,
2941 wxObject *clientData, int position, int tool_sel,
2942 opencpn_plugin *pplugin) {
2943 if (s_ppim)
2944 return s_ppim->AddToolbarTool(label, bitmap, bmpRollover, kind, shortHelp,
2945 longHelp, clientData, position, tool_sel,
2946 pplugin);
2947 else
2948 return -1;
2949}
2950
2951void RemovePlugInTool(int tool_id) {
2952 if (s_ppim) s_ppim->RemoveToolbarTool(tool_id);
2953}
2954
2955void SetToolbarToolViz(int item, bool viz) {
2956 if (s_ppim) s_ppim->SetToolbarToolViz(item, viz);
2957}
2958
2959void SetToolbarItemState(int item, bool toggle) {
2960 if (s_ppim) s_ppim->SetToolbarItemState(item, toggle);
2961}
2962
2963void SetToolbarToolBitmaps(int item, wxBitmap *bitmap, wxBitmap *bmpRollover) {
2964 if (s_ppim) s_ppim->SetToolbarItemBitmaps(item, bitmap, bmpRollover);
2965}
2966
2967int InsertPlugInToolSVG(wxString label, wxString SVGfile,
2968 wxString SVGfileRollover, wxString SVGfileToggled,
2969 wxItemKind kind, wxString shortHelp, wxString longHelp,
2970 wxObject *clientData, int position, int tool_sel,
2971 opencpn_plugin *pplugin) {
2972 if (s_ppim)
2973 return s_ppim->AddToolbarTool(label, SVGfile, SVGfileRollover,
2974 SVGfileToggled, kind, shortHelp, longHelp,
2975 clientData, position, tool_sel, pplugin);
2976 else
2977 return -1;
2978}
2979
2980void SetToolbarToolBitmapsSVG(int item, wxString SVGfile,
2981 wxString SVGfileRollover,
2982 wxString SVGfileToggled) {
2983 if (s_ppim)
2984 s_ppim->SetToolbarItemBitmaps(item, SVGfile, SVGfileRollover,
2985 SVGfileToggled);
2986}
2987
2988int AddCanvasMenuItem(wxMenuItem *pitem, opencpn_plugin *pplugin,
2989 const char *name) {
2990 if (s_ppim)
2991 return s_ppim->AddCanvasContextMenuItem(pitem, pplugin, name);
2992 else
2993 return -1;
2994}
2995
2996void SetCanvasMenuItemViz(int item, bool viz, const char *name) {
2997 if (s_ppim) s_ppim->SetCanvasContextMenuItemViz(item, viz, name);
2998}
2999
3000void SetCanvasMenuItemGrey(int item, bool grey, const char *name) {
3001 if (s_ppim) s_ppim->SetCanvasContextMenuItemGrey(item, grey, name);
3002}
3003
3004void RemoveCanvasMenuItem(int item, const char *name) {
3005 if (s_ppim) s_ppim->RemoveCanvasContextMenuItem(item, name);
3006}
3007
3008int AddCanvasContextMenuItem(wxMenuItem *pitem, opencpn_plugin *pplugin) {
3009 /* main context popup menu */
3010 return AddCanvasMenuItem(pitem, pplugin, "");
3011}
3012
3013void SetCanvasContextMenuItemViz(int item, bool viz) {
3014 SetCanvasMenuItemViz(item, viz);
3015}
3016
3017void SetCanvasContextMenuItemGrey(int item, bool grey) {
3018 SetCanvasMenuItemGrey(item, grey);
3019}
3020
3021void RemoveCanvasContextMenuItem(int item) { RemoveCanvasMenuItem(item); }
3022
3023wxFileConfig *GetOCPNConfigObject(void) {
3024 if (s_ppim)
3025 return pConfig; // return the global application config object
3026 else
3027 return NULL;
3028}
3029
3030wxWindow *GetOCPNCanvasWindow() {
3031 wxWindow *pret = NULL;
3032 if (s_ppim) {
3033 MyFrame *pFrame = s_ppim->GetParentFrame();
3034 pret = (wxWindow *)pFrame->GetPrimaryCanvas();
3035 }
3036 return pret;
3037}
3038
3039void RequestRefresh(wxWindow *win) {
3040 if (win) win->Refresh();
3041}
3042
3043void GetCanvasPixLL(PlugIn_ViewPort *vp, wxPoint *pp, double lat, double lon) {
3044 // Make enough of an application viewport to run its method....
3045 ViewPort ocpn_vp;
3046 ocpn_vp.clat = vp->clat;
3047 ocpn_vp.clon = vp->clon;
3048 ocpn_vp.m_projection_type = vp->m_projection_type;
3049 ocpn_vp.view_scale_ppm = vp->view_scale_ppm;
3050 ocpn_vp.skew = vp->skew;
3051 ocpn_vp.rotation = vp->rotation;
3052 ocpn_vp.pix_width = vp->pix_width;
3053 ocpn_vp.pix_height = vp->pix_height;
3054
3055 wxPoint ret = ocpn_vp.GetPixFromLL(lat, lon);
3056 pp->x = ret.x;
3057 pp->y = ret.y;
3058}
3059
3060void GetDoubleCanvasPixLL(PlugIn_ViewPort *vp, wxPoint2DDouble *pp, double lat,
3061 double lon) {
3062 // Make enough of an application viewport to run its method....
3063 ViewPort ocpn_vp;
3064 ocpn_vp.clat = vp->clat;
3065 ocpn_vp.clon = vp->clon;
3066 ocpn_vp.m_projection_type = vp->m_projection_type;
3067 ocpn_vp.view_scale_ppm = vp->view_scale_ppm;
3068 ocpn_vp.skew = vp->skew;
3069 ocpn_vp.rotation = vp->rotation;
3070 ocpn_vp.pix_width = vp->pix_width;
3071 ocpn_vp.pix_height = vp->pix_height;
3072
3073 *pp = ocpn_vp.GetDoublePixFromLL(lat, lon);
3074}
3075
3076void GetCanvasLLPix(PlugIn_ViewPort *vp, wxPoint p, double *plat,
3077 double *plon) {
3078 // Make enough of an application viewport to run its method....
3079 ViewPort ocpn_vp;
3080 ocpn_vp.clat = vp->clat;
3081 ocpn_vp.clon = vp->clon;
3082 ocpn_vp.m_projection_type = vp->m_projection_type;
3083 ocpn_vp.view_scale_ppm = vp->view_scale_ppm;
3084 ocpn_vp.skew = vp->skew;
3085 ocpn_vp.rotation = vp->rotation;
3086 ocpn_vp.pix_width = vp->pix_width;
3087 ocpn_vp.pix_height = vp->pix_height;
3088
3089 return ocpn_vp.GetLLFromPix(p, plat, plon);
3090}
3091
3092bool GetGlobalColor(wxString colorName, wxColour *pcolour) {
3093 wxColour c = GetGlobalColor(colorName);
3094 *pcolour = c;
3095
3096 return true;
3097}
3098
3099wxFont *OCPNGetFont(wxString TextElement, int default_size) {
3100 return FontMgr::Get().GetFont(TextElement, default_size);
3101}
3102
3103wxFont *GetOCPNScaledFont_PlugIn(wxString TextElement, int default_size) {
3104 return GetOCPNScaledFont(TextElement, default_size);
3105}
3106
3107double GetOCPNGUIToolScaleFactor_PlugIn(int GUIScaleFactor) {
3108 return g_Platform->GetToolbarScaleFactor(GUIScaleFactor);
3109}
3110
3111double GetOCPNGUIToolScaleFactor_PlugIn() {
3112 return g_Platform->GetToolbarScaleFactor(g_GUIScaleFactor);
3113}
3114
3115float GetOCPNChartScaleFactor_Plugin() {
3116 return g_Platform->GetChartScaleFactorExp(g_ChartScaleFactor);
3117}
3118
3119wxFont GetOCPNGUIScaledFont_PlugIn(wxString item) {
3120 return GetOCPNGUIScaledFont(item);
3121}
3122
3123bool AddPersistentFontKey(wxString TextElement) {
3124 return FontMgr::Get().AddAuxKey(TextElement);
3125}
3126
3127wxString GetActiveStyleName() {
3128 if (g_StyleManager)
3129 return g_StyleManager->GetCurrentStyle()->name;
3130 else
3131 return _T("");
3132}
3133
3134wxBitmap GetBitmapFromSVGFile(wxString filename, unsigned int width,
3135 unsigned int height) {
3136 wxBitmap bmp = LoadSVG(filename, width, height);
3137
3138 if(bmp.IsOk())
3139 return bmp;
3140 else {
3141 // On error in requested width/height parameters,
3142 // try to find and use dimensions embedded in the SVG file
3143 unsigned int w, h;
3144 SVGDocumentPixelSize(filename, w, h);
3145 return LoadSVG(filename, w, h);
3146 }
3147}
3148
3149bool IsTouchInterface_PlugIn(void) { return g_btouch; }
3150
3151wxColour GetFontColour_PlugIn(wxString TextElement) {
3152 return FontMgr::Get().GetFontColor(TextElement);
3153}
3154
3155wxString *GetpSharedDataLocation(void) {
3156 return g_Platform->GetSharedDataDirPtr();
3157}
3158
3159wxString *GetpPrivateApplicationDataLocation(void) {
3160 return g_Platform->GetPrivateDataDirPtr();
3161}
3162
3163ArrayOfPlugIn_AIS_Targets *GetAISTargetArray(void) {
3164 if (!g_pAIS) return NULL;
3165
3166 ArrayOfPlugIn_AIS_Targets *pret = new ArrayOfPlugIn_AIS_Targets;
3167
3168 // Iterate over the AIS Target Hashmap
3169 for (const auto &it : g_pAIS->GetTargetList()) {
3170 auto td = it.second;
3171 PlugIn_AIS_Target *ptarget = Create_PI_AIS_Target(td.get());
3172 pret->Add(ptarget);
3173 }
3174
3175// Test one alarm target
3176#if 0
3177 AisTargetData td;
3178 td.n_alarm_state = AIS_ALARM_SET;
3179 PlugIn_AIS_Target *ptarget = Create_PI_AIS_Target(&td);
3180 pret->Add(ptarget);
3181#endif
3182 return pret;
3183}
3184
3185wxAuiManager *GetFrameAuiManager(void) { return g_pauimgr; }
3186
3187bool AddLocaleCatalog(wxString catalog) {
3188#if wxUSE_XLOCALE || !wxCHECK_VERSION(3, 0, 0)
3189
3190 if (plocale_def_lang) {
3191 // Add this catalog to the persistent catalog array
3192 g_locale_catalog_array.Add(catalog);
3193
3194 return plocale_def_lang->AddCatalog(catalog);
3195 } else
3196#endif
3197 return false;
3198}
3199
3200void PushNMEABuffer(wxString buf) {
3201
3202 std::string full_sentence = buf.ToStdString();
3203
3204 if ((full_sentence[0] == '$') || (full_sentence[0] == '!')) { // Sanity check
3205 std::string identifier;
3206 // We notify based on full message, including the Talker ID
3207 identifier = full_sentence.substr(1, 5);
3208
3209 // notify message listener and also "ALL" N0183 messages, to support plugin
3210 // API using original talker id
3211 auto address = std::make_shared<NavAddr0183>("virtual");
3212 auto msg = std::make_shared<const Nmea0183Msg>(identifier, full_sentence,
3213 address);
3214 auto msg_all = std::make_shared<const Nmea0183Msg>(*msg, "ALL");
3215
3216 auto& msgbus = NavMsgBus::GetInstance();
3217
3218 msgbus.Notify(std::move(msg));
3219 msgbus.Notify(std::move(msg_all));
3220 }
3221}
3222
3223wxXmlDocument GetChartDatabaseEntryXML(int dbIndex, bool b_getGeom) {
3224 wxXmlDocument doc = ChartData->GetXMLDescription(dbIndex, b_getGeom);
3225
3226 return doc;
3227}
3228
3229bool UpdateChartDBInplace(wxArrayString dir_array, bool b_force_update,
3230 bool b_ProgressDialog) {
3231 // Make an array of CDI
3232 ArrayOfCDI ChartDirArray;
3233 for (unsigned int i = 0; i < dir_array.GetCount(); i++) {
3234 wxString dirname = dir_array[i];
3235 ChartDirInfo cdi;
3236 cdi.fullpath = dirname;
3237 cdi.magic_number = _T("");
3238 ChartDirArray.Add(cdi);
3239 }
3240
3241 bool b_ret = gFrame->UpdateChartDatabaseInplace(ChartDirArray, b_force_update,
3242 b_ProgressDialog,
3243 ChartData->GetDBFileName());
3244
3245 gFrame->ChartsRefresh();
3246
3247 return b_ret;
3248}
3249
3250wxArrayString GetChartDBDirArrayString() {
3251 return ChartData->GetChartDirArrayString();
3252}
3253
3254int AddChartToDBInPlace(wxString &full_path, bool b_RefreshCanvas) {
3255 // extract the path from the chart name
3256 wxFileName fn(full_path);
3257 wxString fdir = fn.GetPath();
3258
3259 bool bret = false;
3260 if (ChartData) {
3261 bret = ChartData->AddSingleChart(full_path);
3262
3263 if (bret) {
3264 // Save to disk
3265 pConfig->UpdateChartDirs(ChartData->GetChartDirArray());
3266 ChartData->SaveBinary(ChartListFileName);
3267
3268 // Completely reload the chart database, for a fresh start
3269 ArrayOfCDI XnewChartDirArray;
3270 pConfig->LoadChartDirArray(XnewChartDirArray);
3271 delete ChartData;
3272 ChartData = new ChartDB();
3273 ChartData->LoadBinary(ChartListFileName, XnewChartDirArray);
3274
3275 // Update group contents
3276 if (g_pGroupArray) ChartData->ApplyGroupArray(g_pGroupArray);
3277
3278 if (g_boptionsactive) {
3279 g_options->UpdateDisplayedChartDirList(ChartData->GetChartDirArray());
3280 }
3281
3282 if (b_RefreshCanvas || !gFrame->GetPrimaryCanvas()->GetQuiltMode()) {
3283 gFrame->ChartsRefresh();
3284 }
3285 }
3286 }
3287 return bret;
3288}
3289
3290int RemoveChartFromDBInPlace(wxString &full_path) {
3291 bool bret = false;
3292 if (ChartData) {
3293 bret = ChartData->RemoveSingleChart(full_path);
3294
3295 // Save to disk
3296 pConfig->UpdateChartDirs(ChartData->GetChartDirArray());
3297 ChartData->SaveBinary(ChartListFileName);
3298
3299 // Completely reload the chart database, for a fresh start
3300 ArrayOfCDI XnewChartDirArray;
3301 pConfig->LoadChartDirArray(XnewChartDirArray);
3302 delete ChartData;
3303 ChartData = new ChartDB();
3304 ChartData->LoadBinary(ChartListFileName, XnewChartDirArray);
3305
3306 // Update group contents
3307 if (g_pGroupArray) ChartData->ApplyGroupArray(g_pGroupArray);
3308
3309 if (g_boptionsactive) {
3310 g_options->UpdateDisplayedChartDirList(ChartData->GetChartDirArray());
3311 }
3312
3313 gFrame->ChartsRefresh();
3314 }
3315
3316 return bret;
3317}
3318
3319wxString GetLocaleCanonicalName() { return g_locale; }
3320
3321void SendPluginMessage(wxString message_id, wxString message_body) {
3322 s_ppim->SendMessageToAllPlugins(message_id, message_body);
3323
3324 // We will send an event to the main application frame (gFrame)
3325 // for informational purposes.
3326 // Of course, gFrame is encouraged to use any or all the
3327 // data flying by if judged useful and dependable....
3328
3329 OCPN_MsgEvent Nevent(wxEVT_OCPN_MSG, 0);
3330 Nevent.SetID(message_id);
3331 Nevent.SetJSONText(message_body);
3332 gFrame->GetEventHandler()->AddPendingEvent(Nevent);
3333}
3334
3335void DimeWindow(wxWindow *win) { DimeControl(win); }
3336
3337void JumpToPosition(double lat, double lon, double scale) {
3338 gFrame->JumpToPosition(gFrame->GetFocusCanvas(), lat, lon, scale);
3339}
3340
3341/* API 1.9 */
3342wxScrolledWindow *AddOptionsPage(OptionsParentPI parent, wxString title) {
3343 if (!g_pOptions) return NULL;
3344
3345 size_t parentid;
3346 switch (parent) {
3347 case PI_OPTIONS_PARENT_DISPLAY:
3348 parentid = g_pOptions->m_pageDisplay;
3349 break;
3350 case PI_OPTIONS_PARENT_CONNECTIONS:
3351 parentid = g_pOptions->m_pageConnections;
3352 break;
3353 case PI_OPTIONS_PARENT_CHARTS:
3354 parentid = g_pOptions->m_pageCharts;
3355 break;
3356 case PI_OPTIONS_PARENT_SHIPS:
3357 parentid = g_pOptions->m_pageShips;
3358 break;
3359 case PI_OPTIONS_PARENT_UI:
3360 parentid = g_pOptions->m_pageUI;
3361 break;
3362 case PI_OPTIONS_PARENT_PLUGINS:
3363 parentid = g_pOptions->m_pagePlugins;
3364 break;
3365 default:
3366 wxLogMessage(
3367 _T("Error in PluginManager::AddOptionsPage: Unknown parent"));
3368 return NULL;
3369 break;
3370 }
3371
3372 return g_pOptions->AddPage(parentid, title);
3373}
3374
3375bool DeleteOptionsPage(wxScrolledWindow *page) {
3376 if (!g_pOptions) return false;
3377 return g_pOptions->DeletePluginPage(page);
3378}
3379
3380bool DecodeSingleVDOMessage(const wxString &str, PlugIn_Position_Fix_Ex *pos,
3381 wxString *accumulator) {
3382 if (!pos) return false;
3383
3384 GenericPosDatEx gpd;
3385 AisError nerr = AIS_GENERIC_ERROR;
3386 if (g_pAIS) nerr = g_pAIS->DecodeSingleVDO(str, &gpd, accumulator);
3387 if (nerr == AIS_NoError) {
3388 pos->Lat = gpd.kLat;
3389 pos->Lon = gpd.kLon;
3390 pos->Cog = gpd.kCog;
3391 pos->Sog = gpd.kSog;
3392 pos->Hdt = gpd.kHdt;
3393
3394 // Fill in the dummy values
3395 pos->FixTime = 0;
3396 pos->Hdm = 1000;
3397 pos->Var = 1000;
3398 pos->nSats = 0;
3399
3400 return true;
3401 }
3402
3403 return false;
3404}
3405
3406int GetChartbarHeight(void) {
3407 int val = 0;
3408 if (g_bShowChartBar) {
3409 ChartCanvas *cc = gFrame->GetPrimaryCanvas();
3410 if (cc && cc->GetPiano()) {
3411 val = cc->GetPiano()->GetHeight();
3412 }
3413 }
3414 return val;
3415}
3416
3417bool GetRoutepointGPX(RoutePoint *pRoutePoint, char *buffer,
3418 unsigned int buffer_length) {
3419 bool ret = false;
3420
3422 pgpx->AddGPXWaypoint(pRoutePoint);
3423 wxString gpxfilename = wxFileName::CreateTempFileName(wxT("gpx"));
3424 pgpx->SaveFile(gpxfilename);
3425 delete pgpx;
3426
3427 wxFFile gpxfile(gpxfilename);
3428 wxString s;
3429 if (gpxfile.ReadAll(&s)) {
3430 if (s.Length() < buffer_length) {
3431 strncpy(buffer, (const char *)s.mb_str(wxConvUTF8), buffer_length - 1);
3432 ret = true;
3433 }
3434 }
3435
3436 gpxfile.Close();
3437 ::wxRemoveFile(gpxfilename);
3438
3439 return ret;
3440}
3441
3442bool GetActiveRoutepointGPX(char *buffer, unsigned int buffer_length) {
3443 if (g_pRouteMan->IsAnyRouteActive())
3444 return GetRoutepointGPX(g_pRouteMan->GetpActivePoint(), buffer,
3445 buffer_length);
3446 else
3447 return false;
3448}
3449
3450void PositionBearingDistanceMercator_Plugin(double lat, double lon, double brg,
3451 double dist, double *dlat,
3452 double *dlon) {
3453 PositionBearingDistanceMercator(lat, lon, brg, dist, dlat, dlon);
3454}
3455
3456void DistanceBearingMercator_Plugin(double lat0, double lon0, double lat1,
3457 double lon1, double *brg, double *dist) {
3458 DistanceBearingMercator(lat0, lon0, lat1, lon1, brg, dist);
3459}
3460
3461double DistGreatCircle_Plugin(double slat, double slon, double dlat,
3462 double dlon) {
3463 return DistGreatCircle(slat, slon, dlat, dlon);
3464}
3465
3466void toTM_Plugin(float lat, float lon, float lat0, float lon0, double *x,
3467 double *y) {
3468 toTM(lat, lon, lat0, lon0, x, y);
3469}
3470
3471void fromTM_Plugin(double x, double y, double lat0, double lon0, double *lat,
3472 double *lon) {
3473 fromTM(x, y, lat0, lon0, lat, lon);
3474}
3475
3476void toSM_Plugin(double lat, double lon, double lat0, double lon0, double *x,
3477 double *y) {
3478 toSM(lat, lon, lat0, lon0, x, y);
3479}
3480
3481void fromSM_Plugin(double x, double y, double lat0, double lon0, double *lat,
3482 double *lon) {
3483 fromSM(x, y, lat0, lon0, lat, lon);
3484}
3485
3486void toSM_ECC_Plugin(double lat, double lon, double lat0, double lon0,
3487 double *x, double *y) {
3488 toSM_ECC(lat, lon, lat0, lon0, x, y);
3489}
3490
3491void fromSM_ECC_Plugin(double x, double y, double lat0, double lon0,
3492 double *lat, double *lon) {
3493 fromSM_ECC(x, y, lat0, lon0, lat, lon);
3494}
3495
3496double toUsrDistance_Plugin(double nm_distance, int unit) {
3497 return toUsrDistance(nm_distance, unit);
3498}
3499
3500double fromUsrDistance_Plugin(double usr_distance, int unit) {
3501 return fromUsrDistance(usr_distance, unit);
3502}
3503
3504double toUsrSpeed_Plugin(double kts_speed, int unit) {
3505 return toUsrSpeed(kts_speed, unit);
3506}
3507
3508double fromUsrSpeed_Plugin(double usr_speed, int unit) {
3509 return fromUsrSpeed(usr_speed, unit);
3510}
3511
3512double toUsrTemp_Plugin(double cel_temp, int unit) {
3513 return toUsrTemp(cel_temp, unit);
3514}
3515
3516double fromUsrTemp_Plugin(double usr_temp, int unit) {
3517 return fromUsrTemp(usr_temp, unit);
3518}
3519
3520wxString getUsrDistanceUnit_Plugin(int unit) {
3521 return getUsrDistanceUnit(unit);
3522}
3523
3524wxString getUsrSpeedUnit_Plugin(int unit) { return getUsrSpeedUnit(unit); }
3525
3526wxString getUsrTempUnit_Plugin(int unit) { return getUsrTempUnit(unit); }
3527
3528bool PlugIn_GSHHS_CrossesLand(double lat1, double lon1, double lat2,
3529 double lon2) {
3530 static bool loaded = false;
3531 if (!loaded) {
3532 gshhsCrossesLandInit();
3533 loaded = true;
3534 }
3535
3536 return gshhsCrossesLand(lat1, lon1, lat2, lon2);
3537}
3538
3539void PlugInPlaySound(wxString &sound_file) {
3540 PlugInPlaySoundEx(sound_file, -1);
3541}
3542
3543// API 1.10 Route and Waypoint Support
3544// wxBitmap *FindSystemWaypointIcon( wxString& icon_name );
3545
3546// PlugInWaypoint implementation
3547PlugIn_Waypoint::PlugIn_Waypoint() { m_HyperlinkList = NULL; }
3548
3549PlugIn_Waypoint::PlugIn_Waypoint(double lat, double lon,
3550 const wxString &icon_ident,
3551 const wxString &wp_name,
3552 const wxString &GUID) {
3553 wxDateTime now = wxDateTime::Now();
3554 m_CreateTime = now.ToUTC();
3555 m_HyperlinkList = NULL;
3556
3557 m_lat = lat;
3558 m_lon = lon;
3559 m_IconName = icon_ident;
3560 m_MarkName = wp_name;
3561 m_GUID = GUID;
3562}
3563
3564PlugIn_Waypoint::~PlugIn_Waypoint() {}
3565
3566// PlugInRoute implementation
3567PlugIn_Route::PlugIn_Route(void) { pWaypointList = new Plugin_WaypointList; }
3568
3569PlugIn_Route::~PlugIn_Route(void) {
3570 pWaypointList->DeleteContents(false); // do not delete Waypoints
3571 pWaypointList->Clear();
3572
3573 delete pWaypointList;
3574}
3575
3576// PlugInTrack implementation
3577PlugIn_Track::PlugIn_Track(void) { pWaypointList = new Plugin_WaypointList; }
3578
3579PlugIn_Track::~PlugIn_Track(void) {
3580 pWaypointList->DeleteContents(false); // do not delete Waypoints
3581 pWaypointList->Clear();
3582
3583 delete pWaypointList;
3584}
3585
3586wxString GetNewGUID(void) { return GpxDocument::GetUUID(); }
3587
3588bool AddCustomWaypointIcon(wxBitmap *pimage, wxString key,
3589 wxString description) {
3590 WayPointmanGui(*pWayPointMan).ProcessIcon(*pimage, key, description);
3591 return true;
3592}
3593
3594static void cloneHyperlinkList(RoutePoint *dst, const PlugIn_Waypoint *src) {
3595 // Transcribe (clone) the html HyperLink List, if present
3596 if (src->m_HyperlinkList == nullptr) return;
3597
3598 if (src->m_HyperlinkList->GetCount() > 0) {
3599 wxPlugin_HyperlinkListNode *linknode = src->m_HyperlinkList->GetFirst();
3600 while (linknode) {
3601 Plugin_Hyperlink *link = linknode->GetData();
3602
3603 Hyperlink *h = new Hyperlink();
3604 h->DescrText = link->DescrText;
3605 h->Link = link->Link;
3606 h->LType = link->Type;
3607
3608 dst->m_HyperlinkList->Append(h);
3609
3610 linknode = linknode->GetNext();
3611 }
3612 }
3613}
3614
3615bool AddSingleWaypoint(PlugIn_Waypoint *pwaypoint, bool b_permanent) {
3616 // Validate the waypoint parameters a little bit
3617
3618 // GUID
3619 // Make sure that this GUID is indeed unique in the Routepoint list
3620 bool b_unique = true;
3621 wxRoutePointListNode *prpnode = pWayPointMan->GetWaypointList()->GetFirst();
3622 while (prpnode) {
3623 RoutePoint *prp = prpnode->GetData();
3624
3625 if (prp->m_GUID == pwaypoint->m_GUID) {
3626 b_unique = false;
3627 break;
3628 }
3629 prpnode = prpnode->GetNext(); // RoutePoint
3630 }
3631
3632 if (!b_unique) return false;
3633
3634 RoutePoint *pWP =
3635 new RoutePoint(pwaypoint->m_lat, pwaypoint->m_lon, pwaypoint->m_IconName,
3636 pwaypoint->m_MarkName, pwaypoint->m_GUID);
3637
3638 pWP->m_bIsolatedMark = true; // This is an isolated mark
3639
3640 cloneHyperlinkList(pWP, pwaypoint);
3641
3642 pWP->m_MarkDescription = pwaypoint->m_MarkDescription;
3643
3644 if (pwaypoint->m_CreateTime.IsValid())
3645 pWP->SetCreateTime(pwaypoint->m_CreateTime);
3646 else {
3647 wxDateTime dtnow(wxDateTime::Now());
3648 pWP->SetCreateTime(dtnow);
3649 }
3650
3651 pWP->m_btemp = (b_permanent == false);
3652
3653 pSelect->AddSelectableRoutePoint(pwaypoint->m_lat, pwaypoint->m_lon, pWP);
3654 if (b_permanent) pConfig->AddNewWayPoint(pWP, -1);
3655
3656 if (pRouteManagerDialog && pRouteManagerDialog->IsShown())
3657 pRouteManagerDialog->UpdateWptListCtrl();
3658
3659 return true;
3660}
3661
3662bool DeleteSingleWaypoint(wxString &GUID) {
3663 // Find the RoutePoint
3664 bool b_found = false;
3665 RoutePoint *prp = pWayPointMan->FindRoutePointByGUID(GUID);
3666
3667 if (prp) b_found = true;
3668
3669 if (b_found) {
3670 pWayPointMan->DestroyWaypoint(prp);
3671 if (pRouteManagerDialog && pRouteManagerDialog->IsShown())
3672 pRouteManagerDialog->UpdateWptListCtrl();
3673 }
3674
3675 return b_found;
3676}
3677
3678bool UpdateSingleWaypoint(PlugIn_Waypoint *pwaypoint) {
3679 // Find the RoutePoint
3680 bool b_found = false;
3681 RoutePoint *prp = pWayPointMan->FindRoutePointByGUID(pwaypoint->m_GUID);
3682
3683 if (prp) b_found = true;
3684
3685 if (b_found) {
3686 double lat_save = prp->m_lat;
3687 double lon_save = prp->m_lon;
3688
3689 prp->m_lat = pwaypoint->m_lat;
3690 prp->m_lon = pwaypoint->m_lon;
3691 prp->SetIconName(pwaypoint->m_IconName);
3692 prp->SetName(pwaypoint->m_MarkName);
3693 prp->m_MarkDescription = pwaypoint->m_MarkDescription;
3694 prp->SetVisible(pwaypoint->m_IsVisible);
3695 if (pwaypoint->m_CreateTime.IsValid())
3696 prp->SetCreateTime(pwaypoint->m_CreateTime);
3697
3698 // Transcribe (clone) the html HyperLink List, if present
3699
3700 if (pwaypoint->m_HyperlinkList) {
3701 prp->m_HyperlinkList->Clear();
3702 if (pwaypoint->m_HyperlinkList->GetCount() > 0) {
3703 wxPlugin_HyperlinkListNode *linknode =
3704 pwaypoint->m_HyperlinkList->GetFirst();
3705 while (linknode) {
3706 Plugin_Hyperlink *link = linknode->GetData();
3707
3708 Hyperlink *h = new Hyperlink();
3709 h->DescrText = link->DescrText;
3710 h->Link = link->Link;
3711 h->LType = link->Type;
3712
3713 prp->m_HyperlinkList->Append(h);
3714
3715 linknode = linknode->GetNext();
3716 }
3717 }
3718 }
3719
3720 if (prp) prp->ReLoadIcon();
3721
3722 auto canvas = gFrame->GetPrimaryCanvas();
3723 SelectCtx ctx(canvas->m_bShowNavobjects, canvas->GetCanvasTrueScale());
3724 SelectItem *pFind = pSelect->FindSelection(ctx, lat_save, lon_save,
3725 SELTYPE_ROUTEPOINT);
3726 if (pFind) {
3727 pFind->m_slat = pwaypoint->m_lat; // update the SelectList entry
3728 pFind->m_slon = pwaypoint->m_lon;
3729 }
3730
3731 if (!prp->m_btemp) pConfig->UpdateWayPoint(prp);
3732
3733 if (pRouteManagerDialog && pRouteManagerDialog->IsShown())
3734 pRouteManagerDialog->UpdateWptListCtrl();
3735 }
3736
3737 return b_found;
3738}
3739
3740// translate O route class to Plugin one
3741static void PlugInFromRoutePoint(PlugIn_Waypoint *dst,
3742 /* const*/ RoutePoint *src) {
3743 dst->m_lat = src->m_lat;
3744 dst->m_lon = src->m_lon;
3745 dst->m_IconName = src->GetIconName();
3746 dst->m_MarkName = src->GetName();
3747 dst->m_MarkDescription = src->m_MarkDescription;
3748 dst->m_IsVisible = src->IsVisible();
3749 dst->m_CreateTime = src->GetCreateTime(); // not const
3750 dst->m_GUID = src->m_GUID;
3751
3752 // Transcribe (clone) the html HyperLink List, if present
3753 if (src->m_HyperlinkList == nullptr) return;
3754
3755 delete dst->m_HyperlinkList;
3756 dst->m_HyperlinkList = nullptr;
3757
3758 if (src->m_HyperlinkList->GetCount() > 0) {
3759 dst->m_HyperlinkList = new Plugin_HyperlinkList;
3760
3761 wxHyperlinkListNode *linknode = src->m_HyperlinkList->GetFirst();
3762 while (linknode) {
3763 Hyperlink *link = linknode->GetData();
3764
3766 h->DescrText = link->DescrText;
3767 h->Link = link->Link;
3768 h->Type = link->LType;
3769
3770 dst->m_HyperlinkList->Append(h);
3771
3772 linknode = linknode->GetNext();
3773 }
3774 }
3775}
3776
3777bool GetSingleWaypoint(wxString GUID, PlugIn_Waypoint *pwaypoint) {
3778 // Find the RoutePoint
3779 RoutePoint *prp = pWayPointMan->FindRoutePointByGUID(GUID);
3780
3781 if (!prp) return false;
3782
3783 PlugInFromRoutePoint(pwaypoint, prp);
3784
3785 return true;
3786}
3787
3788wxArrayString GetWaypointGUIDArray(void) {
3789 wxArrayString result;
3790 RoutePointList *list = pWayPointMan->GetWaypointList();
3791
3792 wxRoutePointListNode *prpnode = list->GetFirst();
3793 while (prpnode) {
3794 RoutePoint *prp = prpnode->GetData();
3795 result.Add(prp->m_GUID);
3796
3797 prpnode = prpnode->GetNext(); // RoutePoint
3798 }
3799
3800 return result;
3801}
3802
3803wxArrayString GetRouteGUIDArray(void) {
3804 wxArrayString result;
3805 RouteList *list = pRouteList;
3806
3807 wxRouteListNode *prpnode = list->GetFirst();
3808 while (prpnode) {
3809 Route *proute = prpnode->GetData();
3810 result.Add(proute->m_GUID);
3811
3812 prpnode = prpnode->GetNext(); // Route
3813 }
3814
3815 return result;
3816}
3817
3818wxArrayString GetTrackGUIDArray(void) {
3819 wxArrayString result;
3820 for (Track *ptrack : g_TrackList) {
3821 result.Add(ptrack->m_GUID);
3822 }
3823
3824 return result;
3825}
3826
3827wxArrayString GetIconNameArray(void) {
3828 wxArrayString result;
3829
3830 for (int i = 0; i < pWayPointMan->GetNumIcons(); i++) {
3831 wxString *ps = pWayPointMan->GetIconKey(i);
3832 result.Add(*ps);
3833 }
3834 return result;
3835}
3836
3837bool AddPlugInRoute(PlugIn_Route *proute, bool b_permanent) {
3838 Route *route = new Route();
3839
3840 PlugIn_Waypoint *pwp;
3841 RoutePoint *pWP_src;
3842 int ip = 0;
3843 wxDateTime plannedDeparture;
3844
3845 wxPlugin_WaypointListNode *pwpnode = proute->pWaypointList->GetFirst();
3846 while (pwpnode) {
3847 pwp = pwpnode->GetData();
3848
3849 RoutePoint *pWP = new RoutePoint(pwp->m_lat, pwp->m_lon, pwp->m_IconName,
3850 pwp->m_MarkName, pwp->m_GUID);
3851
3852 // Transcribe (clone) the html HyperLink List, if present
3853 cloneHyperlinkList(pWP, pwp);
3854 pWP->m_MarkDescription = pwp->m_MarkDescription;
3855 pWP->m_bShowName = false;
3856 pWP->SetCreateTime(pwp->m_CreateTime);
3857
3858 route->AddPoint(pWP);
3859
3860 pSelect->AddSelectableRoutePoint(pWP->m_lat, pWP->m_lon, pWP);
3861
3862 if (ip > 0)
3863 pSelect->AddSelectableRouteSegment(pWP_src->m_lat, pWP_src->m_lon,
3864 pWP->m_lat, pWP->m_lon, pWP_src, pWP,
3865 route);
3866 else
3867 plannedDeparture = pwp->m_CreateTime;
3868 ip++;
3869 pWP_src = pWP;
3870
3871 pwpnode = pwpnode->GetNext(); // PlugInWaypoint
3872 }
3873
3874 route->m_PlannedDeparture = plannedDeparture;
3875
3876 route->m_RouteNameString = proute->m_NameString;
3877 route->m_RouteStartString = proute->m_StartString;
3878 route->m_RouteEndString = proute->m_EndString;
3879 if (!proute->m_GUID.IsEmpty()) {
3880 route->m_GUID = proute->m_GUID;
3881 }
3882 route->m_btemp = (b_permanent == false);
3883
3884 pRouteList->Append(route);
3885
3886 if (b_permanent) pConfig->AddNewRoute(route);
3887
3888 if (pRouteManagerDialog && pRouteManagerDialog->IsShown())
3889 pRouteManagerDialog->UpdateRouteListCtrl();
3890
3891 return true;
3892}
3893
3894bool DeletePlugInRoute(wxString &GUID) {
3895 bool b_found = false;
3896
3897 // Find the Route
3898 Route *pRoute = g_pRouteMan->FindRouteByGUID(GUID);
3899 if (pRoute) {
3900 g_pRouteMan->DeleteRoute(pRoute, NavObjectChanges::getInstance());
3901 b_found = true;
3902 }
3903 return b_found;
3904}
3905
3906bool UpdatePlugInRoute(PlugIn_Route *proute) {
3907 bool b_found = false;
3908
3909 // Find the Route
3910 Route *pRoute = g_pRouteMan->FindRouteByGUID(proute->m_GUID);
3911 if (pRoute) b_found = true;
3912
3913 if (b_found) {
3914 bool b_permanent = (pRoute->m_btemp == false);
3915 g_pRouteMan->DeleteRoute(pRoute, NavObjectChanges::getInstance());
3916
3917 b_found = AddPlugInRoute(proute, b_permanent);
3918 }
3919
3920 return b_found;
3921}
3922
3923bool AddPlugInTrack(PlugIn_Track *ptrack, bool b_permanent) {
3924 Track *track = new Track();
3925
3926 PlugIn_Waypoint *pwp = 0;
3927 TrackPoint *pWP_src = 0;
3928 int ip = 0;
3929
3930 wxPlugin_WaypointListNode *pwpnode = ptrack->pWaypointList->GetFirst();
3931 while (pwpnode) {
3932 pwp = pwpnode->GetData();
3933
3934 TrackPoint *pWP = new TrackPoint(pwp->m_lat, pwp->m_lon);
3935 pWP->SetCreateTime(pwp->m_CreateTime);
3936
3937 track->AddPoint(pWP);
3938
3939 if (ip > 0)
3940 pSelect->AddSelectableTrackSegment(pWP_src->m_lat, pWP_src->m_lon,
3941 pWP->m_lat, pWP->m_lon, pWP_src, pWP,
3942 track);
3943 ip++;
3944 pWP_src = pWP;
3945
3946 pwpnode = pwpnode->GetNext(); // PlugInWaypoint
3947 }
3948
3949 track->SetName(ptrack->m_NameString);
3950 track->m_TrackStartString = ptrack->m_StartString;
3951 track->m_TrackEndString = ptrack->m_EndString;
3952 track->m_GUID = ptrack->m_GUID;
3953 track->m_btemp = (b_permanent == false);
3954
3955 g_TrackList.push_back(track);
3956
3957 if (b_permanent) pConfig->AddNewTrack(track);
3958
3959 if (pRouteManagerDialog && pRouteManagerDialog->IsShown())
3960 pRouteManagerDialog->UpdateTrkListCtrl();
3961
3962 return true;
3963}
3964
3965bool DeletePlugInTrack(wxString &GUID) {
3966 bool b_found = false;
3967
3968 // Find the Route
3969 Track *pTrack = g_pRouteMan->FindTrackByGUID(GUID);
3970 if (pTrack) {
3971 RoutemanGui(*g_pRouteMan).DeleteTrack(pTrack);
3972 b_found = true;
3973 }
3974
3975 if (pRouteManagerDialog && pRouteManagerDialog->IsShown())
3976 pRouteManagerDialog->UpdateTrkListCtrl();
3977
3978 return b_found;
3979}
3980
3981bool UpdatePlugInTrack(PlugIn_Track *ptrack) {
3982 bool b_found = false;
3983
3984 // Find the Track
3985 Track *pTrack = g_pRouteMan->FindTrackByGUID(ptrack->m_GUID);
3986 if (pTrack) b_found = true;
3987
3988 if (b_found) {
3989 bool b_permanent = (pTrack->m_btemp == false);
3990 RoutemanGui(*g_pRouteMan).DeleteTrack(pTrack);
3991
3992 b_found = AddPlugInTrack(ptrack, b_permanent);
3993 }
3994
3995 return b_found;
3996}
3997
3998bool PlugInHasNormalizedViewPort(PlugIn_ViewPort *vp) {
3999#ifdef ocpnUSE_GL
4000 ViewPort ocpn_vp;
4001 ocpn_vp.m_projection_type = vp->m_projection_type;
4002
4003 return glChartCanvas::HasNormalizedViewPort(ocpn_vp);
4004#else
4005 return false;
4006#endif
4007}
4008
4009void PlugInMultMatrixViewport(PlugIn_ViewPort *vp, float lat, float lon) {
4010#ifdef ocpnUSE_GL
4011 ViewPort ocpn_vp;
4012 ocpn_vp.clat = vp->clat;
4013 ocpn_vp.clon = vp->clon;
4014 ocpn_vp.m_projection_type = vp->m_projection_type;
4015 ocpn_vp.view_scale_ppm = vp->view_scale_ppm;
4016 ocpn_vp.skew = vp->skew;
4017 ocpn_vp.rotation = vp->rotation;
4018 ocpn_vp.pix_width = vp->pix_width;
4019 ocpn_vp.pix_height = vp->pix_height;
4020
4021// TODO fix for multicanvas glChartCanvas::MultMatrixViewPort(ocpn_vp, lat,
4022// lon);
4023#endif
4024}
4025
4026void PlugInNormalizeViewport(PlugIn_ViewPort *vp, float lat, float lon) {
4027#ifdef ocpnUSE_GL
4028 ViewPort ocpn_vp;
4029 glChartCanvas::NormalizedViewPort(ocpn_vp, lat, lon);
4030
4031 vp->clat = ocpn_vp.clat;
4032 vp->clon = ocpn_vp.clon;
4033 vp->view_scale_ppm = ocpn_vp.view_scale_ppm;
4034 vp->rotation = ocpn_vp.rotation;
4035 vp->skew = ocpn_vp.skew;
4036#endif
4037}
4038
4039// Helper and interface classes
4040
4041//-------------------------------------------------------------------------------
4042// PlugIn_AIS_Target Implementation
4043//-------------------------------------------------------------------------------
4044
4045PlugIn_AIS_Target *Create_PI_AIS_Target(AisTargetData *ptarget) {
4047
4048 pret->MMSI = ptarget->MMSI;
4049 pret->Class = ptarget->Class;
4050 pret->NavStatus = ptarget->NavStatus;
4051 pret->SOG = ptarget->SOG;
4052 pret->COG = ptarget->COG;
4053 pret->HDG = ptarget->HDG;
4054 pret->Lon = ptarget->Lon;
4055 pret->Lat = ptarget->Lat;
4056 pret->ROTAIS = ptarget->ROTAIS;
4057 pret->ShipType = ptarget->ShipType;
4058 pret->IMO = ptarget->IMO;
4059
4060 pret->Range_NM = ptarget->Range_NM;
4061 pret->Brg = ptarget->Brg;
4062
4063 // Per target collision parameters
4064 pret->bCPA_Valid = ptarget->bCPA_Valid;
4065 pret->TCPA = ptarget->TCPA; // Minutes
4066 pret->CPA = ptarget->CPA; // Nautical Miles
4067
4068 pret->alarm_state = (plugin_ais_alarm_type)ptarget->n_alert_state;
4069
4070 memcpy(pret->CallSign, ptarget->CallSign, CALL_SIGN_LEN);
4071 memcpy(pret->ShipName, ptarget->ShipName, SHIP_NAME_LEN);
4072
4073 return pret;
4074}
4075
4076//-------------------------------------------------------------------------------
4077// PluginListPanel & PluginPanel Implementation
4078//-------------------------------------------------------------------------------
4079
4080#define DISABLED_SETTINGS_MSG \
4081 _("These settings might destabilize OpenCPN and are by default disabled." \
4082 " To despite the dangers enable them manually add a CatalogExpert=1" \
4083 " line in the [PlugIns] section in the configuration file.")
4084
4085/*
4086 * Panel with buttons to control plugin catalog management.
4087 */
4088CatalogMgrPanel::CatalogMgrPanel(wxWindow *parent)
4089 : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize),
4090 m_parent(parent) {
4091 wxBoxSizer *topSizer = new wxBoxSizer(wxVERTICAL);
4092 SetSizer(topSizer);
4093
4094 topSizer->Add(new wxStaticLine(this), 0, wxGROW | wxLEFT | wxRIGHT, 4);
4095
4096 wxStaticBox *itemStaticBoxSizer4Static =
4097 new wxStaticBox(this, wxID_ANY, _("Plugin Catalog"));
4098 wxStaticBoxSizer *itemStaticBoxSizer4 =
4099 new wxStaticBoxSizer(itemStaticBoxSizer4Static, wxVERTICAL);
4100 topSizer->Add(itemStaticBoxSizer4, 1, wxEXPAND | wxALL, 2);
4101
4102#ifndef __ANDROID__
4103 // First line
4104 m_catalogText = new wxStaticText(this, wxID_STATIC, _T(""));
4105 itemStaticBoxSizer4->Add(m_catalogText,
4106 wxSizerFlags().Border().Proportion(1));
4107 m_catalogText->SetLabel(GetCatalogText(false));
4108
4109 // Next line
4110 wxBoxSizer *rowSizer2 = new wxBoxSizer(wxHORIZONTAL);
4111 itemStaticBoxSizer4->Add(rowSizer2,
4112 wxSizerFlags().Expand().Border().Proportion(1));
4113
4114 m_updateButton = new wxButton(this, wxID_ANY, _("Update Plugin Catalog"),
4115 wxDefaultPosition, wxDefaultSize, 0);
4116 rowSizer2->Add(m_updateButton, 0, wxALIGN_LEFT);
4117 m_updateButton->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
4118 &CatalogMgrPanel::OnUpdateButton, this);
4119 rowSizer2->AddSpacer(4 * GetCharWidth());
4120 m_tarballButton = new wxButton(this, wxID_ANY, _("Import plugin..."),
4121 wxDefaultPosition, wxDefaultSize, 0);
4122 rowSizer2->Add(m_tarballButton, 0, wxALIGN_LEFT | wxLEFT, 2 * GetCharWidth());
4123 m_tarballButton->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
4124 &CatalogMgrPanel::OnTarballButton, this);
4125
4126 rowSizer2->AddSpacer(4 * GetCharWidth());
4127 m_adv_button = new wxButton(this, wxID_ANY, _("Settings..."),
4128 wxDefaultPosition, wxDefaultSize, 0);
4129 ConfigVar<bool> expert("/PlugIns", "CatalogExpert", pConfig);
4130 if (expert.Get(false)) {
4131 m_adv_button->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
4132 &CatalogMgrPanel::OnPluginSettingsButton, this);
4133 } else {
4134 m_adv_button->Bind(wxEVT_COMMAND_BUTTON_CLICKED, [&](wxCommandEvent &) {
4135 wxMessageBox(DISABLED_SETTINGS_MSG, _("Disabled"));
4136 });
4137 }
4138 rowSizer2->AddSpacer(4 * GetCharWidth());
4139 rowSizer2->Add(m_adv_button, 0, wxALIGN_LEFT);
4140
4141 SetUpdateButtonLabel();
4142
4143 // Next line
4144 wxBoxSizer *rowSizer3 = new wxBoxSizer(wxHORIZONTAL);
4145 itemStaticBoxSizer4->Add(rowSizer3, 0, wxEXPAND | wxALL, 4);
4146
4147 SetMinSize(wxSize(m_parent->GetClientSize().x - (4 * GetCharWidth()), -1));
4148 Fit();
4149
4150 GlobalVar<wxString> catalog(&g_catalog_channel);
4151 wxDEFINE_EVENT(EVT_CATALOG_CHANGE, wxCommandEvent);
4152 catalog_listener.Listen(catalog, this, EVT_CATALOG_CHANGE);
4153 Bind(EVT_CATALOG_CHANGE, [&](wxCommandEvent &) { SetUpdateButtonLabel(); });
4154
4155#else // __ANDROID__
4156 SetBackgroundColour(wxColour(0x7c, 0xb0, 0xe9)); // light blue
4157 ConfigVar<bool> expert("/PlugIns", "CatalogExpert", pConfig);
4158 if (!expert.Get(false)) {
4159 m_updateButton =
4160 new wxButton(this, wxID_ANY, _("Update Plugin Catalog: master"),
4161 wxDefaultPosition, wxDefaultSize, 0);
4162 itemStaticBoxSizer4->Add(m_updateButton, 0, wxALIGN_LEFT);
4163 m_updateButton->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
4164 &CatalogMgrPanel::OnUpdateButton, this);
4165 SetUpdateButtonLabel();
4166 m_tarballButton = NULL;
4167 m_adv_button = NULL;
4168 } else {
4169 // First line
4170 m_catalogText = new wxStaticText(this, wxID_STATIC, _T(""));
4171 itemStaticBoxSizer4->Add(m_catalogText,
4172 wxSizerFlags().Border().Proportion(1));
4173 m_catalogText->SetLabel(GetCatalogText(false));
4174
4175 m_updateButton =
4176 new wxButton(this, wxID_ANY, _("Update Plugin Catalog:master"),
4177 wxDefaultPosition, wxDefaultSize, 0);
4178 itemStaticBoxSizer4->Add(m_updateButton, 0, wxALIGN_LEFT);
4179 m_updateButton->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
4180 &CatalogMgrPanel::OnUpdateButton, this);
4181 SetUpdateButtonLabel();
4182
4183 // Next line
4184 m_adv_button = new wxButton(this, wxID_ANY, _("Settings..."),
4185 wxDefaultPosition, wxDefaultSize, 0);
4186 itemStaticBoxSizer4->Add(m_adv_button, 0, wxALIGN_LEFT);
4187 m_adv_button->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
4188 &CatalogMgrPanel::OnPluginSettingsButton, this);
4189
4190 // Next line
4191 m_tarballButton = new wxButton(this, wxID_ANY, _("Import plugin..."),
4192 wxDefaultPosition, wxDefaultSize, 0);
4193 itemStaticBoxSizer4->Add(m_tarballButton, 0, wxALIGN_LEFT | wxLEFT,
4194 2 * GetCharWidth());
4195 m_tarballButton->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
4196 &CatalogMgrPanel::OnTarballButton, this);
4197 }
4198
4199#endif
4200}
4201
4202CatalogMgrPanel::~CatalogMgrPanel() {
4203 m_updateButton->Unbind(wxEVT_COMMAND_BUTTON_CLICKED,
4204 &CatalogMgrPanel::OnUpdateButton, this);
4205 if (m_tarballButton)
4206 m_tarballButton->Unbind(wxEVT_COMMAND_BUTTON_CLICKED,
4207 &CatalogMgrPanel::OnTarballButton, this);
4208}
4209
4210static const char *const DOWNLOAD_REPO_PROTO =
4211 "https://raw.githubusercontent.com/OpenCPN/plugins/@branch@/"
4212 "ocpn-plugins.xml";
4213
4214void CatalogMgrPanel::OnUpdateButton(wxCommandEvent &event) {
4215 // Craft the url
4216 std::string catalog(g_catalog_channel == "" ? "master" : g_catalog_channel);
4217 std::string url(g_catalog_custom_url);
4218 if (catalog != "custom") {
4219 url = std::string(DOWNLOAD_REPO_PROTO);
4220 ocpn::replace(url, "@branch@", catalog);
4221 }
4222 // Download to a temp file
4223 std::string filePath =
4224 wxFileName::CreateTempFileName("ocpn_dl").ToStdString();
4225
4226 auto catalogHdlr = CatalogHandler::getInstance();
4227
4228 g_Platform->ShowBusySpinner();
4229 auto status = catalogHdlr->DownloadCatalog(filePath, url);
4230 g_Platform->HideBusySpinner();
4231
4232 std::string message;
4233 if (status != CatalogHandler::ServerStatus::OK) {
4234 message = _("Cannot download data from url");
4235 OCPNMessageBox(this, message, _("OpenCPN Catalog update"),
4236 wxICON_ERROR | wxOK);
4237 return;
4238 }
4239
4240 // TODO Validate xml using xsd here....
4241#ifdef __OCPN__ANDROID__
4242 if (!AndroidSecureCopyFile(wxString(filePath.c_str()),
4243 g_Platform->GetPrivateDataDir() +
4244 wxFileName::GetPathSeparator() +
4245 _T("ocpn-plugins.xml"))) {
4246 OCPNMessageBox(this, _("Unable to copy catalog file"),
4247 _("OpenCPN Catalog update"), wxICON_ERROR | wxOK);
4248 return;
4249 }
4250#else
4251 // Copy the downloaded file to proper local location
4252 if (!wxCopyFile(wxString(filePath.c_str()),
4253 g_Platform->GetPrivateDataDir() +
4254 wxFileName::GetPathSeparator() +
4255 _T("ocpn-plugins.xml"))) {
4256 OCPNMessageBox(this, _("Unable to copy catalog file"),
4257 _("OpenCPN Catalog update"), wxICON_ERROR | wxOK);
4258 return;
4259 }
4260#endif
4261
4262 // If this is the "master" catalog, also copy to plugin cache
4263 if (catalog == "master") {
4264 if (!ocpn::store_metadata(filePath.c_str())) {
4265 OCPNMessageBox(this, _("Unable to copy catalog file to cache"),
4266 _("OpenCPN Catalog update"), wxICON_ERROR | wxOK);
4267 return;
4268 }
4269 }
4270
4271 // Record in the config file the name of the catalog downloaded
4272 pConfig->SetPath(_T("/PlugIns/"));
4273 pConfig->Write("LatestCatalogDownloaded", catalog.c_str());
4274 pConfig->Flush();
4275
4276 // Reset the PluginHandler catalog file source.
4277 // This will case the Handler to find, load, and parse the just-downloaded
4278 // catalog as copied to g_Platform->GetPrivateDataDir()...
4279 auto pluginHandler = PluginHandler::getInstance();
4280 pluginHandler->setMetadata("");
4281
4282 // Reload all plugins, which will also update the status fields
4283 LoadAllPlugIns(false);
4284
4285 // Update this Panel, and the entire list.
4286#ifndef __OCPN__ANDROID__
4287 m_catalogText->SetLabel(GetCatalogText(true));
4288#endif
4289 if (m_PluginListPanel)
4290 m_PluginListPanel->ReloadPluginPanels();
4291 OCPNMessageBox(this, _("Catalog update successful"),
4292 _("OpenCPN Catalog update"), wxICON_INFORMATION | wxOK);
4293}
4294
4295static bool parsePluginNode(pugi::xml_node &pluginRoot,
4296 PluginMetadata &plugin) {
4297 for (pugi::xml_node element = pluginRoot.first_child(); element;
4298 element = element.next_sibling()) {
4299 if (!strcmp(element.name(), "name")) {
4300 plugin.name = element.first_child().value();
4301 } else if (!strcmp(element.name(), "version")) {
4302 plugin.version = element.first_child().value();
4303 } else if (!strcmp(element.name(), "release")) {
4304 plugin.release = element.first_child().value();
4305 } else if (!strcmp(element.name(), "summary")) {
4306 plugin.summary = element.first_child().value();
4307 } else if (!strcmp(element.name(), "api-version")) {
4308 plugin.api_version = element.first_child().value();
4309 } else if (!strcmp(element.name(), "open-source")) {
4310 plugin.openSource = element.first_child().value();
4311 } else if (!strcmp(element.name(), "author")) {
4312 plugin.author = element.first_child().value();
4313 } else if (!strcmp(element.name(), "source")) {
4314 plugin.source = element.first_child().value();
4315 } else if (!strcmp(element.name(), "info-url")) {
4316 plugin.info_url = element.first_child().value();
4317 } else if (!strcmp(element.name(), "description")) {
4318 plugin.description = element.first_child().value();
4319 } else if (!strcmp(element.name(), "target")) {
4320 plugin.target = element.first_child().value();
4321 } else if (!strcmp(element.name(), "target-version")) {
4322 plugin.target_version = element.first_child().value();
4323 } else if (!strcmp(element.name(), "target-arch")) {
4324 plugin.target_arch = element.first_child().value();
4325 } else if (!strcmp(element.name(), "tarball-url")) {
4326 plugin.tarball_url = element.first_child().value();
4327 } else if (!strcmp(element.name(), "build-gtk")) {
4328 plugin.build_gtk = element.first_child().value();
4329 }
4330 }
4331
4332 return true;
4333}
4334
4335static void populatePluginNode(pugi::xml_node &pluginNode,
4336 PluginMetadata &workingMetadata) {
4337 pugi::xml_node child;
4338
4339 child = pluginNode.append_child("name");
4340 child.append_child(pugi::node_pcdata).set_value(workingMetadata.name.c_str());
4341
4342 child = pluginNode.append_child("version");
4343 child.append_child(pugi::node_pcdata)
4344 .set_value(workingMetadata.version.c_str());
4345
4346 child = pluginNode.append_child("release");
4347 child.append_child(pugi::node_pcdata)
4348 .set_value(workingMetadata.release.c_str());
4349
4350 child = pluginNode.append_child("summary");
4351 child.append_child(pugi::node_pcdata)
4352 .set_value(workingMetadata.summary.c_str());
4353
4354 child = pluginNode.append_child("api-version");
4355 child.append_child(pugi::node_pcdata)
4356 .set_value(workingMetadata.api_version.c_str());
4357
4358 child = pluginNode.append_child("open-source");
4359 char b[2];
4360 sprintf(b, "%1d", workingMetadata.openSource);
4361 child.append_child(pugi::node_pcdata).set_value(b);
4362
4363 child = pluginNode.append_child("author");
4364 child.append_child(pugi::node_pcdata)
4365 .set_value(workingMetadata.author.c_str());
4366
4367 child = pluginNode.append_child("source");
4368 child.append_child(pugi::node_pcdata)
4369 .set_value(workingMetadata.source.c_str());
4370
4371 child = pluginNode.append_child("info-url");
4372 child.append_child(pugi::node_pcdata)
4373 .set_value(workingMetadata.info_url.c_str());
4374
4375 child = pluginNode.append_child("description");
4376 child.append_child(pugi::node_pcdata)
4377 .set_value(workingMetadata.description.c_str());
4378
4379 child = pluginNode.append_child("target");
4380 child.append_child(pugi::node_pcdata)
4381 .set_value(workingMetadata.target.c_str());
4382
4383 child = pluginNode.append_child("target-version");
4384 child.append_child(pugi::node_pcdata)
4385 .set_value(workingMetadata.target_version.c_str());
4386
4387 child = pluginNode.append_child("target-arch");
4388 child.append_child(pugi::node_pcdata)
4389 .set_value(workingMetadata.target_arch.c_str());
4390
4391 child = pluginNode.append_child("tarball-url");
4392 child.append_child(pugi::node_pcdata)
4393 .set_value(workingMetadata.tarball_url.c_str());
4394}
4395
4396void CatalogMgrPanel::OnPluginSettingsButton(wxCommandEvent &event) {
4397 auto dialog = new CatalogSettingsDialog(this);
4398
4399#ifdef __OCPN__ANDROID__
4400 androidDisableRotation();
4401#endif
4402
4403 dialog->ShowModal();
4404
4405#ifdef __OCPN__ANDROID__
4406 androidEnableRotation();
4407#endif
4408}
4409
4410void CatalogMgrPanel::OnTarballButton(wxCommandEvent &event) {
4411 // Present a file selector dialog to get the file name..
4412 wxString tarballPath;
4413 int response = g_Platform->DoFileSelectorDialog(
4414 this, &tarballPath, _("Select tarball file"), GetImportInitDir(),
4415 wxEmptyString, wxT("tar files (*.tar.gz)|*.tar.gz|All Files (*.*)|*.*"));
4416
4417 if (response == wxID_OK) {
4418 // Record the path to the last import file for next time
4419 wxFileName f = tarballPath;
4420 wxString used_path = f.GetPath(wxPATH_GET_VOLUME | wxPATH_NO_SEPARATOR);
4421 if (used_path != wxEmptyString) {
4422 pConfig->SetPath(_T("/PlugIns/"));
4423 pConfig->Write("LatestImportDir", used_path);
4424 pConfig->Flush();
4425 }
4426
4427 // Traverse the tarball to find the required "metadata.xml file
4428
4429 // Store the metadata file in temp location
4430 wxString tmpMetadata = wxFileName::CreateTempFileName("meta");
4431
4432 struct archive *src = archive_read_new();
4433 archive_read_support_filter_gzip(src);
4434 archive_read_support_format_tar(src);
4435 int r = archive_read_open_filename(src, tarballPath.ToStdString().c_str(),
4436 10240);
4437 if (r != ARCHIVE_OK) {
4438 std::ostringstream os;
4439 // os << "Cannot read installation tarball: " << path;
4440 wxLogWarning(os.str().c_str());
4441 // last_error_msg = os.str();
4442 return;
4443 }
4444
4445 struct archive *dest = archive_write_disk_new();
4446 archive_write_disk_set_options(dest, ARCHIVE_EXTRACT_TIME);
4447
4448 bool bFoundMetadata = false;
4449 struct archive_entry *entry = 0;
4450 while (true) {
4451 int r = archive_read_next_header(src, &entry);
4452 if (r == ARCHIVE_EOF) {
4453 break;
4454 }
4455 if (r < ARCHIVE_OK) {
4456 break;
4457 }
4458
4459 // Find the file "metadata.xml"
4460 std::string path = archive_entry_pathname(entry);
4461 if (std::string::npos != path.find("metadata.xml")) {
4462 bFoundMetadata = true;
4463 archive_entry_set_pathname(entry, tmpMetadata.mb_str());
4464
4465 if (r >= ARCHIVE_OK && archive_entry_size(entry) > 0) {
4466 const void *buff;
4467 size_t size;
4468 la_int64_t offset;
4469
4470 r = archive_write_header(dest, entry);
4471 if (r < ARCHIVE_OK) {
4472 break;
4473 }
4474
4475 while (true) {
4476 r = archive_read_data_block(src, &buff, &size, &offset);
4477 if (r == ARCHIVE_EOF) {
4478 break;
4479 }
4480 if (r < ARCHIVE_OK) {
4481 break;
4482 }
4483 r = archive_write_data_block(dest, buff, size, offset);
4484 if (r < ARCHIVE_OK) {
4485 wxLogWarning("Error copying install data: %s",
4486 archive_error_string(dest));
4487
4488 break;
4489 }
4490 }
4491 if (r < ARCHIVE_OK) {
4492 break;
4493 }
4494
4495 r = archive_write_finish_entry(dest);
4496 if (r < ARCHIVE_OK) {
4497 break;
4498 }
4499 }
4500 } else {
4501 continue;
4502 }
4503 }
4504 archive_read_free(src);
4505 archive_write_free(dest);
4506
4507 // Any tarball extraction problem?
4508 if (r < ARCHIVE_OK) {
4509 OCPNMessageBox(this, _("Error extracting import plugin tarball."),
4510 _("OpenCPN Plugin Import Error"));
4511 return;
4512 }
4513
4514 // Found the metadata?
4515 if (!bFoundMetadata) {
4516 OCPNMessageBox(
4517 this,
4518 _("Error, import plugin tarball does not contain required metadata."),
4519 _("OpenCPN Plugin Import Error"));
4520 return;
4521 }
4522
4523 // Parse the import metadata
4524 PluginMetadata importPlugin;
4525
4526 pugi::xml_document doc;
4527 bool ret = doc.load_file(tmpMetadata.mb_str());
4528 if (ret) {
4529 pugi::xml_node pluginRoot = doc.first_child();
4530
4531 if (!parsePluginNode(pluginRoot, importPlugin)) {
4532 OCPNMessageBox(this, _("Error processing import plugin metadata."),
4533 _("OpenCPN Plugin Import Error"));
4534 return;
4535 }
4536 }
4537
4538 // TODO Validate the metadata, in some simplistic way...
4539 if (!PluginHandler::isCompatible(importPlugin)) {
4540 OCPNMessageBox(this, _("Incompatible import plugin detected."),
4541 _("OpenCPN Plugin Import Error"));
4542 return;
4543 }
4544
4545 // Load and parse a working copy of the currently active catalog...
4546 std::vector<PluginMetadata> pluginArray;
4547
4548 pugi::xml_document catalog;
4549 wxString currentCatalog = g_Platform->GetPrivateDataDir() +
4550 wxFileName::GetPathSeparator() +
4551 _T("ocpn-plugins.xml");
4552 bool badCatalog = false;
4553 if (!wxFileExists(currentCatalog)) {
4554 badCatalog = true;
4555 } else {
4556 ret = catalog.load_file(currentCatalog.mb_str());
4557 if (!ret) {
4558 badCatalog = true;
4559 }
4560 }
4561
4562 // If current default catalog is corrupt, or missing,
4563 // then create a new "stub" catalog
4564
4565 if (badCatalog) {
4566 if (wxFileExists(currentCatalog)) wxRemoveFile(currentCatalog);
4567
4568 wxTextFile stubCatalog(currentCatalog);
4569 stubCatalog.Create();
4570 stubCatalog.AddLine(_T("<?xml version=\"1.0\" ?>"));
4571 stubCatalog.AddLine(_T("<plugins>"));
4572 stubCatalog.AddLine(_T("<version>0.0.0</version>"));
4573 stubCatalog.AddLine(_T("<date>2021-06-01 00:01</date>"));
4574 stubCatalog.AddLine(_T("</plugins>"));
4575 stubCatalog.Write();
4576 stubCatalog.Close();
4577
4578 catalog.load_file(currentCatalog.mb_str());
4579 }
4580
4581 pugi::xml_node catalogRoot = catalog.first_child();
4582 for (pugi::xml_node element = catalogRoot.first_child(); element;
4583 element = element.next_sibling()) {
4584 if (!strcmp(element.name(), "plugin")) {
4585 PluginMetadata catalogPlugin;
4586 parsePluginNode(element, catalogPlugin);
4587 pluginArray.push_back(catalogPlugin);
4588 }
4589 }
4590
4591 // Merge the import plugin metadata with the current catalog
4592 // By merge, we mean this:
4593 // 1. If the import metadata does not exist in the catalog, append it
4594 // 2. If the import metadata does exist (name, target, and target-version
4595 // match), then..
4596 // update the catalog version with the import version
4597 bool bmerge = false;
4598 for (size_t i = 0; i < pluginArray.size(); i++) {
4599 PluginMetadata candidate = pluginArray[i];
4600 if (importPlugin.name == candidate.name) {
4601 if (importPlugin.target == candidate.target) {
4602 if (importPlugin.target_version == candidate.target_version) {
4603 bmerge = true;
4604 pluginArray[i].version = importPlugin.version;
4605 pluginArray[i].release = importPlugin.release;
4606 pluginArray[i].summary = importPlugin.summary;
4607 pluginArray[i].description = importPlugin.description;
4608 pluginArray[i].tarball_url = importPlugin.tarball_url;
4609 break;
4610 }
4611 }
4612 }
4613 }
4614 // If there was no exact merge, then simply add the importPlugin metadata
4615 // (case 1)
4616 if (!bmerge) pluginArray.push_back(importPlugin);
4617
4618 // Write out the newly merged catalog, replacing the currently active
4619 // catalog
4620 pugi::xml_document newCatalog;
4621 pugi::xml_node pluginsNode = newCatalog.append_child("plugins");
4622
4623 pugi::xml_node childT = pluginsNode.append_child("version");
4624 childT.append_child(pugi::node_pcdata).set_value("0.0.0");
4625 childT = pluginsNode.append_child("date");
4626 wxDateTime now = wxDateTime::GetTimeNow();
4627 wxString timeFormat = now.FormatISOCombined(' ');
4628 childT.append_child(pugi::node_pcdata).set_value(timeFormat.mb_str());
4629
4630 for (size_t i = 0; i < pluginArray.size(); i++) {
4631 PluginMetadata workingMetadata = pluginArray[i];
4632
4633 pugi::xml_node pluginNode = pluginsNode.append_child("plugin");
4634 pugi::xml_attribute version = pluginNode.append_attribute("version");
4635 version.set_value("1");
4636
4637 populatePluginNode(pluginNode, workingMetadata);
4638 }
4639
4640 wxString catalogName = g_Platform->GetPrivateDataDir() +
4641 wxFileName::GetPathSeparator() +
4642 _T("ocpn-plugins.xml");
4643 newCatalog.save_file(catalogName.mb_str(), " ");
4644
4645 // Copy the metadata to the tarball cache
4646
4647 wxFileName fn(tarballPath);
4648 if (ocpn::store_tarball(tarballPath.ToStdString().c_str(),
4649 fn.GetFullName().ToStdString().c_str())) {
4650 wxLogMessage("Copied %s to local cache",
4651 tarballPath.ToStdString().c_str());
4652 }
4653
4654 // Ready to load and process the merged catalog...
4655
4656 // Reset the PluginHandler catalog file source.
4657 // This will cause the Handler to find, load, and parse the just-merged
4658 // catalog as copied to g_Platform->GetPrivateDataDir()...
4659 auto pluginHandler = PluginHandler::getInstance();
4660 pluginHandler->setMetadata("");
4661
4662 // Reload all plugins, which will also update the status fields
4663 LoadAllPlugIns(false);
4664
4665 // Update this Panel, and the entire list.
4666 if (m_PluginListPanel) {
4667 auto loader = PluginLoader::getInstance();
4668 m_PluginListPanel->ReloadPluginPanels();
4669 }
4670
4671 // Success!
4672 wxString msg = _("Plugin imported successfully");
4673 msg += _T("\n");
4674 msg += _("Active catalog updated.");
4675 msg += _T("\n");
4676 msg += _("Plugin may be installed or updated now.");
4677 OCPNMessageBox(this, msg, _("OpenCPN Plugin Import Successful"));
4678 }
4679}
4680
4681wxString CatalogMgrPanel::GetCatalogText(bool updated) {
4682 wxString catalog;
4683 catalog = updated ? _("Active Catalog") : _("Last Catalog");
4684 catalog += _T(": ");
4685
4686 // Check the config file to learn what was the last catalog downloaded.
4687 pConfig->SetPath(_T("/PlugIns/"));
4688 wxString latestCatalog =
4689 pConfig->Read(_T("LatestCatalogDownloaded"), _T("default"));
4690 catalog += latestCatalog;
4691
4692 // Get the version from the currently active catalog, by which we mean
4693 // the latest catalog parsed.
4694 auto pluginHandler = PluginHandler::getInstance();
4695 std::string date = pluginHandler->GetCatalogData()->date;
4696
4697 catalog += wxString(" ") + _("Last change: ") + " " + date;
4698 if (!updated) catalog += _T(" : ") + _("Please Update Plugin Catalog.");
4699
4700 return catalog;
4701}
4702
4703void CatalogMgrPanel::SetUpdateButtonLabel() {
4704 wxString label = _("Update Plugin Catalog");
4705 label += _T(": ");
4706 label += g_catalog_channel == "" ? "master" : g_catalog_channel;
4707 m_updateButton->SetLabel(label);
4708 Layout();
4709}
4710
4711wxString CatalogMgrPanel::GetImportInitDir() {
4712 // Check the config file for the last Import path.
4713 pConfig->SetPath(_T("/PlugIns/"));
4714 wxString lastImportDir;
4715 lastImportDir = pConfig->Read(_T("LatestImportDir"),
4716 g_Platform->GetWritableDocumentsDir());
4717 if (wxDirExists(lastImportDir)) {
4718 return lastImportDir;
4719 }
4720 return (g_Platform->GetWritableDocumentsDir());
4721}
4722
4723BEGIN_EVENT_TABLE(PluginListPanel, wxScrolledWindow)
4724// EVT_BUTTON( ID_CMD_BUTTON_PERFORM_ACTION,
4725// PluginListPanel::OnPluginPanelAction )
4726END_EVENT_TABLE()
4727
4728PluginListPanel::PluginListPanel(wxWindow *parent, wxWindowID id,
4729 const wxPoint &pos, const wxSize &size,
4730 ArrayOfPlugIns *pPluginArray)
4731 : wxScrolledWindow(parent, id, pos, size, wxTAB_TRAVERSAL | wxVSCROLL),
4732 m_PluginSelected(0) {
4733 SetSizer(new wxBoxSizer(wxVERTICAL));
4734 ReloadPluginPanels();
4735 // SetScrollRate(0, 1);
4736}
4737
4738void PluginListPanel::SelectByName(wxString &name) {
4739 for (auto it = GetChildren().GetFirst(); it; it = it->GetNext()) {
4740 auto pluginPanel = dynamic_cast<PluginPanel *>(it->GetData());
4741 if (pluginPanel) {
4742 if (pluginPanel->GetPluginPtr()->m_common_name.IsSameAs(name)) {
4743 pluginPanel->SetSelected(true);
4744 pluginPanel->Layout();
4745 SelectPlugin(pluginPanel);
4746 break;
4747 }
4748 }
4749 }
4750}
4751
4753 auto plugins = PluginLoader::getInstance()->GetPlugInArray();
4754 m_pPluginArray = plugins;
4755 m_PluginItems.Clear();
4756
4757 wxWindowList kids = GetChildren();
4758 for (unsigned int i = 0; i < kids.GetCount(); i++) {
4759 wxWindowListNode *node = kids.Item(i);
4760 wxWindow *win = node->GetData();
4761 PluginPanel *pp = dynamic_cast<PluginPanel *>(win);
4762 if (pp) win->Destroy();
4763 }
4764
4765 GetSizer()->Clear();
4766
4767 Hide();
4768 m_PluginSelected = 0;
4769 for (size_t i = m_pPluginArray->GetCount(); i > 0; i -= 1) {
4770 PlugInContainer *pic = m_pPluginArray->Item(i - 1);
4771 AddPlugin(pic);
4772 }
4773 Show();
4774 Layout();
4775 Refresh(true);
4776
4777 Scroll(0, 0);
4778}
4779
4780void PluginListPanel::AddPlugin(PlugInContainer *pic) {
4781 auto pPluginPanel =
4782 new PluginPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, pic);
4783 pPluginPanel->SetSelected(false);
4784 GetSizer()->Add(pPluginPanel, 0, wxEXPAND);
4785 m_PluginItems.Add(pPluginPanel);
4786
4787 m_pluginSpacer = g_Platform->GetDisplayDPmm() * 1.0;
4788 GetSizer()->AddSpacer(m_pluginSpacer);
4789
4790 // wxStaticLine* itemStaticLine = new wxStaticLine( m_panel, wxID_ANY,
4791 // wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
4792 // m_pitemBoxSizer01->Add( itemStaticLine, wxSizerFlags().Expand());
4793}
4794
4795// When a child Panel is selected, its size grows to include "Preferences"
4796// and Enable" buttons. As a consequence, the vertical size of the
4797// ListPanel grows as well.Calculate and add a spacer to bottom of
4798// ListPanel so that initial ListPanel minimum size calculations account
4799// for selected Panel size growth. Sadly, this does not work right on wxQt.
4800// So, just punt for now...
4801int PluginListPanel::ComputePluginSpace(ArrayOfPluginPanel plugins,
4802 wxBoxSizer *sizer) {
4803 int max_dy = 0;
4804 for (size_t i = 0; i < plugins.GetCount(); i++) {
4805 auto panel = plugins.Item(i);
4806 bool was_selected = panel->GetSelected();
4807 panel->SetSelected(false);
4808 sizer->Layout();
4809 wxSize unselected = panel->GetSize();
4810
4811 panel->SetSelected(true); // switch to selected, a bit bigger
4812 sizer->Layout();
4813 wxSize selected = panel->GetSize();
4814
4815 int dy = selected.GetHeight() - unselected.GetHeight();
4816 max_dy = wxMax(max_dy, dy);
4817 panel->SetSelected(was_selected);
4818 }
4819 return max_dy;
4820}
4821
4822void PluginListPanel::UpdatePluginsOrder() {
4823 m_pPluginArray->Clear();
4824 for (unsigned int i = 0; i < m_PluginItems.GetCount(); i++) {
4825 m_pPluginArray->Insert(m_PluginItems[i]->GetPluginPtr(), 0);
4826 }
4827}
4828
4829PluginListPanel::~PluginListPanel() {}
4830
4831void PluginListPanel::UpdateSelections() {
4832 for (unsigned int i = 0; i < m_PluginItems.GetCount(); i++) {
4833 PluginPanel *pPluginPanel = m_PluginItems[i];
4834 if (pPluginPanel) {
4835 pPluginPanel->SetSelected(pPluginPanel->GetSelected());
4836 }
4837 }
4838}
4839
4840void PluginListPanel::SelectPlugin(PluginPanel *pi) {
4841 int xs, ys;
4842 GetViewStart(&xs, &ys);
4843 Scroll(0, 0);
4844
4845 if (m_PluginSelected) {
4846 m_PluginSelected->SetSelected(false);
4847 m_PluginSelected->Layout();
4848 }
4849
4850 if (pi == NULL) m_PluginSelected->SetSelected(false);
4851
4852 m_PluginSelected = pi;
4853
4854 GetSizer()->Layout();
4855 Refresh(false);
4856 wxSize size = GetBestVirtualSize();
4857 SetVirtualSize(size);
4858
4859 // Measure, and ensure that the selected item is fully visible in the
4860 // vertical scroll box.
4861 int htop = 0;
4862 for (unsigned int i = 0; i < m_PluginItems.GetCount(); i++) {
4863 PluginPanel *pPluginPanel = m_PluginItems[i];
4864 int yd = pPluginPanel->GetSize().y;
4865 htop += yd;
4866 htop += m_pluginSpacer;
4867 if (pPluginPanel == pi) {
4868 int piBottom = htop - (ys * g_options->GetScrollRate());
4869 if (piBottom > GetClientSize().y) {
4870 ys += (piBottom - GetClientSize().y) / g_options->GetScrollRate();
4871 }
4872 break;
4873 }
4874 }
4875
4876 Scroll(xs, ys);
4877}
4878
4879void PluginListPanel::MoveUp(PluginPanel *pi) {
4880 int pos = m_PluginItems.Index(pi);
4881 if (pos == 0) // The first one can't be moved further up
4882 return;
4883 m_PluginItems.RemoveAt(pos);
4884 // m_pitemBoxSizer01->Remove( pos * 2 + 1 );
4885 // m_pitemBoxSizer01->Remove( pos * 2 );
4886 m_PluginItems.Insert(pi, pos - 1);
4887 wxStaticLine *itemStaticLine = new wxStaticLine(
4888 this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL);
4889 // m_pitemBoxSizer01->Insert( (pos - 1) * 2, itemStaticLine, 0,
4890 // wxEXPAND|wxALL, 0 ); m_pitemBoxSizer01->Insert( (pos - 1) * 2, pi, 0,
4891 // wxEXPAND|wxALL, 0 );
4892
4893 m_PluginSelected = pi;
4894
4895 GetSizer()->Layout();
4896 m_parent->Layout();
4897 Refresh(true);
4898}
4899
4900void PluginListPanel::MoveDown(PluginPanel *pi) {
4901 int pos = m_PluginItems.Index(pi);
4902 if (pos == (int)m_PluginItems.Count() -
4903 1) // The last one can't be moved further down
4904 return;
4905 m_PluginItems.RemoveAt(pos);
4906 // m_pitemBoxSizer01->Remove( pos * 2 + 1 );
4907 // m_pitemBoxSizer01->Remove( pos * 2 );
4908 m_PluginItems.Insert(pi, pos + 1);
4909 wxStaticLine *itemStaticLine = new wxStaticLine(
4910 this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL);
4911 // m_pitemBoxSizer01->Insert( (pos + 1) * 2 - 1, itemStaticLine, 0,
4912 // wxEXPAND|wxALL, 0 ); m_pitemBoxSizer01->Insert( (pos + 1) * 2, pi, 0,
4913 // wxEXPAND|wxALL, 0 );
4914
4915 m_PluginSelected = pi;
4916
4917 GetSizer()->Layout();
4918 m_parent->Layout();
4919 Refresh(false);
4920}
4921
4922static bool canUninstall(std::string name) {
4923 PluginHandler *pluginHandler = PluginHandler::getInstance();
4924 // std::transform(name.begin(), name.end(), name.begin(), ::tolower);
4925
4926 for (auto plugin : pluginHandler->getInstalled()) {
4927 if (plugin.name == name) {
4928 if (safe_mode::get_mode())
4929 return true;
4930 else
4931 return !plugin.readonly;
4932 }
4933 }
4934 return false;
4935}
4936
4937BEGIN_EVENT_TABLE(PluginPanel, wxPanel)
4938EVT_PAINT(PluginPanel::OnPaint)
4939END_EVENT_TABLE()
4940
4941PluginPanel::PluginPanel(wxPanel *parent, wxWindowID id, const wxPoint &pos,
4942 const wxSize &size, PlugInContainer *p_plugin)
4943 : wxPanel(parent, id, pos, size, wxBORDER_NONE) {
4944 m_PluginListPanel = (PluginListPanel *)parent; //->GetParent();
4945 m_PluginListPanel = dynamic_cast<PluginListPanel *>(parent /*->GetParent()*/);
4946 wxASSERT(m_PluginListPanel != 0);
4947
4948 m_pPlugin = p_plugin;
4949 m_bSelected = false;
4950 m_penWidthUnselected = g_Platform->GetDisplayDPmm() * .25;
4951 m_penWidthSelected = g_Platform->GetDisplayDPmm() * .5;
4952
4953 wxBoxSizer *topSizer = new wxBoxSizer(wxVERTICAL);
4954 SetSizer(topSizer);
4955
4956 wxBoxSizer *itemBoxSizer01 = new wxBoxSizer(wxHORIZONTAL);
4957 topSizer->Add(itemBoxSizer01, 0, wxEXPAND);
4958 Bind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this);
4959 Bind(wxEVT_LEFT_UP, &PluginPanel::OnPluginSelectedUp, this);
4960
4961 double iconSize = GetCharWidth() * 4;
4962 double dpi_mult = g_Platform->GetDisplayDIPMult(this);
4963 int icon_scale = iconSize * dpi_mult;
4964
4965 wxImage plugin_icon;
4966 ocpnStyle::Style *style = g_StyleManager->GetCurrentStyle();
4967 if (m_pPlugin->m_bitmap && m_pPlugin->m_bitmap->IsOk()) {
4968 plugin_icon = m_pPlugin->m_bitmap->ConvertToImage();
4969 }
4970 wxBitmap bitmap;
4971 if (plugin_icon.IsOk()) {
4972 int nowSize = plugin_icon.GetWidth();
4973 plugin_icon.Rescale(icon_scale, icon_scale, wxIMAGE_QUALITY_HIGH);
4974 bitmap = wxBitmap(plugin_icon);
4975 } else if (m_pPlugin->m_pluginStatus ==
4976 PluginStatus::ManagedInstallAvailable) {
4977 wxFileName path(g_Platform->GetSharedDataDir(), "packageBox.svg");
4978 path.AppendDir("uidata");
4979 path.AppendDir("traditional");
4980 bitmap = LoadSVG(path.GetFullPath(), icon_scale, icon_scale);
4981 } else {
4982 bitmap = wxBitmap(style->GetIcon(_T("default_pi"), icon_scale, icon_scale));
4983 }
4984 m_itemStaticBitmap = new wxStaticBitmap(this, wxID_ANY, bitmap);
4985
4986 itemBoxSizer01->Add(m_itemStaticBitmap, 0, wxEXPAND | wxALL, 10);
4987 m_itemStaticBitmap->Bind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected,
4988 this);
4989 m_itemStaticBitmap->Bind(wxEVT_LEFT_UP, &PluginPanel::OnPluginSelectedUp,
4990 this);
4991
4992 wxBoxSizer *itemBoxSizer02 = new wxBoxSizer(wxVERTICAL);
4993 itemBoxSizer01->Add(itemBoxSizer02, 1, wxEXPAND | wxALL, 0);
4994
4995 // Calculate character width available
4996 int nChars = g_options->GetSize().x / GetCharWidth();
4997 bool bCompact = false;
4998 if (nChars < 60) // Arbitrary, detecting mobile devices in portrait mode.
4999 bCompact = true;
5000
5001 if (bCompact) {
5002 // Might need to shorten the Plugin name string
5003 wxString nameString = m_pPlugin->m_common_name;
5004 int maxWidth = g_Platform->getDisplaySize().x * 3 / 10;
5005 wxScreenDC dc;
5006 int nameWidth;
5007 dc.GetTextExtent(m_pPlugin->m_common_name, &nameWidth, NULL);
5008 if (nameWidth > maxWidth) {
5009 nameString = wxControl::Ellipsize(m_pPlugin->m_common_name, dc,
5010 wxELLIPSIZE_END, maxWidth);
5011 }
5012 m_pName = new wxStaticText(this, wxID_ANY, nameString);
5013 m_pName->Bind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this);
5014 m_pName->Bind(wxEVT_LEFT_UP, &PluginPanel::OnPluginSelectedUp, this);
5015 itemBoxSizer02->Add(m_pName, 0, /*wxEXPAND|*/ wxALL, 5);
5016
5017 wxFlexGridSizer *sl1 = new wxFlexGridSizer(2, 0, 0);
5018 sl1->AddGrowableCol(1);
5019 itemBoxSizer02->Add(sl1, 0, wxEXPAND);
5020
5021 m_pVersion = new wxStaticText(this, wxID_ANY, _T("X.YY.ZZ.AA"));
5022 sl1->Add(m_pVersion, 0, /*wxEXPAND|*/ wxALL, 5);
5023 if (m_pPlugin->m_pluginStatus == PluginStatus::ManagedInstallAvailable) {
5024 m_pVersion->Hide();
5025 }
5026 m_pVersion->Bind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this);
5027 m_pVersion->Bind(wxEVT_LEFT_UP, &PluginPanel::OnPluginSelectedUp, this);
5028
5029 m_cbEnable = new wxCheckBox(this, wxID_ANY, _("Enabled"));
5030 sl1->Add(m_cbEnable, 1, wxALIGN_RIGHT | wxTOP, 5);
5031 m_cbEnable->Bind(wxEVT_CHECKBOX, &PluginPanel::OnPluginEnableToggle, this);
5032
5033 // Might need to shorten the Plugin description string
5034 wxString descriptionString = m_pPlugin->m_short_description;
5035 int maxDescriptionWidth = g_Platform->getDisplaySize().x - (iconSize * 4);
5036 int descriptionWidth;
5037 dc.GetTextExtent(m_pPlugin->m_short_description, &descriptionWidth, NULL);
5038 if (descriptionWidth > maxDescriptionWidth)
5039 descriptionString =
5040 wxControl::Ellipsize(m_pPlugin->m_short_description, dc,
5041 wxELLIPSIZE_END, maxDescriptionWidth);
5042
5043 // This invocation has the effect of setting the minimum width of the
5044 // descriptor field.
5045 m_pDescription =
5046 new wxStaticText(this, wxID_ANY, descriptionString, wxDefaultPosition,
5047 wxSize(maxDescriptionWidth, -1), wxST_NO_AUTORESIZE);
5048 itemBoxSizer02->Add(m_pDescription, 0, wxEXPAND | wxALL, 5);
5049 m_pDescription->Bind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this);
5050 m_pDescription->Bind(wxEVT_LEFT_UP, &PluginPanel::OnPluginSelectedUp, this);
5051
5052 } else {
5053 wxFlexGridSizer *itemBoxSizer03 = new wxFlexGridSizer(4, 0, 0);
5054 itemBoxSizer03->AddGrowableCol(2);
5055 itemBoxSizer02->Add(itemBoxSizer03, 0, wxEXPAND);
5056
5057 wxString nameString = m_pPlugin->m_common_name;
5058 m_pName = new wxStaticText(this, wxID_ANY, nameString);
5059 m_pName->Bind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this);
5060 m_pName->Bind(wxEVT_LEFT_UP, &PluginPanel::OnPluginSelectedUp, this);
5061
5062 // Avoid known bug in wxGTK3
5063#ifndef __WXGTK3__
5064 wxFont font = GetFont();
5065 font.SetWeight(wxFONTWEIGHT_BOLD);
5066 m_pName->SetFont(font);
5067#endif
5068
5069 itemBoxSizer03->Add(m_pName, 0, /*wxEXPAND|*/ wxALL, 10);
5070
5071 m_pVersion = new wxStaticText(this, wxID_ANY, _T("X.YY.ZZ.AA"));
5072 itemBoxSizer03->Add(m_pVersion, 0, /*wxEXPAND|*/ wxALL, 10);
5073 if (m_pPlugin->m_pluginStatus == PluginStatus::ManagedInstallAvailable) {
5074 m_pVersion->Hide();
5075 }
5076 m_pVersion->Bind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this);
5077 m_pVersion->Bind(wxEVT_LEFT_UP, &PluginPanel::OnPluginSelectedUp, this);
5078
5079 m_cbEnable = new wxCheckBox(this, wxID_ANY, _("Enabled"));
5080 itemBoxSizer03->Add(m_cbEnable, 1, wxALIGN_RIGHT | wxTOP, 10);
5081 m_cbEnable->Bind(wxEVT_CHECKBOX, &PluginPanel::OnPluginEnableToggle, this);
5082
5083 itemBoxSizer03->Add(5 * GetCharWidth(), 1, 0, wxALIGN_RIGHT | wxTOP, 10);
5084
5085 m_pDescription = new wxStaticText(
5086 this, wxID_ANY, m_pPlugin->m_short_description, wxDefaultPosition,
5087 wxSize(-1, -1) /*, wxST_NO_AUTORESIZE*/);
5088 itemBoxSizer02->Add(m_pDescription, 1, wxEXPAND | wxALL, 5);
5089 m_pDescription->Bind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this);
5090 m_pDescription->Bind(wxEVT_LEFT_UP, &PluginPanel::OnPluginSelectedUp, this);
5091 }
5092
5093 if (!bCompact) {
5094 m_info_btn = new WebsiteButton(this, "https:\\opencpn.org");
5095 m_info_btn->Hide();
5096 itemBoxSizer02->Add(m_info_btn, 0);
5097
5098 m_pButtons = new wxBoxSizer(wxHORIZONTAL);
5099 itemBoxSizer02->Add(m_pButtons, 0, /*wxEXPAND|*/ wxALL, 0);
5100 m_pButtonPreferences = new wxButton(this, wxID_ANY, _("Preferences"),
5101 wxDefaultPosition, wxDefaultSize, 0);
5102 m_pButtons->Add(m_pButtonPreferences, 0, wxALIGN_LEFT | wxALL, 2);
5103
5104 m_pButtons->AddSpacer(3 * GetCharWidth());
5105
5106 m_pButtonAction =
5107 new wxButton(this, wxID_ANY, "Upgrade to Version XX.XX.XX",
5108 wxDefaultPosition, wxDefaultSize, 0);
5109 m_pButtons->Add(m_pButtonAction, 0, wxALIGN_LEFT | wxALL, 2);
5110
5111 m_pButtonUninstall = new wxButton(this, wxID_ANY, _("Uninstall"),
5112 wxDefaultPosition, wxDefaultSize, 0);
5113 m_pButtons->Add(m_pButtonUninstall, 0, wxALIGN_LEFT | wxALL, 2);
5114 } else {
5115 m_pButtons = new wxBoxSizer(wxVERTICAL);
5116 itemBoxSizer02->Add(m_pButtons, 0, /*wxEXPAND|*/ wxALL, 0);
5117
5118 wxBoxSizer *tline = new wxBoxSizer(wxHORIZONTAL);
5119 m_pButtons->Add(tline, 0, wxALL, 2);
5120
5121 m_pButtonPreferences = new wxButton(this, wxID_ANY, _("Preferences"),
5122 wxDefaultPosition, wxDefaultSize, 0);
5123 tline->Add(m_pButtonPreferences, 0, wxALIGN_LEFT | wxALL, 0);
5124
5125 tline->AddSpacer(3 * GetCharWidth());
5126
5127 m_info_btn = new WebsiteButton(this, "https:\\opencpn.org");
5128 m_info_btn->Hide();
5129 tline->Add(m_info_btn, 0);
5130
5131 m_pButtonAction =
5132 new wxButton(this, wxID_ANY, "Upgrade to Version XX.XX.XX",
5133 wxDefaultPosition, wxDefaultSize);
5134 m_pButtons->Add(m_pButtonAction, 0, wxALIGN_LEFT | wxALL, 2);
5135
5136 m_pButtonUninstall = new wxButton(this, wxID_ANY, _("Uninstall"),
5137 wxDefaultPosition, wxDefaultSize, 0);
5138 m_pButtons->Add(m_pButtonUninstall, 0, wxALIGN_LEFT | wxALL, 2);
5139 }
5140
5141 wxBitmap statusBitmap;
5142 auto stat = p_plugin->m_pluginStatus;
5143 auto icon_name = icon_by_status[stat];
5144
5145 wxFileName path(g_Platform->GetSharedDataDir(), icon_name);
5146 path.AppendDir("uidata");
5147 path.AppendDir("traditional");
5148 bool ok = false;
5149 int bmsize = GetCharWidth() * 3 * dpi_mult;
5150 if (path.IsFileReadable()) {
5151 statusBitmap = LoadSVG(path.GetFullPath(), bmsize, bmsize);
5152 ok = statusBitmap.IsOk();
5153 }
5154 if (!ok) {
5155 auto style = g_StyleManager->GetCurrentStyle();
5156 statusBitmap = wxBitmap(style->GetIcon(_T("default_pi"), bmsize, bmsize));
5157 wxLogMessage("Icon: %s not found.", path.GetFullPath());
5158 }
5159
5160 m_itemStatusIconBitmap = new wxStaticBitmap(this, wxID_ANY, statusBitmap);
5161 m_itemStatusIconBitmap->SetToolTip(message_by_status(stat));
5162
5163 itemBoxSizer01->Add(m_itemStatusIconBitmap, 0, wxEXPAND | wxALL, 20);
5164
5165 itemBoxSizer02->AddSpacer(GetCharWidth());
5166
5167 m_pButtonPreferences->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
5168 &PluginPanel::OnPluginPreferences, this);
5169 m_pButtonUninstall->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
5170 &PluginPanel::OnPluginUninstall, this);
5171 m_pButtonAction->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
5172 &PluginPanel::OnPluginAction, this);
5173
5174 SetSelected(m_bSelected);
5175 SetAutoLayout(true);
5176 // FitInside();
5177 Fit();
5178}
5179
5180PluginPanel::~PluginPanel() {
5181 Unbind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this);
5182 m_itemStaticBitmap->Unbind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected,
5183 this);
5184 m_pName->Unbind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this);
5185 m_pVersion->Unbind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this);
5186 m_pDescription->Unbind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this);
5187 if (m_pButtonAction) {
5188 m_pButtonAction->Unbind(wxEVT_COMMAND_BUTTON_CLICKED,
5189 &PluginPanel::OnPluginAction, this);
5190 }
5191 m_pButtonPreferences->Unbind(wxEVT_COMMAND_BUTTON_CLICKED,
5192 &PluginPanel::OnPluginPreferences, this);
5193 m_cbEnable->Unbind(wxEVT_COMMAND_BUTTON_CLICKED,
5194 &PluginPanel::OnPluginEnableToggle, this);
5195}
5196
5197void PluginPanel::SetActionLabel(wxString &label) {
5198 m_pButtonAction->SetLabel(label);
5199 Refresh();
5200}
5201
5202static wxStopWatch swclick;
5203static int downx, downy;
5204
5205void PluginPanel::OnPluginSelected(wxMouseEvent &event) {
5206#ifdef __OCPN__ANDROID__
5207 swclick.Start();
5208 event.GetPosition(&downx, &downy);
5209#else
5210 DoPluginSelect();
5211#endif
5212}
5213
5214void PluginPanel::OnPluginSelectedUp(wxMouseEvent &event) {
5215#ifdef __OCPN__ANDROID__
5216 qDebug() << swclick.Time();
5217 if (swclick.Time() < 200) {
5218 int upx, upy;
5219 event.GetPosition(&upx, &upy);
5220 if ((fabs(upx - downx) < GetCharWidth()) &&
5221 (fabs(upy - downy) < GetCharWidth())) {
5222 DoPluginSelect();
5223 }
5224 }
5225 swclick.Start();
5226#endif
5227}
5228
5229void PluginPanel::DoPluginSelect() {
5230 if (m_pPlugin->m_pluginStatus == PluginStatus::ManagedInstallAvailable) {
5231 // auto dialog = dynamic_cast<PluginListPanel*>(GetParent());
5232 // auto dialog = dynamic_cast<PluginListPanel*>(m_parent);
5233 // wxASSERT(dialog != 0);
5234
5235 // Install the new plugin, auto-enabling as a convenience measure.
5236 run_update_dialog(m_PluginListPanel, m_pPlugin, false, 0, true);
5237 } else if (m_bSelected) {
5238 SetSelected(false);
5239 m_PluginListPanel->SelectPlugin(NULL);
5240 } else {
5241 SetSelected(true);
5242 m_PluginListPanel->SelectPlugin(this);
5243 }
5244}
5245
5246void PluginPanel::SetSelected(bool selected) {
5247 m_bSelected = selected;
5248
5249 m_pVersion->SetLabel(m_pPlugin->GetVersion().to_string());
5250
5251 if (m_pPlugin->m_ManagedMetadata.version.size()) {
5252 // Is this a fully managed and current plugin?
5253 // If so, as a special case...
5254 // We show the version from the metadata, thus handling managed plugins
5255 // with API < 117
5256
5257 if (m_pPlugin->m_pluginStatus ==
5258 PluginStatus::ManagedInstalledCurrentVersion)
5259 m_pVersion->SetLabel(m_pPlugin->m_ManagedMetadata.version);
5260
5261 if (m_pPlugin->m_pluginStatus ==
5262 PluginStatus::ManagedInstalledUpdateAvailable)
5263 m_pVersion->SetLabel(wxString(m_pPlugin->m_InstalledManagedVersion));
5264 }
5265
5266 if (selected) {
5267 SetBackgroundColour(GetDialogColor(DLG_SELECTED_BACKGROUND));
5268 m_pButtons->Show(true);
5269 bool unInstallPossible =
5270 canUninstall(m_pPlugin->m_common_name.ToStdString());
5271
5272 // Directly mark Legacy and system plugins as "not uninstallable"
5273 if (m_pPlugin->m_pluginStatus == PluginStatus::LegacyUpdateAvailable ||
5274 m_pPlugin->m_pluginStatus == PluginStatus::Unmanaged ||
5275 m_pPlugin->m_pluginStatus == PluginStatus::System)
5276 unInstallPossible = false;
5277
5278 m_pButtonUninstall->Show(unInstallPossible);
5279
5280 if (m_pPlugin->m_ManagedMetadata.info_url.size()) {
5281 m_info_btn->SetURL(m_pPlugin->m_ManagedMetadata.info_url.c_str());
5282 m_info_btn->Show();
5283 }
5284
5285 m_cbEnable->Show(true);
5286
5287 // Configure the "Action" button
5288 wxString label;
5289 SemanticVersion newVersion;
5290 switch (m_pPlugin->m_pluginStatus) {
5291 case PluginStatus::LegacyUpdateAvailable:
5292 label = _("Upgrade to Version ");
5293 label += wxString(m_pPlugin->m_ManagedMetadata.version.c_str());
5294 m_action = ActionVerb::UPGRADE_TO_MANAGED_VERSION;
5295 m_pButtonAction->Enable();
5296 break;
5297
5298 case PluginStatus::ManagedInstallAvailable:
5299 label = _("Install...");
5300 m_action = ActionVerb::INSTALL_MANAGED_VERSION;
5301 m_pButtonAction->Enable();
5302 break;
5303
5304 case PluginStatus::ManagedInstalledUpdateAvailable:
5305 label = _("Update to ");
5306 label += wxString(m_pPlugin->m_ManagedMetadata.version.c_str());
5307 m_action = ActionVerb::UPGRADE_INSTALLED_MANAGED_VERSION;
5308 m_pButtonAction->Enable();
5309 break;
5310
5311 case PluginStatus::ManagedInstalledCurrentVersion:
5312 label = _("Reinstall");
5313 m_action = ActionVerb::REINSTALL_MANAGED_VERSION;
5314 m_pButtonAction->Enable();
5315 break;
5316
5317 case PluginStatus::ManagedInstalledDowngradeAvailable:
5318 label = _("Downgrade");
5319 m_action = ActionVerb::DOWNGRADE_INSTALLED_MANAGED_VERSION;
5320 m_pButtonAction->Enable();
5321 break;
5322
5323 case PluginStatus::Unmanaged:
5324 m_action = ActionVerb::NOP;
5325 m_pButtonAction->Hide();
5326 break;
5327
5328 case PluginStatus::System:
5329 m_action = ActionVerb::NOP;
5330 m_pButtonAction->Hide();
5331 break;
5332
5333 default:
5334 label = "TBD";
5335 m_action = ActionVerb::NOP;
5336 break;
5337 }
5338 SetActionLabel(label);
5339
5340 Layout();
5341 } else {
5342 SetBackgroundColour(GetDialogColor(DLG_UNSELECTED_BACKGROUND));
5343 // m_pDescription->SetLabel( m_pPlugin->m_short_description );
5344#ifndef __WXQT__
5345 // m_pButtons->Show(false);
5346#else
5347 // m_pButtons->Show(true);
5348#endif
5349 //();
5350
5351 m_pButtons->Show(false);
5352 m_info_btn->Hide();
5353
5354 if (m_pPlugin->m_pluginStatus == PluginStatus::ManagedInstallAvailable)
5355 m_cbEnable->Show(false);
5356
5357 Layout();
5358 }
5359
5360 // m_pButtons->Show(selected); // For most platforms, show buttons if
5361 // selected m_pButtonsUpDown->Show(selected);
5362#ifdef __OCPN__ANDROID__
5363 // Some Android devices (e.g. Kyocera) have trouble with wxBitmapButton...
5364 // m_pButtonsUpDown->Show(false);
5365 // m_pButtons->Show(true); // Always enable buttons for Android
5366#endif
5367
5368 Layout();
5369
5370 if (selected) {
5371 SetBackgroundColour(GetDialogColor(DLG_SELECTED_BACKGROUND));
5372 } else {
5373 SetBackgroundColour(GetDialogColor(DLG_UNSELECTED_BACKGROUND));
5374 }
5375
5376 SetEnabled(m_pPlugin->m_bEnabled);
5377
5378#ifdef __OCPN__ANDROID__
5379 // Android (wxQT) sizers have troubles...
5380 // So we set some layout factors to avoid re-sizing on select/deselect.
5381 // m_rgSizer->Show(true);
5382 // m_pButtons->Show(true);
5383 // m_pButtonAction->Hide();
5384 // m_pButtonUninstall->Hide();
5385
5386 Fit();
5387 // m_PluginListPanel->m_pitemBoxSizer01->Layout();
5388#endif
5389}
5390
5391void PluginPanel::OnPaint(wxPaintEvent &event) {
5392 wxPaintDC dc(this);
5393
5394 int penWidth = m_penWidthUnselected;
5395 wxColour color = GetDialogColor(DLG_UNSELECTED_BACKGROUND);
5396 wxColour border = GetDialogColor(DLG_UNSELECTED_ACCENT);
5397
5398 if (m_bSelected) {
5399 penWidth = m_penWidthSelected;
5400 color = GetDialogColor(DLG_SELECTED_BACKGROUND);
5401 border = GetDialogColor(DLG_SELECTED_ACCENT);
5402 }
5403
5404 wxBrush b(color, wxBRUSHSTYLE_SOLID);
5405 dc.SetBrush(b);
5406 dc.SetPen(wxPen(border, penWidth));
5407
5408 dc.DrawRoundedRectangle(5, 5, GetSize().x - 10, GetSize().y - 10, 5);
5409}
5410
5411void PluginPanel::OnPluginPreferences(wxCommandEvent &event) {
5412 if (m_pPlugin->m_bEnabled && m_pPlugin->m_bInitState &&
5413 (m_pPlugin->m_cap_flag & WANTS_PREFERENCES)) {
5414#ifdef __OCPN__ANDROID__
5415 androidDisableRotation();
5416 m_pPlugin->m_pplugin->ShowPreferencesDialog(
5417 GetGrandParent()); // GrandParent will be the entire list panel, not
5418 // the plugin panel Ensures better centering on
5419 // small screens
5420#else
5421 m_pPlugin->m_pplugin->ShowPreferencesDialog(this);
5422#endif
5423 }
5424}
5425
5426void PluginPanel::OnPluginEnableToggle(wxCommandEvent &event) {
5427 SetEnabled(!m_pPlugin->m_bEnabled);
5428 if (m_pVersion->GetLabel().IsEmpty())
5429 m_pVersion->SetLabel(m_pPlugin->GetVersion().to_string());
5430}
5431
5432void PluginPanel::OnPluginUninstall(wxCommandEvent &event) {
5433 m_action = ActionVerb::UNINSTALL_MANAGED_VERSION;
5434
5435 // Chain up to the utility event handler
5436 wxCommandEvent actionEvent(wxEVT_COMMAND_BUTTON_CLICKED);
5437 actionEvent.SetId(ID_CMD_BUTTON_PERFORM_ACTION);
5438 actionEvent.SetClientData(this);
5439 g_pi_manager->GetUtilHandler()->AddPendingEvent(actionEvent);
5440}
5441
5442void PluginPanel::OnPluginAction(wxCommandEvent &event) {
5443 // Chain up to the utility event handler
5444 wxCommandEvent actionEvent(wxEVT_COMMAND_BUTTON_CLICKED);
5445 actionEvent.SetId(ID_CMD_BUTTON_PERFORM_ACTION);
5446 actionEvent.SetClientData(this);
5447 g_pi_manager->GetUtilHandler()->AddPendingEvent(actionEvent);
5448
5449 return;
5450}
5451
5452void PluginPanel::SetEnabled(bool enabled) {
5453 if (m_pPlugin->m_bEnabled != enabled) {
5454 m_pPlugin->m_bEnabled = enabled;
5455 PluginLoader::getInstance()->UpdatePlugIns();
5456 NotifySetupOptionsPlugin(m_pPlugin);
5457 }
5458 if (!enabled && !m_bSelected) {
5459 m_pName->SetForegroundColour(
5460 wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT));
5461 m_pVersion->SetForegroundColour(
5462 wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT));
5463 m_pDescription->SetForegroundColour(
5464 wxSystemSettings::GetColour(wxSYS_COLOUR_GRAYTEXT));
5465#ifdef x__OCPN__ANDROID__
5466 m_pName->Disable();
5467 m_pVersion->Disable();
5468 m_pDescription->Disable();
5469#endif
5470 } else {
5471 m_pName->SetForegroundColour(
5472 wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
5473 m_pVersion->SetForegroundColour(
5474 wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
5475 m_pDescription->SetForegroundColour(
5476 wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
5477#ifdef x__OCPN__ANDROID__
5478 m_pName->Enable();
5479 m_pVersion->Enable();
5480 m_pDescription->Enable();
5481#endif
5482 }
5483
5484#ifdef __OCPN__ANDROID__
5485 m_pName->Enable(enabled || m_bSelected);
5486 m_pVersion->Enable(enabled || m_bSelected);
5487 m_pDescription->Enable(enabled || m_bSelected);
5488#endif
5489
5490 if (m_bSelected) {
5491 wxString description = m_pPlugin->m_long_description;
5492 if (description.IsEmpty())
5493 description = wxString(m_pPlugin->m_ManagedMetadata.description.c_str());
5494
5495 PanelHardBreakWrapper wrapper(this, description,
5496 g_options->GetSize().x * 7 / 10);
5497 m_pDescription->SetLabel(wrapper.GetWrapped());
5498 if (m_pPlugin->m_ManagedMetadata.info_url.size()) {
5499 m_info_btn->SetURL(m_pPlugin->m_ManagedMetadata.info_url.c_str());
5500 m_info_btn->Show();
5501 }
5502 } else {
5503 wxString description = m_pPlugin->m_short_description;
5504 if (description.IsEmpty())
5505 description = wxString(m_pPlugin->m_ManagedMetadata.summary.c_str());
5506 PanelHardBreakWrapper wrapper(this, description,
5507 g_options->GetSize().x * 7 / 10);
5508 m_pDescription->SetLabel(wrapper.GetWrapped());
5509 }
5510
5511 m_pButtonPreferences->Enable(enabled &&
5512 (m_pPlugin->m_cap_flag & WANTS_PREFERENCES));
5513 m_cbEnable->SetValue(enabled);
5514}
5515
5516void PluginPanel::OnPluginUp(wxCommandEvent &event) {
5517 m_PluginListPanel->MoveUp(this);
5518}
5519
5520void PluginPanel::OnPluginDown(wxCommandEvent &event) {
5521 m_PluginListPanel->MoveDown(this);
5522}
5523
5525WebsiteButton::WebsiteButton(wxWindow *parent, const char *url)
5526 : wxPanel(parent), m_url(url) {
5527 auto vbox = new wxBoxSizer(wxVERTICAL);
5528 auto button = new wxButton(this, wxID_ANY, _("Website"));
5529 button->Enable(strlen(url) > 0);
5530 vbox->Add(button);
5531 SetSizer(vbox);
5532 Bind(wxEVT_COMMAND_BUTTON_CLICKED,
5533 [=](wxCommandEvent &) { wxLaunchDefaultBrowser(m_url); });
5534}
5535
5536// ----------------------------------------------------------------------------
5537// PlugInChartBase Implmentation
5538// This class is the base class for Plug-able chart types
5539// ----------------------------------------------------------------------------
5540
5541PlugInChartBase::PlugInChartBase() { m_Chart_Error_Factor = 0.; }
5542
5543PlugInChartBase::~PlugInChartBase() {}
5544
5545wxString PlugInChartBase::GetFileSearchMask(void) { return _T(""); }
5546
5547int PlugInChartBase::Init(const wxString &name, int init_flags) { return 0; }
5548
5549// Accessors
5550
5551double PlugInChartBase::GetNormalScaleMin(double canvas_scale_factor,
5552 bool b_allow_overzoom) {
5553 return 1.0;
5554}
5555
5556double PlugInChartBase::GetNormalScaleMax(double canvas_scale_factor,
5557 int canvas_width) {
5558 return 2.0e7;
5559}
5560
5561bool PlugInChartBase::GetChartExtent(ExtentPI *pext) { return false; }
5562
5563wxBitmap &PlugInChartBase::RenderRegionView(const PlugIn_ViewPort &VPoint,
5564 const wxRegion &Region) {
5565 return wxNullBitmap;
5566}
5567
5568bool PlugInChartBase::AdjustVP(PlugIn_ViewPort &vp_last,
5569 PlugIn_ViewPort &vp_proposed) {
5570 return false;
5571}
5572
5573void PlugInChartBase::GetValidCanvasRegion(const PlugIn_ViewPort &VPoint,
5574 wxRegion *pValidRegion) {}
5575
5576void PlugInChartBase::SetColorScheme(int cs, bool bApplyImmediate) {}
5577
5578double PlugInChartBase::GetNearestPreferredScalePPM(double target_scale_ppm) {
5579 return 1.0;
5580}
5581
5582wxBitmap *PlugInChartBase::GetThumbnail(int tnx, int tny, int cs) {
5583 return NULL;
5584}
5585
5586void PlugInChartBase::ComputeSourceRectangle(const PlugIn_ViewPort &vp,
5587 wxRect *pSourceRect) {}
5588
5589double PlugInChartBase::GetRasterScaleFactor() { return 1.0; }
5590
5591bool PlugInChartBase::GetChartBits(wxRect &source, unsigned char *pPix,
5592 int sub_samp) {
5593 return false;
5594}
5595
5596int PlugInChartBase::GetSize_X() { return 1; }
5597
5598int PlugInChartBase::GetSize_Y() { return 1; }
5599
5600void PlugInChartBase::latlong_to_chartpix(double lat, double lon, double &pixx,
5601 double &pixy) {}
5602
5603void PlugInChartBase::chartpix_to_latlong(double pixx, double pixy,
5604 double *plat, double *plon) {}
5605
5606// ----------------------------------------------------------------------------
5607// PlugInChartBaseGL Implementation
5608//
5609// ----------------------------------------------------------------------------
5610
5611PlugInChartBaseGL::PlugInChartBaseGL() {}
5612
5613PlugInChartBaseGL::~PlugInChartBaseGL() {}
5614
5615int PlugInChartBaseGL::RenderRegionViewOnGL(const wxGLContext &glc,
5616 const PlugIn_ViewPort &VPoint,
5617 const wxRegion &Region,
5618 bool b_use_stencil) {
5619 return 0;
5620}
5621
5622ListOfPI_S57Obj *PlugInChartBaseGL::GetObjRuleListAtLatLon(
5623 float lat, float lon, float select_radius, PlugIn_ViewPort *VPoint) {
5624 return NULL;
5625}
5626
5627wxString PlugInChartBaseGL::CreateObjDescriptions(ListOfPI_S57Obj *obj_list) {
5628 return _T("");
5629}
5630
5631int PlugInChartBaseGL::GetNoCOVREntries() { return 0; }
5632
5633int PlugInChartBaseGL::GetNoCOVRTablePoints(int iTable) { return 0; }
5634
5635int PlugInChartBaseGL::GetNoCOVRTablenPoints(int iTable) { return 0; }
5636
5637float *PlugInChartBaseGL::GetNoCOVRTableHead(int iTable) { return 0; }
5638
5639// ----------------------------------------------------------------------------
5640// PlugInChartBaseExtended Implementation
5641//
5642// ----------------------------------------------------------------------------
5643
5644PlugInChartBaseExtended::PlugInChartBaseExtended() {}
5645
5646PlugInChartBaseExtended::~PlugInChartBaseExtended() {}
5647
5648int PlugInChartBaseExtended::RenderRegionViewOnGL(const wxGLContext &glc,
5649 const PlugIn_ViewPort &VPoint,
5650 const wxRegion &Region,
5651 bool b_use_stencil) {
5652 return 0;
5653}
5654
5655int PlugInChartBaseExtended::RenderRegionViewOnGLNoText(
5656 const wxGLContext &glc, const PlugIn_ViewPort &VPoint,
5657 const wxRegion &Region, bool b_use_stencil) {
5658 return 0;
5659}
5660
5661int PlugInChartBaseExtended::RenderRegionViewOnGLTextOnly(
5662 const wxGLContext &glc, const PlugIn_ViewPort &VPoint,
5663 const wxRegion &Region, bool b_use_stencil) {
5664 return 0;
5665}
5666
5667wxBitmap &PlugInChartBaseExtended::RenderRegionViewOnDCNoText(
5668 const PlugIn_ViewPort &VPoint, const wxRegion &Region) {
5669 return wxNullBitmap;
5670}
5671
5672bool PlugInChartBaseExtended::RenderRegionViewOnDCTextOnly(
5673 wxMemoryDC &dc, const PlugIn_ViewPort &VPoint, const wxRegion &Region) {
5674 return false;
5675}
5676
5677ListOfPI_S57Obj *PlugInChartBaseExtended::GetObjRuleListAtLatLon(
5678 float lat, float lon, float select_radius, PlugIn_ViewPort *VPoint) {
5679 return NULL;
5680}
5681
5682wxString PlugInChartBaseExtended::CreateObjDescriptions(
5683 ListOfPI_S57Obj *obj_list) {
5684 return _T("");
5685}
5686
5687int PlugInChartBaseExtended::GetNoCOVREntries() { return 0; }
5688
5689int PlugInChartBaseExtended::GetNoCOVRTablePoints(int iTable) { return 0; }
5690
5691int PlugInChartBaseExtended::GetNoCOVRTablenPoints(int iTable) { return 0; }
5692
5693float *PlugInChartBaseExtended::GetNoCOVRTableHead(int iTable) { return 0; }
5694
5695void PlugInChartBaseExtended::ClearPLIBTextList() {}
5696
5697// ----------------------------------------------------------------------------
5698// PlugInChartBaseExtendedPlus2 Implementation
5699//
5700// ----------------------------------------------------------------------------
5701
5702PlugInChartBaseExtendedPlus2::PlugInChartBaseExtendedPlus2() {}
5703
5704PlugInChartBaseExtendedPlus2::~PlugInChartBaseExtendedPlus2() {}
5705
5706ListOfPI_S57Obj *
5707PlugInChartBaseExtendedPlus2::GetLightsObjRuleListVisibleAtLatLon(
5708 float lat, float lon, PlugIn_ViewPort *VPoint) {
5709 return NULL;
5710}
5711
5712// ----------------------------------------------------------------------------
5713// PlugInChartBaseGLPlus2 Implementation
5714//
5715// ----------------------------------------------------------------------------
5716
5717PlugInChartBaseGLPlus2::PlugInChartBaseGLPlus2() {}
5718
5719PlugInChartBaseGLPlus2::~PlugInChartBaseGLPlus2() {}
5720
5721ListOfPI_S57Obj *PlugInChartBaseGLPlus2::GetLightsObjRuleListVisibleAtLatLon(
5722 float lat, float lon, PlugIn_ViewPort *VPoint) {
5723 return NULL;
5724}
5725
5726// ----------------------------------------------------------------------------
5727// ChartPlugInWrapper Implementation
5728// This class is a wrapper/interface to PlugIn charts(PlugInChartBase)
5729// ----------------------------------------------------------------------------
5730
5731ChartPlugInWrapper::ChartPlugInWrapper() {}
5732
5733ChartPlugInWrapper::ChartPlugInWrapper(const wxString &chart_class) {
5734 m_ppo = ::wxCreateDynamicObject(chart_class);
5735 m_ppicb = wxDynamicCast(m_ppo, PlugInChartBase);
5736}
5737
5738ChartPlugInWrapper::~ChartPlugInWrapper() {
5739 if (m_ppicb) delete m_ppicb;
5740}
5741
5742wxString ChartPlugInWrapper::GetFileSearchMask(void) {
5743 if (m_ppicb)
5744 return m_ppicb->GetFileSearchMask();
5745 else
5746 return _T("");
5747}
5748
5749InitReturn ChartPlugInWrapper::Init(const wxString &name,
5750 ChartInitFlag init_flags) {
5751 if (m_ppicb) {
5752 wxWindow *pa = wxWindow::FindFocus();
5753
5754 InitReturn ret_val = (InitReturn)m_ppicb->Init(name, (int)init_flags);
5755
5756 // Here we transcribe all the required wrapped member elements up into
5757 // the chartbase object which is the parent of this class
5758 if (ret_val == INIT_OK) {
5759 m_FullPath = m_ppicb->GetFullPath();
5760 m_ChartType = (ChartTypeEnum)m_ppicb->GetChartType();
5761 m_ChartFamily = (ChartFamilyEnum)m_ppicb->GetChartFamily();
5762 m_projection = (OcpnProjType)m_ppicb->GetChartProjection();
5763 m_EdDate = m_ppicb->GetEditionDate();
5764 m_Name = m_ppicb->GetName();
5765 m_ID = m_ppicb->GetID();
5766 m_DepthUnits = m_ppicb->GetDepthUnits();
5767 m_SoundingsDatum = m_ppicb->GetSoundingsDatum();
5768 m_datum_str = m_ppicb->GetDatumString();
5769 m_SE = m_ppicb->GetSE();
5770 m_EdDate = m_ppicb->GetEditionDate();
5771 m_ExtraInfo = m_ppicb->GetExtraInfo();
5772 Chart_Error_Factor = m_ppicb->GetChartErrorFactor();
5773 m_depth_unit_id = (ChartDepthUnitType)m_ppicb->GetDepthUnitId();
5774 m_Chart_Skew = m_ppicb->GetChartSkew();
5775 m_Chart_Scale = m_ppicb->GetNativeScale();
5776
5777 // We estimate ppm_avg as needed by raster texture cache logic...
5778 // This number works for average BSB charts, scanned with average
5779 // resolution
5780 m_ppm_avg = 10000. / m_ppicb->GetNativeScale(); // fallback value
5781
5782 // Calcuculate a "better" ppm from the chart geo extent and raster size.
5783 if ((fabs(m_Chart_Skew) < .01) &&
5784 (CHART_FAMILY_RASTER == m_ChartFamily)) {
5785 Extent extent;
5786 if (GetChartExtent(&extent)) {
5787 double lon_range = extent.ELON - extent.WLON;
5788 if ((lon_range > 0) &&
5789 (lon_range < 90.0)) // Be safe about IDL crossing and huge charts
5790 m_ppm_avg = GetSize_X() / (lon_range * 1852 * 60);
5791 }
5792 }
5793
5794 m_overlayENC = false;
5795 if (m_ChartFamily == (ChartFamilyEnum)PI_CHART_FAMILY_VECTOR) {
5796 wxCharBuffer buf = m_FullPath.ToUTF8();
5797 m_overlayENC = s57chart::IsCellOverlayType(buf.data());
5798 }
5799
5800 bReadyToRender = m_ppicb->IsReadyToRender();
5801 } else {
5802 // Mark the chart as unable to render
5803 m_ChartType = CHART_TYPE_UNKNOWN;
5804 m_ChartFamily = CHART_FAMILY_UNKNOWN;
5805 }
5806
5807 // PlugIn may invoke wxExecute(), which steals the keyboard focus
5808 // So take it back
5809 ChartCanvas *pc = wxDynamicCast(pa, ChartCanvas);
5810 if (pc) pc->SetFocus();
5811
5812 return ret_val;
5813 } else
5814 return INIT_FAIL_REMOVE;
5815}
5816
5817// Accessors
5818int ChartPlugInWrapper::GetCOVREntries() {
5819 if (m_ppicb)
5820 return m_ppicb->GetCOVREntries();
5821 else
5822 return 0;
5823}
5824
5825int ChartPlugInWrapper::GetCOVRTablePoints(int iTable) {
5826 if (m_ppicb)
5827 return m_ppicb->GetCOVRTablePoints(iTable);
5828 else
5829 return 0;
5830}
5831
5832int ChartPlugInWrapper::GetCOVRTablenPoints(int iTable) {
5833 if (m_ppicb)
5834 return m_ppicb->GetCOVRTablenPoints(iTable);
5835 else
5836 return 0;
5837}
5838
5839float *ChartPlugInWrapper::GetCOVRTableHead(int iTable) {
5840 if (m_ppicb)
5841 return m_ppicb->GetCOVRTableHead(iTable);
5842 else
5843 return 0;
5844}
5845
5846// TODO
5847// PlugIn chart types do not properly support NoCovr Regions
5848// Proper fix is to update PlugIn Chart Type API
5849// Derive an extended PlugIn chart class from existing class,
5850// and use some kind of RTTI to figure out which class to call.
5851int ChartPlugInWrapper::GetNoCOVREntries() {
5852 if (m_ppicb) {
5853 PlugInChartBaseGL *ppicbgl = dynamic_cast<PlugInChartBaseGL *>(m_ppicb);
5854 if (ppicbgl) {
5855 return ppicbgl->GetNoCOVREntries();
5856 }
5857 }
5858 return 0;
5859}
5860
5861int ChartPlugInWrapper::GetNoCOVRTablePoints(int iTable) {
5862 if (m_ppicb) {
5863 PlugInChartBaseGL *ppicbgl = dynamic_cast<PlugInChartBaseGL *>(m_ppicb);
5864 if (ppicbgl) {
5865 return ppicbgl->GetNoCOVRTablePoints(iTable);
5866 }
5867 }
5868 return 0;
5869}
5870
5871int ChartPlugInWrapper::GetNoCOVRTablenPoints(int iTable) {
5872 if (m_ppicb) {
5873 PlugInChartBaseGL *ppicbgl = dynamic_cast<PlugInChartBaseGL *>(m_ppicb);
5874 if (ppicbgl) {
5875 return ppicbgl->GetNoCOVRTablenPoints(iTable);
5876 }
5877 }
5878 return 0;
5879}
5880
5881float *ChartPlugInWrapper::GetNoCOVRTableHead(int iTable) {
5882 if (m_ppicb) {
5883 PlugInChartBaseGL *ppicbgl = dynamic_cast<PlugInChartBaseGL *>(m_ppicb);
5884 if (ppicbgl) {
5885 return ppicbgl->GetNoCOVRTableHead(iTable);
5886 }
5887 }
5888 return 0;
5889}
5890
5891bool ChartPlugInWrapper::GetChartExtent(Extent *pext) {
5892 if (m_ppicb) {
5893 ExtentPI xpi;
5894 if (m_ppicb->GetChartExtent(&xpi)) {
5895 pext->NLAT = xpi.NLAT;
5896 pext->SLAT = xpi.SLAT;
5897 pext->ELON = xpi.ELON;
5898 pext->WLON = xpi.WLON;
5899
5900 return true;
5901 } else
5902 return false;
5903 } else
5904 return false;
5905}
5906
5907ThumbData *ChartPlugInWrapper::GetThumbData(int tnx, int tny, float lat,
5908 float lon) {
5909 if (m_ppicb) {
5910 // Create the bitmap if needed, doing a deep copy from the Bitmap owned
5911 // by the PlugIn Chart
5912 if (!pThumbData->pDIBThumb) {
5913 wxBitmap *pBMPOwnedByChart =
5914 m_ppicb->GetThumbnail(tnx, tny, m_global_color_scheme);
5915 if (pBMPOwnedByChart) {
5916 wxImage img = pBMPOwnedByChart->ConvertToImage();
5917 pThumbData->pDIBThumb = new wxBitmap(img);
5918 } else
5919 pThumbData->pDIBThumb = NULL;
5920 }
5921
5922 pThumbData->Thumb_Size_X = tnx;
5923 pThumbData->Thumb_Size_Y = tny;
5924
5925 /*
5926 // Plot the supplied Lat/Lon on the thumbnail
5927 int divx = m_ppicb->Size_X / tnx;
5928 int divy = m_ppicb->Size_Y / tny;
5929
5930 int div_factor = __min(divx, divy);
5931
5932 int pixx, pixy;
5933
5934
5935 // Using a temporary synthetic ViewPort and source rectangle,
5936 // calculate the ships position on the thumbnail
5937 ViewPort tvp;
5938 tvp.pix_width = tnx;
5939 tvp.pix_height = tny;
5940 tvp.view_scale_ppm = GetPPM() / div_factor;
5941 wxRect trex = Rsrc;
5942 Rsrc.x = 0;
5943 Rsrc.y = 0;
5944 latlong_to_pix_vp(lat, lon, pixx, pixy, tvp);
5945 Rsrc = trex;
5946
5947 pThumbData->ShipX = pixx;// / div_factor;
5948 pThumbData->ShipY = pixy;// / div_factor;
5949 */
5950 pThumbData->ShipX = 0;
5951 pThumbData->ShipY = 0;
5952
5953 return pThumbData;
5954 } else
5955 return NULL;
5956}
5957
5958ThumbData *ChartPlugInWrapper::GetThumbData() { return pThumbData; }
5959
5960bool ChartPlugInWrapper::UpdateThumbData(double lat, double lon) {
5961 return true;
5962}
5963
5964double ChartPlugInWrapper::GetNormalScaleMin(double canvas_scale_factor,
5965 bool b_allow_overzoom) {
5966 if (m_ppicb)
5967 return m_ppicb->GetNormalScaleMin(canvas_scale_factor, b_allow_overzoom);
5968 else
5969 return 1.0;
5970}
5971
5972double ChartPlugInWrapper::GetNormalScaleMax(double canvas_scale_factor,
5973 int canvas_width) {
5974 if (m_ppicb)
5975 return m_ppicb->GetNormalScaleMax(canvas_scale_factor, canvas_width);
5976 else
5977 return 2.0e7;
5978}
5979
5980/* RectRegion:
5981 * This is the Screen region desired to be updated. Will
5982 * be either 1 rectangle(full screen) or two rectangles (panning with FBO
5983 * accelerated pan logic)
5984 *
5985 * Region:
5986 * This is the LLRegion describing the quilt active region
5987 * for this chart.
5988 *
5989 * So, Actual rendering area onscreen should be clipped to the
5990 * intersection of the two regions.
5991 */
5992
5993// Render helpers
5994void RenderRotateToViewPort(const ViewPort &VPoint) {
5995 float xt = VPoint.pix_width / 2.0, yt = VPoint.pix_height / 2.0;
5996 glTranslatef(xt, yt, 0);
5997 glRotatef(VPoint.rotation * 180. / PI, 0, 0, 1);
5998 glTranslatef(-xt, -yt, 0);
5999}
6000
6001void UndoRenderRotateToViewPort(const ViewPort &VPoint) {
6002 float xt = VPoint.pix_width / 2.0, yt = VPoint.pix_height / 2.0;
6003 glTranslatef(xt, yt, 0);
6004 glRotatef(-VPoint.rotation * 180. / PI, 0, 0, 1);
6005 glTranslatef(-xt, -yt, 0);
6006}
6007
6008bool ChartPlugInWrapper::RenderRegionViewOnGL(const wxGLContext &glc,
6009 const ViewPort &VPoint,
6010 const OCPNRegion &RectRegion,
6011 const LLRegion &Region) {
6012#ifdef ocpnUSE_GL
6013 if (m_ppicb) {
6014 ViewPort vp = VPoint; // non-const copy
6015
6016 gs_plib_flags = 0; // reset the CAPs flag
6017 PlugInChartBaseGL *ppicb_gl = dynamic_cast<PlugInChartBaseGL *>(m_ppicb);
6018 PlugInChartBaseExtended *ppicb_x =
6019 dynamic_cast<PlugInChartBaseExtended *>(m_ppicb);
6020 if (!Region.Empty() && (ppicb_gl || ppicb_x)) {
6021 wxRegion *r = RectRegion.GetNew_wxRegion();
6022 for (OCPNRegionIterator upd(RectRegion); upd.HaveRects();
6023 upd.NextRect()) {
6024 LLRegion chart_region = vp.GetLLRegion(upd.GetRect());
6025 chart_region.Intersect(Region);
6026
6027 if (!chart_region.Empty()) {
6028 ViewPort cvp = glChartCanvas::ClippedViewport(VPoint, chart_region);
6029
6030 glChartCanvas::SetClipRect(cvp, upd.GetRect(), false);
6031
6032 //ps52plib->m_last_clip_rect = upd.GetRect();
6033
6034#ifndef USE_ANDROID_GLES2
6035// glPushMatrix(); // Adjust for rotation
6036#endif
6037 RenderRotateToViewPort(VPoint);
6038
6039 PlugIn_ViewPort pivp = CreatePlugInViewport(cvp);
6040 if (ppicb_x)
6041 ppicb_x->RenderRegionViewOnGL(glc, pivp, *r,
6042 glChartCanvas::s_b_useStencil);
6043 else if (ppicb_gl)
6044 ppicb_gl->RenderRegionViewOnGL(glc, pivp, *r,
6045 glChartCanvas::s_b_useStencil);
6046 UndoRenderRotateToViewPort(VPoint);
6047
6048#ifndef USE_ANDROID_GLES2
6049// glPopMatrix();
6050#endif
6051 glChartCanvas::DisableClipRegion();
6052
6053 }
6054 } // for
6055 delete r;
6056 }
6057 } else
6058 return false;
6059#endif
6060 return true;
6061}
6062
6063// int indexrr;
6064
6065bool ChartPlugInWrapper::RenderRegionViewOnGLNoText(
6066 const wxGLContext &glc, const ViewPort &VPoint,
6067 const OCPNRegion &RectRegion, const LLRegion &Region) {
6068#ifdef ocpnUSE_GL
6069 if (m_ppicb) {
6070 // printf("\nCPIW::RRVOGLNT %d %d \n", indexrr++, m_Chart_Scale);
6071
6072 gs_plib_flags = 0; // reset the CAPs flag
6073 PlugInChartBaseExtended *ppicb_x =
6074 dynamic_cast<PlugInChartBaseExtended *>(m_ppicb);
6075 PlugInChartBaseGL *ppicb = dynamic_cast<PlugInChartBaseGL *>(m_ppicb);
6076 if (!Region.Empty() && ppicb_x) {
6077
6078 // Start with a clean slate
6079 glChartCanvas::SetClipRect(VPoint, VPoint.rv_rect, false);
6080 glChartCanvas::DisableClipRegion();
6081
6082 // Apply rotation to this chart
6083 RenderRotateToViewPort(VPoint);
6084
6085 PlugIn_ViewPort pivp = CreatePlugInViewport(VPoint);
6086 wxRegion *r = RectRegion.GetNew_wxRegion();
6087
6088 ppicb_x->RenderRegionViewOnGLNoText(glc, pivp, *r,
6089 glChartCanvas::s_b_useStencil);
6090
6091 // Undo rotation
6092 UndoRenderRotateToViewPort(VPoint);
6093
6094 delete r;
6095 }
6096
6097 else if (!Region.Empty() &&
6098 ppicb) // Legacy Vector GL Plugin chart (e.g.S63)
6099 {
6100 ViewPort vp = VPoint; // non-const copy
6101 wxRegion *r = RectRegion.GetNew_wxRegion();
6102 for (OCPNRegionIterator upd(RectRegion); upd.HaveRects();
6103 upd.NextRect()) {
6104 LLRegion chart_region = vp.GetLLRegion(upd.GetRect());
6105 chart_region.Intersect(Region);
6106
6107 if (!chart_region.Empty()) {
6108 ViewPort cvp = glChartCanvas::ClippedViewport(VPoint, chart_region);
6109
6110 glChartCanvas::SetClipRect(cvp, upd.GetRect(), false);
6111
6112 RenderRotateToViewPort(VPoint);
6113
6114 PlugIn_ViewPort pivp = CreatePlugInViewport(cvp);
6115 ppicb->RenderRegionViewOnGL(glc, pivp, *r,
6116 glChartCanvas::s_b_useStencil);
6117
6118 // Undo rotation
6119 UndoRenderRotateToViewPort(VPoint);
6120
6121 glChartCanvas::DisableClipRegion();
6122
6123 }
6124 } // for
6125 delete r;
6126 }
6127
6128 } else
6129 return false;
6130#endif
6131 return true;
6132}
6133
6134bool ChartPlugInWrapper::RenderRegionViewOnGLTextOnly(
6135 const wxGLContext &glc, const ViewPort &VPoint, const OCPNRegion &Region) {
6136#ifdef ocpnUSE_GL
6137 if (m_ppicb) {
6138 gs_plib_flags = 0; // reset the CAPs flag
6139 PlugInChartBaseExtended *ppicb_x =
6140 dynamic_cast<PlugInChartBaseExtended *>(m_ppicb);
6141 if (!Region.Empty() && ppicb_x) {
6142 wxRegion *r = Region.GetNew_wxRegion();
6143 for (OCPNRegionIterator upd(Region); upd.HaveRects(); upd.NextRect()) {
6144#ifndef USE_ANDROID_GLES2
6145// glPushMatrix(); // Adjust for rotation
6146#endif
6147 RenderRotateToViewPort(VPoint);
6148
6149 PlugIn_ViewPort pivp = CreatePlugInViewport(VPoint);
6150 ppicb_x->RenderRegionViewOnGLTextOnly(glc, pivp, *r,
6151 glChartCanvas::s_b_useStencil);
6152 UndoRenderRotateToViewPort(VPoint);
6153
6154#ifndef USE_ANDROID_GLES2
6155// glPopMatrix();
6156#endif
6157
6158 } // for
6159 delete r;
6160 }
6161 } else
6162 return false;
6163#endif
6164 return true;
6165}
6166
6167bool ChartPlugInWrapper::RenderRegionViewOnDC(wxMemoryDC &dc,
6168 const ViewPort &VPoint,
6169 const OCPNRegion &Region) {
6170 if (m_ppicb) {
6171 gs_plib_flags = 0; // reset the CAPs flag
6172 PlugIn_ViewPort pivp = CreatePlugInViewport(VPoint);
6173 if (Region.IsOk()) {
6174 wxRegion *r = Region.GetNew_wxRegion();
6175 if (!m_overlayENC)
6176 dc.SelectObject(m_ppicb->RenderRegionView(pivp, *r));
6177 else {
6178 wxBitmap &obmp = m_ppicb->RenderRegionView(pivp, *r);
6179
6180 // Create a mask to remove the NODTA areas from overlay cells.
6181 wxColour nodat = GetGlobalColor(_T ( "NODTA" ));
6182 wxColour nodat_sub = nodat;
6183
6184#ifdef ocpnUSE_ocpnBitmap
6185 nodat_sub = wxColour(nodat.Blue(), nodat.Green(), nodat.Red());
6186#endif
6187 m_pMask = new wxMask(obmp, nodat_sub);
6188 obmp.SetMask(m_pMask);
6189
6190 dc.SelectObject(obmp);
6191 }
6192
6193 delete r;
6194 return true;
6195 } else
6196 return false;
6197 } else
6198 return false;
6199}
6200
6201bool ChartPlugInWrapper::RenderRegionViewOnDCNoText(wxMemoryDC &dc,
6202 const ViewPort &VPoint,
6203 const OCPNRegion &Region) {
6204 if (m_ppicb) {
6205 gs_plib_flags = 0; // reset the CAPs flag
6206 PlugIn_ViewPort pivp = CreatePlugInViewport(VPoint);
6207
6209 dynamic_cast<PlugInChartBaseExtended *>(m_ppicb);
6210 PlugInChartBase *ppicb = dynamic_cast<PlugInChartBase *>(m_ppicb);
6211
6212 if (Region.IsOk() && (pCBx || ppicb)) {
6213 wxRegion *r = Region.GetNew_wxRegion();
6214
6215 if (pCBx)
6216 dc.SelectObject(pCBx->RenderRegionViewOnDCNoText(pivp, *r));
6217 else if (ppicb)
6218 dc.SelectObject(ppicb->RenderRegionView(pivp, *r));
6219
6220 delete r;
6221 return true;
6222 } else
6223 return false;
6224 } else
6225 return false;
6226}
6227
6228bool ChartPlugInWrapper::RenderRegionViewOnDCTextOnly(
6229 wxMemoryDC &dc, const ViewPort &VPoint, const OCPNRegion &Region) {
6230 if (m_ppicb) {
6231 bool ret_val = false;
6232 gs_plib_flags = 0; // reset the CAPs flag
6233 PlugIn_ViewPort pivp = CreatePlugInViewport(VPoint);
6234 if (Region.IsOk()) {
6235 wxRegion *r = Region.GetNew_wxRegion();
6236
6238 dynamic_cast<PlugInChartBaseExtended *>(m_ppicb);
6239 if (pCBx) ret_val = pCBx->RenderRegionViewOnDCTextOnly(dc, pivp, *r);
6240
6241 delete r;
6242 return ret_val;
6243 } else
6244 return false;
6245 } else
6246 return false;
6247}
6248
6249void ChartPlugInWrapper::ClearPLIBTextList() {
6250 if (m_ppicb) {
6252 dynamic_cast<PlugInChartBaseExtended *>(m_ppicb);
6253 if (pCBx) pCBx->ClearPLIBTextList();
6254 }
6255}
6256
6257bool ChartPlugInWrapper::AdjustVP(ViewPort &vp_last, ViewPort &vp_proposed) {
6258 if (m_ppicb) {
6259 PlugIn_ViewPort pivp_last = CreatePlugInViewport(vp_last);
6260 PlugIn_ViewPort pivp_proposed = CreatePlugInViewport(vp_proposed);
6261 return m_ppicb->AdjustVP(pivp_last, pivp_proposed);
6262 } else
6263 return false;
6264}
6265
6266void ChartPlugInWrapper::GetValidCanvasRegion(const ViewPort &VPoint,
6267 OCPNRegion *pValidRegion) {
6268 if (m_ppicb) {
6269 PlugIn_ViewPort pivp = CreatePlugInViewport(VPoint);
6270 // currently convert using wxRegion,
6271 // this should be changed as wxRegion is proven unstable/buggy on various
6272 // platforms
6273 wxRegion region;
6274 m_ppicb->GetValidCanvasRegion(pivp, &region);
6275 *pValidRegion = OCPNRegion(region);
6276 }
6277
6278 return;
6279}
6280
6281void ChartPlugInWrapper::SetColorScheme(ColorScheme cs, bool bApplyImmediate) {
6282 if (m_ppicb) {
6283 m_ppicb->SetColorScheme(cs, bApplyImmediate);
6284 }
6285 m_global_color_scheme = cs;
6286 // Force a new thumbnail
6287 if (pThumbData) pThumbData->pDIBThumb = NULL;
6288}
6289
6290double ChartPlugInWrapper::GetNearestPreferredScalePPM(
6291 double target_scale_ppm) {
6292 if (m_ppicb)
6293 return m_ppicb->GetNearestPreferredScalePPM(target_scale_ppm);
6294 else
6295 return 1.0;
6296}
6297
6298void ChartPlugInWrapper::ComputeSourceRectangle(const ViewPort &VPoint,
6299 wxRect *pSourceRect) {
6300 if (m_ppicb) {
6301 PlugIn_ViewPort pivp = CreatePlugInViewport(VPoint);
6302 m_ppicb->ComputeSourceRectangle(pivp, pSourceRect);
6303 }
6304}
6305
6306double ChartPlugInWrapper::GetRasterScaleFactor(const ViewPort &vp) {
6307 if (m_ppicb) {
6308 return (wxRound(100000 * GetPPM() / vp.view_scale_ppm)) / 100000.;
6309 } else
6310 return 1.0;
6311}
6312
6313bool ChartPlugInWrapper::GetChartBits(wxRect &source, unsigned char *pPix,
6314 int sub_samp) {
6315 wxCriticalSectionLocker locker(m_critSect);
6316
6317 if (m_ppicb)
6318
6319 return m_ppicb->GetChartBits(source, pPix, sub_samp);
6320 else
6321 return false;
6322}
6323
6324int ChartPlugInWrapper::GetSize_X() {
6325 if (m_ppicb)
6326 return m_ppicb->GetSize_X();
6327 else
6328 return 1;
6329}
6330
6331int ChartPlugInWrapper::GetSize_Y() {
6332 if (m_ppicb)
6333 return m_ppicb->GetSize_Y();
6334 else
6335 return 1;
6336}
6337
6338void ChartPlugInWrapper::latlong_to_chartpix(double lat, double lon,
6339 double &pixx, double &pixy) {
6340 if (m_ppicb) m_ppicb->latlong_to_chartpix(lat, lon, pixx, pixy);
6341}
6342
6343void ChartPlugInWrapper::chartpix_to_latlong(double pixx, double pixy,
6344 double *plat, double *plon) {
6345 if (m_ppicb) m_ppicb->chartpix_to_latlong(pixx, pixy, plat, plon);
6346}
6347
6348/* API 1.11 */
6349
6350/* API 1.11 adds some more common functions to avoid unnecessary code
6351 * duplication */
6352
6353wxString toSDMM_PlugIn(int NEflag, double a, bool hi_precision) {
6354 return toSDMM(NEflag, a, hi_precision);
6355}
6356
6357wxColour GetBaseGlobalColor(wxString colorName) {
6358 return GetGlobalColor(colorName);
6359}
6360
6361int OCPNMessageBox_PlugIn(wxWindow *parent, const wxString &message,
6362 const wxString &caption, int style, int x, int y) {
6363 return OCPNMessageBox(parent, message, caption, style, 100, x, y);
6364}
6365
6366wxString GetOCPN_ExePath(void) { return g_Platform->GetExePath(); }
6367
6368wxString *GetpPlugInLocation() { return g_Platform->GetPluginDirPtr(); }
6369
6370wxString GetWritableDocumentsDir(void) {
6371 return g_Platform->GetWritableDocumentsDir();
6372}
6373
6374wxString GetPlugInPath(opencpn_plugin *pplugin) {
6375 wxString ret_val;
6376 ArrayOfPlugIns *pi_array = PluginLoader::getInstance()->GetPlugInArray();
6377 for (unsigned int i = 0; i < pi_array->GetCount(); i++) {
6378 PlugInContainer *pic = pi_array->Item(i);
6379 if (pic->m_pplugin == pplugin) {
6380 ret_val = pic->m_plugin_file;
6381 break;
6382 }
6383 }
6384
6385 return ret_val;
6386}
6387
6388// API 1.11 Access to Vector PlugIn charts
6389
6390ListOfPI_S57Obj *PlugInManager::GetPlugInObjRuleListAtLatLon(
6391 ChartPlugInWrapper *target, float zlat, float zlon, float SelectRadius,
6392 const ViewPort &vp) {
6393 ListOfPI_S57Obj *list = NULL;
6394 if (target) {
6395 PlugInChartBaseGL *picbgl =
6396 dynamic_cast<PlugInChartBaseGL *>(target->GetPlugInChart());
6397 if (picbgl) {
6398 PlugIn_ViewPort pi_vp = CreatePlugInViewport(vp);
6399 list = picbgl->GetObjRuleListAtLatLon(zlat, zlon, SelectRadius, &pi_vp);
6400
6401 return list;
6402 }
6404 dynamic_cast<PlugInChartBaseExtended *>(target->GetPlugInChart());
6405 if (picbx) {
6406 PlugIn_ViewPort pi_vp = CreatePlugInViewport(vp);
6407 list = picbx->GetObjRuleListAtLatLon(zlat, zlon, SelectRadius, &pi_vp);
6408
6409 return list;
6410 } else
6411 return list;
6412 } else
6413 return list;
6414}
6415
6416wxString PlugInManager::CreateObjDescriptions(ChartPlugInWrapper *target,
6417 ListOfPI_S57Obj *rule_list) {
6418 wxString ret_str;
6419 if (target) {
6420 PlugInChartBaseGL *picbgl =
6421 dynamic_cast<PlugInChartBaseGL *>(target->GetPlugInChart());
6422 if (picbgl) {
6423 ret_str = picbgl->CreateObjDescriptions(rule_list);
6424 } else {
6426 dynamic_cast<PlugInChartBaseExtended *>(target->GetPlugInChart());
6427 if (picbx) {
6428 ret_str = picbx->CreateObjDescriptions(rule_list);
6429 }
6430 }
6431 }
6432 return ret_str;
6433}
6434
6435// API 1.11 Access to S52 PLIB
6436wxString PI_GetPLIBColorScheme() {
6437 return _T(""); // ps52plib->GetPLIBColorScheme()
6438}
6439
6440int PI_GetPLIBDepthUnitInt() {
6441 if (ps52plib)
6442 return ps52plib->m_nDepthUnitDisplay;
6443 else
6444 return 0;
6445}
6446
6447int PI_GetPLIBSymbolStyle() {
6448 if (ps52plib)
6449 return ps52plib->m_nSymbolStyle;
6450 else
6451 return 0;
6452}
6453
6454int PI_GetPLIBBoundaryStyle() {
6455 if (ps52plib)
6456 return ps52plib->m_nBoundaryStyle;
6457 else
6458 return 0;
6459}
6460
6461bool PI_PLIBObjectRenderCheck(PI_S57Obj *pObj, PlugIn_ViewPort *vp) {
6462 if (ps52plib) {
6463 // Create and populate a compatible s57 Object
6464 S57Obj cobj;
6465 chart_context ctx;
6466 CreateCompatibleS57Object(pObj, &cobj, &ctx);
6467
6468 ViewPort cvp = CreateCompatibleViewport(*vp);
6469
6470 S52PLIB_Context *pContext = (S52PLIB_Context *)pObj->S52_Context;
6471
6472 // Create and populate a minimally compatible object container
6473 ObjRazRules rzRules;
6474 rzRules.obj = &cobj;
6475 rzRules.LUP = pContext->LUP;
6476 rzRules.sm_transform_parms = 0;
6477 rzRules.child = NULL;
6478 rzRules.next = NULL;
6479
6480 if (pContext->LUP){
6481 ps52plib->SetVPointCompat(
6482 cvp.pix_width,
6483 cvp.pix_height,
6484 cvp.view_scale_ppm,
6485 cvp.rotation,
6486 cvp.clat,
6487 cvp.clon,
6488 cvp.chart_scale,
6489 cvp.rv_rect,
6490 cvp.GetBBox(),
6491 cvp.ref_scale,
6492 GetOCPNCanvasWindow()->GetContentScaleFactor()
6493 );
6494 ps52plib->PrepareForRender();
6495
6496 return ps52plib->ObjectRenderCheck(&rzRules);
6497 }
6498 else
6499 return false;
6500 } else
6501 return false;
6502}
6503
6504int PI_GetPLIBStateHash() {
6505 if (ps52plib)
6506 return ps52plib->GetStateHash();
6507 else
6508 return 0;
6509}
6510
6511void CreateCompatibleS57Object(PI_S57Obj *pObj, S57Obj *cobj,
6512 chart_context *pctx) {
6513 strncpy(cobj->FeatureName, pObj->FeatureName, 8);
6514 cobj->Primitive_type = (GeoPrim_t)pObj->Primitive_type;
6515 cobj->att_array = pObj->att_array;
6516 cobj->attVal = pObj->attVal;
6517 cobj->n_attr = pObj->n_attr;
6518
6519 cobj->x = pObj->x;
6520 cobj->y = pObj->y;
6521 cobj->z = pObj->z;
6522 cobj->npt = pObj->npt;
6523
6524 cobj->iOBJL = pObj->iOBJL;
6525 cobj->Index = pObj->Index;
6526
6527 cobj->geoPt = (pt *)pObj->geoPt;
6528 cobj->geoPtz = pObj->geoPtz;
6529 cobj->geoPtMulti = pObj->geoPtMulti;
6530
6531 cobj->m_lat = pObj->m_lat;
6532 cobj->m_lon = pObj->m_lon;
6533
6534 cobj->m_DisplayCat = (DisCat)pObj->m_DisplayCat;
6535 cobj->x_rate = pObj->x_rate;
6536 cobj->y_rate = pObj->y_rate;
6537 cobj->x_origin = pObj->x_origin;
6538 cobj->y_origin = pObj->y_origin;
6539
6540 cobj->Scamin = pObj->Scamin;
6541 cobj->nRef = pObj->nRef;
6542 cobj->bIsAton = pObj->bIsAton;
6543 cobj->bIsAssociable = pObj->bIsAssociable;
6544
6545 cobj->m_n_lsindex = pObj->m_n_lsindex;
6546 cobj->m_lsindex_array = pObj->m_lsindex_array;
6547 cobj->m_n_edge_max_points = pObj->m_n_edge_max_points;
6548
6549 if (gs_plib_flags & PLIB_CAPS_OBJSEGLIST) {
6550 cobj->m_ls_list_legacy =
6552 pObj->m_ls_list; // note the cast, assumes in-sync layout
6553 } else
6554 cobj->m_ls_list_legacy = 0;
6555 cobj->m_ls_list = 0;
6556
6557 if (gs_plib_flags & PLIB_CAPS_OBJCATMUTATE)
6558 cobj->m_bcategory_mutable = pObj->m_bcategory_mutable;
6559 else
6560 cobj->m_bcategory_mutable = true; // assume all objects are mutable
6561
6562 cobj->m_DPRI = -1; // default is unassigned, fixed at render time
6563 if (gs_plib_flags & PLIB_CAPS_OBJCATMUTATE) {
6564 if (pObj->m_DPRI == -1) {
6565 S52PLIB_Context *pCtx = (S52PLIB_Context *)pObj->S52_Context;
6566 if (pCtx->LUP) cobj->m_DPRI = pCtx->LUP->DPRI - '0';
6567 } else
6568 cobj->m_DPRI = pObj->m_DPRI;
6569 }
6570
6571 cobj->pPolyTessGeo = (PolyTessGeo *)pObj->pPolyTessGeo;
6572 cobj->m_chart_context = (chart_context *)pObj->m_chart_context;
6573
6574 if (pObj->auxParm3 != 1234) {
6575 pObj->auxParm3 = 1234;
6576 pObj->auxParm0 = -99;
6577 }
6578
6579 cobj->auxParm0 = pObj->auxParm0;
6580 cobj->auxParm1 = 0;
6581 cobj->auxParm2 = 0;
6582 cobj->auxParm3 = 0;
6583
6584 S52PLIB_Context *pContext = (S52PLIB_Context *)pObj->S52_Context;
6585
6586 if (pContext->bBBObj_valid)
6587 // this is ugly because plugins still use BoundingBox
6588 cobj->BBObj.Set(pContext->BBObj.GetMinY(), pContext->BBObj.GetMinX(),
6589 pContext->BBObj.GetMaxY(), pContext->BBObj.GetMaxX());
6590
6591 cobj->CSrules = pContext->CSrules;
6592 cobj->bCS_Added = pContext->bCS_Added;
6593
6594 cobj->FText = pContext->FText;
6595 cobj->bFText_Added = pContext->bFText_Added;
6596 cobj->rText = pContext->rText;
6597
6598 cobj->bIsClone = true; // Protect cloned object pointers in S57Obj dtor
6599
6600 if (pctx) {
6601 cobj->m_chart_context = pctx;
6602 chart_context *ppctx = (chart_context *)pObj->m_chart_context;
6603
6604 if (ppctx) {
6605 cobj->m_chart_context->m_pvc_hash = ppctx->m_pvc_hash;
6606 cobj->m_chart_context->m_pve_hash = ppctx->m_pve_hash;
6607 cobj->m_chart_context->ref_lat = ppctx->ref_lat;
6608 cobj->m_chart_context->ref_lon = ppctx->ref_lon;
6609 cobj->m_chart_context->pFloatingATONArray = ppctx->pFloatingATONArray;
6610 cobj->m_chart_context->pRigidATONArray = ppctx->pRigidATONArray;
6611 cobj->m_chart_context->safety_contour = ppctx->safety_contour;
6612 cobj->m_chart_context->vertex_buffer = ppctx->vertex_buffer;
6613 }
6614 cobj->m_chart_context->chart =
6615 0; // note bene, this is always NULL for a PlugIn chart
6616 cobj->m_chart_context->chart_type = S52_CHART_TYPE_PLUGIN;
6617
6618 }
6619}
6620
6621bool PI_PLIBSetContext(PI_S57Obj *pObj) {
6622 S52PLIB_Context *ctx;
6623 if (!pObj->S52_Context) {
6624 ctx = new S52PLIB_Context;
6625 pObj->S52_Context = ctx;
6626 }
6627
6628 ctx = (S52PLIB_Context *)pObj->S52_Context;
6629
6630 S57Obj cobj;
6631 CreateCompatibleS57Object(pObj, &cobj, NULL);
6632
6633 LUPname LUP_Name = PAPER_CHART;
6634
6635 // Force a re-evaluation of CS rules
6636 ctx->CSrules = NULL;
6637 ctx->bCS_Added = false;
6638
6639 // Clear the rendered text cache
6640 if (ctx->bFText_Added) {
6641 ctx->bFText_Added = false;
6642 delete ctx->FText;
6643 ctx->FText = NULL;
6644 }
6645
6646 // Reset object selection box
6647 ctx->bBBObj_valid = true;
6648 ctx->BBObj.SetMin(pObj->lon_min, pObj->lat_min);
6649 ctx->BBObj.SetMax(pObj->lon_max, pObj->lat_max);
6650
6651 // This is where Simplified or Paper-Type point features are selected
6652 switch (cobj.Primitive_type) {
6653 case GEO_POINT:
6654 case GEO_META:
6655 case GEO_PRIM:
6656
6657 if (PAPER_CHART == ps52plib->m_nSymbolStyle)
6658 LUP_Name = PAPER_CHART;
6659 else
6660 LUP_Name = SIMPLIFIED;
6661
6662 break;
6663
6664 case GEO_LINE:
6665 LUP_Name = LINES;
6666 break;
6667
6668 case GEO_AREA:
6669 if (PLAIN_BOUNDARIES == ps52plib->m_nBoundaryStyle)
6670 LUP_Name = PLAIN_BOUNDARIES;
6671 else
6672 LUP_Name = SYMBOLIZED_BOUNDARIES;
6673
6674 break;
6675 }
6676
6677 LUPrec *lup = ps52plib->S52_LUPLookup(LUP_Name, cobj.FeatureName, &cobj);
6678 ctx->LUP = lup;
6679
6680 // Convert LUP to rules set
6681 ps52plib->_LUP2rules(lup, &cobj);
6682
6683 ctx->MPSRulesList = NULL;
6684
6685 return true;
6686}
6687
6688void PI_UpdateContext(PI_S57Obj *pObj) {
6689 S52PLIB_Context *pContext = (S52PLIB_Context *)pObj->S52_Context;
6690 if (pContext) {
6691 pContext->bBBObj_valid = true;
6692 pContext->BBObj.SetMin(pObj->lon_min, pObj->lat_min);
6693 pContext->BBObj.SetMax(pObj->lon_max, pObj->lat_max);
6694 }
6695}
6696
6697void UpdatePIObjectPlibContext(PI_S57Obj *pObj, S57Obj *cobj,
6698 ObjRazRules *rzRules) {
6699 // Update the PLIB context after the render operation
6700 S52PLIB_Context *pContext = (S52PLIB_Context *)pObj->S52_Context;
6701
6702 pContext->CSrules = cobj->CSrules;
6703 pContext->bCS_Added = cobj->bCS_Added;
6704
6705 pContext->FText = cobj->FText;
6706 pContext->bFText_Added = cobj->bFText_Added;
6707 pContext->rText = cobj->rText;
6708
6709 if (cobj->BBObj.GetValid()) {
6710 // ugly as plugins still use BoundingBox
6711 pContext->BBObj =
6712 BoundingBox(cobj->BBObj.GetMinLon(), cobj->BBObj.GetMinLat(),
6713 cobj->BBObj.GetMaxLon(), cobj->BBObj.GetMaxLat());
6714 pContext->bBBObj_valid = true;
6715 }
6716
6717 // Render operation may have promoted the object's display category
6718 // (e.g.WRECKS)
6719 pObj->m_DisplayCat = (PI_DisCat)cobj->m_DisplayCat;
6720
6721 if (gs_plib_flags & PLIB_CAPS_OBJCATMUTATE) pObj->m_DPRI = cobj->m_DPRI;
6722
6723 pContext->ChildRazRules = rzRules->child;
6724 pContext->MPSRulesList = rzRules->mps;
6725
6726 pObj->auxParm0 = cobj->auxParm0;
6727}
6728
6729bool PI_GetObjectRenderBox(PI_S57Obj *pObj, double *lat_min, double *lat_max,
6730 double *lon_min, double *lon_max) {
6731 S52PLIB_Context *pContext = (S52PLIB_Context *)pObj->S52_Context;
6732 if (pContext) {
6733 if (lat_min) *lat_min = pContext->BBObj.GetMinY();
6734 if (lat_max) *lat_max = pContext->BBObj.GetMaxY();
6735 if (lon_min) *lon_min = pContext->BBObj.GetMinX();
6736 if (lon_max) *lon_max = pContext->BBObj.GetMaxX();
6737 return pContext->bBBObj_valid;
6738 } else
6739 return false;
6740}
6741
6742PI_LUPname PI_GetObjectLUPName(PI_S57Obj *pObj) {
6743 S52PLIB_Context *pContext = (S52PLIB_Context *)pObj->S52_Context;
6744 if (pContext) {
6745 LUPrec *lup = pContext->LUP;
6746 if (lup) return (PI_LUPname)(lup->TNAM);
6747 }
6748 return (PI_LUPname)(-1);
6749}
6750
6751PI_DisPrio PI_GetObjectDisplayPriority(PI_S57Obj *pObj) {
6752 S52PLIB_Context *pContext = (S52PLIB_Context *)pObj->S52_Context;
6753 if (pContext) {
6754 LUPrec *lup = pContext->LUP;
6755 if (lup) return (PI_DisPrio)(lup->DPRI);
6756 }
6757
6758 return (PI_DisPrio)(-1);
6759}
6760
6761PI_DisCat PI_GetObjectDisplayCategory(PI_S57Obj *pObj) {
6762 S52PLIB_Context *pContext = (S52PLIB_Context *)pObj->S52_Context;
6763 if (pContext) {
6764 LUPrec *lup = pContext->LUP;
6765 if (lup) return (PI_DisCat)(lup->DISC);
6766 }
6767 return (PI_DisCat)(-1);
6768}
6769double PI_GetPLIBMarinerSafetyContour() {
6770 return S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR);
6771}
6772
6773void PI_PLIBSetLineFeaturePriority(PI_S57Obj *pObj, int prio) {
6774 // Create and populate a compatible s57 Object
6775 S57Obj cobj;
6776 chart_context ctx;
6777 CreateCompatibleS57Object(pObj, &cobj, &ctx);
6778
6779 S52PLIB_Context *pContext = (S52PLIB_Context *)pObj->S52_Context;
6780
6781 // Create and populate a minimally compatible object container
6782 ObjRazRules rzRules;
6783 rzRules.obj = &cobj;
6784 rzRules.LUP = pContext->LUP;
6785 rzRules.sm_transform_parms = 0;
6786 rzRules.child = NULL;
6787 rzRules.next = NULL;
6788 rzRules.mps = pContext->MPSRulesList;
6789
6790 if (pContext->LUP) {
6791 ps52plib->SetLineFeaturePriority(&rzRules, prio);
6792
6793 // Update the PLIB context after the render operation
6794 UpdatePIObjectPlibContext(pObj, &cobj, &rzRules);
6795 }
6796}
6797
6798void PI_PLIBPrepareForNewRender(void) {
6799 if (ps52plib) {
6800 ps52plib->PrepareForRender();
6801 ps52plib->ClearTextList();
6802
6803 if (gs_plib_flags & PLIB_CAPS_LINE_BUFFER)
6804 ps52plib->EnableGLLS(true); // Newer PlugIns can use GLLS
6805 else
6806 ps52plib->EnableGLLS(false); // Older cannot
6807 }
6808}
6809
6810void PI_PLIBSetRenderCaps(unsigned int flags) { gs_plib_flags = flags; }
6811
6812void PI_PLIBFreeContext(void *pContext) {
6813 S52PLIB_Context *pctx = (S52PLIB_Context *)pContext;
6814
6815 if (pctx->ChildRazRules) {
6816 ObjRazRules *ctop = pctx->ChildRazRules;
6817 while (ctop) {
6818 delete ctop->obj;
6819
6820 if (ps52plib) ps52plib->DestroyLUP(ctop->LUP);
6821 delete ctop->LUP;
6822
6823 ObjRazRules *cnxx = ctop->next;
6824 delete ctop;
6825 ctop = cnxx;
6826 }
6827 }
6828
6829 if (pctx->MPSRulesList) {
6830 if (ps52plib && pctx->MPSRulesList->cs_rules) {
6831 for (unsigned int i = 0; i < pctx->MPSRulesList->cs_rules->GetCount();
6832 i++) {
6833 Rules *top = pctx->MPSRulesList->cs_rules->Item(i);
6834 ps52plib->DestroyRulesChain(top);
6835 }
6836 delete pctx->MPSRulesList->cs_rules;
6837 }
6838 free(pctx->MPSRulesList);
6839 }
6840
6841 delete pctx->FText;
6842
6843 delete pctx;
6844}
6845
6846int PI_PLIBRenderObjectToDC(wxDC *pdc, PI_S57Obj *pObj, PlugIn_ViewPort *vp) {
6847 // Create and populate a compatible s57 Object
6848 S57Obj cobj;
6849 chart_context ctx;
6850 CreateCompatibleS57Object(pObj, &cobj, &ctx);
6851
6852 S52PLIB_Context *pContext = (S52PLIB_Context *)pObj->S52_Context;
6853
6854 // Set up object SM rendering constants
6855 sm_parms transform;
6856 toSM(vp->clat, vp->clon, pObj->chart_ref_lat, pObj->chart_ref_lon,
6857 &transform.easting_vp_center, &transform.northing_vp_center);
6858
6859 // Create and populate a minimally compatible object container
6860 ObjRazRules rzRules;
6861 rzRules.obj = &cobj;
6862 rzRules.LUP = pContext->LUP;
6863 rzRules.sm_transform_parms = &transform;
6864 rzRules.child = pContext->ChildRazRules;
6865 rzRules.next = NULL;
6866 rzRules.mps = pContext->MPSRulesList;
6867
6868 if (pContext->LUP) {
6869 ViewPort cvp = CreateCompatibleViewport(*vp);
6870
6871 // Do the render
6872 //FIXME (plib)
6873 ps52plib->SetVPointCompat(
6874 cvp.pix_width,
6875 cvp.pix_height,
6876 cvp.view_scale_ppm,
6877 cvp.rotation,
6878 cvp.clat,
6879 cvp.clon,
6880 cvp.chart_scale,
6881 cvp.rv_rect,
6882 cvp.GetBBox(),
6883 cvp.ref_scale,
6884 GetOCPNCanvasWindow()->GetContentScaleFactor()
6885 );
6886 ps52plib->PrepareForRender();
6887
6888 ps52plib->RenderObjectToDC(pdc, &rzRules);
6889
6890 // Update the PLIB context after the render operation
6891 UpdatePIObjectPlibContext(pObj, &cobj, &rzRules);
6892 }
6893
6894 return 1;
6895}
6896
6897int PI_PLIBRenderAreaToDC(wxDC *pdc, PI_S57Obj *pObj, PlugIn_ViewPort *vp,
6898 wxRect rect, unsigned char *pixbuf) {
6899 // Create a compatible render canvas
6900 render_canvas_parms pb_spec;
6901
6902 pb_spec.depth = BPP;
6903 pb_spec.pb_pitch = ((rect.width * pb_spec.depth / 8));
6904 pb_spec.lclip = rect.x;
6905 pb_spec.rclip = rect.x + rect.width - 1;
6906 pb_spec.pix_buff = pixbuf; // the passed buffer
6907 pb_spec.width = rect.width;
6908 pb_spec.height = rect.height;
6909 pb_spec.x = rect.x;
6910 pb_spec.y = rect.y;
6911#ifdef ocpnUSE_ocpnBitmap
6912 pb_spec.b_revrgb = true;
6913#else
6914 pb_spec.b_revrgb = false;
6915#endif
6916
6917 pb_spec.b_revrgb = false;
6918
6919 // Create and populate a compatible s57 Object
6920 S57Obj cobj;
6921 chart_context ctx;
6922 CreateCompatibleS57Object(pObj, &cobj, &ctx);
6923
6924 S52PLIB_Context *pContext = (S52PLIB_Context *)pObj->S52_Context;
6925
6926 // Set up object SM rendering constants
6927 sm_parms transform;
6928 toSM(vp->clat, vp->clon, pObj->chart_ref_lat, pObj->chart_ref_lon,
6929 &transform.easting_vp_center, &transform.northing_vp_center);
6930
6931 // Create and populate a minimally compatible object container
6932 ObjRazRules rzRules;
6933 rzRules.obj = &cobj;
6934 rzRules.LUP = pContext->LUP;
6935 rzRules.sm_transform_parms = &transform;
6936 rzRules.child = pContext->ChildRazRules;
6937 rzRules.next = NULL;
6938 rzRules.mps = pContext->MPSRulesList;
6939
6940 ViewPort cvp = CreateCompatibleViewport(*vp);
6941
6942 // If the PlugIn does not support it nativiely, build a fully described
6943 // Geomoetry
6944 if (!(gs_plib_flags & PLIB_CAPS_SINGLEGEO_BUFFER)) {
6945 if (!pObj->geoPtMulti) { // do this only once
6946 PolyTessGeo *tess = (PolyTessGeo *)pObj->pPolyTessGeo;
6947
6948 if (!tess) return 1; // bail on empty data
6949
6950 PolyTriGroup *ptg = new PolyTriGroup;
6951 ptg->tri_prim_head =
6952 tess->Get_PolyTriGroup_head()->tri_prim_head; // tph;
6953 ptg->bsingle_alloc = false;
6954 ptg->data_type = DATA_TYPE_DOUBLE;
6955 tess->Set_PolyTriGroup_head(ptg);
6956
6957 double *pd = (double *)malloc(sizeof(double));
6958 pObj->geoPtMulti = pd; // Hack hack
6959 }
6960 }
6961
6962 if (pContext->LUP) {
6963 // Do the render
6964 //FIXME (plib)
6965 ps52plib->SetVPointCompat(
6966 cvp.pix_width,
6967 cvp.pix_height,
6968 cvp.view_scale_ppm,
6969 cvp.rotation,
6970 cvp.clat,
6971 cvp.clon,
6972 cvp.chart_scale,
6973 cvp.rv_rect,
6974 cvp.GetBBox(),
6975 cvp.ref_scale,
6976 GetOCPNCanvasWindow()->GetContentScaleFactor()
6977 );
6978 ps52plib->PrepareForRender();
6979
6980 ps52plib->RenderAreaToDC(pdc, &rzRules, &pb_spec);
6981
6982 // Update the PLIB context after the render operation
6983 UpdatePIObjectPlibContext(pObj, &cobj, &rzRules);
6984 }
6985
6986 return 1;
6987}
6988
6989int PI_PLIBRenderAreaToGL(const wxGLContext &glcc, PI_S57Obj *pObj,
6990 PlugIn_ViewPort *vp, wxRect &render_rect) {
6991#ifdef ocpnUSE_GL
6992 // Create and populate a compatible s57 Object
6993 S57Obj cobj;
6994 chart_context ctx;
6995 CreateCompatibleS57Object(pObj, &cobj, &ctx);
6996
6997 // chart_context *pct = (chart_context *)pObj->m_chart_context;
6998
6999 // If the PlugIn does not support it nativiely, build a fully described
7000 // Geomoetry
7001
7002 if (!(gs_plib_flags & PLIB_CAPS_SINGLEGEO_BUFFER)) {
7003 if (!pObj->geoPtMulti) { // only do this once
7004 PolyTessGeo *tess = (PolyTessGeo *)pObj->pPolyTessGeo;
7005
7006 if (!tess) return 1; // bail on empty data
7007
7008 PolyTriGroup *ptg =
7009 new PolyTriGroup; // this will leak a little, but is POD
7010 ptg->tri_prim_head = tess->Get_PolyTriGroup_head()->tri_prim_head;
7011 ptg->bsingle_alloc = false;
7012 ptg->data_type = DATA_TYPE_DOUBLE;
7013 tess->Set_PolyTriGroup_head(ptg);
7014
7015 // Mark this object using geoPtMulti
7016 // The malloc will get free'ed when the object is deleted.
7017 double *pd = (double *)malloc(sizeof(double));
7018 pObj->geoPtMulti = pd; // Hack hack
7019 }
7020 cobj.auxParm0 = -6; // signal that this object render cannot use VBO
7021 cobj.auxParm1 = -1; // signal that this object render cannot have single
7022 // buffer conversion done
7023 } else { // it is a newer PLugIn, so can do single buffer conversion and VBOs
7024 if (pObj->auxParm0 < 1)
7025 cobj.auxParm0 = -7; // signal that this object render can use a
7026 // persistent VBO for area triangle vertices
7027 }
7028
7029 S52PLIB_Context *pContext = (S52PLIB_Context *)pObj->S52_Context;
7030
7031 // Set up object SM rendering constants
7032 sm_parms transform;
7033 toSM(vp->clat, vp->clon, pObj->chart_ref_lat, pObj->chart_ref_lon,
7034 &transform.easting_vp_center, &transform.northing_vp_center);
7035
7036 // Create and populate a minimally compatible object container
7037 ObjRazRules rzRules;
7038 rzRules.obj = &cobj;
7039 rzRules.LUP = pContext->LUP;
7040 rzRules.sm_transform_parms = &transform;
7041 rzRules.child = pContext->ChildRazRules;
7042 rzRules.next = NULL;
7043 rzRules.mps = pContext->MPSRulesList;
7044
7045 if (pContext->LUP) {
7046 ViewPort cvp = CreateCompatibleViewport(*vp);
7047
7048 // Do the render
7049 //FIXME (plib)
7050 ps52plib->SetVPointCompat(
7051 cvp.pix_width,
7052 cvp.pix_height,
7053 cvp.view_scale_ppm,
7054 cvp.rotation,
7055 cvp.clat,
7056 cvp.clon,
7057 cvp.chart_scale,
7058 cvp.rv_rect,
7059 cvp.GetBBox(),
7060 cvp.ref_scale,
7061 GetOCPNCanvasWindow()->GetContentScaleFactor()
7062 );
7063 ps52plib->PrepareForRender();
7064
7065 ps52plib->RenderAreaToGL(glcc, &rzRules);
7066
7067 // Update the PLIB context after the render operation
7068 UpdatePIObjectPlibContext(pObj, &cobj, &rzRules);
7069 }
7070
7071#endif
7072 return 1;
7073}
7074
7075int PI_PLIBRenderObjectToGL(const wxGLContext &glcc, PI_S57Obj *pObj,
7076 PlugIn_ViewPort *vp, wxRect &render_rect) {
7077 // Create and populate a compatible s57 Object
7078 S57Obj cobj;
7079 chart_context ctx;
7080 CreateCompatibleS57Object(pObj, &cobj, &ctx);
7081
7082 S52PLIB_Context *pContext = (S52PLIB_Context *)pObj->S52_Context;
7083
7084 // Set up object SM rendering constants
7085 sm_parms transform;
7086 toSM(vp->clat, vp->clon, pObj->chart_ref_lat, pObj->chart_ref_lon,
7087 &transform.easting_vp_center, &transform.northing_vp_center);
7088
7089 // Create and populate a minimally compatible object container
7090 ObjRazRules rzRules;
7091 rzRules.obj = &cobj;
7092 rzRules.LUP = pContext->LUP;
7093 rzRules.sm_transform_parms = &transform;
7094 rzRules.child = pContext->ChildRazRules;
7095 rzRules.next = NULL;
7096 rzRules.mps = pContext->MPSRulesList;
7097
7098 if (pContext->LUP) {
7099 ViewPort cvp = CreateCompatibleViewport(*vp);
7100
7101 // Do the render
7102 //FIXME (plib)
7103 ps52plib->SetVPointCompat(
7104 cvp.pix_width,
7105 cvp.pix_height,
7106 cvp.view_scale_ppm,
7107 cvp.rotation,
7108 cvp.clat,
7109 cvp.clon,
7110 cvp.chart_scale,
7111 cvp.rv_rect,
7112 cvp.GetBBox(),
7113 cvp.ref_scale,
7114 GetOCPNCanvasWindow()->GetContentScaleFactor()
7115 );
7116 ps52plib->PrepareForRender();
7117
7118 ps52plib->RenderObjectToGL(glcc, &rzRules);
7119
7120 // Update the PLIB context after the render operation
7121 UpdatePIObjectPlibContext(pObj, &cobj, &rzRules);
7122 }
7123
7124 return 1;
7125}
7126
7127/* API 1.13 */
7128
7129/* API 1.13 adds some more common functions to avoid unnecessary code
7130 * duplication */
7131
7132double fromDMM_Plugin(wxString sdms) { return fromDMM(sdms); }
7133
7134void SetCanvasRotation(double rotation) {
7135 gFrame->GetPrimaryCanvas()->DoRotateCanvas(rotation);
7136}
7137
7138double GetCanvasTilt() { return gFrame->GetPrimaryCanvas()->GetVPTilt(); }
7139
7140void SetCanvasTilt(double tilt) {
7141 gFrame->GetPrimaryCanvas()->DoTiltCanvas(tilt);
7142}
7143
7144void SetCanvasProjection(int projection) {
7145 gFrame->GetPrimaryCanvas()->SetVPProjection(projection);
7146}
7147
7148OcpnSound *g_PluginSound = SoundFactory();
7149static void onPlugInPlaySoundExFinished(void *ptr) {}
7150
7151// Start playing a sound to a given device and return status to plugin
7152bool PlugInPlaySoundEx(wxString &sound_file, int deviceIndex) {
7153 bool ok = g_PluginSound->Load(sound_file, deviceIndex);
7154 if (!ok) {
7155 wxLogWarning("Cannot load sound file: %s", sound_file);
7156 return false;
7157 }
7158 auto cmd_sound = dynamic_cast<SystemCmdSound *>(g_PluginSound);
7159 if (cmd_sound) cmd_sound->SetCmd(g_CmdSoundString.mb_str(wxConvUTF8));
7160
7161 g_PluginSound->SetFinishedCallback(onPlugInPlaySoundExFinished, NULL);
7162 ok = g_PluginSound->Play();
7163 if (!ok) {
7164 wxLogWarning("Cannot play sound file: %s", sound_file);
7165 }
7166 return ok;
7167}
7168
7169bool CheckEdgePan_PlugIn(int x, int y, bool dragging, int margin, int delta) {
7170 return gFrame->GetPrimaryCanvas()->CheckEdgePan(x, y, dragging, margin,
7171 delta);
7172}
7173
7174wxBitmap GetIcon_PlugIn(const wxString &name) {
7175 ocpnStyle::Style *style = g_StyleManager->GetCurrentStyle();
7176 return style->GetIcon(name);
7177}
7178
7179void SetCursor_PlugIn(wxCursor *pCursor) {
7180 gFrame->GetPrimaryCanvas()->pPlugIn_Cursor = pCursor;
7181}
7182
7183void AddChartDirectory(wxString &path) {
7184 if (g_options) {
7185 g_options->AddChartDir(path);
7186 }
7187}
7188
7189void ForceChartDBUpdate() {
7190 if (g_options) {
7191 g_options->pScanCheckBox->SetValue(true);
7192 g_options->pUpdateCheckBox->SetValue(true);
7193 }
7194}
7195
7196void ForceChartDBRebuild() {
7197 if (g_options) {
7198 g_options->pUpdateCheckBox->SetValue(true);
7199 }
7200}
7201
7202wxDialog *GetActiveOptionsDialog() { return g_options; }
7203
7204int PlatformDirSelectorDialog(wxWindow *parent, wxString *file_spec,
7205 wxString Title, wxString initDir) {
7206 return g_Platform->DoDirSelectorDialog(parent, file_spec, Title, initDir);
7207}
7208
7209int PlatformFileSelectorDialog(wxWindow *parent, wxString *file_spec,
7210 wxString Title, wxString initDir,
7211 wxString suggestedName, wxString wildcard) {
7212 return g_Platform->DoFileSelectorDialog(parent, file_spec, Title, initDir,
7213 suggestedName, wildcard);
7214}
7215
7216// http File Download Support
7217
7218// OCPN_downloadEvent Implementation
7219
7220OCPN_downloadEvent::OCPN_downloadEvent(wxEventType commandType, int id)
7221 : wxEvent(id, commandType) {
7222 m_stat = OCPN_DL_UNKNOWN;
7223 m_condition = OCPN_DL_EVENT_TYPE_UNKNOWN;
7224 m_b_complete = false;
7225 m_sofarBytes = 0;
7226}
7227
7228OCPN_downloadEvent::~OCPN_downloadEvent() {}
7229
7230wxEvent *OCPN_downloadEvent::Clone() const {
7231 OCPN_downloadEvent *newevent = new OCPN_downloadEvent(*this);
7232 newevent->m_stat = this->m_stat;
7233 newevent->m_condition = this->m_condition;
7234
7235 newevent->m_totalBytes = this->m_totalBytes;
7236 newevent->m_sofarBytes = this->m_sofarBytes;
7237 newevent->m_b_complete = this->m_b_complete;
7238
7239 return newevent;
7240}
7241
7242// const wxEventType wxEVT_DOWNLOAD_EVENT = wxNewEventType();
7243DECL_EXP wxEventType wxEVT_DOWNLOAD_EVENT = wxNewEventType();
7244
7245_OCPN_DLStatus g_download_status;
7246_OCPN_DLCondition g_download_condition;
7247
7248#define DL_EVENT_TIMER 4388
7249
7250class PI_DLEvtHandler : public wxEvtHandler {
7251public:
7254
7255 void onDLEvent(OCPN_downloadEvent &event);
7256 void setBackgroundMode(long ID, wxEvtHandler *handler);
7257 void clearBackgroundMode();
7258 void onTimerEvent(wxTimerEvent &event);
7259
7260 long m_id;
7261 wxTimer m_eventTimer;
7262 wxEvtHandler *m_download_evHandler;
7263
7264 long m_sofarBytes;
7265 long m_totalBytes;
7266};
7267
7268PI_DLEvtHandler::PI_DLEvtHandler() {
7269 g_download_status = OCPN_DL_UNKNOWN;
7270 g_download_condition = OCPN_DL_EVENT_TYPE_UNKNOWN;
7271
7272 m_download_evHandler = NULL;
7273 m_id = -1;
7274 m_sofarBytes = 0;
7275 m_totalBytes = 0;
7276}
7277
7278PI_DLEvtHandler::~PI_DLEvtHandler() {
7279 m_eventTimer.Stop();
7280 Disconnect(
7281 wxEVT_TIMER,
7282 (wxObjectEventFunction)(wxEventFunction)&PI_DLEvtHandler::onTimerEvent);
7283}
7284
7285void PI_DLEvtHandler::onDLEvent(OCPN_downloadEvent &event) {
7286 // qDebug() << "Got Event " << (int)event.getDLEventStatus() <<
7287 // (int)event.getDLEventCondition();
7288
7289 g_download_status = event.getDLEventStatus();
7290 g_download_condition = event.getDLEventCondition();
7291
7292 // This is an END event, happening at the end of BACKGROUND file download
7293 if (m_download_evHandler &&
7294 (OCPN_DL_EVENT_TYPE_END == event.getDLEventCondition())) {
7295 OCPN_downloadEvent ev(wxEVT_DOWNLOAD_EVENT, 0);
7296 ev.setComplete(true);
7297 ev.setTransferred(m_sofarBytes);
7298 ev.setTotal(m_totalBytes);
7299
7300 ev.setDLEventStatus(event.getDLEventStatus());
7301 ev.setDLEventCondition(OCPN_DL_EVENT_TYPE_END);
7302
7303 m_download_evHandler->AddPendingEvent(ev);
7304 m_eventTimer.Stop();
7305#ifdef __OCPN__ANDROID__
7306 finishAndroidFileDownload();
7307#endif
7308 }
7309
7310 event.Skip();
7311}
7312
7313void PI_DLEvtHandler::setBackgroundMode(long ID, wxEvtHandler *handler) {
7314 m_id = ID;
7315 m_download_evHandler = handler;
7316
7317 m_eventTimer.SetOwner(this, DL_EVENT_TIMER);
7318
7319 Connect(
7320 wxEVT_TIMER,
7321 (wxObjectEventFunction)(wxEventFunction)&PI_DLEvtHandler::onTimerEvent);
7322 m_eventTimer.Start(1000, wxTIMER_CONTINUOUS);
7323}
7324
7325void PI_DLEvtHandler::clearBackgroundMode() {
7326 m_download_evHandler = NULL;
7327 m_eventTimer.Stop();
7328}
7329
7330void PI_DLEvtHandler::onTimerEvent(wxTimerEvent &event) {
7331#ifdef __OCPN__ANDROID__
7332 // Query the download status, and post to the original requestor
7333 // This method only happens on Background file downloads
7334
7335 wxString sstat;
7336 int stat = queryAndroidFileDownload(m_id, &sstat);
7337
7338 OCPN_downloadEvent ev(wxEVT_DOWNLOAD_EVENT, 0);
7339 long sofarBytes = 0;
7340 long totalBytes = -1;
7341 long state = -1;
7342
7343 if (stat) { // some error
7344 qDebug() << "Error on queryAndroidFileDownload, ending download";
7345 ev.setComplete(true);
7346 ev.setTransferred(sofarBytes);
7347 ev.setTotal(totalBytes);
7348
7349 ev.setDLEventStatus(OCPN_DL_FAILED);
7350 ev.setDLEventCondition(OCPN_DL_EVENT_TYPE_END);
7351 } else {
7352 wxStringTokenizer tk(sstat, _T(";"));
7353 if (tk.HasMoreTokens()) {
7354 wxString token = tk.GetNextToken();
7355 token.ToLong(&state);
7356 token = tk.GetNextToken();
7357 token.ToLong(&sofarBytes);
7358 token = tk.GetNextToken();
7359 token.ToLong(&totalBytes);
7360 }
7361
7362 qDebug() << state << sofarBytes << totalBytes;
7363
7364 m_sofarBytes = sofarBytes;
7365 m_totalBytes = totalBytes;
7366
7367 ev.setTransferred(sofarBytes);
7368 ev.setTotal(totalBytes);
7369
7370 if (state == 16) { // error
7371 qDebug() << "Event OCPN_DL_FAILED/OCPN_DL_EVENT_TYPE_END";
7372 ev.setComplete(true);
7373 ev.setDLEventStatus(OCPN_DL_FAILED);
7374 ev.setDLEventCondition(OCPN_DL_EVENT_TYPE_END);
7375 } else if (state == 8) { // Completed OK
7376 qDebug() << "Event OCPN_DL_NO_ERROR/OCPN_DL_EVENT_TYPE_END";
7377 ev.setComplete(true);
7378 ev.setDLEventStatus(OCPN_DL_NO_ERROR);
7379 ev.setDLEventCondition(OCPN_DL_EVENT_TYPE_END);
7380 } else {
7381 ev.setComplete(false);
7382 ev.setDLEventStatus(OCPN_DL_UNKNOWN);
7383 ev.setDLEventCondition(OCPN_DL_EVENT_TYPE_PROGRESS);
7384 }
7385
7386 // 2;0;148686
7387 }
7388
7389 if (m_download_evHandler) {
7390 // qDebug() << "Sending event on timer...";
7391 m_download_evHandler->AddPendingEvent(ev);
7392 }
7393
7394 // Background download is all done.
7395 if (OCPN_DL_EVENT_TYPE_END == ev.getDLEventCondition()) {
7396 m_eventTimer.Stop();
7397 finishAndroidFileDownload();
7398 }
7399
7400#endif
7401}
7402
7403PI_DLEvtHandler *g_piEventHandler;
7404
7405// Blocking download of single file
7406_OCPN_DLStatus OCPN_downloadFile(const wxString &url,
7407 const wxString &outputFile,
7408 const wxString &title, const wxString &message,
7409 const wxBitmap &bitmap, wxWindow *parent,
7410 long style, int timeout_secs) {
7411#ifdef __OCPN__ANDROID__
7412
7413 wxString msg = _T("Downloading file synchronously: ");
7414 msg += url;
7415 msg += _T(" to: ");
7416 msg += outputFile;
7417 wxLogMessage(msg);
7418
7419 // Validate the write location
7420 int vres = validateAndroidWriteLocation(outputFile);
7421 if (vres == 0) // Pending permission dialog
7422 return OCPN_DL_ABORTED;
7423
7424 // Create a single event handler to receive status events
7425 if (!g_piEventHandler) g_piEventHandler = new PI_DLEvtHandler;
7426
7427 // Reset global status indicators
7428 g_download_status = OCPN_DL_UNKNOWN;
7429 g_download_condition = OCPN_DL_EVENT_TYPE_UNKNOWN;
7430
7431 // Create a connection for the expected events from Android Activity
7432 g_piEventHandler->Connect(
7433 wxEVT_DOWNLOAD_EVENT,
7434 (wxObjectEventFunction)(wxEventFunction)&PI_DLEvtHandler::onDLEvent);
7435
7436 long dl_ID = -1;
7437
7438 // Make sure the outputfile is a file URI
7439 wxString fURI = outputFile;
7440 if (!fURI.StartsWith(_T("file://"))) {
7441 fURI.Prepend(_T("file://"));
7442 }
7443
7444 int res = startAndroidFileDownload(url, fURI, g_piEventHandler, &dl_ID);
7445 // Started OK?
7446 if (res) {
7447 finishAndroidFileDownload();
7448 g_piEventHandler->Disconnect(
7449 wxEVT_DOWNLOAD_EVENT,
7450 (wxObjectEventFunction)(wxEventFunction)&PI_DLEvtHandler::onDLEvent);
7451 // delete g_piEventHandler;
7452 return OCPN_DL_FAILED;
7453 }
7454
7455 wxDateTime dl_start_time = wxDateTime::Now();
7456
7457 // Spin, waiting for timeout or event from downstream, and checking status
7458 while (1) {
7459 wxTimeSpan dt = wxDateTime::Now() - dl_start_time;
7460 qDebug() << "Spin.." << dt.GetSeconds().GetLo();
7461
7462 if (dt.GetSeconds() > timeout_secs) {
7463 qDebug() << "USER_TIMOUT";
7464 finishAndroidFileDownload();
7465 g_piEventHandler->Disconnect(
7466 wxEVT_DOWNLOAD_EVENT,
7467 (wxObjectEventFunction)(wxEventFunction)&PI_DLEvtHandler::onDLEvent);
7468 // delete g_piEventHandler;
7469 return (OCPN_DL_USER_TIMEOUT);
7470 }
7471
7472 if (g_download_condition != OCPN_DL_EVENT_TYPE_UNKNOWN) {
7473 if (OCPN_DL_EVENT_TYPE_END == g_download_condition) {
7474 _OCPN_DLStatus ss = g_download_status;
7475 finishAndroidFileDownload();
7476 g_piEventHandler->Disconnect(
7477 wxEVT_DOWNLOAD_EVENT,
7478 (wxObjectEventFunction)(wxEventFunction)&PI_DLEvtHandler::
7479 onDLEvent);
7480 // delete g_piEventHandler;
7481 qDebug() << "RETURN DL_END" << (int)ss;
7482 return ss; // The actual return code
7483 }
7484 }
7485
7486 wxString sstat;
7487 int stat = queryAndroidFileDownload(dl_ID, &sstat);
7488 if (stat) { // some error
7489 qDebug() << "Error on queryAndroidFileDownload";
7490 finishAndroidFileDownload();
7491 g_piEventHandler->Disconnect(
7492 wxEVT_DOWNLOAD_EVENT,
7493 (wxObjectEventFunction)(wxEventFunction)&PI_DLEvtHandler::onDLEvent);
7494 // delete g_piEventHandler;
7495
7496 return OCPN_DL_FAILED; // so abort
7497 }
7498
7499 wxSleep(1);
7500 wxSafeYield();
7501 }
7502
7503#elif defined(OCPN_USE_CURL)
7504 wxFileName tfn = wxFileName::CreateTempFileName(outputFile);
7505 wxFileOutputStream output(tfn.GetFullPath());
7506
7507 wxCurlDownloadDialog ddlg(url, &output, title, message + url, bitmap, parent,
7508 style);
7509 wxCurlDialogReturnFlag ret = ddlg.RunModal();
7510 output.Close();
7511
7512 _OCPN_DLStatus result = OCPN_DL_UNKNOWN;
7513
7514 switch (ret) {
7515 case wxCDRF_SUCCESS: {
7516 if (wxCopyFile(tfn.GetFullPath(), outputFile))
7517 result = OCPN_DL_NO_ERROR;
7518 else
7519 result = OCPN_DL_FAILED;
7520 break;
7521 }
7522 case wxCDRF_FAILED: {
7523 result = OCPN_DL_FAILED;
7524 break;
7525 }
7526 case wxCDRF_USER_ABORTED: {
7527 result = OCPN_DL_ABORTED;
7528 break;
7529 }
7530 default:
7531 wxASSERT(false); // This should never happen because we handle all
7532 // possible cases of ret
7533 }
7534 if (wxFileExists(tfn.GetFullPath())) wxRemoveFile(tfn.GetFullPath());
7535 return result;
7536
7537#else
7538 return OCPN_DL_FAILED;
7539#endif
7540}
7541
7542// Non-Blocking download of single file
7543_OCPN_DLStatus OCPN_downloadFileBackground(const wxString &url,
7544 const wxString &outputFile,
7545 wxEvtHandler *handler,
7546 long *handle) {
7547#ifdef __OCPN__ANDROID__
7548 wxString msg = _T("Downloading file asynchronously: ");
7549 msg += url;
7550 msg += _T(" to: ");
7551 msg += outputFile;
7552 wxLogMessage(msg);
7553
7554 // Create a single event handler to receive status events
7555
7556 if (!g_piEventHandler) g_piEventHandler = new PI_DLEvtHandler;
7557
7558 long dl_ID = -1;
7559
7560 int res = startAndroidFileDownload(url, outputFile, NULL /*g_piEventHandler*/,
7561 &dl_ID);
7562 // Started OK?
7563 if (res) {
7564 finishAndroidFileDownload();
7565 return OCPN_DL_FAILED;
7566 }
7567
7568 // configure the local event handler for background transfer
7569 g_piEventHandler->setBackgroundMode(dl_ID, handler);
7570
7571 if (handle) *handle = dl_ID;
7572
7573 return OCPN_DL_STARTED;
7574
7575#elif defined(OCPN_USE_CURL)
7576 if (g_pi_manager->m_pCurlThread) // We allow just one download at a time. Do
7577 // we want more? Or at least return some
7578 // other status in this case?
7579 return OCPN_DL_FAILED;
7580 g_pi_manager->m_pCurlThread =
7581 new wxCurlDownloadThread(g_pi_manager, CurlThreadId);
7582 bool http = (url.StartsWith(wxS("http:")) || url.StartsWith(wxS("https:")));
7583 bool keep = false;
7584 if (http && g_pi_manager->m_pCurl &&
7585 dynamic_cast<wxCurlHTTP *>(g_pi_manager->m_pCurl.get())) {
7586 keep = true;
7587 }
7588 if (!keep) {
7589 g_pi_manager->m_pCurl = 0;
7590 }
7591
7592 bool failed = false;
7593 if (!g_pi_manager->HandleCurlThreadError(
7594 g_pi_manager->m_pCurlThread->SetURL(url, g_pi_manager->m_pCurl),
7595 g_pi_manager->m_pCurlThread, url))
7596 failed = true;
7597 if (!failed) {
7598 g_pi_manager->m_pCurl = g_pi_manager->m_pCurlThread->GetCurlSharedPtr();
7599 if (!g_pi_manager->HandleCurlThreadError(
7600 g_pi_manager->m_pCurlThread->SetOutputStream(
7601 new wxFileOutputStream(outputFile)),
7602 g_pi_manager->m_pCurlThread))
7603 failed = true;
7604 }
7605 if (!failed) {
7606 g_pi_manager->m_download_evHandler = handler;
7607 g_pi_manager->m_downloadHandle = handle;
7608
7609 wxCurlThreadError err = g_pi_manager->m_pCurlThread->Download();
7610 if (err != wxCTE_NO_ERROR) {
7611 g_pi_manager->HandleCurlThreadError(
7612 err, g_pi_manager->m_pCurlThread); // shows a message to the user
7613 g_pi_manager->m_pCurlThread->Abort();
7614 failed = true;
7615 }
7616 }
7617
7618 if (!failed) return OCPN_DL_STARTED;
7619
7620 if (g_pi_manager->m_pCurlThread) {
7621 if (g_pi_manager->m_pCurlThread->IsAlive())
7622 g_pi_manager->m_pCurlThread->Abort();
7623 if (g_pi_manager->m_pCurlThread->GetOutputStream())
7624 delete (g_pi_manager->m_pCurlThread->GetOutputStream());
7625 wxDELETE(g_pi_manager->m_pCurlThread);
7626 g_pi_manager->m_download_evHandler = NULL;
7627 g_pi_manager->m_downloadHandle = NULL;
7628 return OCPN_DL_STARTED;
7629 }
7630 g_pi_manager->m_pCurl = 0;
7631 return OCPN_DL_FAILED;
7632
7633#else
7634 return OCPN_DL_FAILED;
7635#endif
7636}
7637
7638void OCPN_cancelDownloadFileBackground(long handle) {
7639#ifdef OCPN_USE_CURL
7640
7641#ifdef __OCPN__ANDROID__
7642 cancelAndroidFileDownload(handle);
7643 finishAndroidFileDownload();
7644 if (g_piEventHandler) g_piEventHandler->clearBackgroundMode();
7645#else
7646 if (g_pi_manager->m_pCurlThread) {
7647 g_pi_manager->m_pCurlThread->Abort();
7648 delete (g_pi_manager->m_pCurlThread->GetOutputStream());
7649 wxDELETE(g_pi_manager->m_pCurlThread);
7650 g_pi_manager->m_download_evHandler = NULL;
7651 g_pi_manager->m_downloadHandle = NULL;
7652 }
7653 g_pi_manager->m_pCurl = 0;
7654#endif
7655#endif
7656}
7657
7658_OCPN_DLStatus OCPN_postDataHttp(const wxString &url,
7659 const wxString &parameters, wxString &result,
7660 int timeout_secs) {
7661
7662#ifdef __OCPN__ANDROID__
7663 wxString lparms = parameters;
7664 wxString postResult = doAndroidPOST(url, lparms, timeout_secs * 1000);
7665 if (postResult.IsSameAs(_T("NOK"))) return OCPN_DL_FAILED;
7666
7667 result = postResult;
7668 return OCPN_DL_NO_ERROR;
7669
7670#elif defined(OCPN_USE_CURL)
7671 wxCurlHTTP post;
7672 post.SetOpt(CURLOPT_TIMEOUT, timeout_secs);
7673 size_t res = post.Post(parameters.ToAscii(), parameters.Len(), url);
7674
7675 if (res) {
7676 result = wxString(post.GetResponseBody().c_str(), wxConvUTF8);
7677 return OCPN_DL_NO_ERROR;
7678 } else
7679 result = wxEmptyString;
7680
7681 return OCPN_DL_FAILED;
7682
7683#else
7684 return OCPN_DL_FAILED;
7685#endif
7686}
7687
7688bool OCPN_isOnline() {
7689#ifdef __OCPN__ANDROID__
7690 return androidCheckOnline();
7691#endif
7692
7693#if !defined(__OCPN__ANDROID__) && defined(OCPN_USE_CURL)
7694 if (wxDateTime::GetTimeNow() >
7695 g_pi_manager->m_last_online_chk + ONLINE_CHECK_RETRY) {
7696 wxCurlHTTP get;
7697 get.Head(_T("http://yahoo.com/"));
7698 g_pi_manager->m_last_online = get.GetResponseCode() > 0;
7699
7700 g_pi_manager->m_last_online_chk = wxDateTime::GetTimeNow();
7701 }
7702 return g_pi_manager->m_last_online;
7703#else
7704 return false;
7705#endif
7706}
7707
7708#if !defined(__OCPN__ANDROID__) && defined(OCPN_USE_CURL)
7709void PlugInManager::OnEndPerformCurlDownload(wxCurlEndPerformEvent &ev) {
7710 OCPN_downloadEvent event(wxEVT_DOWNLOAD_EVENT, 0);
7711 if (ev.IsSuccessful()) {
7712 event.setDLEventStatus(OCPN_DL_NO_ERROR);
7713 } else {
7714 g_pi_manager->m_pCurl = 0;
7715 event.setDLEventStatus(OCPN_DL_FAILED);
7716 }
7717 event.setDLEventCondition(OCPN_DL_EVENT_TYPE_END);
7718 event.setComplete(true);
7719
7720 if (m_download_evHandler) {
7721 m_download_evHandler->AddPendingEvent(event);
7722 m_download_evHandler = NULL;
7723 m_downloadHandle = NULL;
7724 }
7725
7726 if (m_pCurlThread) {
7727 m_pCurlThread->Wait();
7728 if (!m_pCurlThread->IsAborting()) {
7729 delete (m_pCurlThread->GetOutputStream());
7730 wxDELETE(m_pCurlThread);
7731 }
7732 }
7733}
7734
7735void PlugInManager::OnCurlDownload(wxCurlDownloadEvent &ev) {
7736 OCPN_downloadEvent event(wxEVT_DOWNLOAD_EVENT, 0);
7737 event.setDLEventStatus(OCPN_DL_UNKNOWN);
7738 event.setDLEventCondition(OCPN_DL_EVENT_TYPE_PROGRESS);
7739 event.setTotal(ev.GetTotalBytes());
7740 event.setTransferred(ev.GetDownloadedBytes());
7741 event.setComplete(false);
7742
7743 if (m_download_evHandler) {
7744 m_download_evHandler->AddPendingEvent(event);
7745 }
7746}
7747
7748bool PlugInManager::HandleCurlThreadError(wxCurlThreadError err,
7749 wxCurlBaseThread *p,
7750 const wxString &url) {
7751 switch (err) {
7752 case wxCTE_NO_ERROR:
7753 return true; // ignore this
7754
7755 case wxCTE_NO_RESOURCE:
7756 wxLogError(
7757 wxS("Insufficient resources for correct execution of the program."));
7758 break;
7759
7760 case wxCTE_ALREADY_RUNNING:
7761 wxFAIL; // should never happen!
7762 break;
7763
7764 case wxCTE_INVALID_PROTOCOL:
7765 wxLogError(wxS("The URL '%s' uses an unsupported protocol."),
7766 url.c_str());
7767 break;
7768
7769 case wxCTE_NO_VALID_STREAM:
7770 wxFAIL; // should never happen - the user streams should always be valid!
7771 break;
7772
7773 case wxCTE_ABORTED:
7774 return true; // ignore this
7775
7776 case wxCTE_CURL_ERROR: {
7777 wxString err = wxS("unknown");
7778 if (p->GetCurlSession())
7779 err =
7780 wxString(p->GetCurlSession()->GetErrorString().c_str(), wxConvUTF8);
7781 wxLogError(wxS("Network error: %s"), err.c_str());
7782 } break;
7783 }
7784
7785 // stop the thread
7786 if (p->IsAlive()) p->Abort();
7787
7788 // this is an unrecoverable error:
7789 return false;
7790}
7791#endif
7792
7793bool LaunchDefaultBrowser_Plugin(wxString url) {
7794 if (g_Platform) g_Platform->platformLaunchDefaultBrowser(url);
7795
7796 return true;
7797}
7798
7799/* API 1.14 */
7800
7801void PlugInAISDrawGL(wxGLCanvas *glcanvas, const PlugIn_ViewPort &vp) {
7802 ViewPort ocpn_vp = CreateCompatibleViewport(vp);
7803
7804 ocpnDC dc(*glcanvas);
7805
7806 AISDraw(dc, ocpn_vp, NULL);
7807}
7808
7809bool PlugInSetFontColor(const wxString TextElement, const wxColour color) {
7810 return FontMgr::Get().SetFontColor(TextElement, color);
7811}
7812
7813/* API 1.15 */
7814
7815double PlugInGetDisplaySizeMM() { return g_Platform->GetDisplaySizeMM(); }
7816
7817wxFont *FindOrCreateFont_PlugIn(int point_size, wxFontFamily family,
7818 wxFontStyle style, wxFontWeight weight,
7819 bool underline, const wxString &facename,
7820 wxFontEncoding encoding) {
7821 return FontMgr::Get().FindOrCreateFont(point_size, family, style, weight,
7822 underline, facename, encoding);
7823}
7824
7825int PluginGetMinAvailableGshhgQuality() {
7826 return gFrame->GetPrimaryCanvas()->GetMinAvailableGshhgQuality();
7827}
7828int PluginGetMaxAvailableGshhgQuality() {
7829 return gFrame->GetPrimaryCanvas()->GetMaxAvailableGshhgQuality();
7830}
7831
7832// disable builtin console canvas, and autopilot nmea sentences
7833void PlugInHandleAutopilotRoute(bool enable) {
7834 g_bPluginHandleAutopilotRoute = enable;
7835}
7836
7837/* API 1.16 */
7838wxString GetSelectedWaypointGUID_Plugin() {
7839 ChartCanvas *cc = gFrame->GetFocusCanvas();
7840 if (cc && cc->GetSelectedRoutePoint()) {
7841 return cc->GetSelectedRoutePoint()->m_GUID;
7842 }
7843 return wxEmptyString;
7844}
7845
7846wxString GetSelectedRouteGUID_Plugin() {
7847 ChartCanvas *cc = gFrame->GetFocusCanvas();
7848 if (cc && cc->GetSelectedRoute()) {
7849 return cc->GetSelectedRoute()->m_GUID;
7850 }
7851 return wxEmptyString;
7852}
7853
7854wxString GetSelectedTrackGUID_Plugin() {
7855 ChartCanvas *cc = gFrame->GetFocusCanvas();
7856 if (cc && cc->GetSelectedTrack()) {
7857 return cc->GetSelectedTrack()->m_GUID;
7858 }
7859 return wxEmptyString;
7860}
7861
7862std::unique_ptr<PlugIn_Waypoint> GetWaypoint_Plugin(const wxString &GUID) {
7863 std::unique_ptr<PlugIn_Waypoint> w(new PlugIn_Waypoint);
7864 GetSingleWaypoint(GUID, w.get());
7865 return w;
7866}
7867
7868std::unique_ptr<PlugIn_Route> GetRoute_Plugin(const wxString &GUID) {
7869 std::unique_ptr<PlugIn_Route> r;
7870 Route *route = g_pRouteMan->FindRouteByGUID(GUID);
7871 if (route == nullptr) return r;
7872
7873 r = std::unique_ptr<PlugIn_Route>(new PlugIn_Route);
7874 PlugIn_Route *dst_route = r.get();
7875
7876 // PlugIn_Waypoint *pwp;
7877 RoutePoint *src_wp;
7878 wxRoutePointListNode *node = route->pRoutePointList->GetFirst();
7879
7880 while (node) {
7881 src_wp = node->GetData();
7882
7883 PlugIn_Waypoint *dst_wp = new PlugIn_Waypoint();
7884 PlugInFromRoutePoint(dst_wp, src_wp);
7885
7886 dst_route->pWaypointList->Append(dst_wp);
7887
7888 node = node->GetNext();
7889 }
7890 dst_route->m_NameString = route->m_RouteNameString;
7891 dst_route->m_StartString = route->m_RouteStartString;
7892 dst_route->m_EndString = route->m_RouteEndString;
7893 dst_route->m_GUID = route->m_GUID;
7894
7895 return r;
7896}
7897
7898std::unique_ptr<PlugIn_Track> GetTrack_Plugin(const wxString &GUID) {
7899 std::unique_ptr<PlugIn_Track> t;
7900 // Find the Track
7901 Track *pTrack = g_pRouteMan->FindTrackByGUID(GUID);
7902 if (!pTrack) return t;
7903
7904 std::unique_ptr<PlugIn_Track> tk =
7905 std::unique_ptr<PlugIn_Track>(new PlugIn_Track);
7906 PlugIn_Track *dst_track = tk.get();
7907 dst_track->m_NameString = pTrack->GetName();
7908 dst_track->m_StartString = pTrack->m_TrackStartString;
7909 dst_track->m_EndString = pTrack->m_TrackEndString;
7910 dst_track->m_GUID = pTrack->m_GUID;
7911
7912 for (int i = 0; i < pTrack->GetnPoints(); i++) {
7913 TrackPoint *ptp = pTrack->GetPoint(i);
7914
7915 PlugIn_Waypoint *dst_wp = new PlugIn_Waypoint();
7916
7917 dst_wp->m_lat = ptp->m_lat;
7918 dst_wp->m_lon = ptp->m_lon;
7919 dst_wp->m_CreateTime = ptp->GetCreateTime(); // not const
7920
7921 dst_track->pWaypointList->Append(dst_wp);
7922 }
7923
7924 return tk;
7925}
7926
7927wxWindow *PluginGetFocusCanvas() { return g_focusCanvas; }
7928
7929wxWindow *PluginGetOverlayRenderCanvas() {
7930 // if(g_overlayCanvas)
7931 return g_overlayCanvas;
7932 // else
7933}
7934
7935void CanvasJumpToPosition(wxWindow *canvas, double lat, double lon,
7936 double scale) {
7937 ChartCanvas *oCanvas = wxDynamicCast(canvas, ChartCanvas);
7938 if (oCanvas) gFrame->JumpToPosition(oCanvas, lat, lon, scale);
7939}
7940
7941bool ShuttingDown(void) { return g_bquiting; }
7942
7943wxWindow *GetCanvasUnderMouse(void) { return gFrame->GetCanvasUnderMouse(); }
7944
7945int GetCanvasIndexUnderMouse(void) {
7946 ChartCanvas *l_canvas = gFrame->GetCanvasUnderMouse();
7947 if (l_canvas) {
7948 for (unsigned int i = 0; i < g_canvasArray.GetCount(); ++i) {
7949 if (l_canvas == g_canvasArray[i]) return i;
7950 }
7951 }
7952 return 0;
7953}
7954
7955// std::vector<wxWindow *> GetCanvasArray()
7956// {
7957// std::vector<wxWindow *> rv;
7958// for(unsigned int i=0 ; i < g_canvasArray.GetCount() ; i++){
7959// ChartCanvas *cc = g_canvasArray.Item(i);
7960// rv.push_back(cc);
7961// }
7962//
7963// return rv;
7964// }
7965
7966wxWindow *GetCanvasByIndex(int canvasIndex) {
7967 if (g_canvasConfig == 0)
7968 return gFrame->GetPrimaryCanvas();
7969 else {
7970 if ((canvasIndex >= 0) && g_canvasArray[canvasIndex]) {
7971 return g_canvasArray[canvasIndex];
7972 }
7973 }
7974 return NULL;
7975}
7976
7977bool CheckMUIEdgePan_PlugIn(int x, int y, bool dragging, int margin, int delta,
7978 int canvasIndex) {
7979 if (g_canvasConfig == 0)
7980 return gFrame->GetPrimaryCanvas()->CheckEdgePan(x, y, dragging, margin,
7981 delta);
7982 else {
7983 if ((canvasIndex >= 0) && g_canvasArray[canvasIndex]) {
7984 return g_canvasArray[canvasIndex]->CheckEdgePan(x, y, dragging, margin,
7985 delta);
7986 }
7987 }
7988
7989 return false;
7990}
7991
7992void SetMUICursor_PlugIn(wxCursor *pCursor, int canvasIndex) {
7993 if (g_canvasConfig == 0)
7994 gFrame->GetPrimaryCanvas()->pPlugIn_Cursor = pCursor;
7995 else {
7996 if ((canvasIndex >= 0) && g_canvasArray[canvasIndex]) {
7997 g_canvasArray[canvasIndex]->pPlugIn_Cursor = pCursor;
7998 }
7999 }
8000}
8001
8002int GetCanvasCount() {
8003 if (g_canvasConfig == 1) return 2;
8004 // else
8005 return 1;
8006}
8007
8008int GetLatLonFormat() { return g_iSDMMFormat; }
8009
8010wxRect GetMasterToolbarRect() {
8011 if (g_MainToolbar)
8012 return g_MainToolbar->GetRect();
8013 else
8014 return wxRect(0, 0, 1, 1);
8015}
8016
8017/* API 1.17 */
8018
8019void ZeroXTE() {
8020 if (g_pRouteMan) {
8021 g_pRouteMan->ZeroCurrentXTEToActivePoint();
8022 }
8023}
8024
8025ListOfPI_S57Obj *PlugInManager::GetLightsObjRuleListVisibleAtLatLon(
8026 ChartPlugInWrapper *target, float zlat, float zlon, const ViewPort &vp) {
8027 ListOfPI_S57Obj *list = NULL;
8028 if (target) {
8029 PlugInChartBaseGLPlus2 *picbgl =
8030 dynamic_cast<PlugInChartBaseGLPlus2 *>(target->GetPlugInChart());
8031 if (picbgl) {
8032 PlugIn_ViewPort pi_vp = CreatePlugInViewport(vp);
8033 list = picbgl->GetLightsObjRuleListVisibleAtLatLon(zlat, zlon, &pi_vp);
8034
8035 return list;
8036 }
8038 dynamic_cast<PlugInChartBaseExtendedPlus2 *>(target->GetPlugInChart());
8039 if (picbx) {
8040 PlugIn_ViewPort pi_vp = CreatePlugInViewport(vp);
8041 list = picbx->GetLightsObjRuleListVisibleAtLatLon(zlat, zlon, &pi_vp);
8042
8043 return list;
8044 } else
8045 return list;
8046 } else
8047 return list;
8048}
8049
8050// PlugInWaypointEx implementation
8051WX_DEFINE_LIST(Plugin_WaypointExList);
8052
8053// The class implementations
8054PlugIn_Waypoint_Ex::PlugIn_Waypoint_Ex() { InitDefaults(); }
8055
8056PlugIn_Waypoint_Ex::PlugIn_Waypoint_Ex(
8057 double lat, double lon, const wxString &icon_ident, const wxString &wp_name,
8058 const wxString &GUID, const double ScaMin, const bool bNameVisible,
8059 const int nRangeRings, const double RangeDistance,
8060 const wxColor RangeColor) {
8061 InitDefaults();
8062
8063 wxDateTime now = wxDateTime::Now();
8064 m_CreateTime = now.ToUTC();
8065 m_HyperlinkList = NULL;
8066
8067 m_lat = lat;
8068 m_lon = lon;
8069 IconName = icon_ident;
8070 m_MarkName = wp_name;
8071 m_GUID = GUID;
8072 scamin = ScaMin;
8073 IsNameVisible = bNameVisible;
8074 nrange_rings = nRangeRings;
8075 RangeRingSpace = RangeDistance;
8076 RangeRingColor = RangeColor;
8077}
8078
8079void PlugIn_Waypoint_Ex::InitDefaults() {
8080 m_HyperlinkList = NULL;
8081 scamin = 1e9;
8082 b_useScamin = false;
8083 nrange_rings = 0;
8084 RangeRingSpace = 1;
8085 IsNameVisible = false;
8086 IsVisible = true;
8087 RangeRingColor = *wxBLACK;
8088 m_CreateTime = wxDateTime::Now();
8089 IsActive = false;
8090 m_lat = 0;
8091 m_lon = 0;
8092}
8093
8094bool PlugIn_Waypoint_Ex::GetFSStatus() {
8095 RoutePoint *prp = pWayPointMan->FindRoutePointByGUID(m_GUID);
8096 if (!prp) return false;
8097
8098 if (prp->m_bIsInRoute && !prp->IsShared()) return false;
8099
8100 return true;
8101}
8102
8103int PlugIn_Waypoint_Ex::GetRouteMembershipCount() {
8104 // Search all routes to count the membership of this point
8105 RoutePoint *pWP = pWayPointMan->FindRoutePointByGUID(m_GUID);
8106 if (!pWP) return 0;
8107
8108 int nCount = 0;
8109 wxRouteListNode *node = pRouteList->GetFirst();
8110 while (node) {
8111 Route *proute = node->GetData();
8112 wxRoutePointListNode *pnode = (proute->pRoutePointList)->GetFirst();
8113 while (pnode) {
8114 RoutePoint *prp = pnode->GetData();
8115 if (prp == pWP) nCount++;
8116 pnode = pnode->GetNext();
8117 }
8118
8119 node = node->GetNext();
8120 }
8121
8122 return nCount;
8123}
8124
8125PlugIn_Waypoint_Ex::~PlugIn_Waypoint_Ex() {}
8126
8127// PlugInRouteExtended implementation
8128PlugIn_Route_Ex::PlugIn_Route_Ex(void) {
8129 pWaypointList = new Plugin_WaypointExList;
8130}
8131
8132PlugIn_Route_Ex::~PlugIn_Route_Ex(void) {
8133 pWaypointList->DeleteContents(false); // do not delete Waypoints
8134 pWaypointList->Clear();
8135
8136 delete pWaypointList;
8137}
8138
8139// The utility methods implementations
8140
8141// translate O route class to PlugIn_Waypoint_Ex
8142static void PlugInExFromRoutePoint(PlugIn_Waypoint_Ex *dst,
8143 /* const*/ RoutePoint *src) {
8144 dst->m_lat = src->m_lat;
8145 dst->m_lon = src->m_lon;
8146 dst->IconName = src->GetIconName();
8147 dst->m_MarkName = src->GetName();
8148 dst->m_MarkDescription = src->GetDescription();
8149 dst->IconDescription = pWayPointMan->GetIconDescription(src->GetIconName());
8150 dst->IsVisible = src->IsVisible();
8151 dst->m_CreateTime = src->GetCreateTime(); // not const
8152 dst->m_GUID = src->m_GUID;
8153
8154 // Transcribe (clone) the html HyperLink List, if present
8155 if (src->m_HyperlinkList == nullptr) return;
8156
8157 delete dst->m_HyperlinkList;
8158 dst->m_HyperlinkList = nullptr;
8159
8160 if (src->m_HyperlinkList->GetCount() > 0) {
8161 dst->m_HyperlinkList = new Plugin_HyperlinkList;
8162
8163 wxHyperlinkListNode *linknode = src->m_HyperlinkList->GetFirst();
8164 while (linknode) {
8165 Hyperlink *link = linknode->GetData();
8166
8168 h->DescrText = link->DescrText;
8169 h->Link = link->Link;
8170 h->Type = link->LType;
8171
8172 dst->m_HyperlinkList->Append(h);
8173
8174 linknode = linknode->GetNext();
8175 }
8176 }
8177
8178 // Get the range ring info
8179 dst->nrange_rings = src->m_iWaypointRangeRingsNumber;
8180 dst->RangeRingSpace = src->m_fWaypointRangeRingsStep;
8181 dst->RangeRingColor = src->m_wxcWaypointRangeRingsColour;
8182
8183 // Get other extended info
8184 dst->IsNameVisible = src->m_bShowName;
8185 dst->scamin = src->GetScaMin();
8186 dst->b_useScamin = src->GetUseSca();
8187 dst->IsActive = src->m_bIsActive;
8188}
8189
8190static void cloneHyperlinkListEx(RoutePoint *dst,
8191 const PlugIn_Waypoint_Ex *src) {
8192 // Transcribe (clone) the html HyperLink List, if present
8193 if (src->m_HyperlinkList == nullptr) return;
8194
8195 if (src->m_HyperlinkList->GetCount() > 0) {
8196 wxPlugin_HyperlinkListNode *linknode = src->m_HyperlinkList->GetFirst();
8197 while (linknode) {
8198 Plugin_Hyperlink *link = linknode->GetData();
8199
8200 Hyperlink *h = new Hyperlink();
8201 h->DescrText = link->DescrText;
8202 h->Link = link->Link;
8203 h->LType = link->Type;
8204
8205 dst->m_HyperlinkList->Append(h);
8206
8207 linknode = linknode->GetNext();
8208 }
8209 }
8210}
8211
8212RoutePoint *CreateNewPoint(const PlugIn_Waypoint_Ex *src, bool b_permanent) {
8213 RoutePoint *pWP = new RoutePoint(src->m_lat, src->m_lon, src->IconName,
8214 src->m_MarkName, src->m_GUID);
8215
8216 pWP->m_bIsolatedMark = true; // This is an isolated mark
8217
8218 cloneHyperlinkListEx(pWP, src);
8219
8220 pWP->m_MarkDescription = src->m_MarkDescription;
8221
8222 if (src->m_CreateTime.IsValid())
8223 pWP->SetCreateTime(src->m_CreateTime);
8224 else {
8225 wxDateTime dtnow(wxDateTime::Now());
8226 pWP->SetCreateTime(dtnow);
8227 }
8228
8229 pWP->m_btemp = (b_permanent == false);
8230
8231 // Extended fields
8232 pWP->SetIconName(src->IconName);
8233 pWP->SetWaypointRangeRingsNumber(src->nrange_rings);
8234 pWP->SetWaypointRangeRingsStep(src->RangeRingSpace);
8235 pWP->SetWaypointRangeRingsColour(src->RangeRingColor);
8236 pWP->SetScaMin(src->scamin);
8237 pWP->SetUseSca(src->b_useScamin);
8238 pWP->SetNameShown(src->IsNameVisible);
8239 pWP->SetVisible(src->IsVisible);
8240
8241 return pWP;
8242}
8243bool GetSingleWaypointEx(wxString GUID, PlugIn_Waypoint_Ex *pwaypoint) {
8244 // Find the RoutePoint
8245 RoutePoint *prp = pWayPointMan->FindRoutePointByGUID(GUID);
8246
8247 if (!prp) return false;
8248
8249 PlugInExFromRoutePoint(pwaypoint, prp);
8250
8251 return true;
8252}
8253
8254bool AddSingleWaypointEx(PlugIn_Waypoint_Ex *pwaypointex, bool b_permanent) {
8255 // Validate the waypoint parameters a little bit
8256
8257 // GUID
8258 // Make sure that this GUID is indeed unique in the Routepoint list
8259 bool b_unique = true;
8260 wxRoutePointListNode *prpnode = pWayPointMan->GetWaypointList()->GetFirst();
8261 while (prpnode) {
8262 RoutePoint *prp = prpnode->GetData();
8263
8264 if (prp->m_GUID == pwaypointex->m_GUID) {
8265 b_unique = false;
8266 break;
8267 }
8268 prpnode = prpnode->GetNext(); // RoutePoint
8269 }
8270
8271 if (!b_unique) return false;
8272
8273 RoutePoint *pWP = CreateNewPoint(pwaypointex, b_permanent);
8274
8275 pWP->SetShowWaypointRangeRings(pwaypointex->nrange_rings > 0);
8276
8277 pSelect->AddSelectableRoutePoint(pWP->m_lat, pWP->m_lon, pWP);
8278 if (b_permanent) pConfig->AddNewWayPoint(pWP, -1);
8279
8280 if (pRouteManagerDialog && pRouteManagerDialog->IsShown())
8281 pRouteManagerDialog->UpdateWptListCtrl();
8282
8283 return true;
8284}
8285
8286bool UpdateSingleWaypointEx(PlugIn_Waypoint_Ex *pwaypoint) {
8287 // Find the RoutePoint
8288 bool b_found = false;
8289 RoutePoint *prp = pWayPointMan->FindRoutePointByGUID(pwaypoint->m_GUID);
8290
8291 if (prp) b_found = true;
8292
8293 if (b_found) {
8294 double lat_save = prp->m_lat;
8295 double lon_save = prp->m_lon;
8296
8297 prp->m_lat = pwaypoint->m_lat;
8298 prp->m_lon = pwaypoint->m_lon;
8299 prp->SetIconName(pwaypoint->IconName);
8300 prp->SetName(pwaypoint->m_MarkName);
8301 prp->m_MarkDescription = pwaypoint->m_MarkDescription;
8302 prp->SetVisible(pwaypoint->IsVisible);
8303 if (pwaypoint->m_CreateTime.IsValid())
8304 prp->SetCreateTime(pwaypoint->m_CreateTime);
8305
8306 // Transcribe (clone) the html HyperLink List, if present
8307
8308 if (pwaypoint->m_HyperlinkList) {
8309 prp->m_HyperlinkList->Clear();
8310 if (pwaypoint->m_HyperlinkList->GetCount() > 0) {
8311 wxPlugin_HyperlinkListNode *linknode =
8312 pwaypoint->m_HyperlinkList->GetFirst();
8313 while (linknode) {
8314 Plugin_Hyperlink *link = linknode->GetData();
8315
8316 Hyperlink *h = new Hyperlink();
8317 h->DescrText = link->DescrText;
8318 h->Link = link->Link;
8319 h->LType = link->Type;
8320
8321 prp->m_HyperlinkList->Append(h);
8322
8323 linknode = linknode->GetNext();
8324 }
8325 }
8326 }
8327
8328 // Extended fields
8329 prp->SetWaypointRangeRingsNumber(pwaypoint->nrange_rings);
8330 prp->SetWaypointRangeRingsStep(pwaypoint->RangeRingSpace);
8331 prp->SetWaypointRangeRingsColour(pwaypoint->RangeRingColor);
8332 prp->SetScaMin(pwaypoint->scamin);
8333 prp->SetUseSca(pwaypoint->b_useScamin);
8334 prp->SetNameShown(pwaypoint->IsNameVisible);
8335
8336 prp->SetShowWaypointRangeRings(pwaypoint->nrange_rings > 0);
8337
8338 if (prp) prp->ReLoadIcon();
8339
8340 auto canvas = gFrame->GetPrimaryCanvas();
8341 SelectCtx ctx(canvas->m_bShowNavobjects, canvas->GetCanvasTrueScale());
8342 SelectItem *pFind = pSelect->FindSelection(ctx, lat_save, lon_save,
8343 SELTYPE_ROUTEPOINT);
8344 if (pFind) {
8345 pFind->m_slat = pwaypoint->m_lat; // update the SelectList entry
8346 pFind->m_slon = pwaypoint->m_lon;
8347 }
8348
8349 if (!prp->m_btemp) pConfig->UpdateWayPoint(prp);
8350
8351 if (pRouteManagerDialog && pRouteManagerDialog->IsShown())
8352 pRouteManagerDialog->UpdateWptListCtrl();
8353 }
8354
8355 return b_found;
8356}
8357
8358bool AddPlugInRouteEx(PlugIn_Route_Ex *proute, bool b_permanent) {
8359 Route *route = new Route();
8360
8361 PlugIn_Waypoint_Ex *pwaypointex;
8362 RoutePoint *pWP, *pWP_src;
8363 int ip = 0;
8364 wxDateTime plannedDeparture;
8365
8366 wxPlugin_WaypointExListNode *pwpnode = proute->pWaypointList->GetFirst();
8367 while (pwpnode) {
8368 pwaypointex = pwpnode->GetData();
8369
8370 pWP = pWayPointMan->FindRoutePointByGUID(pwaypointex->m_GUID);
8371 if (!pWP) {
8372 pWP = CreateNewPoint(pwaypointex, b_permanent);
8373 pWP->m_bIsolatedMark = false;
8374 }
8375
8376 route->AddPoint(pWP);
8377
8378 pSelect->AddSelectableRoutePoint(pWP->m_lat, pWP->m_lon, pWP);
8379
8380 if (ip > 0)
8381 pSelect->AddSelectableRouteSegment(pWP_src->m_lat, pWP_src->m_lon,
8382 pWP->m_lat, pWP->m_lon, pWP_src, pWP,
8383 route);
8384
8385 plannedDeparture = pwaypointex->m_CreateTime;
8386 ip++;
8387 pWP_src = pWP;
8388
8389 pwpnode = pwpnode->GetNext(); // PlugInWaypoint
8390 }
8391
8392 route->m_PlannedDeparture = plannedDeparture;
8393
8394 route->m_RouteNameString = proute->m_NameString;
8395 route->m_RouteStartString = proute->m_StartString;
8396 route->m_RouteEndString = proute->m_EndString;
8397 if (!proute->m_GUID.IsEmpty()) {
8398 route->m_GUID = proute->m_GUID;
8399 }
8400 route->m_btemp = (b_permanent == false);
8401
8402 pRouteList->Append(route);
8403
8404 if (b_permanent) pConfig->AddNewRoute(route);
8405
8406 if (pRouteManagerDialog && pRouteManagerDialog->IsShown())
8407 pRouteManagerDialog->UpdateRouteListCtrl();
8408
8409 return true;
8410}
8411
8412bool UpdatePlugInRouteEx(PlugIn_Route_Ex *proute) {
8413 bool b_found = false;
8414
8415 // Find the Route
8416 Route *pRoute = g_pRouteMan->FindRouteByGUID(proute->m_GUID);
8417 if (pRoute) b_found = true;
8418
8419 if (b_found) {
8420 bool b_permanent = (pRoute->m_btemp == false);
8421 g_pRouteMan->DeleteRoute(pRoute, NavObjectChanges::getInstance());
8422
8423 b_found = AddPlugInRouteEx(proute, b_permanent);
8424 }
8425
8426 return b_found;
8427}
8428
8429// std::unique_ptr<PlugIn_Waypoint_Ex> GetWaypointEx_Plugin(const wxString &)
8430// {
8431// }
8432
8433// std::unique_ptr<PlugIn_Route_Ex> GetRouteEx_Plugin(const wxString &)
8434// {
8435// }
8436
8437std::unique_ptr<PlugIn_Waypoint_Ex> GetWaypointEx_Plugin(const wxString &GUID) {
8438 std::unique_ptr<PlugIn_Waypoint_Ex> w(new PlugIn_Waypoint_Ex);
8439 GetSingleWaypointEx(GUID, w.get());
8440 return w;
8441}
8442
8443std::unique_ptr<PlugIn_Route_Ex> GetRouteEx_Plugin(const wxString &GUID) {
8444 std::unique_ptr<PlugIn_Route_Ex> r;
8445 Route *route = g_pRouteMan->FindRouteByGUID(GUID);
8446 if (route == nullptr) return r;
8447
8448 r = std::unique_ptr<PlugIn_Route_Ex>(new PlugIn_Route_Ex);
8449 PlugIn_Route_Ex *dst_route = r.get();
8450
8451 // PlugIn_Waypoint *pwp;
8452 RoutePoint *src_wp;
8453 wxRoutePointListNode *node = route->pRoutePointList->GetFirst();
8454
8455 while (node) {
8456 src_wp = node->GetData();
8457
8458 PlugIn_Waypoint_Ex *dst_wp = new PlugIn_Waypoint_Ex();
8459 PlugInExFromRoutePoint(dst_wp, src_wp);
8460
8461 dst_route->pWaypointList->Append(dst_wp);
8462
8463 node = node->GetNext();
8464 }
8465 dst_route->m_NameString = route->m_RouteNameString;
8466 dst_route->m_StartString = route->m_RouteStartString;
8467 dst_route->m_EndString = route->m_RouteEndString;
8468 dst_route->m_GUID = route->m_GUID;
8469 dst_route->m_isActive = g_pRouteMan->GetpActiveRoute() == route;
8470
8471 return r;
8472}
8473
8474wxString GetActiveWaypointGUID(
8475 void) { // if no active waypoint, returns wxEmptyString
8476 RoutePoint *rp = g_pRouteMan->GetpActivePoint();
8477 if (!rp)
8478 return wxEmptyString;
8479 else
8480 return rp->m_GUID;
8481}
8482
8483wxString GetActiveRouteGUID(
8484 void) { // if no active route, returns wxEmptyString
8485 Route *rt = g_pRouteMan->GetpActiveRoute();
8486 if (!rt)
8487 return wxEmptyString;
8488 else
8489 return rt->m_GUID;
8490}
8491
8493int GetGlobalWatchdogTimoutSeconds(){
8494 return gps_watchdog_timeout_ticks;
8495}
8496
8498std::vector<std::string> GetPriorityMaps() {
8499 MyApp& app = wxGetApp();
8500 return (app.m_comm_bridge.GetPriorityMaps());
8501}
8502
8503std::vector<std::string> GetActivePriorityIdentifiers() {
8504 std::vector<std::string> result;
8505
8506 MyApp& app = wxGetApp();
8507
8508 std::string id = app.m_comm_bridge.GetPriorityContainer("position").active_source;
8509 result.push_back(id);
8510 id = app.m_comm_bridge.GetPriorityContainer("velocity").active_source;
8511 result.push_back(id);
8512 id = app.m_comm_bridge.GetPriorityContainer("heading").active_source;
8513 result.push_back(id);
8514 id = app.m_comm_bridge.GetPriorityContainer("variation").active_source;
8515 result.push_back(id);
8516 id = app.m_comm_bridge.GetPriorityContainer("satellites").active_source;
8517 result.push_back(id);
8518
8519 return result;
8520}
8521
8522double OCPN_GetDisplayContentScaleFactor() {
8523 double rv = 1.0;
8524#if defined(__WXOSX__) || defined(__WXGTK3__)
8525 // Support scaled HDPI displays.
8526 if (gFrame)
8527 rv = gFrame->GetContentScaleFactor();
8528#endif
8529 return rv;
8530}
8531double OCPN_GetWinDIPScaleFactor() {
8532 double scaler = 1.0;
8533#ifdef __WXMSW__
8534 if (gFrame)
8535 scaler = (double)(gFrame->ToDIP(100))/100.;
8536#endif
8537 return scaler;
8538}
8539
8542std::vector<DriverHandle> GetActiveDrivers() {
8543
8544 std::vector<DriverHandle> result;
8545
8546 auto& registry = CommDriverRegistry::GetInstance();
8547 const std::vector<std::shared_ptr<AbstractCommDriver>>& drivers = registry.GetDrivers();
8548
8549 for (auto& driver : drivers)
8550 result.push_back(driver->Key());
8551
8552 return result;
8553}
8554
8555
8556const std::unordered_map<std::string, std::string> GetAttributes(DriverHandle handle) {
8557 auto& registry = CommDriverRegistry::GetInstance();
8558 auto drivers = registry.GetDrivers();
8559 auto func = [handle](const DriverPtr d) { return d->Key() == handle; };
8560 auto found = std::find_if(drivers.begin(), drivers.end(), func);
8561
8562 std::unordered_map<std::string, std::string> rv;
8563 if (found == drivers.end()){
8564 return rv;
8565 }
8566
8567 return found->get()->GetAttributes();
8568}
8569
8570CommDriverResult WriteCommDriverN2K( DriverHandle handle, int PGN,
8571 int destinationCANAddress, int priority,
8572 const std::shared_ptr <std::vector<uint8_t>> &payload){
8573
8574 uint64_t _PGN;
8575 _PGN = PGN;
8576
8577 // Find the driver from the handle
8578 auto& registry = CommDriverRegistry::GetInstance();
8579 auto drivers = registry.GetDrivers();
8580 auto func = [handle](const DriverPtr d) { return d->Key() == handle; };
8581 auto driver = std::find_if(drivers.begin(), drivers.end(), func);
8582
8583 if (driver == drivers.end()){
8584 return RESULT_COMM_INVALID_HANDLE;
8585 }
8586
8587 auto dest_addr = std::make_shared<const NavAddr2000>(driver->get()->iface, destinationCANAddress);
8588
8589 const std::vector<uint8_t> load;
8590 size_t data_len = payload.get()->size();
8591
8592 auto msg = std::make_shared<const Nmea2000Msg>(_PGN, *(payload), dest_addr);
8593
8594 bool result = driver->get()->SendMessage(msg, dest_addr);
8595
8596 return RESULT_COMM_NO_ERROR;
8597}
8598
8599CommDriverResult RegisterTXPGNs( DriverHandle handle, std::vector<int> &pgn_list) {
8600 if (!pgn_list.size())
8601 return RESULT_COMM_INVALID_PARMS;
8602
8603 // Find the driver from the handle
8604 auto& registry = CommDriverRegistry::GetInstance();
8605 auto drivers = registry.GetDrivers();
8606 auto func = [handle](const DriverPtr d) { return d->Key() == handle; };
8607 auto driver = std::find_if(drivers.begin(), drivers.end(), func);
8608
8609 if (driver == drivers.end()){
8610 return RESULT_COMM_INVALID_HANDLE;
8611 }
8612
8613 std::shared_ptr<CommDriverN2K> dn2k = std::dynamic_pointer_cast<CommDriverN2K>(*driver);
8614
8615 int nloop;
8616 for (size_t i=0 ; i < pgn_list.size() ; i++){
8617 int nTry = 5;
8618 int iresult = -1;
8619 nloop = 0;
8620 while (nTry && iresult < 0) {
8621 iresult = dn2k->SetTXPGN( pgn_list[i]);
8622 nTry--;
8623 nloop++;
8624 }
8625
8626 if (iresult < 0){
8627 printf("####TXPGN Fail\n");
8628 return RESULT_COMM_REGISTER_PGN_ERROR;
8629 }
8630 }
8631
8632 printf("----TXPGN PASS nloop: %d \n", nloop);
8633 return RESULT_COMM_NO_ERROR;
8634}
8635
EventVar plugin_msg
A JSON message should be sent.
Definition: ais_decoder.h:126
Handle messages for blacklisted plugins.
Modal dialog, displays settings for plugin catalog.
Definition: cat_settings.h:34
const std::vector< DriverPtr > & GetDrivers()
Wrapper for configuration variables which lives in a wxBaseConfig object.
Wrapper for global variable, supports notification events when value changes.
Add progress and final message dialogs to the basic Downloader.
Definition: download_mgr.h:47
Definition: ocpn_app.h:44
static std::string MessageKey(const char *type="ALL")
Return key which should be used to listen to given message type.
Definition: comm_navmsg.h:272
void Listen(const std::string &key, wxEvtHandler *listener, wxEventType evt)
Set object to send wxEventType ev to listener on changes in key.
Definition: observable.cpp:98
Adds a std::shared<void> element to wxCommandEvent.
Definition: ocpn_plugin.h:1615
SemanticVersion GetVersion()
Return version from plugin API.
const std::vector< PluginMetadata > getInstalled()
Return list of all installed plugins.
static std::string fileListPath(std::string name)
Return path to installation manifest for given plugin.
static std::string versionPath(std::string name)
Return path to file containing version for given plugin.
bool uninstall(const std::string plugin)
Uninstall an installed plugin.
static bool isCompatible(const PluginMetadata &metadata, const char *os=PKG_TARGET, const char *os_version=PKG_TARGET_VERSION)
Return true if given plugin is loadable on given os/version.
void ReloadPluginPanels()
Complete reload from plugins array.
std::vector< std::string > Libdirs()
List of directories from which we load plugins.
Definition: plugin_paths.h:26
std::vector< std::string > Bindirs()
'List of directories for plugin binary helpers.
Definition: plugin_paths.h:29
static PluginPaths * getInstance()
Return the singleton instance.
Definition: route.h:70
bool DeleteRoute(Route *pRoute, NavObjectChanges *nav_obj_changes)
Definition: routeman.cpp:726
EventVar json_msg
Notified with message targeting all plugins.
Definition: routeman.h:148
EventVar json_leg_info
Notified with a shared_ptr<ActiveLegDat>, leg info to all plugins.
Definition: routeman.h:151
Definition: select.h:51
A parsed, raw SignalK message.
Definition: comm_navmsg.h:283
Definition: track.h:79
Modal dialog, displays available updates (possibly just one) and lets user select and eventually conf...
Definition: update_mgr.h:41
Invokes client browser on plugin info_url when clicked.
WebsiteButton(wxWindow *parent, const char *url)
Invokes client browser on plugin info_url when clicked.
Definition: ocpndc.h:55
virtual bool RenderOverlayMultiCanvas(wxDC &dc, PlugIn_ViewPort *vp, int canvasIndex, int priority=-1)
Render plugin overlay over chart canvas in non-OpenGL mode.
virtual bool RenderGLOverlayMultiCanvas(wxGLContext *pcontext, PlugIn_ViewPort *vp, int canvasIndex, int priority=-1)
Render plugin overlay over chart canvas in OpenGL mode.
std::string lookup_tarball(const char *uri)
Get path to tarball in cache for given filename.
bool store_metadata(const char *path)
Store metadata in metadata cache, return success/fail.
bool store_tarball(const char *path, const char *basename)
Store a tarball in tarball cache, return success/fail.
Plugin metadata, reflects the xml format directly.
Versions uses a modified semantic versioning scheme: major.minor.revision.post-tag+build.
Definition: semantic_vers.h:51
static SemanticVersion parse(std::string s)
Parse a version string, sets major == -1 on errors.
std::string to_string()
Return printable representation.
Definition: Quilt.cpp:864