OpenCPN Partial API docs
Loading...
Searching...
No Matches
pluginmanager.cpp
Go to the documentation of this file.
1/**************************************************************************
2 * Copyright (C) 2010 by David S. Register *
3 * *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
8 * *
9 * This program is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 * GNU General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU General Public License *
15 * along with this program; if not, write to the *
16 * Free Software Foundation, Inc., *
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
18 **************************************************************************/
19
24#include <algorithm>
25#include <archive.h>
26#include <cstdio>
27#include <cstdio>
28#include <errno.h>
29#include <fcntl.h>
30#include <fstream>
31#include <iostream>
32#include <iostream>
33#include <memory>
34#include <set>
35#include <sstream>
36#include <stdint.h>
37#include <string>
38#include <unordered_map>
39
40#ifdef __MINGW32__
41#undef IPV6STRICT // mingw FTBS fix: missing struct ip_mreq
42#include <windows.h>
43#endif
44
45#include <typeinfo>
46#if defined(__linux__) && !defined(__ANDROID__)
47#include <wordexp.h>
48#endif
49#include <wx/wx.h>
50#include <wx/dir.h>
51#include <wx/event.h>
52#include <wx/filename.h>
53#include <wx/aui/aui.h>
54#include <wx/platinfo.h>
55#include <wx/popupwin.h>
56#include <wx/progdlg.h>
57#include <wx/statline.h>
58#include <wx/tokenzr.h>
59#include <wx/tooltip.h>
60#include <wx/app.h>
61#include <wx/hashset.h>
62#include <wx/hashmap.h>
63#include <wx/jsonval.h>
64#include <wx/jsonreader.h>
65#include <wx/uri.h>
66#include <wx/zipstrm.h>
67#include <wx/zstream.h>
68#include <wx/tarstrm.h>
69#include <wx/textwrapper.h>
70#include <wx/app.h>
71
72#ifndef __WXMSW__
73#include <cxxabi.h>
74#endif // __WXMSW__
75
76#include <archive_entry.h>
77typedef __LA_INT64_T la_int64_t; // "older" libarchive versions support
78
79#ifdef USE_LIBELF
80#include <elf.h>
81#include <libelf.h>
82#include <gelf.h>
83#endif
84
85#include "config.h"
86
87#include "model/ais_target_data.h"
91#include "model/comm_drv_n2k.h"
94#include "model/comm_vars.h"
95#include "model/config_vars.h"
96#include "model/datetime.h"
97#include "model/downloader.h"
98#include "model/georef.h"
99#include "model/json_event.h"
100#include "model/logger.h"
101#include "model/multiplexer.h"
102#include "model/nav_object_database.h"
103#include "model/navutil_base.h"
104#include "model/ocpn_utils.h"
105#include "model/plugin_cache.h"
106#include "model/plugin_comm.h"
107#include "model/plugin_handler.h"
108#include "model/plugin_loader.h"
109#include "model/plugin_paths.h"
110#include "model/route.h"
111#include "model/routeman.h"
112#include "model/safe_mode.h"
113#include "model/select.h"
114#include "model/semantic_vers.h"
115#include "model/track.h"
116
117#include "ais.h"
118#include "canvasMenu.h"
119#include "cat_settings.h"
120#include "chartbase.h" // for ChartPlugInWrapper
121#include "chartdb.h"
122#include "chartdbs.h"
123#include "chcanv.h"
124#include "config.h"
125#include "download_mgr.h"
126#include "dychart.h"
127#include "FontMgr.h"
128#include "gshhs.h"
129#include "model/ais_decoder.h"
130#include "mygeom.h"
131#include "navutil.h"
132#include "observable_confvar.h"
133#include "observable_globvar.h"
134#include "ocpn_app.h"
135#include "OCPN_AUIManager.h"
136#include "ocpndc.h"
137#include "ocpn_frame.h"
138#include "ocpn_pixel.h"
139#include "OCPNPlatform.h"
140#include "OCPNRegion.h"
141#include "options.h"
142#include "piano.h"
143#include "pluginmanager.h"
144#include "routemanagerdialog.h"
145#include "routeman_gui.h"
146#include "s52plib.h"
147#include "s52utils.h"
148#include "SoundFactory.h"
149#include "styles.h"
150#include "model/svg_utils.h"
151#include "SystemCmdSound.h"
152#include "toolbar.h"
153#include "update_mgr.h"
154#include "waypointman_gui.h"
155
156#ifdef __ANDROID__
157#include <dlfcn.h>
158#include "androidUTIL.h"
159#endif
160
161#ifdef ocpnUSE_GL
162#include "glChartCanvas.h"
163#endif
164extern MyConfig* pConfig;
165extern OCPN_AUIManager* g_pauimgr;
166
167#if wxUSE_XLOCALE || !wxCHECK_VERSION(3, 0, 0)
168extern wxLocale* plocale_def_lang;
169#endif
170
171extern OCPNPlatform* g_Platform;
172extern ChartDB* ChartData;
173extern MyFrame* gFrame;
174extern ocpnStyle::StyleManager* g_StyleManager;
175extern options* g_pOptions;
176extern Multiplexer* g_pMUX;
177extern bool g_bShowChartBar;
178extern Routeman* g_pRouteMan;
179extern Select* pSelect;
180extern RouteManagerDialog* pRouteManagerDialog;
181extern RouteList* pRouteList;
182extern std::vector<Track*> g_TrackList;
183extern PlugInManager* g_pi_manager;
184extern s52plib* ps52plib;
185extern wxString ChartListFileName;
186extern options* g_options;
187extern ColorScheme global_color_scheme;
188extern wxArrayString g_locale_catalog_array;
189extern int g_GUIScaleFactor;
190extern int g_ChartScaleFactor;
191extern wxString g_locale;
192extern ocpnFloatingToolbarDialog* g_MainToolbar;
193
194extern int g_chart_zoom_modifier_raster;
195extern int g_chart_zoom_modifier_vector;
196extern double g_display_size_mm;
197extern bool g_bopengl;
198
199extern ChartGroupArray* g_pGroupArray;
200extern unsigned int g_canvasConfig;
201
202extern wxString g_CmdSoundString;
203
204extern unsigned int gs_plib_flags;
205extern ChartCanvas* g_focusCanvas;
206extern ChartCanvas* g_overlayCanvas;
207extern bool g_bquiting;
208
209WX_DEFINE_ARRAY_PTR(ChartCanvas*, arrayofCanvasPtr);
210extern arrayofCanvasPtr g_canvasArray;
211
212void NotifySetupOptionsPlugin(const PlugInData* pic);
213
214enum { CurlThreadId = wxID_HIGHEST + 1 };
215
216#include <wx/listimpl.cpp>
217WX_DEFINE_LIST(Plugin_WaypointList);
218WX_DEFINE_LIST(Plugin_HyperlinkList);
219
220wxDEFINE_EVENT(EVT_SIGNALK, ObservedEvt);
221
223class RawKey : public KeyProvider {
224public:
225 explicit RawKey(const std::string& key) : m_key(key) {}
226 [[nodiscard]] std::string GetKey() const override { return m_key; }
227
228private:
229 std::string m_key;
230};
231
232static void SendAisJsonMessage(std::shared_ptr<const AisTargetData> pTarget) {
233 // Only send messages if someone is listening...
234 if (!GetJSONMessageTargetCount()) return;
235
236 // Do JSON message to all Plugin to inform of target
237 wxJSONValue jMsg;
238
239 wxLongLong t = ::wxGetLocalTimeMillis();
240
241 jMsg[wxS("Source")] = wxS("AisDecoder");
242 jMsg[wxT("Type")] = wxT("Information");
243 jMsg[wxT("Msg")] = wxS("AIS Target");
244 jMsg[wxT("MsgId")] = t.GetValue();
245 jMsg[wxS("lat")] = pTarget->Lat;
246 jMsg[wxS("lon")] = pTarget->Lon;
247 jMsg[wxS("sog")] = pTarget->SOG;
248 jMsg[wxS("cog")] = pTarget->COG;
249 jMsg[wxS("hdg")] = pTarget->HDG;
250 jMsg[wxS("mmsi")] = pTarget->MMSI;
251 jMsg[wxS("class")] = pTarget->Class;
252 jMsg[wxS("ownship")] = pTarget->b_OwnShip;
253 jMsg[wxS("active")] = pTarget->b_active;
254 jMsg[wxS("lost")] = pTarget->b_lost;
255 wxString l_ShipName = wxString::FromUTF8(pTarget->ShipName);
256 for (size_t i = 0; i < l_ShipName.Len(); i++) {
257 if (l_ShipName.GetChar(i) == '@') l_ShipName.SetChar(i, '\n');
258 }
259 jMsg[wxS("shipname")] = l_ShipName;
260 wxString l_CallSign = wxString::FromUTF8(pTarget->CallSign);
261 for (size_t i = 0; i < l_CallSign.Len(); i++) {
262 if (l_CallSign.GetChar(i) == '@') l_CallSign.SetChar(i, '\n');
263 }
264 jMsg[wxS("callsign")] = l_CallSign;
265 jMsg[wxS("removed")] = pTarget->b_removed;
266 SendJSONMessageToAllPlugins(wxT("AIS"), jMsg);
267}
268
269static int ComparePlugins(PlugInContainer** p1, PlugInContainer** p2) {
270 return (*p1)->Key().compare((*p2)->Key());
271}
272
273static SemanticVersion ParseVersion(const PluginMetadata& metadata) {
274 auto version = metadata.version;
275 // Handle tag versions like v1.2.3:
276 if (version[0] == 'v') version = version.substr(1);
277 return SemanticVersion::parse(version);
278}
279
280static bool IsUpdateAvailable(const PluginMetadata& metadata) {
281 auto imported_version = ParseVersion(metadata);
282 for (auto& md : PluginHandler::GetInstance()->GetAvailable()) {
283 if (md.name != metadata.name) continue;
284 if (md.is_imported) continue;
285 if (!PluginHandler::GetInstance()->IsCompatible(md)) continue;
286 if (ParseVersion(md) >= imported_version) return true;
287 }
288 return false;
289}
290
296public:
297 void message(const std::string& message) {
298 if (m_defer)
299 m_deferred_messages.push_back(message);
300 else
301 show_msg(message);
302 }
303
304 void show_deferred_messages() {
305 for (auto m : m_deferred_messages) show_msg(m);
306 m_defer = false;
307 }
308
309 BlacklistUI() : m_defer(true) {};
310
311private:
312 void show_msg(wxString msg) {
313 OCPNMessageBox(NULL, msg, wxString(_("OpenCPN Info")),
314 wxICON_INFORMATION | wxOK, 10); // 10 second timeout
315 }
316
317 bool m_defer; // defer dialogs until setup completed
318 std::vector<wxString> m_deferred_messages;
319};
320
321class PanelHardBreakWrapper : public wxTextWrapper {
322public:
323 PanelHardBreakWrapper(wxWindow* win, const wxString& text, int widthMax) {
324 m_lineCount = 0;
325 Wrap(win, text, widthMax);
326 }
327 wxString const& GetWrapped() const { return m_wrapped; }
328 int const GetLineCount() const { return m_lineCount; }
329 wxArrayString GetLineArray() { return m_array; }
330
331protected:
332 virtual void OnOutputLine(const wxString& line) {
333 m_wrapped += line;
334 m_array.Add(line);
335 }
336 virtual void OnNewLine() {
337 m_wrapped += '\n';
338 m_lineCount++;
339 }
340
341private:
342 wxString m_wrapped;
343 int m_lineCount;
344 wxArrayString m_array;
345};
346
348 template <typename T>
349 std::size_t operator()(T t) const {
350 return static_cast<std::size_t>(t);
351 }
352};
353
354wxString message_by_status(PluginStatus stat) {
355 switch (stat) {
357 return _("Plugin is a standard system plugin");
359 return _("Plugin is managed by OpenCPN");
361 return _("Plugin is not managed by OpenCPN");
363 return ("");
364 case PluginStatus::Unknown:
365 return _("Plugin status unknown");
366 case PluginStatus::LegacyUpdateAvailable:
367 return _("Update to managed Plugin is available");
368 case PluginStatus::ManagedInstallAvailable:
369 return _("New managed Plugin installation available");
370 case PluginStatus::ManagedInstalledUpdateAvailable:
371 return _("Update to installed Plugin is available");
372 case PluginStatus::ManagedInstalledCurrentVersion:
373 return _("Plugin is latest available");
374 case PluginStatus::Imported:
375 return _("Plugin is imported");
376 case PluginStatus::ManagedInstalledDowngradeAvailable:
377 return ("");
378 case PluginStatus::PendingListRemoval:
379 return ("");
380 default:
381 return ("");
382 }
383}
384
385static const std::unordered_map<PluginStatus, const char*, EnumClassHash>
386 icon_by_status(
387 {{PluginStatus::System, "emblem-default.svg"},
388 {PluginStatus::Managed, "emblem-default.svg"},
389 {PluginStatus::Unmanaged, "emblem-unmanaged.svg"},
390 {PluginStatus::Ghost, "ghost.svg"},
391 {PluginStatus::Unknown, "emblem-unmanaged.svg"},
392 {PluginStatus::LegacyUpdateAvailable, "emblem-legacy-update.svg"},
393 {PluginStatus::ManagedInstallAvailable, "emblem-download.svg"},
394 {PluginStatus::ManagedInstalledUpdateAvailable,
395 "emblem-legacy-update.svg"},
396 {PluginStatus::ManagedInstalledCurrentVersion, "emblem-default.svg"},
397 {PluginStatus::ManagedInstalledDowngradeAvailable,
398 "emblem-default.svg"},
399 {PluginStatus::PendingListRemoval, "emblem-default.svg"},
400 {PluginStatus::Imported, "emblem-default.svg"}});
401
402static const std::unordered_map<PluginStatus, const char*, EnumClassHash>
403 literalstatus_by_status(
404 {{PluginStatus::System, "System"},
405 {PluginStatus::Managed, "Managed"},
406 {PluginStatus::Unmanaged, "Unmanaged"},
407 {PluginStatus::Ghost, "Ghost"},
408 {PluginStatus::Unknown, "Unknown"},
409 {PluginStatus::LegacyUpdateAvailable, "LegacyUpdateAvailable"},
410 {PluginStatus::ManagedInstallAvailable, "ManagedInstallAvailable"},
411 {PluginStatus::ManagedInstalledUpdateAvailable,
412 "ManagedInstalledUpdateAvailable"},
413 {PluginStatus::ManagedInstalledCurrentVersion,
414 "ManagedInstalledCurrentVersion"},
415 {PluginStatus::ManagedInstalledDowngradeAvailable,
416 "ManagedInstalledDowngradeAvailable"},
417 {PluginStatus::PendingListRemoval, "PendingListRemoval"},
418 {PluginStatus::Imported, "Imported"}
419
420 });
421
426static std::vector<PluginMetadata> getCompatiblePlugins() {
428 struct metadata_compare {
429 bool operator()(const PluginMetadata& lhs,
430 const PluginMetadata& rhs) const {
431 return lhs.key() < rhs.key();
432 }
433 };
434
435 std::vector<PluginMetadata> returnArray;
436
437 std::set<PluginMetadata, metadata_compare> unique_plugins;
438 for (auto plugin : PluginHandler::GetInstance()->GetAvailable()) {
439 unique_plugins.insert(plugin);
440 }
441 for (auto plugin : unique_plugins) {
442 if (PluginHandler::IsCompatible(plugin)) {
443 returnArray.push_back(plugin);
444 }
445 }
446 return returnArray;
447}
448
449static SemanticVersion metadata_version(const PluginMetadata pm) {
450 return SemanticVersion::parse(pm.name);
451}
452
453// Get installed version from manifest for given plugin. For
454// older plugins this contains more detailed info then the
455// plugin API. From API level 117 the API should contain the
456// same info.
457//
458// TODO: Get version from API for api level 117+
459SemanticVersion getInstalledVersion(const std::string name) {
460 std::string installed;
461 std::string path = PluginHandler::VersionPath(name);
462 if (path == "" || !wxFileName::IsFileReadable(path)) {
463 return SemanticVersion(-1, -1);
464 }
465 std::ifstream stream;
466 stream.open(path, std::ifstream::in);
467 stream >> installed;
468 return SemanticVersion::parse(installed);
469}
470
475static std::vector<PluginMetadata> getUpdates(const char* name) {
476 auto updates = getCompatiblePlugins();
477 updates.erase(
478 std::remove_if(updates.begin(), updates.end(),
479 [&](const PluginMetadata m) { return m.name != name; }),
480 updates.end());
481
482 auto inst_vers = getInstalledVersion(name);
483 if (inst_vers.major == -1) {
484 return updates;
485 }
486
487 // Drop already installed plugin, it has its own update options.
488 updates.erase(std::remove_if(updates.begin(), updates.end(),
489 [&](const PluginMetadata m) {
490 return metadata_version(m) == inst_vers;
491 }),
492 updates.end());
493 return updates;
494}
495
497static void gui_uninstall(const PlugInData* pic, const char* plugin) {
498 g_Platform->ShowBusySpinner();
499 PluginLoader::GetInstance()->DeactivatePlugIn(*pic);
500 PluginLoader::GetInstance()->SetEnabled(pic->m_common_name, false);
501 PluginLoader::GetInstance()->UpdatePlugIns();
502
503 wxLogMessage("Uninstalling %s", plugin);
505 PluginLoader::GetInstance()->UpdatePlugIns();
506 g_Platform->HideBusySpinner();
507}
508
509static bool LoadAllPlugIns(bool load_enabled, bool keep_orphans = false) {
510 g_Platform->ShowBusySpinner();
511 bool b =
512 PluginLoader::GetInstance()->LoadAllPlugIns(load_enabled, keep_orphans);
513 g_Platform->HideBusySpinner();
514 return b;
515}
516
518static void UninstallPlugin(const std::string& name) {
519 auto handler = PluginHandler::GetInstance();
520 auto loader = PluginLoader::GetInstance();
521 auto finder = [name](const PluginMetadata pm) { return pm.name == name; };
522 const auto& installed = handler->GetInstalled();
523 auto found = std::find_if(installed.begin(), installed.end(), finder);
524 if (found != installed.end()) {
525 for (size_t i = 0; i < loader->GetPlugInArray()->GetCount(); i++) {
526 auto const& item = loader->GetPlugInArray()->Item(i);
527 if (item->m_common_name.ToStdString() == name) {
528 DEBUG_LOG << "Unloading plugin: " << name;
529 loader->UnLoadPlugIn(i);
530 break;
531 }
532 }
533 handler->Uninstall(found->name);
534 DEBUG_LOG << "Uninstalling: " << found->name;
535 }
536}
537
538static void run_update_dialog(PluginListPanel* parent, const PlugInData* pic,
539 bool uninstall, const char* name = 0,
540 bool b_forceEnable = false) {
541 wxString pluginName = pic->m_common_name;
542 const char* plugin = name == 0 ? pic->m_common_name.mb_str().data() : name;
543 auto updates = getUpdates(plugin);
544 auto parent_dlg = dynamic_cast<wxScrolledWindow*>(parent);
545 wxASSERT(parent_dlg != 0);
546 UpdateDialog dialog(parent_dlg, updates);
547 auto status = dialog.ShowModal();
548 status = dialog.GetReturnCode();
549 if (status != wxID_OK) {
550 return;
551 }
552
553 auto update = dialog.GetUpdate();
554 if (!g_pi_manager->CheckBlacklistedPlugin(update)) {
555 return;
556 }
557
558 wxLogMessage("Installing %s", update.name.c_str());
559
560 auto pluginHandler = PluginHandler::GetInstance();
561 auto path = ocpn::lookup_tarball(update.tarball_url.c_str());
562 if (uninstall && path != "") {
563 gui_uninstall(pic, update.name.c_str());
564 }
565 bool cacheResult = pluginHandler->InstallPluginFromCache(update);
566
567 if (!cacheResult) {
568 g_Platform->ShowBusySpinner(); // Will be cancelled in downloader->run()
569 wxYield();
570
571 auto downloader = new GuiDownloader(parent_dlg, update);
572 std::string tempTarballPath = downloader->run(parent_dlg, uninstall);
573
574 if (!tempTarballPath.size()) // Error, dialog already presented
575 return;
576
577 // Provisional error check
578 bool bOK = true;
579 std::string manifestPath = PluginHandler::FileListPath(update.name);
580 if (!isRegularFile(manifestPath.c_str())) {
581 wxLogMessage("Installation of %s failed", update.name.c_str());
582 PluginHandler::CleanupFiles(manifestPath, update.name);
583 bOK = false;
584 }
585
586 // On successful installation, copy the temp tarball to the local cache
587 if (bOK) {
588 wxLogMessage("Installation of %s successful", update.name.c_str());
589 wxURI uri(wxString(update.tarball_url.c_str()));
590 wxFileName fn(uri.GetPath());
591 std::string basename = fn.GetFullName().ToStdString();
592
593 if (ocpn::store_tarball(tempTarballPath.c_str(), basename.c_str())) {
594 wxLogDebug("Copied %s to local cache at %s", tempTarballPath.c_str(),
595 basename);
596 remove(tempTarballPath.c_str());
597 }
598 }
599 }
600
601 // Check the library compatibility of the subject plugin
602 // Find the plugin library file, looking for "_pi.{dll/so/dylib file}
603#ifdef __WXMSW__
604 wxString pispec = _T("_pi.dll");
605#elif defined(__WXOSX__)
606 wxString pispec = _T("_pi.dylib");
607#else
608 wxString pispec = _T("_pi.so");
609#endif
610
611 std::string manifestPath = PluginHandler::FileListPath(update.name);
612 wxTextFile manifest_file(manifestPath);
613 wxString pluginFile;
614 if (manifest_file.Open()) {
615 wxString val;
616 for (wxString str = manifest_file.GetFirstLine(); !manifest_file.Eof();
617 str = manifest_file.GetNextLine()) {
618 if (str.Contains(pispec)) {
619 if (getenv("OCPN_KEEP_PLUGINS")) {
620 // Undocumented debug hook
621 continue;
622 }
623 auto loader = PluginLoader::GetInstance();
624 if (!loader->CheckPluginCompatibility(str)) {
625 wxString msg =
626 _("The plugin is not compatible with this version of OpenCPN, "
627 "and will be uninstalled.");
628 OCPNMessageBox(NULL, msg, wxString(_("OpenCPN Info")),
629 wxICON_INFORMATION | wxOK, 10);
630
631 PluginHandler::CleanupFiles(manifestPath, update.name);
632 } else {
633 pluginFile = str;
634 }
635 break;
636 }
637 }
638 }
639
640 if (b_forceEnable && pluginFile.Length()) {
641 wxString config_section = (_T ( "/PlugIns/" ));
642 wxFileName fn(pluginFile);
643 config_section += fn.GetFullName();
644 pConfig->SetPath(config_section);
645 pConfig->Write(_T ( "bEnabled" ), true);
646 }
647
648 // This is installed from catalog, remove possible imported
649 // metadata leftovers
650 auto handler = PluginHandler::GetInstance();
651 std::remove(handler->ImportedMetadataPath(update.name).c_str());
652
653 // Reload all plugins, which will bring in the action results.
654 LoadAllPlugIns(false);
655
656 parent->ReloadPluginPanels();
657}
658
659static PlugIn_ViewPort CreatePlugInViewport(const ViewPort& vp) {
660 // Create a PlugIn Viewport
661 ViewPort tvp = vp;
662 PlugIn_ViewPort pivp;
663
664 pivp.clat = tvp.clat; // center point
665 pivp.clon = tvp.clon;
667 pivp.skew = tvp.skew;
668 pivp.rotation = tvp.rotation;
669 pivp.chart_scale = tvp.chart_scale;
670 pivp.pix_width = tvp.pix_width;
671 pivp.pix_height = tvp.pix_height;
672 pivp.rv_rect = tvp.rv_rect;
673 pivp.b_quilt = tvp.b_quilt;
674 pivp.m_projection_type = tvp.m_projection_type;
675
676 pivp.lat_min = tvp.GetBBox().GetMinLat();
677 pivp.lat_max = tvp.GetBBox().GetMaxLat();
678 pivp.lon_min = tvp.GetBBox().GetMinLon();
679 pivp.lon_max = tvp.GetBBox().GetMaxLon();
680
681 pivp.bValid = tvp.IsValid(); // This VP is valid
682
683 return pivp;
684}
685
686static ViewPort CreateCompatibleViewport(const PlugIn_ViewPort& pivp) {
687 // Create a system ViewPort
688 ViewPort vp;
689
690 vp.clat = pivp.clat; // center point
691 vp.clon = pivp.clon;
693 vp.skew = pivp.skew;
694 vp.rotation = pivp.rotation;
695 vp.chart_scale = pivp.chart_scale;
696 vp.pix_width = pivp.pix_width;
697 vp.pix_height = pivp.pix_height;
698 vp.rv_rect = pivp.rv_rect;
699 vp.b_quilt = pivp.b_quilt;
700 vp.m_projection_type = pivp.m_projection_type;
701
702 if (gFrame->GetPrimaryCanvas())
703 vp.ref_scale = gFrame->GetPrimaryCanvas()->GetVP().ref_scale;
704 else
705 vp.ref_scale = vp.chart_scale;
706
707 vp.SetBoxes();
708 vp.Validate(); // This VP is valid
709
710 return vp;
711}
712
713class pluginUtilHandler : public wxEvtHandler {
714public:
717
718 void OnPluginUtilAction(wxCommandEvent& event);
719
720 DECLARE_EVENT_TABLE()
721};
722
723BEGIN_EVENT_TABLE(pluginUtilHandler, wxEvtHandler)
724EVT_BUTTON(ID_CMD_BUTTON_PERFORM_ACTION, pluginUtilHandler::OnPluginUtilAction)
725END_EVENT_TABLE()
726
728
729void pluginUtilHandler::OnPluginUtilAction(wxCommandEvent& event) {
730 auto panel = static_cast<PluginPanel*>(event.GetClientData());
731 PluginListPanel* plugin_list_panel =
732 dynamic_cast<PluginListPanel*>(panel->GetParent());
733 wxASSERT(plugin_list_panel != 0);
734
735 auto actionPIC = panel->GetPlugin();
736 wxString name = actionPIC->m_common_name;
737
738 // Perform the indicated action according to the verb...
739 switch (panel->GetAction()) {
740 case ActionVerb::UPGRADE_TO_MANAGED_VERSION: {
741 auto loader = PluginLoader::GetInstance();
742
743 // capture the plugin name
744 std::string pluginName = actionPIC->m_managed_metadata.name;
745
746 wxLogMessage("Installing managed plugin: %s", pluginName.c_str());
747 auto downloader =
748 new GuiDownloader(plugin_list_panel, actionPIC->m_managed_metadata);
749 downloader->run(plugin_list_panel, false);
750
751 // Provisional error check
752 std::string manifestPath = PluginHandler::FileListPath(pluginName);
753 if (isRegularFile(manifestPath.c_str())) {
754 // dynamically deactivate the legacy plugin, making way for the upgrade.
755 for (unsigned i = 0; i < loader->GetPlugInArray()->GetCount(); i += 1) {
756 if (actionPIC->m_managed_metadata.name ==
757 loader->GetPlugInArray()->Item(i)->m_common_name.ToStdString()) {
758 loader->UnLoadPlugIn(i);
759 break;
760 }
761 }
762
763 // Reload all plugins, which will bring in the new, managed version.
764 LoadAllPlugIns(false);
765 } else {
766 PluginHandler::CleanupFiles(manifestPath,
767 actionPIC->m_managed_metadata.name);
768 }
769 plugin_list_panel->ReloadPluginPanels();
770 plugin_list_panel->SelectByName(name);
771
772 break;
773 }
774
775 case ActionVerb::UPDATE_IMPORTED_VERSION:
776 case ActionVerb::UPGRADE_INSTALLED_MANAGED_VERSION:
777 case ActionVerb::REINSTALL_MANAGED_VERSION:
778 case ActionVerb::DOWNGRADE_INSTALLED_MANAGED_VERSION: {
779 // Grab a copy of the managed metadata
780 auto metaSave = actionPIC->m_managed_metadata;
781 run_update_dialog(plugin_list_panel, actionPIC, true,
782 metaSave.name.c_str());
783 break;
784 }
785
786 case ActionVerb::INSTALL_MANAGED_VERSION: {
787 wxLogMessage("Installing new managed plugin.");
788 run_update_dialog(plugin_list_panel, actionPIC, false);
789 break;
790 }
791
792 case ActionVerb::UNINSTALL_MANAGED_VERSION: {
793 PluginLoader::GetInstance()->DeactivatePlugIn(*actionPIC);
794
795 // Capture the confirmation dialog contents before the plugin goes away
796 wxString message;
797 message.Printf("%s %s\n", actionPIC->m_managed_metadata.name.c_str(),
798 actionPIC->m_managed_metadata.version.c_str());
799 message += _("successfully un-installed");
800
801 wxLogMessage("Uninstalling %s",
802 actionPIC->m_managed_metadata.name.c_str());
803
805 actionPIC->m_managed_metadata.name);
806
807 // Reload all plugins, which will bring in the action results.
808 auto loader = PluginLoader::GetInstance();
809 LoadAllPlugIns(false);
810 plugin_list_panel->ReloadPluginPanels();
811
812 OCPNMessageBox(gFrame, message, _("Un-Installation complete"),
813 wxICON_INFORMATION | wxOK);
814
815 break;
816 }
817
818 case ActionVerb::NOP:
819 default:
820 break;
821 }
822}
823
824//------------------------------------------------------------------------------
825// NMEA Event Implementation
826// PlugIn Messaging scheme Event
827//------------------------------------------------------------------------------
828
829const wxEventType wxEVT_OCPN_MSG = wxNewEventType();
830
831OCPN_MsgEvent::OCPN_MsgEvent(wxEventType commandType, int id)
832 : wxEvent(id, commandType) {}
833
834OCPN_MsgEvent::~OCPN_MsgEvent() {}
835
836wxEvent* OCPN_MsgEvent::Clone() const {
837 OCPN_MsgEvent* newevent = new OCPN_MsgEvent(*this);
838 newevent->m_MessageID =
839 this->m_MessageID
840 .c_str(); // this enforces a deep copy of the string data
841 newevent->m_MessageText = this->m_MessageText.c_str();
842 return newevent;
843}
844
845//------------------------------------------------------------------------------------------------
846//
847// The PlugInToolbarToolContainer Implementation
848//
849//------------------------------------------------------------------------------------------------
850PlugInToolbarToolContainer::PlugInToolbarToolContainer() {
851 bitmap_dusk = NULL;
852 bitmap_night = NULL;
853 bitmap_day = NULL;
854 bitmap_Rollover_day = NULL;
855 bitmap_Rollover_dusk = NULL;
856 bitmap_Rollover_night = NULL;
857}
858
859PlugInToolbarToolContainer::~PlugInToolbarToolContainer() {
860 delete bitmap_dusk;
861 delete bitmap_night;
862 delete bitmap_day;
863 delete bitmap_Rollover_day;
864 delete bitmap_Rollover_dusk;
865 delete bitmap_Rollover_night;
866}
867
868//-----------------------------------------------------------------------------------------------------
869//
870// The PlugIn Manager Implementation
871//
872//-----------------------------------------------------------------------------------------------------
873PlugInManager* s_ppim;
874
875BEGIN_EVENT_TABLE(PlugInManager, wxEvtHandler)
876#if !defined(__ANDROID__) && defined(OCPN_USE_CURL)
877EVT_CURL_END_PERFORM(CurlThreadId, PlugInManager::OnEndPerformCurlDownload)
878EVT_CURL_DOWNLOAD(CurlThreadId, PlugInManager::OnCurlDownload)
879#endif
880END_EVENT_TABLE()
881
882static void event_message_box(const wxString& msg) {
883 OCPNMessageBox(NULL, msg, wxString(_("OpenCPN Info")),
884 wxICON_INFORMATION | wxOK, 0); // no timeout
885}
886
887static void OnLoadPlugin(const PlugInContainer* pic) {
888 if (g_options) {
889 if ((pic->m_cap_flag & INSTALLS_TOOLBOX_PAGE)) {
890 if (!pic->m_toolbox_panel) NotifySetupOptionsPlugin(pic);
891 }
892 }
893}
894
895PlugInManager::PlugInManager(MyFrame* parent) {
896#if !defined(__ANDROID__) && defined(OCPN_USE_CURL)
897 m_pCurlThread = NULL;
898 m_pCurl = 0;
899#endif
900 pParent = parent;
901 s_ppim = this;
902
903 MyFrame* pFrame = GetParentFrame();
904 if (pFrame) {
905 m_plugin_menu_item_id_next = CanvasMenuHandler::GetNextContextMenuId();
906 m_plugin_tool_id_next = pFrame->GetNextToolbarToolId();
907 }
908
909#ifdef __ANDROID__
910 // Due to the oddball mixed static/dynamic linking model used in the Android
911 // architecture, all classes used in PlugIns must be present in the core,
912 // even if stubs.
913 //
914 // Here is where we do that....
915 if (pFrame) {
916 wxArrayString as;
917 as.Add(_T("Item0"));
918 wxRadioBox* box =
919 new wxRadioBox(pFrame, -1, _T(""), wxPoint(0, 0), wxSize(-1, -1), as);
920 delete box;
921 }
922
923#endif
924
925#if !defined(__ANDROID__) && defined(OCPN_USE_CURL)
926 wxCurlBase::Init();
927 m_last_online = false;
928 m_last_online_chk = -1;
929#endif
930
931 m_utilHandler = new pluginUtilHandler();
932 m_listPanel = NULL;
933 m_blacklist = blacklist_factory();
934 m_blacklist_ui = std::unique_ptr<BlacklistUI>(new BlacklistUI());
935
936 wxDEFINE_EVENT(EVT_JSON_TO_ALL_PLUGINS, ObservedEvt);
937 evt_json_to_all_plugins_listener.Listen(g_pRouteMan->json_msg, this,
938 EVT_JSON_TO_ALL_PLUGINS);
939 Bind(EVT_JSON_TO_ALL_PLUGINS, [&](ObservedEvt& ev) {
940 auto json = std::static_pointer_cast<const wxJSONValue>(ev.GetSharedPtr());
941 SendJSONMessageToAllPlugins(ev.GetString(), *json);
942 });
943
944 wxDEFINE_EVENT(EVT_LEGINFO_TO_ALL_PLUGINS, ObservedEvt);
945 evt_routeman_leginfo_listener.Listen(g_pRouteMan->json_leg_info, this,
946 EVT_LEGINFO_TO_ALL_PLUGINS);
947 Bind(EVT_LEGINFO_TO_ALL_PLUGINS, [&](ObservedEvt& ev) {
948 auto ptr = UnpackEvtPointer<ActiveLegDat>(ev);
949 SendActiveLegInfoToAllPlugIns(ptr.get());
950 });
951
952 HandlePluginLoaderEvents();
953 InitCommListeners();
954 auto msg_sent_action = [](ObservedEvt ev) {
955 SendNMEASentenceToAllPlugIns(ev.GetString());
956 };
957 m_on_msg_sent_listener.Init(g_pRouteMan->on_message_sent, msg_sent_action);
958 m_new_msgtype_lstnr.Init(NavMsgBus::GetInstance().new_msg_event,
959 [&](ObservedEvt&) { OnNewMessageType(); });
960}
961PlugInManager::~PlugInManager() {
962#if !defined(__ANDROID__) && defined(OCPN_USE_CURL)
963 wxCurlBase::Shutdown();
964#endif
965 delete m_utilHandler;
966}
967
968void PlugInManager::InitCommListeners(void) {
969 // Initialize the comm listener to support
970 // void SetNMEASentence(wxString &sentence);
971
972 auto& msgbus = NavMsgBus::GetInstance();
973
974 SignalkMsg sk_msg;
975 m_listener_SignalK.Listen(sk_msg, this, EVT_SIGNALK);
976
977 Bind(EVT_SIGNALK, [&](ObservedEvt ev) {
978 HandleSignalK(UnpackEvtPointer<SignalkMsg>(ev));
979 });
980}
981
982void PlugInManager::OnNewMessageType() {
983 for (auto msg_key : NavMsgBus::GetInstance().GetActiveMessages()) {
984 if (m_0183_listeners.find(msg_key) != m_0183_listeners.end()) continue;
985 if (msg_key.find("::") == std::string::npos) continue;
986 auto key_parts = ocpn::split(msg_key, "::");
987 if (key_parts.size() < 2) continue;
988 if (NavMsg::GetBusByKey(msg_key) == NavAddr::Bus::N0183) {
989 ObsListener ol(RawKey(key_parts[1]), [&](ObservedEvt& ev) {
990 HandleN0183(UnpackEvtPointer<Nmea0183Msg>(ev));
991 });
992 m_0183_listeners[msg_key] = std::move(ol);
993 }
994 }
995}
996void PlugInManager::HandleN0183(std::shared_ptr<const Nmea0183Msg> n0183_msg) {
997 assert(n0183_msg->bus == NavAddr::Bus::N0183);
998 const std::string& payload = n0183_msg->payload;
999
1000 if (payload[0] == '$') {
1001 const auto& drivers = CommDriverRegistry::GetInstance().GetDrivers();
1002 auto& target_driver = FindDriver(drivers, n0183_msg->source->iface);
1003
1004 // Get the params for the driver sending this message, check if it
1005 // passes the input filter
1006 bool passes_input_filter = true;
1007 auto drv_n0183 = dynamic_cast<CommDriverN0183*>(target_driver.get());
1008 if (drv_n0183) {
1009 ConnectionParams params = drv_n0183->GetParams();
1010 passes_input_filter =
1011 params.SentencePassesFilter(payload.c_str(), FILTER_INPUT);
1012 }
1013 if (passes_input_filter) SendNMEASentenceToAllPlugIns(payload.c_str());
1014 } else if (payload[0] == '!') {
1015 SendAISSentenceToAllPlugIns(payload.c_str());
1016 }
1017}
1018
1019void PlugInManager::HandleSignalK(std::shared_ptr<const SignalkMsg> sK_msg) {
1020 g_ownshipMMSI_SK = sK_msg->context_self;
1021
1022 wxJSONReader jsonReader;
1023 wxJSONValue root;
1024
1025 std::string msgTerminated = sK_msg->raw_message;
1026 ;
1027
1028 int errors = jsonReader.Parse(msgTerminated, &root);
1029 if (errors == 0) SendJSONMessageToAllPlugins(wxT("OCPN_CORE_SIGNALK"), root);
1030}
1031
1037wxDEFINE_EVENT(EVT_PLUGMGR_AIS_MSG, ObservedEvt);
1038wxDEFINE_EVENT(EVT_PLUGMGR_ROUTEMAN_MSG, ObservedEvt);
1039wxDEFINE_EVENT(EVT_BLACKLISTED_PLUGIN, wxCommandEvent);
1040wxDEFINE_EVENT(EVT_LOAD_DIRECTORY, wxCommandEvent);
1041wxDEFINE_EVENT(EVT_LOAD_PLUGIN, wxCommandEvent);
1042wxDEFINE_EVENT(EVT_PLUGIN_UNLOAD, wxCommandEvent);
1043wxDEFINE_EVENT(EVT_PLUGLIST_CHANGE, wxCommandEvent);
1044wxDEFINE_EVENT(EVT_UPDATE_CHART_TYPES, wxCommandEvent);
1045wxDEFINE_EVENT(EVT_PLUGIN_LOADALL_FINALIZE, wxCommandEvent);
1046
1047void PlugInManager::HandlePluginLoaderEvents() {
1048 auto loader = PluginLoader::GetInstance();
1049
1050 loader->SetOnDeactivateCb(
1051 [&](const PlugInContainer* pic) { OnPluginDeactivate(pic); });
1052 evt_pluglist_change_listener.Listen(loader->evt_pluglist_change, this,
1053 EVT_PLUGLIST_CHANGE);
1054 Bind(EVT_PLUGLIST_CHANGE, [&](wxCommandEvent&) {
1055 if (m_listPanel) m_listPanel->ReloadPluginPanels();
1056 if (g_options) g_options->itemBoxSizerPanelPlugins->Layout();
1057 });
1058
1059 evt_load_directory_listener.Listen(loader->evt_load_directory, this,
1060 EVT_LOAD_DIRECTORY);
1061 Bind(EVT_LOAD_DIRECTORY, [&](wxCommandEvent&) {
1062 pConfig->SetPath("/PlugIns/");
1063 SetPluginOrder(pConfig->Read("PluginOrder", wxEmptyString));
1064 });
1065
1066 evt_load_plugin_listener.Listen(loader->evt_load_plugin, this,
1067 EVT_LOAD_PLUGIN);
1068 Bind(EVT_LOAD_PLUGIN, [&](wxCommandEvent& ev) {
1069 auto pic = static_cast<const PlugInContainer*>(ev.GetClientData());
1070 OnLoadPlugin(pic);
1071 });
1072
1073 evt_update_chart_types_listener.Listen(loader->evt_update_chart_types, this,
1074 EVT_UPDATE_CHART_TYPES);
1075 Bind(EVT_UPDATE_CHART_TYPES,
1076 [&](wxCommandEvent& ev) { UpDateChartDataTypes(); });
1077
1078 evt_plugin_loadall_finalize_listener.Listen(
1079 loader->evt_plugin_loadall_finalize, this, EVT_PLUGIN_LOADALL_FINALIZE);
1080 Bind(EVT_PLUGIN_LOADALL_FINALIZE,
1081 [&](wxCommandEvent& ev) { FinalizePluginLoadall(); });
1082
1083 evt_ais_json_listener.Listen(g_pAIS->plugin_msg, this, EVT_PLUGMGR_AIS_MSG);
1084 evt_routeman_json_listener.Listen(g_pRouteMan->json_msg, this,
1085 EVT_PLUGMGR_ROUTEMAN_MSG);
1086 Bind(EVT_PLUGMGR_AIS_MSG, [&](ObservedEvt& ev) {
1087 auto pTarget = UnpackEvtPointer<AisTargetData>(ev);
1088 SendAisJsonMessage(pTarget);
1089 });
1090 Bind(EVT_PLUGMGR_ROUTEMAN_MSG, [&](ObservedEvt& ev) {
1091 auto msg = UnpackEvtPointer<wxJSONValue>(ev);
1092 SendJSONMessageToAllPlugins(ev.GetString(), *msg);
1093 });
1094}
1095
1100wxDEFINE_EVENT(EVT_DOWNLOAD_FAILED, wxCommandEvent);
1101wxDEFINE_EVENT(EVT_DOWNLOAD_OK, wxCommandEvent);
1102
1103void PlugInManager::HandlePluginHandlerEvents() {
1104 auto loader = PluginLoader::GetInstance();
1105
1106 evt_download_failed_listener.Listen(loader->evt_update_chart_types, this,
1107 EVT_DOWNLOAD_FAILED);
1108 Bind(EVT_DOWNLOAD_FAILED, [&](wxCommandEvent& ev) {
1109 wxString message = _("Please check system log for more info.");
1110 OCPNMessageBox(gFrame, message, _("Installation error"),
1111 wxICON_ERROR | wxOK | wxCENTRE);
1112 });
1113
1114 evt_download_ok_listener.Listen(loader->evt_update_chart_types, this,
1115 EVT_DOWNLOAD_OK);
1116 Bind(EVT_DOWNLOAD_OK, [&](wxCommandEvent& ev) {
1117 wxString message(ev.GetString());
1118 message += _(" successfully installed from cache");
1119 OCPNMessageBox(gFrame, message, _("Installation complete"),
1120 wxICON_INFORMATION | wxOK | wxCENTRE);
1121 });
1122}
1123
1124bool PlugInManager::CallLateInit(void) {
1125 bool bret = true;
1126
1127 auto plugin_array = PluginLoader::GetInstance()->GetPlugInArray();
1128 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
1129 PlugInContainer* pic = (*plugin_array)[i];
1130
1131 switch (pic->m_api_version) {
1132 case 110:
1133 case 111:
1134 case 112:
1135 case 113:
1136 case 114:
1137 case 115:
1138 case 116:
1139 case 117:
1140 case 118:
1141 case 119:
1142 case 120:
1143 case 121:
1144 ProcessLateInit(pic);
1145 break;
1146 }
1147 }
1148
1149 return bret;
1150}
1151
1152void PlugInManager::ProcessLateInit(const PlugInContainer* pic) {
1153 if (pic->m_cap_flag & WANTS_LATE_INIT) {
1154 wxString msg("PlugInManager: Calling LateInit PlugIn: ");
1155 msg += pic->m_plugin_file;
1156 wxLogMessage(msg);
1157
1158 opencpn_plugin_110* ppi = dynamic_cast<opencpn_plugin_110*>(pic->m_pplugin);
1159 if (ppi) ppi->LateInit();
1160 }
1161}
1162
1163void PlugInManager::OnPluginDeactivate(const PlugInContainer* pic) {
1164 // Unload chart cache if this plugin is responsible for any charts
1165 if ((pic->m_cap_flag & INSTALLS_PLUGIN_CHART) ||
1167 ChartData->PurgeCachePlugins();
1168 gFrame->InvalidateAllQuilts();
1169 }
1170 // Deactivate (Remove) any ToolbarTools added by this PlugIn
1171 for (unsigned int i = 0; i < m_PlugInToolbarTools.GetCount(); i++) {
1172 PlugInToolbarToolContainer* pttc = m_PlugInToolbarTools[i];
1173
1174 if (pttc->m_pplugin == pic->m_pplugin) {
1175 m_PlugInToolbarTools.Remove(pttc);
1176 delete pttc;
1177 }
1178 }
1179
1180 // Deactivate (Remove) any ContextMenu items addded by this PlugIn
1181 for (unsigned int i = 0; i < m_PlugInMenuItems.GetCount(); i++) {
1182 PlugInMenuItemContainer* pimis = m_PlugInMenuItems[i];
1183 if (pimis->m_pplugin == pic->m_pplugin) {
1184 m_PlugInMenuItems.Remove(pimis);
1185 delete pimis;
1186 }
1187 }
1188}
1189
1190bool PlugInManager::IsAnyPlugInChartEnabled() {
1191 // Is there a PlugIn installed and active that implements PlugIn Chart
1192 // type(s)?
1193 auto plugin_array = PluginLoader::GetInstance()->GetPlugInArray();
1194 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
1195 PlugInContainer* pic = (*plugin_array)[i];
1196 if (pic->m_enabled && pic->m_init_state) {
1197 if ((pic->m_cap_flag & INSTALLS_PLUGIN_CHART) ||
1199 return true;
1200 }
1201 }
1202 return false;
1203}
1204
1205void PlugInManager::UpdateManagedPlugins() {
1206 PluginLoader::GetInstance()->UpdateManagedPlugins(false);
1207 PluginLoader::GetInstance()->SortPlugins(ComparePlugins);
1208
1209 if (m_listPanel) m_listPanel->ReloadPluginPanels();
1210 g_options->itemBoxSizerPanelPlugins->Layout();
1211}
1212
1213bool PlugInManager::UpDateChartDataTypes() {
1214 bool bret = false;
1215 if (NULL == ChartData) return bret;
1216
1217 auto plugin_array = PluginLoader::GetInstance()->GetPlugInArray();
1218 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
1219 PlugInContainer* pic = plugin_array->Item(i);
1220
1221 if (pic->m_init_state) {
1222 if ((pic->m_cap_flag & INSTALLS_PLUGIN_CHART) ||
1224 bret = true;
1225 }
1226 }
1227
1228 if (bret) ChartData->UpdateChartClassDescriptorArray();
1229
1230 return bret;
1231}
1232
1233void PlugInManager::FinalizePluginLoadall() {
1234 // FIXME
1235 // Maybe this does not need to be done for CLI instance?
1236 // Inform plugins of the current color scheme
1237 SetColorSchemeForAllPlugIns(global_color_scheme);
1238
1239 // Tell all the PlugIns about the current OCPN configuration
1240 SendBaseConfigToAllPlugIns();
1241 SendS52ConfigToAllPlugIns(true);
1242 SendSKConfigToAllPlugIns();
1243
1244 // Inform Plugins of OpenGL configuration, if enabled
1245#ifdef ocpnUSE_GL
1246 if (g_bopengl) {
1247 if (gFrame->GetPrimaryCanvas()->GetglCanvas())
1248 gFrame->GetPrimaryCanvas()->GetglCanvas()->SendJSONConfigMessage();
1249 }
1250#endif
1251
1252 // And then reload all catalogs.
1253 ReloadLocale();
1254}
1255
1256void PlugInManager::SetPluginOrder(wxString serialized_names) {
1257 m_plugin_order.Empty();
1258 wxStringTokenizer tokenizer(serialized_names, ";");
1259 while (tokenizer.HasMoreTokens()) {
1260 m_plugin_order.Add(tokenizer.GetNextToken());
1261 }
1262}
1263
1264wxString PlugInManager::GetPluginOrder() {
1265 wxString plugins = wxEmptyString;
1266 auto plugin_array = PluginLoader::GetInstance()->GetPlugInArray();
1267 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
1268 plugins.Append(plugin_array->Item(i)->m_common_name);
1269 if (i < plugin_array->GetCount() - 1) plugins.Append(';');
1270 }
1271 return plugins;
1272}
1273
1274bool PlugInManager::UpdateConfig() {
1275 // pConfig->SetPath( _T("/PlugIns/") );
1276 // pConfig->Write( _T("PluginOrder"), GetPluginOrder() );
1277
1278 auto plugin_array = PluginLoader::GetInstance()->GetPlugInArray();
1279 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
1280 PlugInContainer* pic = plugin_array->Item(i);
1281
1282 if (pic) {
1283 wxString config_section = (_T ( "/PlugIns/" ));
1284 config_section += pic->m_plugin_filename;
1285 pConfig->SetPath(config_section);
1286 pConfig->Write(_T ( "bEnabled" ), pic->m_enabled);
1287 }
1288 }
1289
1290 return true;
1291}
1292
1293void PlugInManager::ShowDeferredBlacklistMessages() {
1294 m_blacklist_ui->show_deferred_messages();
1295}
1296
1297bool PlugInManager::CheckBlacklistedPlugin(const PluginMetadata plugin) {
1298 auto v = SemanticVersion::parse(plugin.version);
1299 return CheckBlacklistedPlugin(wxString(plugin.name), v.major, v.minor);
1300}
1301
1302bool PlugInManager::CheckBlacklistedPlugin(opencpn_plugin* plugin) {
1303 int major = plugin->GetPlugInVersionMajor();
1304 int minor = plugin->GetPlugInVersionMinor();
1305
1306#ifdef __WXMSW__
1307 wxString name = wxString::FromAscii(typeid(*plugin).name());
1308 name.Replace("class ", wxEmptyString);
1309#else
1310 const std::type_info& ti = typeid(*plugin);
1311 int status;
1312 char* realname = abi::__cxa_demangle(ti.name(), 0, 0, &status);
1313 wxString name = wxString::FromAscii(realname);
1314 free(realname);
1315#endif // __WXMSW__
1316 return CheckBlacklistedPlugin(name, major, minor);
1317}
1318
1319bool PlugInManager::CheckBlacklistedPlugin(wxString name, int major,
1320 int minor) {
1321 auto block_status = m_blacklist->get_status(name.ToStdString(), major, minor);
1322 if (block_status == plug_status::unblocked) return true;
1323 plug_data data(name.ToStdString(), major, minor);
1324 auto msg = m_blacklist->get_message(block_status, data);
1325 m_blacklist_ui->message(msg);
1326 return false;
1327}
1328
1329bool PlugInManager::RenderAllCanvasOverlayPlugIns(ocpnDC& dc,
1330 const ViewPort& vp,
1331 int canvasIndex,
1332 int priority) {
1333 auto plugin_array = PluginLoader::GetInstance()->GetPlugInArray();
1334 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
1335 PlugInContainer* pic = plugin_array->Item(i);
1336 if (pic->m_enabled && pic->m_init_state) {
1338 PlugIn_ViewPort pivp = CreatePlugInViewport(vp);
1339
1340 wxDC* pdc = dc.GetDC();
1341 if (pdc) // not in OpenGL mode
1342 {
1343 switch (pic->m_api_version) {
1344 case 106: {
1345 if (priority > 0) break;
1346 opencpn_plugin_16* ppi =
1347 dynamic_cast<opencpn_plugin_16*>(pic->m_pplugin);
1348 if (ppi) ppi->RenderOverlay(*pdc, &pivp);
1349 break;
1350 }
1351 case 107: {
1352 if (priority > 0) break;
1353 opencpn_plugin_17* ppi =
1354 dynamic_cast<opencpn_plugin_17*>(pic->m_pplugin);
1355 if (ppi) ppi->RenderOverlay(*pdc, &pivp);
1356 break;
1357 }
1358 case 108:
1359 case 109:
1360 case 110:
1361 case 111:
1362 case 112:
1363 case 113:
1364 case 114:
1365 case 115: {
1366 if (priority > 0) break;
1367 opencpn_plugin_18* ppi =
1368 dynamic_cast<opencpn_plugin_18*>(pic->m_pplugin);
1369 if (ppi) ppi->RenderOverlay(*pdc, &pivp);
1370 break;
1371 }
1372 case 116:
1373 case 117: {
1374 if (priority > 0) break;
1375 opencpn_plugin_18* ppi =
1376 dynamic_cast<opencpn_plugin_18*>(pic->m_pplugin);
1377 if (ppi) {
1378 ppi->RenderOverlay(*pdc, &pivp);
1379 }
1380 opencpn_plugin_116* ppi116 =
1381 dynamic_cast<opencpn_plugin_116*>(pic->m_pplugin);
1382 if (ppi116)
1383 ppi116->RenderOverlayMultiCanvas(*pdc, &pivp, canvasIndex);
1384 break;
1385 }
1386 case 118:
1387 case 119:
1388 case 120:
1389 case 121: {
1390 if (priority <= 0) {
1391 opencpn_plugin_18* ppi =
1392 dynamic_cast<opencpn_plugin_18*>(pic->m_pplugin);
1393 if (ppi) {
1394 ppi->RenderOverlay(*pdc, &pivp);
1395 }
1396 }
1397 opencpn_plugin_118* ppi118 =
1398 dynamic_cast<opencpn_plugin_118*>(pic->m_pplugin);
1399 if (ppi118)
1400 ppi118->RenderOverlayMultiCanvas(*pdc, &pivp, canvasIndex,
1401 priority);
1402 break;
1403 }
1404 default:
1405 break;
1406 }
1407 } else {
1408 // If in OpenGL mode, and the PlugIn has requested OpenGL render
1409 // callbacks, then there is no need to render by wxDC here.
1410 if (pic->m_cap_flag & WANTS_OPENGL_OVERLAY_CALLBACK) continue;
1411
1412 if ((m_cached_overlay_bm.GetWidth() != vp.pix_width) ||
1413 (m_cached_overlay_bm.GetHeight() != vp.pix_height))
1414 m_cached_overlay_bm.Create(vp.pix_width, vp.pix_height, -1);
1415
1416 wxMemoryDC mdc;
1417 mdc.SelectObject(m_cached_overlay_bm);
1418 mdc.SetBackground(*wxBLACK_BRUSH);
1419 mdc.Clear();
1420
1421 bool b_rendered = false;
1422
1423 switch (pic->m_api_version) {
1424 case 106: {
1425 if (priority > 0) break;
1426 opencpn_plugin_16* ppi =
1427 dynamic_cast<opencpn_plugin_16*>(pic->m_pplugin);
1428 if (ppi) b_rendered = ppi->RenderOverlay(mdc, &pivp);
1429 break;
1430 }
1431 case 107: {
1432 if (priority > 0) break;
1433 opencpn_plugin_17* ppi =
1434 dynamic_cast<opencpn_plugin_17*>(pic->m_pplugin);
1435 if (ppi) b_rendered = ppi->RenderOverlay(mdc, &pivp);
1436 break;
1437 }
1438 case 108:
1439 case 109:
1440 case 110:
1441 case 111:
1442 case 112:
1443 case 113:
1444 case 114:
1445 case 115: {
1446 if (priority > 0) break;
1447 opencpn_plugin_18* ppi =
1448 dynamic_cast<opencpn_plugin_18*>(pic->m_pplugin);
1449 if (ppi) b_rendered = ppi->RenderOverlay(mdc, &pivp);
1450 break;
1451 }
1452 case 116:
1453 case 117: {
1454 if (priority > 0) break;
1455 opencpn_plugin_18* ppi =
1456 dynamic_cast<opencpn_plugin_18*>(pic->m_pplugin);
1457 if (ppi) {
1458 b_rendered = ppi->RenderOverlay(mdc, &pivp);
1459 }
1460 opencpn_plugin_116* ppi116 =
1461 dynamic_cast<opencpn_plugin_116*>(pic->m_pplugin);
1462 if (ppi116)
1463 b_rendered = ppi116->RenderOverlayMultiCanvas(mdc, &pivp,
1464 g_canvasConfig);
1465 break;
1466 }
1467 case 118:
1468 case 119:
1469 case 120:
1470 case 121: {
1471 if (priority <= 0) {
1472 opencpn_plugin_18* ppi =
1473 dynamic_cast<opencpn_plugin_18*>(pic->m_pplugin);
1474 if (ppi) {
1475 b_rendered = ppi->RenderOverlay(mdc, &pivp);
1476 }
1477 }
1478 opencpn_plugin_118* ppi118 =
1479 dynamic_cast<opencpn_plugin_118*>(pic->m_pplugin);
1480 if (ppi118)
1481 b_rendered = ppi118->RenderOverlayMultiCanvas(
1482 mdc, &pivp, g_canvasConfig, priority);
1483 break;
1484 }
1485 default: {
1486 b_rendered = pic->m_pplugin->RenderOverlay(&mdc, &pivp);
1487 break;
1488 }
1489 }
1490
1491 mdc.SelectObject(wxNullBitmap);
1492
1493 if (b_rendered) {
1494 wxMask* p_msk = new wxMask(m_cached_overlay_bm, wxColour(0, 0, 0));
1495 m_cached_overlay_bm.SetMask(p_msk);
1496
1497 dc.DrawBitmap(m_cached_overlay_bm, 0, 0, true);
1498 }
1499 }
1500 } else if (pic->m_cap_flag & WANTS_OPENGL_OVERLAY_CALLBACK) {
1501 }
1502 }
1503 }
1504
1505 return true;
1506}
1507
1508bool PlugInManager::RenderAllGLCanvasOverlayPlugIns(wxGLContext* pcontext,
1509 const ViewPort& vp,
1510 int canvasIndex,
1511 int priority) {
1512 auto plugin_array = PluginLoader::GetInstance()->GetPlugInArray();
1513 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
1514 PlugInContainer* pic = plugin_array->Item(i);
1515 if (pic->m_enabled && pic->m_init_state) {
1516 if (pic->m_cap_flag & WANTS_OPENGL_OVERLAY_CALLBACK) {
1517 PlugIn_ViewPort pivp = CreatePlugInViewport(vp);
1518
1519 switch (pic->m_api_version) {
1520 case 107: {
1521 if (priority > 0) break;
1522 opencpn_plugin_17* ppi =
1523 dynamic_cast<opencpn_plugin_17*>(pic->m_pplugin);
1524 if (ppi) ppi->RenderGLOverlay(pcontext, &pivp);
1525 break;
1526 }
1527
1528 case 108:
1529 case 109:
1530 case 110:
1531 case 111:
1532 case 112:
1533 case 113:
1534 case 114:
1535 case 115: {
1536 if (priority > 0) break;
1537 opencpn_plugin_18* ppi =
1538 dynamic_cast<opencpn_plugin_18*>(pic->m_pplugin);
1539 if (ppi) ppi->RenderGLOverlay(pcontext, &pivp);
1540 break;
1541 }
1542 case 116:
1543 case 117: {
1544 if (priority > 0) break;
1545 opencpn_plugin_18* ppi =
1546 dynamic_cast<opencpn_plugin_18*>(pic->m_pplugin);
1547 if (ppi) {
1548 ppi->RenderGLOverlay(pcontext, &pivp);
1549 }
1550 opencpn_plugin_116* ppi116 =
1551 dynamic_cast<opencpn_plugin_116*>(pic->m_pplugin);
1552 if (ppi116) {
1553 ppi116->RenderGLOverlayMultiCanvas(pcontext, &pivp, canvasIndex);
1554 }
1555 break;
1556 }
1557 case 118:
1558 case 119:
1559 case 120:
1560 case 121: {
1561 if (priority <= 0) {
1562 opencpn_plugin_18* ppi =
1563 dynamic_cast<opencpn_plugin_18*>(pic->m_pplugin);
1564 if (ppi) {
1565 ppi->RenderGLOverlay(pcontext, &pivp);
1566 }
1567 }
1568 opencpn_plugin_118* ppi118 =
1569 dynamic_cast<opencpn_plugin_118*>(pic->m_pplugin);
1570 if (ppi118) {
1571 ppi118->RenderGLOverlayMultiCanvas(pcontext, &pivp, canvasIndex,
1572 priority);
1573 }
1574 break;
1575 }
1576 default:
1577 break;
1578 }
1579 }
1580 }
1581 }
1582
1583 return true;
1584}
1585
1586void PlugInManager::SendViewPortToRequestingPlugIns(ViewPort& vp) {
1587 auto plugin_array = PluginLoader::GetInstance()->GetPlugInArray();
1588 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
1589 PlugInContainer* pic = plugin_array->Item(i);
1590 if (pic->m_enabled && pic->m_init_state) {
1592 PlugIn_ViewPort pivp = CreatePlugInViewport(vp);
1593 if (pic->m_pplugin) pic->m_pplugin->SetCurrentViewPort(pivp);
1594 }
1595 }
1596 }
1597}
1598
1599void NotifySetupOptionsPlugin(const PlugInData* pd) {
1600 PluginLoader::GetInstance()->NotifySetupOptionsPlugin(pd);
1601}
1602
1603void PlugInManager::NotifySetupOptions() {
1604 auto plugin_array = PluginLoader::GetInstance()->GetPlugInArray();
1605 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
1606 PlugInContainer* pic = plugin_array->Item(i);
1607 NotifySetupOptionsPlugin(pic);
1608 }
1609}
1610
1611void PlugInManager::ClosePlugInPanel(const PlugInContainer* pic,
1612 int ok_apply_cancel) {
1613 if (pic->m_enabled && pic->m_init_state) {
1614 if ((pic->m_cap_flag & INSTALLS_TOOLBOX_PAGE) && pic->m_toolbox_panel) {
1615 pic->m_pplugin->OnCloseToolboxPanel(0, ok_apply_cancel);
1616 auto loader = PluginLoader::GetInstance();
1617 loader->SetToolboxPanel(pic->m_common_name, false);
1618 loader->SetSetupOptions(pic->m_common_name, false);
1619 }
1620 }
1621}
1622
1623void PlugInManager::CloseAllPlugInPanels(int ok_apply_cancel) {
1624 auto plugin_array = PluginLoader::GetInstance()->GetPlugInArray();
1625 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
1626 PlugInContainer* pic = plugin_array->Item(i);
1627 if (pic) {
1628 ClosePlugInPanel(pic, ok_apply_cancel);
1629 }
1630 }
1631}
1632
1633int PlugInManager::AddCanvasContextMenuItemPIM(wxMenuItem* pitem,
1634 opencpn_plugin* pplugin,
1635 const char* name,
1636 bool is_extended) {
1638 pmic->pmenu_item = pitem;
1639 pmic->m_pplugin = pplugin;
1640 pmic->id = pitem->GetId() == wxID_SEPARATOR ? wxID_SEPARATOR
1641 : m_plugin_menu_item_id_next;
1642 pmic->b_viz = true;
1643 pmic->b_grey = false;
1644 pmic->m_in_menu = name;
1645 pmic->extended = is_extended;
1646
1647 m_PlugInMenuItems.Add(pmic);
1648
1649 m_plugin_menu_item_id_next++;
1650
1651 return pmic->id;
1652}
1653
1654void PlugInManager::RemoveCanvasContextMenuItem(int item, const char* name) {
1655 for (unsigned int i = 0; i < m_PlugInMenuItems.GetCount(); i++) {
1656 PlugInMenuItemContainer* pimis = m_PlugInMenuItems[i];
1657 {
1658 if (pimis->id == item) {
1659 m_PlugInMenuItems.Remove(pimis);
1660 delete pimis;
1661 break;
1662 }
1663 }
1664 }
1665}
1666
1667void PlugInManager::SetCanvasContextMenuItemViz(int item, bool viz,
1668 const char* name) {
1669 for (unsigned int i = 0; i < m_PlugInMenuItems.GetCount(); i++) {
1670 PlugInMenuItemContainer* pimis = m_PlugInMenuItems[i];
1671 {
1672 if (pimis->id == item && !strcmp(name, pimis->m_in_menu)) {
1673 pimis->b_viz = viz;
1674 break;
1675 }
1676 }
1677 }
1678}
1679
1680void PlugInManager::SetCanvasContextMenuItemGrey(int item, bool grey,
1681 const char* name) {
1682 for (unsigned int i = 0; i < m_PlugInMenuItems.GetCount(); i++) {
1683 PlugInMenuItemContainer* pimis = m_PlugInMenuItems[i];
1684 {
1685 if (pimis->id == item && !strcmp(name, pimis->m_in_menu)) {
1686 pimis->b_grey = grey;
1687 break;
1688 }
1689 }
1690 }
1691}
1692
1693void PlugInManager::SendResizeEventToAllPlugIns(int x, int y) {
1694 auto plugin_array = PluginLoader::GetInstance()->GetPlugInArray();
1695 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
1696 PlugInContainer* pic = plugin_array->Item(i);
1697 if (pic->m_enabled && pic->m_init_state)
1698 pic->m_pplugin->ProcessParentResize(x, y);
1699 }
1700}
1701
1702void PlugInManager::SetColorSchemeForAllPlugIns(ColorScheme cs) {
1703 auto plugin_array = PluginLoader::GetInstance()->GetPlugInArray();
1704 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
1705 PlugInContainer* pic = plugin_array->Item(i);
1706 if (pic->m_enabled && pic->m_init_state)
1707 pic->m_pplugin->SetColorScheme((PI_ColorScheme)cs);
1708 }
1709}
1710
1711void PlugInManager::PrepareAllPluginContextMenus() {
1712 int canvasIndex = gFrame->GetCanvasIndexUnderMouse();
1713 if (canvasIndex < 0) return;
1714
1715 auto plugin_array = PluginLoader::GetInstance()->GetPlugInArray();
1716 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
1717 PlugInContainer* pic = plugin_array->Item(i);
1718 if (pic->m_enabled && pic->m_init_state) {
1720 switch (pic->m_api_version) {
1721 case 116:
1722 case 117:
1723 case 118:
1724 case 119:
1725 case 120:
1726 case 121: {
1727 opencpn_plugin_116* ppi =
1728 dynamic_cast<opencpn_plugin_116*>(pic->m_pplugin);
1729 if (ppi) ppi->PrepareContextMenu(canvasIndex);
1730 break;
1731 }
1732 default:
1733 break;
1734 }
1735 }
1736 }
1737 }
1738}
1739
1740void PlugInManager::SendSKConfigToAllPlugIns() {
1741 // Send the current ownship MMSI, encoded as sK, to all PlugIns
1742 wxJSONValue v;
1743 v[_T("self")] = g_ownshipMMSI_SK;
1744 wxJSONWriter w;
1745 wxString out;
1746 w.Write(v, out);
1747 SendMessageToAllPlugins(wxString(_T("OCPN_CORE_SIGNALK")), out);
1748}
1749
1750void PlugInManager::SendBaseConfigToAllPlugIns() {
1751 // Send the current run-time configuration to all PlugIns
1752 wxJSONValue v;
1753 v[_T("OpenCPN Version Major")] = VERSION_MAJOR;
1754 v[_T("OpenCPN Version Minor")] = VERSION_MINOR;
1755 v[_T("OpenCPN Version Patch")] = VERSION_PATCH;
1756 v[_T("OpenCPN Version Date")] = VERSION_DATE;
1757 v[_T("OpenCPN Version Full")] = VERSION_FULL;
1758
1759 // Some useful display metrics
1760 if (g_MainToolbar) {
1761 v[_T("OpenCPN Toolbar Width")] = g_MainToolbar->GetToolbarRect().width;
1762 v[_T("OpenCPN Toolbar Height")] = g_MainToolbar->GetToolbarRect().height;
1763 v[_T("OpenCPN Toolbar PosnX")] = g_MainToolbar->GetToolbarRect().x;
1764 v[_T("OpenCPN Toolbar PosnY")] = g_MainToolbar->GetToolbarRect().y;
1765 }
1766
1767 // Some rendering parameters
1768 v[_T("OpenCPN Zoom Mod Vector")] = g_chart_zoom_modifier_vector;
1769 v[_T("OpenCPN Zoom Mod Other")] = g_chart_zoom_modifier_raster;
1770 v[_T("OpenCPN Scale Factor Exp")] =
1771 g_Platform->GetChartScaleFactorExp(g_ChartScaleFactor);
1772 v[_T("OpenCPN Display Width")] = (int)g_display_size_mm;
1773 v[_T("OpenCPN Content Scale Factor")] = OCPN_GetDisplayContentScaleFactor();
1774 v[_T("OpenCPN Display DIP Scale Factor")] = OCPN_GetWinDIPScaleFactor();
1775
1776 wxJSONWriter w;
1777 wxString out;
1778 w.Write(v, out);
1779 SendMessageToAllPlugins(wxString(_T("OpenCPN Config")), out);
1780}
1781
1782void PlugInManager::SendS52ConfigToAllPlugIns(bool bReconfig) {
1783 // Send the current run-time configuration to all PlugIns
1784 wxJSONValue v;
1785 v[_T("OpenCPN Version Major")] = VERSION_MAJOR;
1786 v[_T("OpenCPN Version Minor")] = VERSION_MINOR;
1787 v[_T("OpenCPN Version Patch")] = VERSION_PATCH;
1788 v[_T("OpenCPN Version Date")] = VERSION_DATE;
1789 v[_T("OpenCPN Version Full")] = VERSION_FULL;
1790
1791 // S52PLIB state
1792 if (ps52plib) {
1793 // v[_T("OpenCPN S52PLIB ShowText")] = ps52plib->GetShowS57Text();
1794 // v[_T("OpenCPN S52PLIB ShowSoundings")] =
1795 // ps52plib->GetShowSoundings(); v[_T("OpenCPN S52PLIB ShowLights")]
1796 // = !ps52plib->GetLightsOff();
1797 v[_T("OpenCPN S52PLIB ShowAnchorConditions")] = ps52plib->GetAnchorOn();
1798 v[_T("OpenCPN S52PLIB ShowQualityOfData")] = ps52plib->GetQualityOfData();
1799 // v[_T("OpenCPN S52PLIB DisplayCategory")] =
1800 // ps52plib->GetDisplayCategory();
1801
1802 // Global parameters
1803 v[_T("OpenCPN S52PLIB MetaDisplay")] = ps52plib->m_bShowMeta;
1804 v[_T("OpenCPN S52PLIB DeclutterText")] = ps52plib->m_bDeClutterText;
1805 v[_T("OpenCPN S52PLIB ShowNationalText")] = ps52plib->m_bShowNationalTexts;
1806 v[_T("OpenCPN S52PLIB ShowImportantTextOnly")] =
1807 ps52plib->m_bShowS57ImportantTextOnly;
1808 v[_T("OpenCPN S52PLIB UseSCAMIN")] = ps52plib->m_bUseSCAMIN;
1809 v[_T("OpenCPN S52PLIB UseSUPER_SCAMIN")] = ps52plib->m_bUseSUPER_SCAMIN;
1810 v[_T("OpenCPN S52PLIB SymbolStyle")] = ps52plib->m_nSymbolStyle;
1811 v[_T("OpenCPN S52PLIB BoundaryStyle")] = ps52plib->m_nBoundaryStyle;
1812 v[_T("OpenCPN S52PLIB ColorShades")] =
1813 S52_getMarinerParam(S52_MAR_TWO_SHADES);
1814 v[_T("OpenCPN S52PLIB Safety Depth")] =
1815 (double)S52_getMarinerParam(S52_MAR_SAFETY_DEPTH);
1816 v[_T("OpenCPN S52PLIB Shallow Contour")] =
1817 (double)S52_getMarinerParam(S52_MAR_SHALLOW_CONTOUR);
1818 v[_T("OpenCPN S52PLIB Deep Contour")] =
1819 (double)S52_getMarinerParam(S52_MAR_DEEP_CONTOUR);
1820 }
1821
1822 // Notify plugins that S52PLIB may have reconfigured global options
1823 v[_T("OpenCPN S52PLIB GlobalReconfig")] = bReconfig;
1824
1825 wxJSONWriter w;
1826 wxString out;
1827 w.Write(v, out);
1828 SendMessageToAllPlugins(wxString(_T("OpenCPN Config")), out);
1829}
1830
1831void PlugInManager::NotifyAuiPlugIns(void) {
1832 auto plugin_array = PluginLoader::GetInstance()->GetPlugInArray();
1833 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
1834 PlugInContainer* pic = plugin_array->Item(i);
1835 if (pic->m_enabled && pic->m_init_state &&
1837 pic->m_pplugin->UpdateAuiStatus();
1838 }
1839}
1840
1841int PlugInManager::AddToolbarTool(wxString label, wxBitmap* bitmap,
1842 wxBitmap* bmpRollover, wxItemKind kind,
1843 wxString shortHelp, wxString longHelp,
1844 wxObject* clientData, int position,
1845 int tool_sel, opencpn_plugin* pplugin) {
1847 pttc->label = label;
1848
1849 if (!bitmap->IsOk()) {
1850 ocpnStyle::Style* style = g_StyleManager->GetCurrentStyle();
1851 pttc->bitmap_day = new wxBitmap(style->GetIcon(_T("default_pi")));
1852 } else {
1853 // Force a non-reference copy of the bitmap from the PlugIn
1854 pttc->bitmap_day = new wxBitmap(*bitmap);
1855 pttc->bitmap_day->UnShare();
1856 }
1857
1858 if (!bmpRollover->IsOk()) {
1859 ocpnStyle::Style* style = g_StyleManager->GetCurrentStyle();
1860 pttc->bitmap_Rollover_day = new wxBitmap(style->GetIcon(_T("default_pi")));
1861 } else {
1862 // Force a non-reference copy of the bitmap from the PlugIn
1863 pttc->bitmap_Rollover_day = new wxBitmap(*bmpRollover);
1864 pttc->bitmap_Rollover_day->UnShare();
1865 }
1866
1867 pttc->bitmap_dusk = BuildDimmedToolBitmap(pttc->bitmap_day, 128);
1868 pttc->bitmap_night = BuildDimmedToolBitmap(pttc->bitmap_day, 32);
1869 pttc->bitmap_Rollover_dusk =
1870 BuildDimmedToolBitmap(pttc->bitmap_Rollover_day, 128);
1871 pttc->bitmap_Rollover_night =
1872 BuildDimmedToolBitmap(pttc->bitmap_Rollover_day, 32);
1873
1874 pttc->kind = kind;
1875 pttc->shortHelp = shortHelp;
1876 pttc->longHelp = longHelp;
1877 pttc->clientData = clientData;
1878 pttc->position = position;
1879 pttc->m_pplugin = pplugin;
1880 pttc->tool_sel = tool_sel;
1881 pttc->b_viz = true;
1882 pttc->b_toggle = false;
1883 pttc->id = m_plugin_tool_id_next;
1884
1885 m_PlugInToolbarTools.Add(pttc);
1886
1887 m_plugin_tool_id_next++;
1888
1889 return pttc->id;
1890}
1891
1892int PlugInManager::AddToolbarTool(wxString label, wxString SVGfile,
1893 wxString SVGRolloverfile,
1894 wxString SVGToggledfile, wxItemKind kind,
1895 wxString shortHelp, wxString longHelp,
1896 wxObject* clientData, int position,
1897 int tool_sel, opencpn_plugin* pplugin) {
1899 pttc->label = label;
1900
1901 pttc->pluginNormalIconSVG = SVGfile;
1902 pttc->pluginRolloverIconSVG = SVGRolloverfile;
1903 pttc->pluginToggledIconSVG = SVGToggledfile;
1904
1905 // Build a set of bitmaps based on the generic "puzzle piece" icon,
1906 // In case there is some problem with the SVG file(s) specified.
1907 ocpnStyle::Style* style = g_StyleManager->GetCurrentStyle();
1908 pttc->bitmap_day = new wxBitmap(style->GetIcon(_T("default_pi")));
1909 pttc->bitmap_dusk = BuildDimmedToolBitmap(pttc->bitmap_day, 128);
1910 pttc->bitmap_night = BuildDimmedToolBitmap(pttc->bitmap_day, 32);
1911 pttc->bitmap_Rollover_day = new wxBitmap(*pttc->bitmap_day);
1912 pttc->bitmap_Rollover_dusk =
1913 BuildDimmedToolBitmap(pttc->bitmap_Rollover_day, 128);
1914 pttc->bitmap_Rollover_night =
1915 BuildDimmedToolBitmap(pttc->bitmap_Rollover_day, 32);
1916
1917 pttc->kind = kind;
1918 pttc->shortHelp = shortHelp;
1919 pttc->longHelp = longHelp;
1920 pttc->clientData = clientData;
1921 pttc->position = position;
1922 pttc->m_pplugin = pplugin;
1923 pttc->tool_sel = tool_sel;
1924 pttc->b_viz = true;
1925 pttc->b_toggle = false;
1926 pttc->id = m_plugin_tool_id_next;
1927
1928 m_PlugInToolbarTools.Add(pttc);
1929
1930 m_plugin_tool_id_next++;
1931
1932 return pttc->id;
1933}
1934
1935void PlugInManager::RemoveToolbarTool(int tool_id) {
1936 for (unsigned int i = 0; i < m_PlugInToolbarTools.GetCount(); i++) {
1937 PlugInToolbarToolContainer* pttc = m_PlugInToolbarTools[i];
1938 {
1939 if (pttc->id == tool_id) {
1940 m_PlugInToolbarTools.Remove(pttc);
1941 delete pttc;
1942 break;
1943 }
1944 }
1945 }
1946 pParent->RequestNewToolbars();
1947}
1948
1949void PlugInManager::SetToolbarToolViz(int item, bool viz) {
1950 for (unsigned int i = 0; i < m_PlugInToolbarTools.GetCount(); i++) {
1951 PlugInToolbarToolContainer* pttc = m_PlugInToolbarTools[i];
1952 {
1953 if (pttc->id == item) {
1954 pttc->b_viz = viz;
1955 pParent->RequestNewToolbars(); // Apply the change
1956 break;
1957 }
1958 }
1959 }
1960}
1961
1962void PlugInManager::SetToolbarItemState(int item, bool toggle) {
1963 for (unsigned int i = 0; i < m_PlugInToolbarTools.GetCount(); i++) {
1964 PlugInToolbarToolContainer* pttc = m_PlugInToolbarTools[i];
1965 {
1966 if (pttc->id == item) {
1967 pttc->b_toggle = toggle;
1968 pParent->SetMasterToolbarItemState(item, toggle);
1969 break;
1970 }
1971 }
1972 }
1973}
1974
1975void PlugInManager::SetToolbarItemBitmaps(int item, wxBitmap* bitmap,
1976 wxBitmap* bmpRollover) {
1977 for (unsigned int i = 0; i < m_PlugInToolbarTools.GetCount(); i++) {
1978 PlugInToolbarToolContainer* pttc = m_PlugInToolbarTools[i];
1979 {
1980 if (pttc->id == item) {
1981 delete pttc->bitmap_day;
1982 delete pttc->bitmap_dusk;
1983 delete pttc->bitmap_night;
1984 delete pttc->bitmap_Rollover_day;
1985
1986 if (!bitmap->IsOk()) {
1987 ocpnStyle::Style* style = g_StyleManager->GetCurrentStyle();
1988 pttc->bitmap_day = new wxBitmap(style->GetIcon(_T("default_pi")));
1989 } else {
1990 // Force a non-reference copy of the bitmap from the PlugIn
1991 pttc->bitmap_day = new wxBitmap(*bitmap);
1992 pttc->bitmap_day->UnShare();
1993 }
1994
1995 if (!bmpRollover->IsOk()) {
1996 ocpnStyle::Style* style = g_StyleManager->GetCurrentStyle();
1997 pttc->bitmap_Rollover_day =
1998 new wxBitmap(style->GetIcon(_T("default_pi")));
1999 } else {
2000 // Force a non-reference copy of the bitmap from the PlugIn
2001 pttc->bitmap_Rollover_day = new wxBitmap(*bmpRollover);
2002 pttc->bitmap_Rollover_day->UnShare();
2003 }
2004
2005 pttc->bitmap_dusk = BuildDimmedToolBitmap(pttc->bitmap_day, 128);
2006 pttc->bitmap_night = BuildDimmedToolBitmap(pttc->bitmap_day, 32);
2007
2008 pParent->SetToolbarItemBitmaps(item, pttc->bitmap_day,
2009 pttc->bitmap_Rollover_day);
2010 break;
2011 }
2012 }
2013 }
2014}
2015
2016void PlugInManager::SetToolbarItemBitmaps(int item, wxString SVGfile,
2017 wxString SVGfileRollover,
2018 wxString SVGfileToggled) {
2019 for (unsigned int i = 0; i < m_PlugInToolbarTools.GetCount(); i++) {
2020 PlugInToolbarToolContainer* pttc = m_PlugInToolbarTools[i];
2021 {
2022 if (pttc->id == item) {
2023 pttc->pluginNormalIconSVG = SVGfile;
2024 pttc->pluginRolloverIconSVG = SVGfileRollover;
2025 pttc->pluginToggledIconSVG = SVGfileToggled;
2026 pParent->SetToolbarItemSVG(item, pttc->pluginNormalIconSVG,
2027 pttc->pluginRolloverIconSVG,
2028 pttc->pluginToggledIconSVG);
2029 break;
2030 }
2031 }
2032 }
2033}
2034
2035opencpn_plugin* PlugInManager::FindToolOwner(const int id) {
2036 for (unsigned int i = 0; i < m_PlugInToolbarTools.GetCount(); i++) {
2037 PlugInToolbarToolContainer* pc = m_PlugInToolbarTools[i];
2038 if (id == pc->id) return pc->m_pplugin;
2039 }
2040
2041 return NULL;
2042}
2043
2044wxString PlugInManager::GetToolOwnerCommonName(const int id) {
2045 opencpn_plugin* ppi = FindToolOwner(id);
2046 if (ppi) {
2047 auto plugin_array = PluginLoader::GetInstance()->GetPlugInArray();
2048 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
2049 PlugInContainer* pic = plugin_array->Item(i);
2050 if (pic && (pic->m_pplugin == ppi)) return pic->m_common_name;
2051 }
2052 }
2053
2054 return wxEmptyString;
2055}
2056
2057wxString PlugInManager::GetLastError() { return m_last_error_string; }
2058
2059wxBitmap* PlugInManager::BuildDimmedToolBitmap(wxBitmap* pbmp_normal,
2060 unsigned char dim_ratio) {
2061 wxImage img_dup = pbmp_normal->ConvertToImage();
2062
2063 if (!img_dup.IsOk()) return NULL;
2064
2065 if (dim_ratio < 200) {
2066 // Create a dimmed version of the image/bitmap
2067 int gimg_width = img_dup.GetWidth();
2068 int gimg_height = img_dup.GetHeight();
2069
2070 double factor = (double)(dim_ratio) / 256.0;
2071
2072 for (int iy = 0; iy < gimg_height; iy++) {
2073 for (int ix = 0; ix < gimg_width; ix++) {
2074 if (!img_dup.IsTransparent(ix, iy)) {
2075 wxImage::RGBValue rgb(img_dup.GetRed(ix, iy),
2076 img_dup.GetGreen(ix, iy),
2077 img_dup.GetBlue(ix, iy));
2078 wxImage::HSVValue hsv = wxImage::RGBtoHSV(rgb);
2079 hsv.value = hsv.value * factor;
2080 wxImage::RGBValue nrgb = wxImage::HSVtoRGB(hsv);
2081 img_dup.SetRGB(ix, iy, nrgb.red, nrgb.green, nrgb.blue);
2082 }
2083 }
2084 }
2085 }
2086 // Make a bitmap
2087 wxBitmap* ptoolBarBitmap;
2088
2089#ifdef __WXMSW__
2090 wxBitmap tbmp(img_dup.GetWidth(), img_dup.GetHeight(), -1);
2091 wxMemoryDC dwxdc;
2092 dwxdc.SelectObject(tbmp);
2093
2094 ptoolBarBitmap = new wxBitmap(img_dup, (wxDC&)dwxdc);
2095#else
2096 ptoolBarBitmap = new wxBitmap(img_dup);
2097#endif
2098
2099 // store it
2100 return ptoolBarBitmap;
2101}
2102
2103wxArrayString PlugInManager::GetPlugInChartClassNameArray(void) {
2104 wxArrayString array;
2105 auto plugin_array = PluginLoader::GetInstance()->GetPlugInArray();
2106 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
2107 PlugInContainer* pic = plugin_array->Item(i);
2108 if (pic && pic->m_enabled && pic->m_init_state &&
2111 wxArrayString carray = pic->m_pplugin->GetDynamicChartClassNameArray();
2112
2113 for (unsigned int j = 0; j < carray.GetCount(); j++) {
2114 array.Add(carray[j]);
2115 }
2116 }
2117 }
2118
2119 // Scrub the list for duplicates
2120 // Corrects a flaw in BSB4 and NVC PlugIns
2121 unsigned int j = 0;
2122 while (j < array.GetCount()) {
2123 wxString test = array[j];
2124 unsigned int k = j + 1;
2125 while (k < array.GetCount()) {
2126 if (test == array[k]) {
2127 array.RemoveAt(k);
2128 j = -1;
2129 break;
2130 } else
2131 k++;
2132 }
2133
2134 j++;
2135 }
2136
2137 return array;
2138}
2139
2140//-------------------------------------------------------------------------------
2141// PluginListPanel & PluginPanel Implementation
2142//-------------------------------------------------------------------------------
2143
2144#define DISABLED_SETTINGS_MSG \
2145 _("These settings might destabilize OpenCPN and are by default disabled." \
2146 " To despite the dangers enable them manually add a CatalogExpert=1" \
2147 " line in the [PlugIns] section in the configuration file.")
2148
2149/*
2150 * Panel with buttons to control plugin catalog management.
2151 */
2152CatalogMgrPanel::CatalogMgrPanel(wxWindow* parent)
2153 : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize),
2154 m_parent(parent) {
2155 wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL);
2156 SetSizer(topSizer);
2157
2158 topSizer->Add(new wxStaticLine(this), 0, wxGROW | wxLEFT | wxRIGHT, 4);
2159
2160 wxStaticBox* itemStaticBoxSizer4Static =
2161 new wxStaticBox(this, wxID_ANY, _("Plugin Catalog"));
2162 wxStaticBoxSizer* itemStaticBoxSizer4 =
2163 new wxStaticBoxSizer(itemStaticBoxSizer4Static, wxVERTICAL);
2164 topSizer->Add(itemStaticBoxSizer4, 1, wxEXPAND | wxALL, 2);
2165
2166#ifndef __ANDROID__
2167 // First line
2168 m_catalogText = new wxStaticText(this, wxID_STATIC, _T(""));
2169 itemStaticBoxSizer4->Add(m_catalogText,
2170 wxSizerFlags().Border().Proportion(1));
2171 m_catalogText->SetLabel(GetCatalogText(false));
2172
2173 // Next line
2174 wxBoxSizer* rowSizer2 = new wxBoxSizer(wxHORIZONTAL);
2175 itemStaticBoxSizer4->Add(rowSizer2,
2176 wxSizerFlags().Expand().Border().Proportion(1));
2177
2178 m_updateButton = new wxButton(this, wxID_ANY, _("Update Plugin Catalog"),
2179 wxDefaultPosition, wxDefaultSize, 0);
2180 rowSizer2->Add(m_updateButton, 0, wxALIGN_LEFT);
2181 m_updateButton->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
2182 &CatalogMgrPanel::OnUpdateButton, this);
2183 rowSizer2->AddSpacer(4 * GetCharWidth());
2184 m_tarballButton = new wxButton(this, wxID_ANY, _("Import plugin..."),
2185 wxDefaultPosition, wxDefaultSize, 0);
2186 rowSizer2->Add(m_tarballButton, 0, wxALIGN_LEFT | wxLEFT, 2 * GetCharWidth());
2187 m_tarballButton->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
2188 &CatalogMgrPanel::OnTarballButton, this);
2189
2190 rowSizer2->AddSpacer(4 * GetCharWidth());
2191 m_adv_button = new wxButton(this, wxID_ANY, _("Settings..."),
2192 wxDefaultPosition, wxDefaultSize, 0);
2193 ConfigVar<bool> expert("/PlugIns", "CatalogExpert", pConfig);
2194 if (expert.Get(false)) {
2195 m_adv_button->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
2196 &CatalogMgrPanel::OnPluginSettingsButton, this);
2197 } else {
2198 m_adv_button->Bind(wxEVT_COMMAND_BUTTON_CLICKED, [&](wxCommandEvent&) {
2199 wxMessageBox(DISABLED_SETTINGS_MSG, _("Disabled"));
2200 });
2201 }
2202 rowSizer2->AddSpacer(4 * GetCharWidth());
2203 rowSizer2->Add(m_adv_button, 0, wxALIGN_LEFT);
2204
2205 SetUpdateButtonLabel();
2206
2207 // Next line
2208 wxBoxSizer* rowSizer3 = new wxBoxSizer(wxHORIZONTAL);
2209 itemStaticBoxSizer4->Add(rowSizer3, 0, wxEXPAND | wxALL, 4);
2210
2211 SetMinSize(wxSize(m_parent->GetClientSize().x - (4 * GetCharWidth()), -1));
2212 Fit();
2213
2214 GlobalVar<wxString> catalog(&g_catalog_channel);
2215 wxDEFINE_EVENT(EVT_CATALOG_CHANGE, wxCommandEvent);
2216 catalog_listener.Listen(catalog, this, EVT_CATALOG_CHANGE);
2217 Bind(EVT_CATALOG_CHANGE, [&](wxCommandEvent&) { SetUpdateButtonLabel(); });
2218
2219#else // __ANDROID__
2220 SetBackgroundColour(wxColour(0x7c, 0xb0, 0xe9)); // light blue
2221 ConfigVar<bool> expert("/PlugIns", "CatalogExpert", pConfig);
2222 if (!expert.Get(false)) {
2223 m_updateButton =
2224 new wxButton(this, wxID_ANY, _("Update Plugin Catalog: master"),
2225 wxDefaultPosition, wxDefaultSize, 0);
2226 itemStaticBoxSizer4->Add(m_updateButton, 0, wxALIGN_LEFT);
2227 m_updateButton->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
2228 &CatalogMgrPanel::OnUpdateButton, this);
2229 SetUpdateButtonLabel();
2230 m_tarballButton = NULL;
2231 m_adv_button = NULL;
2232 } else {
2233 // First line
2234 m_catalogText = new wxStaticText(this, wxID_STATIC, GetCatalogText(false));
2235 itemStaticBoxSizer4->Add(m_catalogText,
2236 wxSizerFlags().Border(wxALL, 5).Proportion(1));
2237 // m_catalogText->SetLabel(GetCatalogText(false));
2238
2239 m_updateButton = new wxButton(
2240 this, wxID_ANY, "Update Plugin Catalog:master ",
2241 wxDefaultPosition, wxDefaultSize, 0);
2242 itemStaticBoxSizer4->Add(m_updateButton, 0, wxALIGN_LEFT | wxTOP, 5);
2243 m_updateButton->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
2244 &CatalogMgrPanel::OnUpdateButton, this);
2245 SetUpdateButtonLabel();
2246
2247 // Next line
2248 m_adv_button = new wxButton(this, wxID_ANY, _("Settings..."),
2249 wxDefaultPosition, wxDefaultSize, 0);
2250 itemStaticBoxSizer4->Add(m_adv_button, 0, wxALIGN_LEFT | wxTOP,
2251 GetCharWidth());
2252 m_adv_button->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
2253 &CatalogMgrPanel::OnPluginSettingsButton, this);
2254
2255 // Next line
2256 m_tarballButton = new wxButton(this, wxID_ANY, _("Import plugin..."),
2257 wxDefaultPosition, wxDefaultSize, 0);
2258 itemStaticBoxSizer4->Add(m_tarballButton, 0, wxALIGN_LEFT | wxALL,
2259 2 * GetCharWidth());
2260 m_tarballButton->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
2261 &CatalogMgrPanel::OnTarballButton, this);
2262 }
2263
2264#endif
2265}
2266
2267CatalogMgrPanel::~CatalogMgrPanel() {
2268 m_updateButton->Unbind(wxEVT_COMMAND_BUTTON_CLICKED,
2269 &CatalogMgrPanel::OnUpdateButton, this);
2270 if (m_tarballButton)
2271 m_tarballButton->Unbind(wxEVT_COMMAND_BUTTON_CLICKED,
2272 &CatalogMgrPanel::OnTarballButton, this);
2273}
2274
2275static const char* const DOWNLOAD_REPO_PROTO =
2276 "https://raw.githubusercontent.com/OpenCPN/plugins/@branch@/"
2277 "ocpn-plugins.xml";
2278
2279void CatalogMgrPanel::OnUpdateButton(wxCommandEvent& event) {
2280 // Craft the url
2281 std::string catalog(g_catalog_channel == "" ? "master" : g_catalog_channel);
2282 std::string url(g_catalog_custom_url);
2283 if (catalog != "custom") {
2284 url = std::string(DOWNLOAD_REPO_PROTO);
2285 ocpn::replace(url, "@branch@", catalog);
2286 }
2287 // Download to a temp file
2288 std::string filePath =
2289 wxFileName::CreateTempFileName("ocpn_dl").ToStdString();
2290
2291 auto catalogHdlr = CatalogHandler::GetInstance();
2292
2293 g_Platform->ShowBusySpinner();
2294 auto status = catalogHdlr->DownloadCatalog(filePath, url);
2295 g_Platform->HideBusySpinner();
2296
2297 std::string message;
2298 if (status != CatalogHandler::ServerStatus::OK) {
2299 message = _("Cannot download data from url");
2300 OCPNMessageBox(this, message, _("OpenCPN Catalog update"),
2301 wxICON_ERROR | wxOK);
2302 return;
2303 }
2304
2305 // TODO Validate xml using xsd here....
2306#ifdef __ANDROID__
2307 if (!AndroidSecureCopyFile(wxString(filePath.c_str()),
2308 g_Platform->GetPrivateDataDir() +
2309 wxFileName::GetPathSeparator() +
2310 _T("ocpn-plugins.xml"))) {
2311 OCPNMessageBox(this, _("Unable to copy catalog file"),
2312 _("OpenCPN Catalog update"), wxICON_ERROR | wxOK);
2313 return;
2314 }
2315#else
2316 // Copy the downloaded file to proper local location
2317 if (!wxCopyFile(wxString(filePath.c_str()),
2318 g_Platform->GetPrivateDataDir() +
2319 wxFileName::GetPathSeparator() +
2320 _T("ocpn-plugins.xml"))) {
2321 OCPNMessageBox(this, _("Unable to copy catalog file"),
2322 _("OpenCPN Catalog update"), wxICON_ERROR | wxOK);
2323 return;
2324 }
2325#endif
2326
2327 // If this is the "master" catalog, also copy to plugin cache
2328 if (catalog == "master") {
2329 if (!ocpn::store_metadata(filePath.c_str())) {
2330 OCPNMessageBox(this, _("Unable to copy catalog file to cache"),
2331 _("OpenCPN Catalog update"), wxICON_ERROR | wxOK);
2332 return;
2333 }
2334 }
2335
2336 // Record in the config file the name of the catalog downloaded
2337 pConfig->SetPath(_T("/PlugIns/"));
2338 pConfig->Write("LatestCatalogDownloaded", catalog.c_str());
2339 pConfig->Flush();
2340
2341 // Reset the PluginHandler catalog file source.
2342 // This will case the Handler to find, load, and parse the just-downloaded
2343 // catalog as copied to g_Platform->GetPrivateDataDir()...
2344 auto pluginHandler = PluginHandler::GetInstance();
2345 pluginHandler->setMetadata("");
2346
2347 // Also clear the cached values in the CatalogHandler, forcing
2348 // a reload and parse of the catalog.
2349 auto cataloghdlr = CatalogHandler::GetInstance();
2350 cataloghdlr->ClearCatalogData();
2351
2352 // Reload all plugins, which will also update the status fields
2353 LoadAllPlugIns(false);
2354
2355 // Update this Panel, and the entire list.
2356#ifndef __ANDROID__
2357 m_catalogText->SetLabel(GetCatalogText(true));
2358#endif
2359 if (m_PluginListPanel) m_PluginListPanel->ReloadPluginPanels();
2360 OCPNMessageBox(this, _("Catalog update successful"),
2361 _("OpenCPN Catalog update"), wxICON_INFORMATION | wxOK);
2362}
2363
2364void CatalogMgrPanel::OnPluginSettingsButton(wxCommandEvent& event) {
2365 auto dialog = new CatalogSettingsDialog(this);
2366
2367#ifdef __ANDROID__
2368 androidDisableRotation();
2369#endif
2370
2371 dialog->ShowModal();
2372
2373#ifdef __ANDROID__
2374 androidEnableRotation();
2375#endif
2376}
2377
2378void CatalogMgrPanel::OnTarballButton(wxCommandEvent& event) {
2379 // Present a file selector dialog to get the file name..
2380 wxString path;
2381 int response = g_Platform->DoFileSelectorDialog(
2382 this, &path, _("Select tarball file"), GetImportInitDir(), "",
2383 "tar files (*.tar.gz)|*.tar.gz|All Files (*.*)|*.*");
2384
2385 if (response != wxID_OK) {
2386 return;
2387 }
2388 auto handler = PluginHandler::GetInstance();
2389 PluginMetadata metadata;
2390 bool ok = handler->ExtractMetadata(path.ToStdString(), metadata);
2391 if (!ok) {
2392 OCPNMessageBox(
2393 this,
2394 _("Error extracting metadata from tarball (missing metadata.xml?)"),
2395 _("OpenCPN Plugin Import Error"));
2396 return;
2397 }
2398 if (!PluginHandler::IsCompatible(metadata)) {
2399 OCPNMessageBox(this, _("Incompatible import plugin detected."),
2400 _("OpenCPN Plugin Import Error"));
2401 handler->Uninstall(metadata.name);
2402 return;
2403 }
2404 UninstallPlugin(metadata.name);
2405 ok = handler->InstallPlugin(metadata, path.ToStdString());
2406 if (!ok) {
2407 OCPNMessageBox(this, _("Error extracting import plugin tarball."),
2408 _("OpenCPN Plugin Import Error"));
2409 return;
2410 }
2411 metadata.is_imported = true;
2412 auto metadata_path = PluginHandler::ImportedMetadataPath(metadata.name);
2413 std::ofstream file(metadata_path);
2414 file << metadata.to_string();
2415 if (!file.good()) {
2416 WARNING_LOG << "Error saving metadata file: " << metadata_path
2417 << " for imported plugin: " << metadata.name;
2418 }
2419 LoadAllPlugIns(false, true);
2421 m_PluginListPanel->ReloadPluginPanels();
2422 wxString ws(_("Plugin"));
2423 ws += metadata.name + _(" successfully imported");
2424 OCPNMessageBox(gFrame, ws, _("Installation complete"),
2425 wxICON_INFORMATION | wxOK | wxCENTRE);
2426}
2427
2428wxString CatalogMgrPanel::GetCatalogText(bool updated) {
2429 wxString catalog;
2430 catalog = updated ? _("Active Catalog") : _("Last Catalog");
2431 catalog += _T(": ");
2432
2433 // Check the config file to learn what was the last catalog downloaded.
2434 pConfig->SetPath(_T("/PlugIns/"));
2435 wxString latestCatalog =
2436 pConfig->Read(_T("LatestCatalogDownloaded"), _T("default"));
2437 catalog += latestCatalog;
2438
2439#ifndef __ANDROID__
2440 // Get the version from the currently active catalog, by which we mean
2441 // the latest catalog parsed.
2442 auto pluginHandler = PluginHandler::GetInstance();
2443 std::string date = pluginHandler->GetCatalogData()->date;
2444
2445 catalog += wxString(" ") + _("Last change: ") + " " + date;
2446 if (!updated) catalog += _T(" : ") + _("Please Update Plugin Catalog.");
2447#endif
2448
2449 return catalog;
2450}
2451
2452void CatalogMgrPanel::SetUpdateButtonLabel() {
2453 wxString label = _("Update Plugin Catalog");
2454 label += _T(": ");
2455 label += g_catalog_channel == "" ? "master" : g_catalog_channel;
2456 m_updateButton->SetLabel(label);
2457 Layout();
2458}
2459
2460wxString CatalogMgrPanel::GetImportInitDir() {
2461 // Check the config file for the last Import path.
2462 pConfig->SetPath(_T("/PlugIns/"));
2463 wxString lastImportDir;
2464 lastImportDir = pConfig->Read(_T("LatestImportDir"),
2465 g_Platform->GetWritableDocumentsDir());
2466 if (wxDirExists(lastImportDir)) {
2467 return lastImportDir;
2468 }
2469 return (g_Platform->GetWritableDocumentsDir());
2470}
2471
2472BEGIN_EVENT_TABLE(PluginListPanel, wxScrolledWindow)
2473// EVT_BUTTON( ID_CMD_BUTTON_PERFORM_ACTION,
2474// PluginListPanel::OnPluginPanelAction )
2475END_EVENT_TABLE()
2476
2477PluginListPanel::PluginListPanel(wxWindow* parent, wxWindowID id,
2478 const wxPoint& pos, const wxSize& size)
2479 : wxScrolledWindow(parent, id, pos, size, wxTAB_TRAVERSAL | wxVSCROLL),
2480 m_PluginSelected(0) {
2481 m_is_loading.clear();
2482 SetSizer(new wxBoxSizer(wxVERTICAL));
2483 ReloadPluginPanels();
2484}
2485
2486void PluginListPanel::SelectByName(wxString& name) {
2487 for (auto it = GetChildren().GetFirst(); it; it = it->GetNext()) {
2488 auto pluginPanel = dynamic_cast<PluginPanel*>(it->GetData());
2489 if (pluginPanel) {
2490 if (pluginPanel->GetPluginPtr()->m_common_name.IsSameAs(name)) {
2491 pluginPanel->SetSelected(true);
2492 pluginPanel->Layout();
2493 SelectPlugin(pluginPanel);
2494 break;
2495 }
2496 }
2497 }
2498}
2499
2501std::vector<const PlugInData*> GetInstalled() {
2502 std::vector<const PlugInData*> result;
2503 auto loader = PluginLoader::GetInstance();
2504 for (size_t i = 0; i < loader->GetPlugInArray()->GetCount(); i++) {
2505 auto const item = loader->GetPlugInArray()->Item(i);
2506 if (item->m_managed_metadata.name.empty()) {
2507 const auto name = item->m_common_name.ToStdString();
2508 item->m_managed_metadata = PluginLoader::MetadataByName(name);
2509 }
2510 PluginLoader::UpdatePlugin(item, item->m_managed_metadata);
2511 result.push_back(item);
2512 }
2513 auto compare = [](const PlugInData* lhs, const PlugInData* rhs) {
2514 std::string slhs, srhs;
2515 for (auto& cl : lhs->Key()) slhs += toupper(cl);
2516 for (auto& cr : rhs->Key()) srhs += toupper(cr);
2517 return slhs.compare(srhs) < 0;
2518 };
2519 std::sort(result.begin(), result.end(), compare);
2520 return result;
2521}
2522
2523/* Is plugin with given name present in loaded or safe list if installed? */
2524static bool IsPluginLoaded(const std::string& name) {
2525 if (safe_mode::get_mode()) {
2527 auto found =
2528 std::find(installed.begin(), installed.end(), ocpn::tolower(name));
2529 return found != installed.end();
2530 } else {
2531 auto loaded = PluginLoader::GetInstance()->GetPlugInArray();
2532 for (size_t i = 0; i < loaded->GetCount(); i++) {
2533 if (loaded->Item(i)->m_common_name.ToStdString() == name) return true;
2534 }
2535 return false;
2536 }
2537}
2538
2540 if (m_is_loading.test_and_set()) {
2541 // recursive call...
2542 DEBUG_LOG << "LoadAllPlugins: recursive invocation";
2543 return;
2544 }
2545
2546 auto plugins = PluginLoader::GetInstance()->GetPlugInArray();
2547 m_PluginItems.Clear();
2548
2549 wxWindowList kids = GetChildren();
2550 for (unsigned int i = 0; i < kids.GetCount(); i++) {
2551 wxWindowListNode* node = kids.Item(i);
2552 wxWindow* win = node->GetData();
2553 PluginPanel* pp = dynamic_cast<PluginPanel*>(win);
2554 if (pp) win->Destroy();
2555 }
2556 GetSizer()->Clear();
2557
2558 Hide();
2559 m_PluginSelected = 0;
2560
2561 if (safe_mode::get_mode()) {
2564 for (const auto& name : installed) AddPlugin(name);
2565 } else {
2566 /* The catalog entries. */
2567 auto available = getCompatiblePlugins();
2568
2569 /* Remove those which are loaded or in safe list. */
2570 auto predicate = [](const PluginMetadata& md) {
2571 return IsPluginLoaded(md.name);
2572 };
2573 auto end = std::remove_if(available.begin(), available.end(), predicate);
2574 available.erase(end, available.end());
2575
2576 // Sort on case-insensitive name
2577 struct CompSort {
2578 bool operator()(const PluginMetadata& lhs,
2579 const PluginMetadata rhs) const {
2580 std::string slhs, srhs;
2581 for (auto& cl : lhs.name) slhs += toupper(cl);
2582 for (auto& cr : rhs.name) srhs += toupper(cr);
2583 return slhs.compare(srhs) < 0;
2584 }
2585 } comp_sort;
2586
2587 std::set<PluginMetadata, CompSort> unique_sorted_entries(comp_sort);
2588 for (const auto& p : available) unique_sorted_entries.insert(p);
2589
2590 // Build the list of panels.
2591
2592 // Add Installed and active plugins
2593 for (const auto& p : GetInstalled())
2594 if (p->m_enabled) AddPlugin(*p);
2595
2596 // Add Installed and inactive plugins
2597 for (const auto& p : GetInstalled())
2598 if (!p->m_enabled) AddPlugin(*p);
2599
2600 // Add available plugins, sorted
2601 for (const auto& p : unique_sorted_entries) AddPlugin(PlugInData(p));
2602 }
2603
2604 Show();
2605 Layout();
2606 Refresh(true);
2607 Scroll(0, 0);
2608
2609 m_is_loading.clear();
2610}
2611
2612void PluginListPanel::AddPlugin(const std::string& name) {
2613 auto panel = new PluginPanel(this, name);
2614 DimeControl(panel);
2615 panel->SetSelected(false);
2616 GetSizer()->Add(panel, 0, wxEXPAND);
2617 m_PluginItems.Add(panel);
2618 m_pluginSpacer = g_Platform->GetDisplayDPmm() * 1.0;
2619 GetSizer()->AddSpacer(m_pluginSpacer);
2620}
2621
2622void PluginListPanel::AddPlugin(const PlugInData& pic) {
2623 auto pPluginPanel =
2624 new PluginPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, pic);
2625 pPluginPanel->SetSelected(false);
2626 GetSizer()->Add(pPluginPanel, 0, wxEXPAND);
2627 m_PluginItems.Add(pPluginPanel);
2628
2629 m_pluginSpacer = g_Platform->GetDisplayDPmm() * 1.0;
2630 GetSizer()->AddSpacer(m_pluginSpacer);
2631
2632 // wxStaticLine* itemStaticLine = new wxStaticLine( m_panel, wxID_ANY,
2633 // wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
2634 // m_pitemBoxSizer01->Add( itemStaticLine, wxSizerFlags().Expand());
2635}
2636
2637// When a child Panel is selected, its size grows to include "Preferences"
2638// and Enable" buttons. As a consequence, the vertical size of the
2639// ListPanel grows as well.Calculate and add a spacer to bottom of
2640// ListPanel so that initial ListPanel minimum size calculations account
2641// for selected Panel size growth. Sadly, this does not work right on wxQt.
2642// So, just punt for now...
2643int PluginListPanel::ComputePluginSpace(ArrayOfPluginPanel plugins,
2644 wxBoxSizer* sizer) {
2645 int max_dy = 0;
2646 for (size_t i = 0; i < plugins.GetCount(); i++) {
2647 auto panel = plugins.Item(i);
2648 bool was_selected = panel->GetSelected();
2649 panel->SetSelected(false);
2650 sizer->Layout();
2651 wxSize unselected = panel->GetSize();
2652
2653 panel->SetSelected(true); // switch to selected, a bit bigger
2654 sizer->Layout();
2655 wxSize selected = panel->GetSize();
2656
2657 int dy = selected.GetHeight() - unselected.GetHeight();
2658 max_dy = wxMax(max_dy, dy);
2659 panel->SetSelected(was_selected);
2660 }
2661 return max_dy;
2662}
2663
2664PluginListPanel::~PluginListPanel() {}
2665
2666void PluginListPanel::UpdateSelections() {
2667 for (unsigned int i = 0; i < m_PluginItems.GetCount(); i++) {
2668 PluginPanel* pPluginPanel = m_PluginItems[i];
2669 if (pPluginPanel) {
2670 pPluginPanel->SetSelected(pPluginPanel->GetSelected());
2671 }
2672 }
2673}
2674
2675void PluginListPanel::SelectPlugin(PluginPanel* pi) {
2676 int xs, ys;
2677 GetViewStart(&xs, &ys);
2678 Scroll(0, 0);
2679
2680 if (m_PluginSelected) {
2681 m_PluginSelected->SetSelected(false);
2682 m_PluginSelected->Layout();
2683 }
2684
2685 if (pi == NULL) m_PluginSelected->SetSelected(false);
2686
2687 m_PluginSelected = pi;
2688
2689 GetSizer()->Layout();
2690 Refresh(false);
2691 wxSize size = GetBestVirtualSize();
2692 SetVirtualSize(size);
2693
2694 // Measure, and ensure that the selected item is fully visible in the
2695 // vertical scroll box.
2696 int htop = 0;
2697 for (unsigned int i = 0; i < m_PluginItems.GetCount(); i++) {
2698 PluginPanel* pPluginPanel = m_PluginItems[i];
2699 int yd = pPluginPanel->GetSize().y;
2700 htop += yd;
2701 htop += m_pluginSpacer;
2702 if (pPluginPanel == pi) {
2703 int piBottom = htop - (ys * g_options->GetScrollRate());
2704 if (piBottom > GetClientSize().y) {
2705 ys += (piBottom - GetClientSize().y) / g_options->GetScrollRate();
2706 }
2707 break;
2708 }
2709 }
2710
2711 Scroll(xs, ys);
2712}
2713
2714void PluginListPanel::MoveUp(PluginPanel* pi) {
2715 int pos = m_PluginItems.Index(pi);
2716 if (pos == 0) // The first one can't be moved further up
2717 return;
2718 m_PluginItems.RemoveAt(pos);
2719 // m_pitemBoxSizer01->Remove( pos * 2 + 1 );
2720 // m_pitemBoxSizer01->Remove( pos * 2 );
2721 m_PluginItems.Insert(pi, pos - 1);
2722 wxStaticLine* itemStaticLine = new wxStaticLine(
2723 this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL);
2724 // m_pitemBoxSizer01->Insert( (pos - 1) * 2, itemStaticLine, 0,
2725 // wxEXPAND|wxALL, 0 ); m_pitemBoxSizer01->Insert( (pos - 1) * 2, pi, 0,
2726 // wxEXPAND|wxALL, 0 );
2727
2728 m_PluginSelected = pi;
2729
2730 GetSizer()->Layout();
2731 m_parent->Layout();
2732 Refresh(true);
2733}
2734
2735void PluginListPanel::MoveDown(PluginPanel* pi) {
2736 int pos = m_PluginItems.Index(pi);
2737 if (pos == (int)m_PluginItems.Count() -
2738 1) // The last one can't be moved further down
2739 return;
2740 m_PluginItems.RemoveAt(pos);
2741 // m_pitemBoxSizer01->Remove( pos * 2 + 1 );
2742 // m_pitemBoxSizer01->Remove( pos * 2 );
2743 m_PluginItems.Insert(pi, pos + 1);
2744 wxStaticLine* itemStaticLine = new wxStaticLine(
2745 this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL);
2746 // m_pitemBoxSizer01->Insert( (pos + 1) * 2 - 1, itemStaticLine, 0,
2747 // wxEXPAND|wxALL, 0 ); m_pitemBoxSizer01->Insert( (pos + 1) * 2, pi, 0,
2748 // wxEXPAND|wxALL, 0 );
2749
2750 m_PluginSelected = pi;
2751
2752 GetSizer()->Layout();
2753 m_parent->Layout();
2754 Refresh(false);
2755}
2756
2757static bool canUninstall(std::string name) {
2758 PluginHandler* pluginHandler = PluginHandler::GetInstance();
2759 // std::transform(name.begin(), name.end(), name.begin(), ::tolower);
2760
2761 for (auto plugin : pluginHandler->GetInstalled()) {
2762 if (plugin.name == name) {
2763 if (safe_mode::get_mode())
2764 return true;
2765 else
2766 return !plugin.readonly;
2767 }
2768 }
2769 return false;
2770}
2771
2772PluginPanel::PluginPanel(wxPanel* parent, const std::string& name)
2773 : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize,
2774 wxBORDER_NONE),
2775 m_is_safe_panel(true) {
2776 m_PluginListPanel = dynamic_cast<PluginListPanel*>(parent);
2777 wxASSERT(m_PluginListPanel != 0);
2778 wxBoxSizer* top_sizer = new wxBoxSizer(wxVERTICAL);
2779 SetSizer(top_sizer);
2780 wxBoxSizer* top_horizontal = new wxBoxSizer(wxHORIZONTAL);
2781 top_sizer->Add(top_horizontal, 0, wxEXPAND);
2782
2783 double iconSize = GetCharWidth() * 4;
2784 double dpi_mult = g_Platform->GetDisplayDIPMult(this);
2785 int icon_scale = iconSize * dpi_mult;
2786 ocpnStyle::Style* style = g_StyleManager->GetCurrentStyle();
2787 wxBitmap bitmap(style->GetIcon("default_pi", icon_scale, icon_scale));
2788 m_itemStaticBitmap = new wxStaticBitmap(this, wxID_ANY, bitmap);
2789 top_horizontal->Add(m_itemStaticBitmap, 0, wxEXPAND | wxALL, 10);
2790
2791 m_pName = new wxStaticText(this, wxID_ANY, name);
2792 top_horizontal->Add(m_pName, wxID_ANY, wxALIGN_CENTER_VERTICAL);
2793 m_pVersion = new wxStaticText(this, wxID_ANY, "");
2794 top_horizontal->Add(m_pVersion);
2795 m_pVersion->Hide();
2796
2797 m_pButtons = new wxBoxSizer(wxHORIZONTAL);
2798 top_horizontal->Add(m_pButtons);
2799 m_info_btn = new WebsiteButton(this, "https:\\opencpn.org");
2800 top_horizontal->Add(m_info_btn);
2801 m_pButtonUninstall = new wxButton(this, wxID_ANY, _("Uninstall"),
2802 wxDefaultPosition, wxDefaultSize, 0);
2803 top_horizontal->Add(m_pButtonUninstall, 0, wxALIGN_CENTER_VERTICAL | wxALL,
2804 2);
2805 auto uninstall = [&](wxCommandEvent ev) {
2806 auto n = m_pName->GetLabel().ToStdString();
2807 int result =
2808 OCPNMessageBox(gFrame, std::string(_("Uninstall plugin ")) + n + "?",
2809 _("Un-Installation"), wxICON_QUESTION | wxOK | wxCANCEL);
2810 if (result != wxID_OK) return;
2812 m_PluginListPanel->ReloadPluginPanels();
2813 };
2814 m_pButtonUninstall->Bind(wxEVT_COMMAND_BUTTON_CLICKED, uninstall);
2815}
2816
2817BEGIN_EVENT_TABLE(PluginPanel, wxPanel)
2818EVT_PAINT(PluginPanel::OnPaint)
2819END_EVENT_TABLE()
2820
2821PluginPanel::PluginPanel(wxPanel* parent, wxWindowID id, const wxPoint& pos,
2822 const wxSize& size, const PlugInData plugin)
2823 : wxPanel(parent, id, pos, size, wxBORDER_NONE),
2824 m_plugin(plugin),
2825 m_is_safe_panel(false) {
2826 m_PluginListPanel = (PluginListPanel*)parent; //->GetParent();
2827 m_PluginListPanel = dynamic_cast<PluginListPanel*>(parent /*->GetParent()*/);
2828 wxASSERT(m_PluginListPanel != 0);
2829
2830 m_bSelected = false;
2831 m_penWidthUnselected = g_Platform->GetDisplayDPmm() * .25;
2832 m_penWidthSelected = g_Platform->GetDisplayDPmm() * .5;
2833
2834 wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL);
2835 SetSizer(topSizer);
2836
2837 wxBoxSizer* itemBoxSizer01 = new wxBoxSizer(wxHORIZONTAL);
2838 topSizer->Add(itemBoxSizer01, 0, wxEXPAND);
2839 Bind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this);
2840 Bind(wxEVT_LEFT_UP, &PluginPanel::OnPluginSelectedUp, this);
2841
2842 double iconSize = GetCharWidth() * 4;
2843 double dpi_mult = g_Platform->GetDisplayDIPMult(this);
2844 int icon_scale = iconSize * dpi_mult;
2845
2846 wxImage plugin_icon;
2847 ocpnStyle::Style* style = g_StyleManager->GetCurrentStyle();
2848 if (m_plugin.m_bitmap.IsOk()) {
2849 plugin_icon = m_plugin.m_bitmap.ConvertToImage();
2850 }
2851 wxBitmap bitmap;
2852 if (m_plugin.m_status == PluginStatus::ManagedInstallAvailable) {
2853 wxFileName path(g_Platform->GetSharedDataDir(), "packageBox.svg");
2854 path.AppendDir("uidata");
2855 path.AppendDir("traditional"); // FIXME(leamas) cache it.
2856 bitmap = LoadSVG(path.GetFullPath(), icon_scale, icon_scale);
2857 } else if (plugin_icon.IsOk()) {
2858 int nowSize = plugin_icon.GetWidth();
2859 plugin_icon.Rescale(icon_scale, icon_scale, wxIMAGE_QUALITY_HIGH);
2860 bitmap = wxBitmap(plugin_icon);
2861 } else {
2862 bitmap = wxBitmap(style->GetIcon("default_pi", icon_scale, icon_scale));
2863 }
2864 m_itemStaticBitmap = new wxStaticBitmap(this, wxID_ANY, bitmap);
2865
2866 itemBoxSizer01->Add(m_itemStaticBitmap, 0, wxEXPAND | wxALL, 10);
2867 m_itemStaticBitmap->Bind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected,
2868 this);
2869 m_itemStaticBitmap->Bind(wxEVT_LEFT_UP, &PluginPanel::OnPluginSelectedUp,
2870 this);
2871
2872 wxBoxSizer* itemBoxSizer02 = new wxBoxSizer(wxVERTICAL);
2873 itemBoxSizer01->Add(itemBoxSizer02, 1, wxEXPAND | wxALL, 0);
2874
2875 // Calculate character width available
2876 int nChars = g_options->GetSize().x / GetCharWidth();
2877 bool bCompact = false;
2878 if (nChars < 60) // Arbitrary, detecting mobile devices in portrait mode.
2879 bCompact = true;
2880
2881 if (bCompact) {
2882 // Might need to shorten the Plugin name string
2883 wxString nameString = m_plugin.m_common_name;
2884 int maxWidth = g_Platform->getDisplaySize().x * 3 / 10;
2885 wxScreenDC dc;
2886 int nameWidth;
2887 dc.GetTextExtent(m_plugin.m_common_name, &nameWidth, NULL);
2888 if (nameWidth > maxWidth) {
2889 nameString = wxControl::Ellipsize(m_plugin.m_common_name, dc,
2890 wxELLIPSIZE_END, maxWidth);
2891 }
2892 m_pName = new wxStaticText(this, wxID_ANY, nameString);
2893 m_pName->Bind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this);
2894 m_pName->Bind(wxEVT_LEFT_UP, &PluginPanel::OnPluginSelectedUp, this);
2895 itemBoxSizer02->Add(m_pName, 0, /*wxEXPAND|*/ wxALL, 5);
2896
2897 wxFlexGridSizer* sl1 = new wxFlexGridSizer(2, 0, 0);
2898 sl1->AddGrowableCol(1);
2899 itemBoxSizer02->Add(sl1, 0, wxEXPAND);
2900
2901 m_pVersion = new wxStaticText(this, wxID_ANY, _T("X.YY.ZZ.AA"));
2902 sl1->Add(m_pVersion, 0, /*wxEXPAND|*/ wxALL, 5);
2903 if (m_plugin.m_status == PluginStatus::ManagedInstallAvailable) {
2904 m_pVersion->Hide();
2905 }
2906 m_pVersion->Bind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this);
2907 m_pVersion->Bind(wxEVT_LEFT_UP, &PluginPanel::OnPluginSelectedUp, this);
2908
2909 m_cbEnable = new wxCheckBox(this, wxID_ANY, _("Enabled"));
2910 sl1->Add(m_cbEnable, 1, wxALIGN_RIGHT | wxTOP, 5);
2911 m_cbEnable->Bind(wxEVT_CHECKBOX, &PluginPanel::OnPluginEnableToggle, this);
2912
2913 // Might need to shorten the Plugin description string
2914 wxString descriptionString = m_plugin.m_short_description;
2915 int maxDescriptionWidth = g_Platform->getDisplaySize().x - (iconSize * 4);
2916 int descriptionWidth;
2917 dc.GetTextExtent(m_plugin.m_short_description, &descriptionWidth, NULL);
2918 if (descriptionWidth > maxDescriptionWidth)
2919 descriptionString =
2920 wxControl::Ellipsize(m_plugin.m_short_description, dc,
2921 wxELLIPSIZE_END, maxDescriptionWidth);
2922
2923 // This invocation has the effect of setting the minimum width of the
2924 // descriptor field.
2925 m_pDescription =
2926 new wxStaticText(this, wxID_ANY, descriptionString, wxDefaultPosition,
2927 wxSize(maxDescriptionWidth, -1), wxST_NO_AUTORESIZE);
2928 itemBoxSizer02->Add(m_pDescription, 0, wxEXPAND | wxALL, 5);
2929 m_pDescription->Bind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this);
2930 m_pDescription->Bind(wxEVT_LEFT_UP, &PluginPanel::OnPluginSelectedUp, this);
2931
2932 } else {
2933 wxFlexGridSizer* itemBoxSizer03 = new wxFlexGridSizer(4, 0, 0);
2934 itemBoxSizer03->AddGrowableCol(2);
2935 itemBoxSizer02->Add(itemBoxSizer03, 0, wxEXPAND);
2936
2937 wxString nameString = m_plugin.m_common_name;
2938 m_pName = new wxStaticText(this, wxID_ANY, nameString);
2939 m_pName->Bind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this);
2940 m_pName->Bind(wxEVT_LEFT_UP, &PluginPanel::OnPluginSelectedUp, this);
2941
2942 // Avoid known bug in wxGTK3
2943#ifndef __WXGTK3__
2944 wxFont font = GetFont();
2945 font.SetWeight(wxFONTWEIGHT_BOLD);
2946 m_pName->SetFont(font);
2947#endif
2948
2949 itemBoxSizer03->Add(m_pName, 0, /*wxEXPAND|*/ wxALL, 10);
2950
2951 m_pVersion = new wxStaticText(this, wxID_ANY, _T("X.YY.ZZ.AA"));
2952 itemBoxSizer03->Add(m_pVersion, 0, /*wxEXPAND|*/ wxALL, 10);
2953 if (m_plugin.m_status == PluginStatus::ManagedInstallAvailable ||
2954 m_plugin.m_status == PluginStatus::System ||
2955 (m_plugin.m_status == PluginStatus::Unmanaged &&
2956 !m_plugin.m_managed_metadata.is_orphan)) {
2957 m_pVersion->Hide();
2958 }
2959 m_pVersion->Bind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this);
2960 m_pVersion->Bind(wxEVT_LEFT_UP, &PluginPanel::OnPluginSelectedUp, this);
2961
2962 m_cbEnable = new wxCheckBox(this, wxID_ANY, _("Enabled"));
2963 itemBoxSizer03->Add(m_cbEnable, 1, wxALIGN_RIGHT | wxTOP, 10);
2964 m_cbEnable->Bind(wxEVT_CHECKBOX, &PluginPanel::OnPluginEnableToggle, this);
2965
2966 itemBoxSizer03->Add(5 * GetCharWidth(), 1, 0, wxALIGN_RIGHT | wxTOP, 10);
2967
2968 m_pDescription = new wxStaticText(
2969 this, wxID_ANY, m_plugin.m_short_description, wxDefaultPosition,
2970 wxSize(-1, -1) /*, wxST_NO_AUTORESIZE*/);
2971 itemBoxSizer02->Add(m_pDescription, 1, wxEXPAND | wxALL, 5);
2972 m_pDescription->Bind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this);
2973 m_pDescription->Bind(wxEVT_LEFT_UP, &PluginPanel::OnPluginSelectedUp, this);
2974 }
2975
2976 if (!bCompact) {
2977 m_info_btn = new WebsiteButton(this, "https:\\opencpn.org");
2978 m_info_btn->Hide();
2979 itemBoxSizer02->Add(m_info_btn, 0);
2980
2981 m_pButtons = new wxBoxSizer(wxHORIZONTAL);
2982 itemBoxSizer02->Add(m_pButtons, 0, /*wxEXPAND|*/ wxALL, 0);
2983 m_pButtonPreferences = new wxButton(this, wxID_ANY, _("Preferences"),
2984 wxDefaultPosition, wxDefaultSize, 0);
2985 m_pButtons->Add(m_pButtonPreferences, 0, wxALIGN_LEFT | wxALL, 2);
2986
2987 m_pButtons->AddSpacer(3 * GetCharWidth());
2988
2989 m_pButtonAction =
2990 new wxButton(this, wxID_ANY, "Upgrade to Version XX.XX.XX",
2991 wxDefaultPosition, wxDefaultSize, 0);
2992 m_pButtons->Add(m_pButtonAction, 0, wxALIGN_LEFT | wxALL, 2);
2993
2994 m_pButtonUninstall = new wxButton(this, wxID_ANY, _("Uninstall"),
2995 wxDefaultPosition, wxDefaultSize, 0);
2996 m_pButtons->Add(m_pButtonUninstall, 0, wxALIGN_LEFT | wxALL, 2);
2997 } else {
2998 m_pButtons = new wxBoxSizer(wxVERTICAL);
2999 itemBoxSizer02->Add(m_pButtons, 0, /*wxEXPAND|*/ wxALL, 0);
3000
3001 wxBoxSizer* tline = new wxBoxSizer(wxHORIZONTAL);
3002 m_pButtons->Add(tline, 0, wxALL, 2);
3003
3004 m_pButtonPreferences = new wxButton(this, wxID_ANY, _("Preferences"),
3005 wxDefaultPosition, wxDefaultSize, 0);
3006 tline->Add(m_pButtonPreferences, 0, wxALIGN_LEFT | wxALL, 0);
3007
3008 tline->AddSpacer(3 * GetCharWidth());
3009
3010 m_info_btn = new WebsiteButton(this, "https:\\opencpn.org");
3011 m_info_btn->Hide();
3012 tline->Add(m_info_btn, 0);
3013
3014 m_pButtonAction =
3015 new wxButton(this, wxID_ANY, "Upgrade to Version XX.XX.XX",
3016 wxDefaultPosition, wxDefaultSize);
3017 m_pButtons->Add(m_pButtonAction, 0, wxALIGN_LEFT | wxALL, 2);
3018
3019 m_pButtonUninstall = new wxButton(this, wxID_ANY, _("Uninstall"),
3020 wxDefaultPosition, wxDefaultSize, 0);
3021 m_pButtons->Add(m_pButtonUninstall, 0, wxALIGN_LEFT | wxALL, 2);
3022 }
3023
3024 wxBitmap statusBitmap;
3025 const auto stat = m_plugin.m_status;
3026 auto icon_name = icon_by_status.at(stat);
3027 if (stat == PluginStatus::Imported &&
3028 IsUpdateAvailable(m_plugin.m_managed_metadata)) {
3029 icon_name =
3030 icon_by_status.at(PluginStatus::ManagedInstalledUpdateAvailable);
3031 }
3032
3033 wxFileName path(g_Platform->GetSharedDataDir(), icon_name);
3034 path.AppendDir("uidata");
3035 path.AppendDir("traditional");
3036 bool ok = false;
3037 int bmsize = GetCharWidth() * 3 * dpi_mult;
3038 if (path.IsFileReadable()) {
3039 statusBitmap = LoadSVG(path.GetFullPath(), bmsize, bmsize);
3040 ok = statusBitmap.IsOk();
3041 }
3042 if (!ok) {
3043 auto style = g_StyleManager->GetCurrentStyle();
3044 statusBitmap = wxBitmap(style->GetIcon(_T("default_pi"), bmsize, bmsize));
3045 wxLogMessage("Icon: %s not found.", path.GetFullPath());
3046 }
3047
3048 m_itemStatusIconBitmap = new wxStaticBitmap(this, wxID_ANY, statusBitmap);
3049 m_itemStatusIconBitmap->SetToolTip(message_by_status(stat));
3050 m_itemStatusIconBitmap->Bind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected,
3051 this);
3052 m_itemStatusIconBitmap->Bind(wxEVT_LEFT_UP, &PluginPanel::OnPluginSelectedUp,
3053 this);
3054
3055 itemBoxSizer01->Add(m_itemStatusIconBitmap, 0, wxEXPAND | wxALL, 20);
3056
3057 itemBoxSizer02->AddSpacer(GetCharWidth());
3058
3059 m_pButtonPreferences->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
3060 &PluginPanel::OnPluginPreferences, this);
3061 m_pButtonUninstall->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
3062 &PluginPanel::OnPluginUninstall, this);
3063 m_pButtonAction->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
3064 &PluginPanel::OnPluginAction, this);
3065
3066 SetSelected(m_bSelected);
3067 SetAutoLayout(true);
3068 Fit();
3069}
3070
3071PluginPanel::PluginPanel(wxPanel* parent, wxWindowID id, const wxPoint& pos,
3072 const wxSize& size, PluginMetadata md)
3073 : PluginPanel(parent, id, pos, size, PlugInData(md)) {};
3074
3075PluginPanel::~PluginPanel() {
3076 Unbind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this);
3077 if (m_is_safe_panel) return;
3078 m_itemStaticBitmap->Unbind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected,
3079 this);
3080 m_pName->Unbind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this);
3081 m_pVersion->Unbind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this);
3082 m_pDescription->Unbind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this);
3083 if (m_pButtonAction) {
3084 m_pButtonAction->Unbind(wxEVT_COMMAND_BUTTON_CLICKED,
3085 &PluginPanel::OnPluginAction, this);
3086 }
3087 m_pButtonPreferences->Unbind(wxEVT_COMMAND_BUTTON_CLICKED,
3088 &PluginPanel::OnPluginPreferences, this);
3089 m_cbEnable->Unbind(wxEVT_COMMAND_BUTTON_CLICKED,
3090 &PluginPanel::OnPluginEnableToggle, this);
3091}
3092
3093void PluginPanel::SetActionLabel(wxString& label) {
3094 m_pButtonAction->SetLabel(label);
3095 Refresh();
3096}
3097
3098static wxStopWatch swclick;
3099static int downx, downy;
3100
3101void PluginPanel::OnPluginSelected(wxMouseEvent& event) {
3102#ifdef __ANDROID__
3103 swclick.Start();
3104 event.GetPosition(&downx, &downy);
3105#else
3106 DoPluginSelect();
3107#endif
3108}
3109
3110void PluginPanel::OnPluginSelectedUp(wxMouseEvent& event) {
3111#ifdef __ANDROID__
3112 qDebug() << swclick.Time();
3113 if (swclick.Time() < 200) {
3114 int upx, upy;
3115 event.GetPosition(&upx, &upy);
3116 if ((fabs(upx - downx) < GetCharWidth()) &&
3117 (fabs(upy - downy) < GetCharWidth())) {
3118 DoPluginSelect();
3119 }
3120 }
3121 swclick.Start();
3122#endif
3123}
3124
3125void PluginPanel::DoPluginSelect() {
3126 if (m_plugin.m_status == PluginStatus::ManagedInstallAvailable) {
3127 // auto dialog = dynamic_cast<PluginListPanel*>(GetParent());
3128 // auto dialog = dynamic_cast<PluginListPanel*>(m_parent);
3129 // wxASSERT(dialog != 0);
3130
3131 // Install the new plugin, auto-enabling as a convenience measure.
3132 run_update_dialog(m_PluginListPanel, &m_plugin, false, 0, true);
3133 } else if (m_bSelected) {
3134 SetSelected(false);
3135 m_PluginListPanel->SelectPlugin(NULL);
3136 } else {
3137 SetSelected(true);
3138 m_PluginListPanel->SelectPlugin(this);
3139 }
3140}
3141
3146static PluginMetadata GetMetadataByName(const std::string& name) {
3147 auto plugins = PluginHandler::GetInstance()->GetInstalled();
3148 auto predicate = [name](const PluginMetadata& pm) { return pm.name == name; };
3149 auto found = std::find_if(plugins.begin(), plugins.end(), predicate);
3150 if (found == plugins.end()) {
3151 wxLogDebug("Cannot find metadata for %s", name.c_str());
3152 }
3153 return found != plugins.end() ? *found : PluginMetadata();
3154}
3155
3156void PluginPanel::SetSelected(bool selected) {
3157 m_bSelected = selected;
3158
3159 m_pVersion->SetLabel(
3160 PluginLoader::GetPluginVersion(m_plugin, GetMetadataByName));
3161 if (selected) {
3162 SetBackgroundColour(GetDialogColor(DLG_SELECTED_BACKGROUND));
3163 m_pButtons->Show(true);
3164 bool unInstallPossible = canUninstall(m_plugin.m_common_name.ToStdString());
3165
3166 // Directly mark Legacy and system plugins as "not uninstallable"
3167 if (m_plugin.m_status == PluginStatus::LegacyUpdateAvailable ||
3168 m_plugin.m_status == PluginStatus::Unmanaged ||
3169 m_plugin.m_status == PluginStatus::System)
3170 unInstallPossible = false;
3171
3172 // Orphan plugins can usually be uninstalled, at best effort.
3173 if (m_plugin.m_managed_metadata.is_orphan) unInstallPossible = true;
3174
3175 m_pButtonUninstall->Show(unInstallPossible);
3176
3177 if (m_plugin.m_managed_metadata.info_url.size()) {
3178 m_info_btn->SetURL(m_plugin.m_managed_metadata.info_url.c_str());
3179 m_info_btn->Show();
3180 }
3181
3182 m_cbEnable->Show(true);
3183
3184 // Configure the "Action" button
3185 wxString label;
3186 SemanticVersion newVersion;
3187 switch (m_plugin.m_status) {
3188 case PluginStatus::LegacyUpdateAvailable:
3189 label = _("Upgrade to Version ");
3190 label += wxString(m_plugin.m_managed_metadata.version.c_str());
3191 m_action = ActionVerb::UPGRADE_TO_MANAGED_VERSION;
3192 m_pButtonAction->Enable();
3193 break;
3194
3195 case PluginStatus::ManagedInstallAvailable:
3196 label = _("Install...");
3197 m_action = ActionVerb::INSTALL_MANAGED_VERSION;
3198 m_pButtonAction->Enable();
3199 break;
3200
3201 case PluginStatus::ManagedInstalledUpdateAvailable:
3202 label = _("Update to ");
3203 label += wxString(m_plugin.m_managed_metadata.version.c_str());
3204 m_action = ActionVerb::UPGRADE_INSTALLED_MANAGED_VERSION;
3205 m_pButtonAction->Enable();
3206 break;
3207
3208 case PluginStatus::ManagedInstalledCurrentVersion:
3209 label = _("Reinstall");
3210 m_action = ActionVerb::REINSTALL_MANAGED_VERSION;
3211 m_pButtonAction->Enable();
3212 break;
3213
3214 case PluginStatus::ManagedInstalledDowngradeAvailable:
3215 label = _("Downgrade");
3216 m_action = ActionVerb::DOWNGRADE_INSTALLED_MANAGED_VERSION;
3217 m_pButtonAction->Enable();
3218 break;
3219
3220 case PluginStatus::Imported:
3221 if (IsUpdateAvailable(m_plugin.m_managed_metadata)) {
3222 label = _("Update");
3223 m_action = ActionVerb::UPDATE_IMPORTED_VERSION;
3224 } else {
3225 m_pButtonAction->Hide();
3226 m_action = ActionVerb::NOP;
3227 }
3228 break;
3229
3231 m_action = ActionVerb::NOP;
3232 m_pButtonAction->Hide();
3233 break;
3234
3236 m_action = ActionVerb::NOP;
3237 m_pButtonAction->Hide();
3238 break;
3239
3240 default:
3241 label = "TBD";
3242 m_action = ActionVerb::NOP;
3243 break;
3244 }
3245 SetActionLabel(label);
3246 Layout();
3247 } else {
3248 SetBackgroundColour(GetDialogColor(DLG_UNSELECTED_BACKGROUND));
3249 // m_pDescription->SetLabel( m_pPlugin->m_short_description );
3250#ifndef __WXQT__
3251 // m_pButtons->Show(false);
3252#else
3253 // m_pButtons->Show(true);
3254#endif
3255 //();
3256
3257 m_pButtons->Show(false);
3258 m_info_btn->Hide();
3259
3260 if (m_plugin.m_status == PluginStatus::ManagedInstallAvailable)
3261 m_cbEnable->Show(false);
3262
3263 Layout();
3264 }
3265
3266 // m_pButtons->Show(selected); // For most platforms, show buttons if
3267 // selected m_pButtonsUpDown->Show(selected);
3268#ifdef __ANDROID__
3269 // Some Android devices (e.g. Kyocera) have trouble with wxBitmapButton...
3270 // m_pButtonsUpDown->Show(false);
3271 // m_pButtons->Show(true); // Always enable buttons for Android
3272#endif
3273
3274 Layout();
3275
3276 if (selected) {
3277 SetBackgroundColour(GetDialogColor(DLG_SELECTED_BACKGROUND));
3278 } else {
3279 SetBackgroundColour(GetDialogColor(DLG_UNSELECTED_BACKGROUND));
3280 }
3281
3282 SetEnabled(m_plugin.m_enabled);
3283
3284#ifdef __ANDROID__
3285 // Android (wxQT) sizers have troubles...
3286 // So we set some layout factors to avoid re-sizing on select/deselect.
3287 // m_rgSizer->Show(true);
3288 // m_pButtons->Show(true);
3289 // m_pButtonAction->Hide();
3290 // m_pButtonUninstall->Hide();
3291
3292 Fit();
3293 // m_PluginListPanel->m_pitemBoxSizer01->Layout();
3294#endif
3295}
3296
3297void PluginPanel::OnPaint(wxPaintEvent& event) {
3298 wxPaintDC dc(this);
3299
3300 int penWidth = m_penWidthUnselected;
3301 wxColour color = GetDialogColor(DLG_UNSELECTED_BACKGROUND);
3302 wxColour border = GetDialogColor(DLG_UNSELECTED_ACCENT);
3303
3304 if (m_bSelected) {
3305 penWidth = m_penWidthSelected;
3306 color = GetDialogColor(DLG_SELECTED_BACKGROUND);
3307 border = GetDialogColor(DLG_SELECTED_ACCENT);
3308 }
3309
3310 wxBrush b(color, wxBRUSHSTYLE_SOLID);
3311 dc.SetBrush(b);
3312 dc.SetPen(wxPen(border, penWidth));
3313
3314 dc.DrawRoundedRectangle(5, 5, GetSize().x - 10, GetSize().y - 10, 5);
3315}
3316
3317void PluginPanel::OnPluginPreferences(wxCommandEvent& event) {
3318 if (m_plugin.m_enabled && m_plugin.m_init_state &&
3319 (m_plugin.m_cap_flag & WANTS_PREFERENCES)) {
3320#ifdef __ANDROID__
3321 androidDisableRotation();
3322 PluginLoader::GetInstance()->ShowPreferencesDialog(m_plugin,
3323 GetGrandParent());
3324 // GrandParent will be the entire list panel, not the plugin panel.
3325 // Ensures better centering on small screens
3326#else
3327 PluginLoader::GetInstance()->ShowPreferencesDialog(m_plugin, this);
3328#endif
3329 }
3330}
3331
3332void PluginPanel::OnPluginEnableToggle(wxCommandEvent& event) {
3333 SetEnabled(event.IsChecked());
3334 m_pVersion->SetLabel(
3335 PluginLoader::GetPluginVersion(m_plugin, GetMetadataByName));
3336 if (m_plugin.m_status == PluginStatus::System) {
3337 // Force pluginmanager to reload all panels. Not kosher --
3338 // the EventVar should really only be notified from within PluginLoader.
3339 PluginLoader::GetInstance()->evt_pluglist_change.Notify();
3340 }
3341}
3342
3343void PluginPanel::OnPluginUninstall(wxCommandEvent& event) {
3344 m_action = ActionVerb::UNINSTALL_MANAGED_VERSION;
3345
3346 // Chain up to the utility event handler
3347 wxCommandEvent actionEvent(wxEVT_COMMAND_BUTTON_CLICKED);
3348 actionEvent.SetId(ID_CMD_BUTTON_PERFORM_ACTION);
3349 actionEvent.SetClientData(this);
3350 g_pi_manager->GetUtilHandler()->AddPendingEvent(actionEvent);
3351}
3352
3353void PluginPanel::OnPluginAction(wxCommandEvent& event) {
3354 // Chain up to the utility event handler
3355 wxCommandEvent actionEvent(wxEVT_COMMAND_BUTTON_CLICKED);
3356 actionEvent.SetId(ID_CMD_BUTTON_PERFORM_ACTION);
3357 actionEvent.SetClientData(this);
3358 g_pi_manager->GetUtilHandler()->AddPendingEvent(actionEvent);
3359
3360 return;
3361}
3362
3363static void SetWindowFontStyle(wxWindow* window, wxFontStyle style) {
3364 auto font = window->GetFont();
3365 font.SetStyle(style);
3366 window->SetFont(font);
3367}
3368
3369void PluginPanel::SetEnabled(bool enabled) {
3370 if (m_is_safe_panel) return;
3371 PluginLoader::GetInstance()->SetEnabled(m_plugin.m_common_name, enabled);
3372 PluginLoader::GetInstance()->UpdatePlugIns();
3373 if (enabled) NotifySetupOptionsPlugin(&m_plugin);
3374 if (!enabled && !m_bSelected) {
3375 SetWindowFontStyle(m_pName, wxFONTSTYLE_ITALIC);
3376 SetWindowFontStyle(m_pVersion, wxFONTSTYLE_ITALIC);
3377 SetWindowFontStyle(m_pDescription, wxFONTSTYLE_ITALIC);
3378#ifdef x__ANDROID__
3379 m_pName->Disable();
3380 m_pVersion->Disable();
3381 m_pDescription->Disable();
3382#endif
3383 } else {
3384 SetWindowFontStyle(m_pName, wxFONTSTYLE_NORMAL);
3385 SetWindowFontStyle(m_pVersion, wxFONTSTYLE_NORMAL);
3386 SetWindowFontStyle(m_pDescription, wxFONTSTYLE_NORMAL);
3387#ifdef x__ANDROID__
3388 m_pName->Enable();
3389 m_pVersion->Enable();
3390 m_pDescription->Enable();
3391#endif
3392 }
3393
3394#ifdef __ANDROID__
3395 m_pName->Enable(enabled || m_bSelected);
3396 m_pVersion->Enable(enabled || m_bSelected);
3397 m_pDescription->Enable(enabled || m_bSelected);
3398#endif
3399
3400 if (m_bSelected) {
3401 wxString description = m_plugin.m_long_description;
3402 if (description.IsEmpty())
3403 description = wxString(m_plugin.m_managed_metadata.description.c_str());
3404
3405 PanelHardBreakWrapper wrapper(this, description,
3406 g_options->GetSize().x * 7 / 10);
3407 m_pDescription->SetLabel(wrapper.GetWrapped());
3408 if (m_plugin.m_managed_metadata.info_url.size()) {
3409 m_info_btn->SetURL(m_plugin.m_managed_metadata.info_url.c_str());
3410 m_info_btn->Show();
3411 }
3412 } else {
3413 wxString description = m_plugin.m_short_description;
3414 if (description.IsEmpty())
3415 description = wxString(m_plugin.m_managed_metadata.summary.c_str());
3416 PanelHardBreakWrapper wrapper(this, description,
3417 g_options->GetSize().x * 7 / 10);
3418 m_pDescription->SetLabel(wrapper.GetWrapped());
3419 }
3420
3421 m_pButtonPreferences->Enable(enabled &&
3422 (m_plugin.m_cap_flag & WANTS_PREFERENCES));
3423 m_cbEnable->SetValue(enabled);
3424}
3425
3426void PluginPanel::OnPluginUp(wxCommandEvent& event) {
3427 m_PluginListPanel->MoveUp(this);
3428}
3429
3430void PluginPanel::OnPluginDown(wxCommandEvent& event) {
3431 m_PluginListPanel->MoveDown(this);
3432}
3433
3435WebsiteButton::WebsiteButton(wxWindow* parent, const char* url)
3436 : wxPanel(parent), m_url(url) {
3437 auto vbox = new wxBoxSizer(wxVERTICAL);
3438 auto button = new wxButton(this, wxID_ANY, _("Website"));
3439 button->Enable(strlen(url) > 0);
3440 vbox->Add(button);
3441 SetSizer(vbox);
3442 Bind(wxEVT_COMMAND_BUTTON_CLICKED,
3443 [=](wxCommandEvent&) { wxLaunchDefaultBrowser(m_url); });
3444}
3445
3446// ----------------------------------------------------------------------------
3447// PlugInChartBase Implmentation
3448// This class is the base class for Plug-able chart types
3449// ----------------------------------------------------------------------------
3450
3451PlugInChartBase::PlugInChartBase() { m_Chart_Error_Factor = 0.; }
3452
3453PlugInChartBase::~PlugInChartBase() {}
3454
3455wxString PlugInChartBase::GetFileSearchMask(void) { return _T(""); }
3456
3457int PlugInChartBase::Init(const wxString& name, int init_flags) { return 0; }
3458
3459// Accessors
3460
3461double PlugInChartBase::GetNormalScaleMin(double canvas_scale_factor,
3462 bool b_allow_overzoom) {
3463 return 1.0;
3464}
3465
3466double PlugInChartBase::GetNormalScaleMax(double canvas_scale_factor,
3467 int canvas_width) {
3468 return 2.0e7;
3469}
3470
3471bool PlugInChartBase::GetChartExtent(ExtentPI* pext) { return false; }
3472
3474 const wxRegion& Region) {
3475 return wxNullBitmap;
3476}
3477
3479 PlugIn_ViewPort& vp_proposed) {
3480 return false;
3481}
3482
3484 wxRegion* pValidRegion) {}
3485
3486void PlugInChartBase::SetColorScheme(int cs, bool bApplyImmediate) {}
3487
3488double PlugInChartBase::GetNearestPreferredScalePPM(double target_scale_ppm) {
3489 return 1.0;
3490}
3491
3492wxBitmap* PlugInChartBase::GetThumbnail(int tnx, int tny, int cs) {
3493 return NULL;
3494}
3495
3497 wxRect* pSourceRect) {}
3498
3500
3501bool PlugInChartBase::GetChartBits(wxRect& source, unsigned char* pPix,
3502 int sub_samp) {
3503 return false;
3504}
3505
3507
3509
3510void PlugInChartBase::latlong_to_chartpix(double lat, double lon, double& pixx,
3511 double& pixy) {}
3512
3513void PlugInChartBase::chartpix_to_latlong(double pixx, double pixy,
3514 double* plat, double* plon) {}
3515
3516// ----------------------------------------------------------------------------
3517// PlugInChartBaseGL Implementation
3518//
3519// ----------------------------------------------------------------------------
3520
3521PlugInChartBaseGL::PlugInChartBaseGL() {}
3522
3523PlugInChartBaseGL::~PlugInChartBaseGL() {}
3524
3526 const PlugIn_ViewPort& VPoint,
3527 const wxRegion& Region,
3528 bool b_use_stencil) {
3529 return 0;
3530}
3531
3533 float lat, float lon, float select_radius, PlugIn_ViewPort* VPoint) {
3534 return NULL;
3535}
3536
3537wxString PlugInChartBaseGL::CreateObjDescriptions(ListOfPI_S57Obj* obj_list) {
3538 return _T("");
3539}
3540
3542
3543int PlugInChartBaseGL::GetNoCOVRTablePoints(int iTable) { return 0; }
3544
3545int PlugInChartBaseGL::GetNoCOVRTablenPoints(int iTable) { return 0; }
3546
3547float* PlugInChartBaseGL::GetNoCOVRTableHead(int iTable) { return 0; }
3548
3549// ----------------------------------------------------------------------------
3550// PlugInChartBaseExtended Implementation
3551//
3552// ----------------------------------------------------------------------------
3553
3554PlugInChartBaseExtended::PlugInChartBaseExtended() {}
3555
3556PlugInChartBaseExtended::~PlugInChartBaseExtended() {}
3557
3559 const PlugIn_ViewPort& VPoint,
3560 const wxRegion& Region,
3561 bool b_use_stencil) {
3562 return 0;
3563}
3564
3566 const wxGLContext& glc, const PlugIn_ViewPort& VPoint,
3567 const wxRegion& Region, bool b_use_stencil) {
3568 return 0;
3569}
3570
3572 const wxGLContext& glc, const PlugIn_ViewPort& VPoint,
3573 const wxRegion& Region, bool b_use_stencil) {
3574 return 0;
3575}
3576
3578 const PlugIn_ViewPort& VPoint, const wxRegion& Region) {
3579 return wxNullBitmap;
3580}
3581
3583 wxMemoryDC& dc, const PlugIn_ViewPort& VPoint, const wxRegion& Region) {
3584 return false;
3585}
3586
3587ListOfPI_S57Obj* PlugInChartBaseExtended::GetObjRuleListAtLatLon(
3588 float lat, float lon, float select_radius, PlugIn_ViewPort* VPoint) {
3589 return NULL;
3590}
3591
3592wxString PlugInChartBaseExtended::CreateObjDescriptions(
3593 ListOfPI_S57Obj* obj_list) {
3594 return _T("");
3595}
3596
3597int PlugInChartBaseExtended::GetNoCOVREntries() { return 0; }
3598
3599int PlugInChartBaseExtended::GetNoCOVRTablePoints(int iTable) { return 0; }
3600
3601int PlugInChartBaseExtended::GetNoCOVRTablenPoints(int iTable) { return 0; }
3602
3603float* PlugInChartBaseExtended::GetNoCOVRTableHead(int iTable) { return 0; }
3604
3606
3607// ----------------------------------------------------------------------------
3608// PlugInChartBaseExtendedPlus2 Implementation
3609//
3610// ----------------------------------------------------------------------------
3611
3612PlugInChartBaseExtendedPlus2::PlugInChartBaseExtendedPlus2() {}
3613
3614PlugInChartBaseExtendedPlus2::~PlugInChartBaseExtendedPlus2() {}
3615
3616ListOfPI_S57Obj*
3617PlugInChartBaseExtendedPlus2::GetLightsObjRuleListVisibleAtLatLon(
3618 float lat, float lon, PlugIn_ViewPort* VPoint) {
3619 return NULL;
3620}
3621
3622// ----------------------------------------------------------------------------
3623// PlugInChartBaseGLPlus2 Implementation
3624//
3625// ----------------------------------------------------------------------------
3626
3627PlugInChartBaseGLPlus2::PlugInChartBaseGLPlus2() {}
3628
3629PlugInChartBaseGLPlus2::~PlugInChartBaseGLPlus2() {}
3630
3632 float lat, float lon, PlugIn_ViewPort* VPoint) {
3633 return NULL;
3634}
3635
3636// ----------------------------------------------------------------------------
3637// ChartPlugInWrapper Implementation
3638// This class is a wrapper/interface to PlugIn charts(PlugInChartBase)
3639// ----------------------------------------------------------------------------
3640
3641ChartPlugInWrapper::ChartPlugInWrapper() {}
3642
3643ChartPlugInWrapper::ChartPlugInWrapper(const wxString& chart_class) {
3644 m_ppo = ::wxCreateDynamicObject(chart_class);
3645 m_ppicb = dynamic_cast<PlugInChartBase*>(m_ppo);
3646}
3647
3648ChartPlugInWrapper::~ChartPlugInWrapper() {
3649 if (m_ppicb) delete m_ppicb;
3650}
3651
3652wxString ChartPlugInWrapper::GetFileSearchMask(void) {
3653 if (m_ppicb)
3654 return m_ppicb->GetFileSearchMask();
3655 else
3656 return _T("");
3657}
3658
3659InitReturn ChartPlugInWrapper::Init(const wxString& name,
3660 ChartInitFlag init_flags) {
3661 if (m_ppicb) {
3662 wxWindow* pa = wxWindow::FindFocus();
3663
3664 InitReturn ret_val = (InitReturn)m_ppicb->Init(name, (int)init_flags);
3665
3666 // Here we transcribe all the required wrapped member elements up into
3667 // the chartbase object which is the parent of this class
3668 if (ret_val == INIT_OK) {
3669 m_FullPath = m_ppicb->GetFullPath();
3670 m_ChartType = (ChartTypeEnum)m_ppicb->GetChartType();
3671 m_ChartFamily = (ChartFamilyEnum)m_ppicb->GetChartFamily();
3672 m_projection = (OcpnProjType)m_ppicb->GetChartProjection();
3673 m_EdDate = m_ppicb->GetEditionDate();
3674 m_Name = m_ppicb->GetName();
3675 m_ID = m_ppicb->GetID();
3676 m_DepthUnits = m_ppicb->GetDepthUnits();
3677 m_SoundingsDatum = m_ppicb->GetSoundingsDatum();
3678 m_datum_str = m_ppicb->GetDatumString();
3679 m_SE = m_ppicb->GetSE();
3680 m_EdDate = m_ppicb->GetEditionDate();
3681 m_ExtraInfo = m_ppicb->GetExtraInfo();
3682 Chart_Error_Factor = m_ppicb->GetChartErrorFactor();
3683 m_depth_unit_id = (ChartDepthUnitType)m_ppicb->GetDepthUnitId();
3684 m_Chart_Skew = m_ppicb->GetChartSkew();
3685 m_Chart_Scale = m_ppicb->GetNativeScale();
3686
3687 // We estimate ppm_avg as needed by raster texture cache logic...
3688 // This number works for average BSB charts, scanned with average
3689 // resolution
3690 m_ppm_avg = 10000. / m_ppicb->GetNativeScale(); // fallback value
3691
3692 // Calcuculate a "better" ppm from the chart geo extent and raster size.
3693 if ((fabs(m_Chart_Skew) < .01) &&
3694 (CHART_FAMILY_RASTER == m_ChartFamily)) {
3695 Extent extent;
3696 if (GetChartExtent(&extent)) {
3697 double lon_range = extent.ELON - extent.WLON;
3698 if ((lon_range > 0) &&
3699 (lon_range < 90.0)) // Be safe about IDL crossing and huge charts
3700 m_ppm_avg = GetSize_X() / (lon_range * 1852 * 60);
3701 }
3702 }
3703
3704 m_overlayENC = false;
3705 if (m_ChartFamily == (ChartFamilyEnum)PI_CHART_FAMILY_VECTOR) {
3706 wxCharBuffer buf = m_FullPath.ToUTF8();
3707 m_overlayENC = s57chart::IsCellOverlayType(buf.data());
3708 }
3709
3710 bReadyToRender = m_ppicb->IsReadyToRender();
3711 } else {
3712 // Mark the chart as unable to render
3713 m_ChartType = CHART_TYPE_UNKNOWN;
3714 m_ChartFamily = CHART_FAMILY_UNKNOWN;
3715 }
3716
3717 // PlugIn may invoke wxExecute(), which steals the keyboard focus
3718 // So take it back
3719 auto pc = dynamic_cast<ChartCanvas*>(pa);
3720 if (pc) pc->SetFocus();
3721
3722 return ret_val;
3723 } else
3724 return INIT_FAIL_REMOVE;
3725}
3726
3727// Accessors
3728int ChartPlugInWrapper::GetCOVREntries() {
3729 if (m_ppicb)
3730 return m_ppicb->GetCOVREntries();
3731 else
3732 return 0;
3733}
3734
3735int ChartPlugInWrapper::GetCOVRTablePoints(int iTable) {
3736 if (m_ppicb)
3737 return m_ppicb->GetCOVRTablePoints(iTable);
3738 else
3739 return 0;
3740}
3741
3742int ChartPlugInWrapper::GetCOVRTablenPoints(int iTable) {
3743 if (m_ppicb)
3744 return m_ppicb->GetCOVRTablenPoints(iTable);
3745 else
3746 return 0;
3747}
3748
3749float* ChartPlugInWrapper::GetCOVRTableHead(int iTable) {
3750 if (m_ppicb)
3751 return m_ppicb->GetCOVRTableHead(iTable);
3752 else
3753 return 0;
3754}
3755
3756// TODO
3757// PlugIn chart types do not properly support NoCovr Regions
3758// Proper fix is to update PlugIn Chart Type API
3759// Derive an extended PlugIn chart class from existing class,
3760// and use some kind of RTTI to figure out which class to call.
3761int ChartPlugInWrapper::GetNoCOVREntries() {
3762 if (m_ppicb) {
3763 PlugInChartBaseGL* ppicbgl = dynamic_cast<PlugInChartBaseGL*>(m_ppicb);
3764 if (ppicbgl) {
3765 return ppicbgl->GetNoCOVREntries();
3766 }
3767 }
3768 return 0;
3769}
3770
3771int ChartPlugInWrapper::GetNoCOVRTablePoints(int iTable) {
3772 if (m_ppicb) {
3773 PlugInChartBaseGL* ppicbgl = dynamic_cast<PlugInChartBaseGL*>(m_ppicb);
3774 if (ppicbgl) {
3775 return ppicbgl->GetNoCOVRTablePoints(iTable);
3776 }
3777 }
3778 return 0;
3779}
3780
3781int ChartPlugInWrapper::GetNoCOVRTablenPoints(int iTable) {
3782 if (m_ppicb) {
3783 PlugInChartBaseGL* ppicbgl = dynamic_cast<PlugInChartBaseGL*>(m_ppicb);
3784 if (ppicbgl) {
3785 return ppicbgl->GetNoCOVRTablenPoints(iTable);
3786 }
3787 }
3788 return 0;
3789}
3790
3791float* ChartPlugInWrapper::GetNoCOVRTableHead(int iTable) {
3792 if (m_ppicb) {
3793 PlugInChartBaseGL* ppicbgl = dynamic_cast<PlugInChartBaseGL*>(m_ppicb);
3794 if (ppicbgl) {
3795 return ppicbgl->GetNoCOVRTableHead(iTable);
3796 }
3797 }
3798 return 0;
3799}
3800
3801bool ChartPlugInWrapper::GetChartExtent(Extent* pext) {
3802 if (m_ppicb) {
3803 ExtentPI xpi;
3804 if (m_ppicb->GetChartExtent(&xpi)) {
3805 pext->NLAT = xpi.NLAT;
3806 pext->SLAT = xpi.SLAT;
3807 pext->ELON = xpi.ELON;
3808 pext->WLON = xpi.WLON;
3809
3810 return true;
3811 } else
3812 return false;
3813 } else
3814 return false;
3815}
3816
3817ThumbData* ChartPlugInWrapper::GetThumbData(int tnx, int tny, float lat,
3818 float lon) {
3819 if (m_ppicb) {
3820 // Create the bitmap if needed, doing a deep copy from the Bitmap owned
3821 // by the PlugIn Chart
3822 if (!pThumbData->pDIBThumb) {
3823 wxBitmap* pBMPOwnedByChart =
3824 m_ppicb->GetThumbnail(tnx, tny, m_global_color_scheme);
3825 if (pBMPOwnedByChart) {
3826 wxImage img = pBMPOwnedByChart->ConvertToImage();
3827 pThumbData->pDIBThumb = new wxBitmap(img);
3828 } else
3829 pThumbData->pDIBThumb = NULL;
3830 }
3831
3832 pThumbData->Thumb_Size_X = tnx;
3833 pThumbData->Thumb_Size_Y = tny;
3834
3835 /*
3836 // Plot the supplied Lat/Lon on the thumbnail
3837 int divx = m_ppicb->Size_X / tnx;
3838 int divy = m_ppicb->Size_Y / tny;
3839
3840 int div_factor = __min(divx, divy);
3841
3842 int pixx, pixy;
3843
3844
3845 // Using a temporary synthetic ViewPort and source rectangle,
3846 // calculate the ships position on the thumbnail
3847 ViewPort tvp;
3848 tvp.pix_width = tnx;
3849 tvp.pix_height = tny;
3850 tvp.view_scale_ppm = GetPPM() / div_factor;
3851 wxRect trex = Rsrc;
3852 Rsrc.x = 0;
3853 Rsrc.y = 0;
3854 latlong_to_pix_vp(lat, lon, pixx, pixy, tvp);
3855 Rsrc = trex;
3856
3857 pThumbData->ShipX = pixx;// / div_factor;
3858 pThumbData->ShipY = pixy;// / div_factor;
3859 */
3860 pThumbData->ShipX = 0;
3861 pThumbData->ShipY = 0;
3862
3863 return pThumbData;
3864 } else
3865 return NULL;
3866}
3867
3868ThumbData* ChartPlugInWrapper::GetThumbData() { return pThumbData; }
3869
3870bool ChartPlugInWrapper::UpdateThumbData(double lat, double lon) {
3871 return true;
3872}
3873
3874double ChartPlugInWrapper::GetNormalScaleMin(double canvas_scale_factor,
3875 bool b_allow_overzoom) {
3876 if (m_ppicb)
3877 return m_ppicb->GetNormalScaleMin(canvas_scale_factor, b_allow_overzoom);
3878 else
3879 return 1.0;
3880}
3881
3882double ChartPlugInWrapper::GetNormalScaleMax(double canvas_scale_factor,
3883 int canvas_width) {
3884 if (m_ppicb)
3885 return m_ppicb->GetNormalScaleMax(canvas_scale_factor, canvas_width);
3886 else
3887 return 2.0e7;
3888}
3889
3890/* RectRegion:
3891 * This is the Screen region desired to be updated. Will
3892 * be either 1 rectangle(full screen) or two rectangles (panning with FBO
3893 * accelerated pan logic)
3894 *
3895 * Region:
3896 * This is the LLRegion describing the quilt active region
3897 * for this chart.
3898 *
3899 * So, Actual rendering area onscreen should be clipped to the
3900 * intersection of the two regions.
3901 */
3902
3903// Render helpers
3904void RenderRotateToViewPort(const ViewPort& VPoint) {
3905#ifndef USE_ANDROID_GLES2
3906 float xt = VPoint.pix_width / 2.0, yt = VPoint.pix_height / 2.0;
3907 glTranslatef(xt, yt, 0);
3908 glRotatef(VPoint.rotation * 180. / PI, 0, 0, 1);
3909 glTranslatef(-xt, -yt, 0);
3910#endif
3911}
3912
3913void UndoRenderRotateToViewPort(const ViewPort& VPoint) {
3914#ifndef USE_ANDROID_GLES2
3915 float xt = VPoint.pix_width / 2.0, yt = VPoint.pix_height / 2.0;
3916 glTranslatef(xt, yt, 0);
3917 glRotatef(-VPoint.rotation * 180. / PI, 0, 0, 1);
3918 glTranslatef(-xt, -yt, 0);
3919#endif
3920}
3921
3922bool ChartPlugInWrapper::RenderRegionViewOnGL(const wxGLContext& glc,
3923 const ViewPort& VPoint,
3924 const OCPNRegion& RectRegion,
3925 const LLRegion& Region) {
3926#ifdef ocpnUSE_GL
3927 if (m_ppicb) {
3928 ViewPort vp = VPoint; // non-const copy
3929
3930 gs_plib_flags = 0; // reset the CAPs flag
3931 PlugInChartBaseGL* ppicb_gl = dynamic_cast<PlugInChartBaseGL*>(m_ppicb);
3932 PlugInChartBaseExtended* ppicb_x =
3933 dynamic_cast<PlugInChartBaseExtended*>(m_ppicb);
3934 if (!Region.Empty() && (ppicb_gl || ppicb_x)) {
3935 wxRegion* r = RectRegion.GetNew_wxRegion();
3936 for (OCPNRegionIterator upd(RectRegion); upd.HaveRects();
3937 upd.NextRect()) {
3938 LLRegion chart_region = vp.GetLLRegion(upd.GetRect());
3939 chart_region.Intersect(Region);
3940
3941 if (!chart_region.Empty()) {
3942 ViewPort cvp = glChartCanvas::ClippedViewport(VPoint, chart_region);
3943
3944 glChartCanvas::SetClipRect(cvp, upd.GetRect(), false);
3945
3946 // ps52plib->m_last_clip_rect = upd.GetRect();
3947
3948#ifndef USE_ANDROID_GLES2
3949// glPushMatrix(); // Adjust for rotation
3950#endif
3951 RenderRotateToViewPort(VPoint);
3952
3953 PlugIn_ViewPort pivp = CreatePlugInViewport(cvp);
3954 if (ppicb_x)
3955 ppicb_x->RenderRegionViewOnGL(glc, pivp, *r,
3956 glChartCanvas::s_b_useStencil);
3957 else if (ppicb_gl)
3958 ppicb_gl->RenderRegionViewOnGL(glc, pivp, *r,
3959 glChartCanvas::s_b_useStencil);
3960 UndoRenderRotateToViewPort(VPoint);
3961
3962#ifndef USE_ANDROID_GLES2
3963// glPopMatrix();
3964#endif
3965 glChartCanvas::DisableClipRegion();
3966
3967 }
3968 } // for
3969 delete r;
3970 }
3971 } else
3972 return false;
3973#endif
3974 return true;
3975}
3976
3977// int indexrr;
3978
3979bool ChartPlugInWrapper::RenderRegionViewOnGLNoText(
3980 const wxGLContext& glc, const ViewPort& VPoint,
3981 const OCPNRegion& RectRegion, const LLRegion& Region) {
3982#ifdef ocpnUSE_GL
3983 if (m_ppicb) {
3984 // printf("\nCPIW::RRVOGLNT %d %d \n", indexrr++, m_Chart_Scale);
3985
3986 gs_plib_flags = 0; // reset the CAPs flag
3987 PlugInChartBaseExtended* ppicb_x =
3988 dynamic_cast<PlugInChartBaseExtended*>(m_ppicb);
3989 PlugInChartBaseGL* ppicb = dynamic_cast<PlugInChartBaseGL*>(m_ppicb);
3990 if (!Region.Empty() && ppicb_x) {
3991 // Start with a clean slate
3992 glChartCanvas::SetClipRect(VPoint, VPoint.rv_rect, false);
3993 glChartCanvas::DisableClipRegion();
3994
3995 // Apply rotation to this chart
3996 RenderRotateToViewPort(VPoint);
3997
3998 PlugIn_ViewPort pivp = CreatePlugInViewport(VPoint);
3999 wxRegion* r = RectRegion.GetNew_wxRegion();
4000
4001 ppicb_x->RenderRegionViewOnGLNoText(glc, pivp, *r,
4002 glChartCanvas::s_b_useStencil);
4003
4004 // Undo rotation
4005 UndoRenderRotateToViewPort(VPoint);
4006
4007 delete r;
4008 }
4009
4010 else if (!Region.Empty() &&
4011 ppicb) // Legacy Vector GL Plugin chart (e.g.S63)
4012 {
4013 ViewPort vp = VPoint; // non-const copy
4014 wxRegion* r = RectRegion.GetNew_wxRegion();
4015 for (OCPNRegionIterator upd(RectRegion); upd.HaveRects();
4016 upd.NextRect()) {
4017 LLRegion chart_region = vp.GetLLRegion(upd.GetRect());
4018 chart_region.Intersect(Region);
4019
4020 if (!chart_region.Empty()) {
4021 ViewPort cvp = glChartCanvas::ClippedViewport(VPoint, chart_region);
4022
4023 glChartCanvas::SetClipRect(cvp, upd.GetRect(), false);
4024
4025 RenderRotateToViewPort(VPoint);
4026
4027 PlugIn_ViewPort pivp = CreatePlugInViewport(cvp);
4028 ppicb->RenderRegionViewOnGL(glc, pivp, *r,
4029 glChartCanvas::s_b_useStencil);
4030
4031 // Undo rotation
4032 UndoRenderRotateToViewPort(VPoint);
4033
4034 glChartCanvas::DisableClipRegion();
4035
4036 }
4037 } // for
4038 delete r;
4039 }
4040
4041 } else
4042 return false;
4043#endif
4044 return true;
4045}
4046
4047bool ChartPlugInWrapper::RenderRegionViewOnGLTextOnly(
4048 const wxGLContext& glc, const ViewPort& VPoint, const OCPNRegion& Region) {
4049#ifdef ocpnUSE_GL
4050 if (m_ppicb) {
4051 gs_plib_flags = 0; // reset the CAPs flag
4052 PlugInChartBaseExtended* ppicb_x =
4053 dynamic_cast<PlugInChartBaseExtended*>(m_ppicb);
4054 if (!Region.Empty() && ppicb_x) {
4055 wxRegion* r = Region.GetNew_wxRegion();
4056 for (OCPNRegionIterator upd(Region); upd.HaveRects(); upd.NextRect()) {
4057#ifndef USE_ANDROID_GLES2
4058// glPushMatrix(); // Adjust for rotation
4059#endif
4060 RenderRotateToViewPort(VPoint);
4061
4062 PlugIn_ViewPort pivp = CreatePlugInViewport(VPoint);
4063 ppicb_x->RenderRegionViewOnGLTextOnly(glc, pivp, *r,
4064 glChartCanvas::s_b_useStencil);
4065 UndoRenderRotateToViewPort(VPoint);
4066
4067#ifndef USE_ANDROID_GLES2
4068// glPopMatrix();
4069#endif
4070
4071 } // for
4072 delete r;
4073 }
4074 } else
4075 return false;
4076#endif
4077 return true;
4078}
4079
4080bool ChartPlugInWrapper::RenderRegionViewOnDC(wxMemoryDC& dc,
4081 const ViewPort& VPoint,
4082 const OCPNRegion& Region) {
4083 if (m_ppicb) {
4084 gs_plib_flags = 0; // reset the CAPs flag
4085 PlugIn_ViewPort pivp = CreatePlugInViewport(VPoint);
4086 if (Region.IsOk()) {
4087 wxRegion* r = Region.GetNew_wxRegion();
4088 if (!m_overlayENC)
4089 dc.SelectObject(m_ppicb->RenderRegionView(pivp, *r));
4090 else {
4091 wxBitmap& obmp = m_ppicb->RenderRegionView(pivp, *r);
4092
4093 // Create a mask to remove the NODTA areas from overlay cells.
4094 wxColour nodat = GetGlobalColor(_T ( "NODTA" ));
4095 wxColour nodat_sub = nodat;
4096
4097#ifdef ocpnUSE_ocpnBitmap
4098 nodat_sub = wxColour(nodat.Blue(), nodat.Green(), nodat.Red());
4099#endif
4100 m_pMask = new wxMask(obmp, nodat_sub);
4101 obmp.SetMask(m_pMask);
4102
4103 dc.SelectObject(obmp);
4104 }
4105
4106 delete r;
4107 return true;
4108 } else
4109 return false;
4110 } else
4111 return false;
4112}
4113
4114bool ChartPlugInWrapper::RenderRegionViewOnDCNoText(wxMemoryDC& dc,
4115 const ViewPort& VPoint,
4116 const OCPNRegion& Region) {
4117 if (m_ppicb) {
4118 gs_plib_flags = 0; // reset the CAPs flag
4119 PlugIn_ViewPort pivp = CreatePlugInViewport(VPoint);
4120
4122 dynamic_cast<PlugInChartBaseExtended*>(m_ppicb);
4123 PlugInChartBase* ppicb = dynamic_cast<PlugInChartBase*>(m_ppicb);
4124
4125 if (Region.IsOk() && (pCBx || ppicb)) {
4126 wxRegion* r = Region.GetNew_wxRegion();
4127
4128 if (pCBx)
4129 dc.SelectObject(pCBx->RenderRegionViewOnDCNoText(pivp, *r));
4130 else if (ppicb)
4131 dc.SelectObject(ppicb->RenderRegionView(pivp, *r));
4132
4133 delete r;
4134 return true;
4135 } else
4136 return false;
4137 } else
4138 return false;
4139}
4140
4141bool ChartPlugInWrapper::RenderRegionViewOnDCTextOnly(
4142 wxMemoryDC& dc, const ViewPort& VPoint, const OCPNRegion& Region) {
4143 if (m_ppicb) {
4144 bool ret_val = false;
4145 gs_plib_flags = 0; // reset the CAPs flag
4146 PlugIn_ViewPort pivp = CreatePlugInViewport(VPoint);
4147 if (Region.IsOk()) {
4148 wxRegion* r = Region.GetNew_wxRegion();
4149
4151 dynamic_cast<PlugInChartBaseExtended*>(m_ppicb);
4152 if (pCBx) ret_val = pCBx->RenderRegionViewOnDCTextOnly(dc, pivp, *r);
4153
4154 delete r;
4155 return ret_val;
4156 } else
4157 return false;
4158 } else
4159 return false;
4160}
4161
4162void ChartPlugInWrapper::ClearPLIBTextList() {
4163 if (m_ppicb) {
4165 dynamic_cast<PlugInChartBaseExtended*>(m_ppicb);
4166 if (pCBx) pCBx->ClearPLIBTextList();
4167 }
4168}
4169
4170bool ChartPlugInWrapper::AdjustVP(ViewPort& vp_last, ViewPort& vp_proposed) {
4171 if (m_ppicb) {
4172 PlugIn_ViewPort pivp_last = CreatePlugInViewport(vp_last);
4173 PlugIn_ViewPort pivp_proposed = CreatePlugInViewport(vp_proposed);
4174 return m_ppicb->AdjustVP(pivp_last, pivp_proposed);
4175 } else
4176 return false;
4177}
4178
4179void ChartPlugInWrapper::GetValidCanvasRegion(const ViewPort& VPoint,
4180 OCPNRegion* pValidRegion) {
4181 if (m_ppicb) {
4182 PlugIn_ViewPort pivp = CreatePlugInViewport(VPoint);
4183 // currently convert using wxRegion,
4184 // this should be changed as wxRegion is proven unstable/buggy on various
4185 // platforms
4186 wxRegion region;
4187 m_ppicb->GetValidCanvasRegion(pivp, &region);
4188 *pValidRegion = OCPNRegion(region);
4189 }
4190
4191 return;
4192}
4193
4194void ChartPlugInWrapper::SetColorScheme(ColorScheme cs, bool bApplyImmediate) {
4195 if (m_ppicb) {
4196 m_ppicb->SetColorScheme(cs, bApplyImmediate);
4197 }
4198 m_global_color_scheme = cs;
4199 // Force a new thumbnail
4200 if (pThumbData) pThumbData->pDIBThumb = NULL;
4201}
4202
4204 double target_scale_ppm) {
4205 if (m_ppicb)
4206 return m_ppicb->GetNearestPreferredScalePPM(target_scale_ppm);
4207 else
4208 return 1.0;
4209}
4210
4211void ChartPlugInWrapper::ComputeSourceRectangle(const ViewPort& VPoint,
4212 wxRect* pSourceRect) {
4213 if (m_ppicb) {
4214 PlugIn_ViewPort pivp = CreatePlugInViewport(VPoint);
4215 m_ppicb->ComputeSourceRectangle(pivp, pSourceRect);
4216 }
4217}
4218
4219double ChartPlugInWrapper::GetRasterScaleFactor(const ViewPort& vp) {
4220 if (m_ppicb) {
4221 return (wxRound(100000 * GetPPM() / vp.view_scale_ppm)) / 100000.;
4222 } else
4223 return 1.0;
4224}
4225
4226bool ChartPlugInWrapper::GetChartBits(wxRect& source, unsigned char* pPix,
4227 int sub_samp) {
4228 wxCriticalSectionLocker locker(m_critSect);
4229
4230 if (m_ppicb)
4231
4232 return m_ppicb->GetChartBits(source, pPix, sub_samp);
4233 else
4234 return false;
4235}
4236
4237int ChartPlugInWrapper::GetSize_X() {
4238 if (m_ppicb)
4239 return m_ppicb->GetSize_X();
4240 else
4241 return 1;
4242}
4243
4244int ChartPlugInWrapper::GetSize_Y() {
4245 if (m_ppicb)
4246 return m_ppicb->GetSize_Y();
4247 else
4248 return 1;
4249}
4250
4251void ChartPlugInWrapper::latlong_to_chartpix(double lat, double lon,
4252 double& pixx, double& pixy) {
4253 if (m_ppicb) m_ppicb->latlong_to_chartpix(lat, lon, pixx, pixy);
4254}
4255
4256void ChartPlugInWrapper::chartpix_to_latlong(double pixx, double pixy,
4257 double* plat, double* plon) {
4258 if (m_ppicb) m_ppicb->chartpix_to_latlong(pixx, pixy, plat, plon);
4259}
4260
4261//----------------------------------------------------------------------------------------------------------
4262// The PlugIn CallBack API Implementation
4263// The definitions of this API are found in ocpn_plugin.h
4264//----------------------------------------------------------------------------------------------------------
4265
4266/* API 1.11 */
4267
4268/* API 1.11 adds some more common functions to avoid unnecessary code
4269 * duplication */
4270
4271wxString toSDMM_PlugIn(int NEflag, double a, bool hi_precision) {
4272 return toSDMM(NEflag, a, hi_precision);
4273}
4274
4275wxColour GetBaseGlobalColor(wxString colorName) {
4276 return GetGlobalColor(colorName);
4277}
4278
4279int OCPNMessageBox_PlugIn(wxWindow* parent, const wxString& message,
4280 const wxString& caption, int style, int x, int y) {
4281 return OCPNMessageBox(parent, message, caption, style, 100, x, y);
4282}
4283
4284wxString GetOCPN_ExePath(void) { return g_Platform->GetExePath(); }
4285
4286wxString* GetpPlugInLocation() { return g_Platform->GetPluginDirPtr(); }
4287
4289 return g_Platform->GetWritableDocumentsDir();
4290}
4291
4292wxString GetPlugInPath(opencpn_plugin* pplugin) {
4293 wxString ret_val;
4294 auto loader = PluginLoader::GetInstance();
4295 for (unsigned int i = 0; i < loader->GetPlugInArray()->GetCount(); i++) {
4296 PlugInContainer* pic = loader->GetPlugInArray()->Item(i);
4297 if (pic->m_pplugin == pplugin) {
4298 ret_val = pic->m_plugin_file;
4299 break;
4300 }
4301 }
4302 return ret_val;
4303}
4304
4305// API 1.11 Access to Vector PlugIn charts
4306
4307ListOfPI_S57Obj* PlugInManager::GetPlugInObjRuleListAtLatLon(
4308 ChartPlugInWrapper* target, float zlat, float zlon, float SelectRadius,
4309 const ViewPort& vp) {
4310 ListOfPI_S57Obj* list = NULL;
4311 if (target) {
4312 PlugInChartBaseGL* picbgl =
4313 dynamic_cast<PlugInChartBaseGL*>(target->GetPlugInChart());
4314 if (picbgl) {
4315 PlugIn_ViewPort pi_vp = CreatePlugInViewport(vp);
4316 list = picbgl->GetObjRuleListAtLatLon(zlat, zlon, SelectRadius, &pi_vp);
4317
4318 return list;
4319 }
4321 dynamic_cast<PlugInChartBaseExtended*>(target->GetPlugInChart());
4322 if (picbx) {
4323 PlugIn_ViewPort pi_vp = CreatePlugInViewport(vp);
4324 list = picbx->GetObjRuleListAtLatLon(zlat, zlon, SelectRadius, &pi_vp);
4325
4326 return list;
4327 } else
4328 return list;
4329 } else
4330 return list;
4331}
4332
4333wxString PlugInManager::CreateObjDescriptions(ChartPlugInWrapper* target,
4334 ListOfPI_S57Obj* rule_list) {
4335 wxString ret_str;
4336 if (target) {
4337 PlugInChartBaseGL* picbgl =
4338 dynamic_cast<PlugInChartBaseGL*>(target->GetPlugInChart());
4339 if (picbgl) {
4340 ret_str = picbgl->CreateObjDescriptions(rule_list);
4341 } else {
4343 dynamic_cast<PlugInChartBaseExtended*>(target->GetPlugInChart());
4344 if (picbx) {
4345 ret_str = picbx->CreateObjDescriptions(rule_list);
4346 }
4347 }
4348 }
4349 return ret_str;
4350}
4351
4352// API 1.11 Access to S52 PLIB
4354 return _T(""); // ps52plib->GetPLIBColorScheme()
4355}
4356
4358 if (ps52plib)
4359 return ps52plib->m_nDepthUnitDisplay;
4360 else
4361 return 0;
4362}
4363
4365 if (ps52plib)
4366 return ps52plib->m_nSymbolStyle;
4367 else
4368 return 0;
4369}
4370
4372 if (ps52plib)
4373 return ps52plib->m_nBoundaryStyle;
4374 else
4375 return 0;
4376}
4377
4379 if (ps52plib) {
4380 // Create and populate a compatible s57 Object
4381 S57Obj cobj;
4382 chart_context ctx;
4383 CreateCompatibleS57Object(pObj, &cobj, &ctx);
4384
4385 ViewPort cvp = CreateCompatibleViewport(*vp);
4386
4387 S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context;
4388
4389 // Create and populate a minimally compatible object container
4390 ObjRazRules rzRules;
4391 rzRules.obj = &cobj;
4392 rzRules.LUP = pContext->LUP;
4393 rzRules.sm_transform_parms = 0;
4394 rzRules.child = NULL;
4395 rzRules.next = NULL;
4396
4397 if (pContext->LUP) {
4398 ps52plib->SetVPointCompat(
4399 cvp.pix_width, cvp.pix_height, cvp.view_scale_ppm, cvp.rotation,
4400 cvp.clat, cvp.clon, cvp.chart_scale, cvp.rv_rect, cvp.GetBBox(),
4401 cvp.ref_scale, GetOCPNCanvasWindow()->GetContentScaleFactor());
4402 ps52plib->PrepareForRender();
4403
4404 return ps52plib->ObjectRenderCheck(&rzRules);
4405 } else
4406 return false;
4407 } else
4408 return false;
4409}
4410
4412 if (ps52plib)
4413 return ps52plib->GetStateHash();
4414 else
4415 return 0;
4416}
4417
4418void CreateCompatibleS57Object(PI_S57Obj* pObj, S57Obj* cobj,
4419 chart_context* pctx) {
4420 strncpy(cobj->FeatureName, pObj->FeatureName, 8);
4421 cobj->Primitive_type = (GeoPrim_t)pObj->Primitive_type;
4422 cobj->att_array = pObj->att_array;
4423 cobj->attVal = pObj->attVal;
4424 cobj->n_attr = pObj->n_attr;
4425
4426 cobj->x = pObj->x;
4427 cobj->y = pObj->y;
4428 cobj->z = pObj->z;
4429 cobj->npt = pObj->npt;
4430
4431 cobj->iOBJL = pObj->iOBJL;
4432 cobj->Index = pObj->Index;
4433
4434 cobj->geoPt = (pt*)pObj->geoPt;
4435 cobj->geoPtz = pObj->geoPtz;
4436 cobj->geoPtMulti = pObj->geoPtMulti;
4437
4438 cobj->m_lat = pObj->m_lat;
4439 cobj->m_lon = pObj->m_lon;
4440
4441 cobj->m_DisplayCat = (DisCat)pObj->m_DisplayCat;
4442 cobj->x_rate = pObj->x_rate;
4443 cobj->y_rate = pObj->y_rate;
4444 cobj->x_origin = pObj->x_origin;
4445 cobj->y_origin = pObj->y_origin;
4446
4447 cobj->Scamin = pObj->Scamin;
4448 cobj->nRef = pObj->nRef;
4449 cobj->bIsAton = pObj->bIsAton;
4450 cobj->bIsAssociable = pObj->bIsAssociable;
4451
4452 cobj->m_n_lsindex = pObj->m_n_lsindex;
4453 cobj->m_lsindex_array = pObj->m_lsindex_array;
4454 cobj->m_n_edge_max_points = pObj->m_n_edge_max_points;
4455
4456 if (gs_plib_flags & PLIB_CAPS_OBJSEGLIST) {
4457 cobj->m_ls_list_legacy =
4459 pObj->m_ls_list; // note the cast, assumes in-sync layout
4460 } else
4461 cobj->m_ls_list_legacy = 0;
4462 cobj->m_ls_list = 0;
4463
4464 if (gs_plib_flags & PLIB_CAPS_OBJCATMUTATE)
4465 cobj->m_bcategory_mutable = pObj->m_bcategory_mutable;
4466 else
4467 cobj->m_bcategory_mutable = true; // assume all objects are mutable
4468
4469 cobj->m_DPRI = -1; // default is unassigned, fixed at render time
4470 if (gs_plib_flags & PLIB_CAPS_OBJCATMUTATE) {
4471 if (pObj->m_DPRI == -1) {
4472 S52PLIB_Context* pCtx = (S52PLIB_Context*)pObj->S52_Context;
4473 if (pCtx->LUP) cobj->m_DPRI = pCtx->LUP->DPRI - '0';
4474 } else
4475 cobj->m_DPRI = pObj->m_DPRI;
4476 }
4477
4478 cobj->pPolyTessGeo = (PolyTessGeo*)pObj->pPolyTessGeo;
4479 cobj->m_chart_context = (chart_context*)pObj->m_chart_context;
4480
4481 if (pObj->auxParm3 != 1234) {
4482 pObj->auxParm3 = 1234;
4483 pObj->auxParm0 = -99;
4484 }
4485
4486 cobj->auxParm0 = pObj->auxParm0;
4487 cobj->auxParm1 = 0;
4488 cobj->auxParm2 = 0;
4489 cobj->auxParm3 = 0;
4490
4491 S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context;
4492
4493 if (pContext->bBBObj_valid)
4494 // this is ugly because plugins still use BoundingBox
4495 cobj->BBObj.Set(pContext->BBObj.GetMinY(), pContext->BBObj.GetMinX(),
4496 pContext->BBObj.GetMaxY(), pContext->BBObj.GetMaxX());
4497
4498 cobj->CSrules = pContext->CSrules;
4499 cobj->bCS_Added = pContext->bCS_Added;
4500
4501 cobj->FText = pContext->FText;
4502 cobj->bFText_Added = pContext->bFText_Added;
4503 cobj->rText = pContext->rText;
4504
4505 cobj->bIsClone = true; // Protect cloned object pointers in S57Obj dtor
4506
4507 if (pctx) {
4508 cobj->m_chart_context = pctx;
4509 chart_context* ppctx = (chart_context*)pObj->m_chart_context;
4510
4511 if (ppctx) {
4512 cobj->m_chart_context->m_pvc_hash = ppctx->m_pvc_hash;
4513 cobj->m_chart_context->m_pve_hash = ppctx->m_pve_hash;
4514 cobj->m_chart_context->ref_lat = ppctx->ref_lat;
4515 cobj->m_chart_context->ref_lon = ppctx->ref_lon;
4516 cobj->m_chart_context->pFloatingATONArray = ppctx->pFloatingATONArray;
4517 cobj->m_chart_context->pRigidATONArray = ppctx->pRigidATONArray;
4518 cobj->m_chart_context->safety_contour = ppctx->safety_contour;
4519 cobj->m_chart_context->vertex_buffer = ppctx->vertex_buffer;
4520 }
4521 cobj->m_chart_context->chart =
4522 0; // note bene, this is always NULL for a PlugIn chart
4523 cobj->m_chart_context->chart_type = S52_CHART_TYPE_PLUGIN;
4524 }
4525}
4526
4528 S52PLIB_Context* ctx;
4529 if (!pObj->S52_Context) {
4530 ctx = new S52PLIB_Context;
4531 pObj->S52_Context = ctx;
4532 }
4533
4534 ctx = (S52PLIB_Context*)pObj->S52_Context;
4535
4536 S57Obj cobj;
4537 CreateCompatibleS57Object(pObj, &cobj, NULL);
4538
4539 LUPname LUP_Name = PAPER_CHART;
4540
4541 // Force a re-evaluation of CS rules
4542 ctx->CSrules = NULL;
4543 ctx->bCS_Added = false;
4544
4545 // Clear the rendered text cache
4546 if (ctx->bFText_Added) {
4547 ctx->bFText_Added = false;
4548 delete ctx->FText;
4549 ctx->FText = NULL;
4550 }
4551
4552 // Reset object selection box
4553 ctx->bBBObj_valid = true;
4554 ctx->BBObj.SetMin(pObj->lon_min, pObj->lat_min);
4555 ctx->BBObj.SetMax(pObj->lon_max, pObj->lat_max);
4556
4557 // This is where Simplified or Paper-Type point features are selected
4558 switch (cobj.Primitive_type) {
4559 case GEO_POINT:
4560 case GEO_META:
4561 case GEO_PRIM:
4562
4563 if (PAPER_CHART == ps52plib->m_nSymbolStyle)
4564 LUP_Name = PAPER_CHART;
4565 else
4566 LUP_Name = SIMPLIFIED;
4567
4568 break;
4569
4570 case GEO_LINE:
4571 LUP_Name = LINES;
4572 break;
4573
4574 case GEO_AREA:
4575 if (PLAIN_BOUNDARIES == ps52plib->m_nBoundaryStyle)
4576 LUP_Name = PLAIN_BOUNDARIES;
4577 else
4578 LUP_Name = SYMBOLIZED_BOUNDARIES;
4579
4580 break;
4581 }
4582
4583 LUPrec* lup = ps52plib->S52_LUPLookup(LUP_Name, cobj.FeatureName, &cobj);
4584 ctx->LUP = lup;
4585
4586 // Convert LUP to rules set
4587 ps52plib->_LUP2rules(lup, &cobj);
4588
4589 ctx->MPSRulesList = NULL;
4590
4591 return true;
4592}
4593
4595 S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context;
4596 if (pContext) {
4597 pContext->bBBObj_valid = true;
4598 pContext->BBObj.SetMin(pObj->lon_min, pObj->lat_min);
4599 pContext->BBObj.SetMax(pObj->lon_max, pObj->lat_max);
4600 }
4601}
4602
4603void UpdatePIObjectPlibContext(PI_S57Obj* pObj, S57Obj* cobj,
4604 ObjRazRules* rzRules) {
4605 // Update the PLIB context after the render operation
4606 S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context;
4607
4608 pContext->CSrules = cobj->CSrules;
4609 pContext->bCS_Added = cobj->bCS_Added;
4610
4611 pContext->FText = cobj->FText;
4612 pContext->bFText_Added = cobj->bFText_Added;
4613 pContext->rText = cobj->rText;
4614
4615 if (cobj->BBObj.GetValid()) {
4616 // ugly as plugins still use BoundingBox
4617 pContext->BBObj =
4618 BoundingBox(cobj->BBObj.GetMinLon(), cobj->BBObj.GetMinLat(),
4619 cobj->BBObj.GetMaxLon(), cobj->BBObj.GetMaxLat());
4620 pContext->bBBObj_valid = true;
4621 }
4622
4623 // Render operation may have promoted the object's display category
4624 // (e.g.WRECKS)
4625 pObj->m_DisplayCat = (PI_DisCat)cobj->m_DisplayCat;
4626
4627 if (gs_plib_flags & PLIB_CAPS_OBJCATMUTATE) pObj->m_DPRI = cobj->m_DPRI;
4628
4629 pContext->ChildRazRules = rzRules->child;
4630 pContext->MPSRulesList = rzRules->mps;
4631
4632 pObj->auxParm0 = cobj->auxParm0;
4633}
4634
4635bool PI_GetObjectRenderBox(PI_S57Obj* pObj, double* lat_min, double* lat_max,
4636 double* lon_min, double* lon_max) {
4637 S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context;
4638 if (pContext) {
4639 if (lat_min) *lat_min = pContext->BBObj.GetMinY();
4640 if (lat_max) *lat_max = pContext->BBObj.GetMaxY();
4641 if (lon_min) *lon_min = pContext->BBObj.GetMinX();
4642 if (lon_max) *lon_max = pContext->BBObj.GetMaxX();
4643 return pContext->bBBObj_valid;
4644 } else
4645 return false;
4646}
4647
4649 S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context;
4650 if (pContext) {
4651 LUPrec* lup = pContext->LUP;
4652 if (lup) return (PI_LUPname)(lup->TNAM);
4653 }
4654 return (PI_LUPname)(-1);
4655}
4656
4658 S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context;
4659 if (pContext) {
4660 LUPrec* lup = pContext->LUP;
4661 if (lup) return (PI_DisPrio)(lup->DPRI);
4662 }
4663
4664 return (PI_DisPrio)(-1);
4665}
4666
4668 S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context;
4669 if (pContext) {
4670 LUPrec* lup = pContext->LUP;
4671 if (lup) return (PI_DisCat)(lup->DISC);
4672 }
4673 return (PI_DisCat)(-1);
4674}
4676 return S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR);
4677}
4678
4680 // Create and populate a compatible s57 Object
4681 S57Obj cobj;
4682 chart_context ctx;
4683 CreateCompatibleS57Object(pObj, &cobj, &ctx);
4684
4685 S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context;
4686
4687 // Create and populate a minimally compatible object container
4688 ObjRazRules rzRules;
4689 rzRules.obj = &cobj;
4690 rzRules.LUP = pContext->LUP;
4691 rzRules.sm_transform_parms = 0;
4692 rzRules.child = NULL;
4693 rzRules.next = NULL;
4694 rzRules.mps = pContext->MPSRulesList;
4695
4696 if (pContext->LUP) {
4697 ps52plib->SetLineFeaturePriority(&rzRules, prio);
4698
4699 // Update the PLIB context after the render operation
4700 UpdatePIObjectPlibContext(pObj, &cobj, &rzRules);
4701 }
4702}
4703
4705 if (ps52plib) {
4706 ps52plib->PrepareForRender();
4707 ps52plib->ClearTextList();
4708
4709 if (gs_plib_flags & PLIB_CAPS_LINE_BUFFER)
4710 ps52plib->EnableGLLS(true); // Newer PlugIns can use GLLS
4711 else
4712 ps52plib->EnableGLLS(false); // Older cannot
4713 }
4714}
4715
4716void PI_PLIBSetRenderCaps(unsigned int flags) { gs_plib_flags = flags; }
4717
4718void PI_PLIBFreeContext(void* pContext) {
4719 S52PLIB_Context* pctx = (S52PLIB_Context*)pContext;
4720
4721 if (pctx->ChildRazRules) {
4722 ObjRazRules* ctop = pctx->ChildRazRules;
4723 while (ctop) {
4724 delete ctop->obj;
4725
4726 if (ps52plib) ps52plib->DestroyLUP(ctop->LUP);
4727
4728 ObjRazRules* cnxx = ctop->next;
4729 delete ctop;
4730 ctop = cnxx;
4731 }
4732 }
4733
4734 if (pctx->MPSRulesList) {
4735 if (ps52plib && pctx->MPSRulesList->cs_rules) {
4736 for (unsigned int i = 0; i < pctx->MPSRulesList->cs_rules->GetCount();
4737 i++) {
4738 Rules* top = pctx->MPSRulesList->cs_rules->Item(i);
4739 ps52plib->DestroyRulesChain(top);
4740 }
4741 delete pctx->MPSRulesList->cs_rules;
4742 }
4743 free(pctx->MPSRulesList);
4744 }
4745
4746 delete pctx->FText;
4747
4748 delete pctx;
4749}
4750
4752 // Create and populate a compatible s57 Object
4753 S57Obj cobj;
4754 chart_context ctx;
4755 CreateCompatibleS57Object(pObj, &cobj, &ctx);
4756
4757 S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context;
4758
4759 // Set up object SM rendering constants
4760 sm_parms transform;
4761 toSM(vp->clat, vp->clon, pObj->chart_ref_lat, pObj->chart_ref_lon,
4762 &transform.easting_vp_center, &transform.northing_vp_center);
4763
4764 // Create and populate a minimally compatible object container
4765 ObjRazRules rzRules;
4766 rzRules.obj = &cobj;
4767 rzRules.LUP = pContext->LUP;
4768 rzRules.sm_transform_parms = &transform;
4769 rzRules.child = pContext->ChildRazRules;
4770 rzRules.next = NULL;
4771 rzRules.mps = pContext->MPSRulesList;
4772
4773 if (pContext->LUP) {
4774 ViewPort cvp = CreateCompatibleViewport(*vp);
4775
4776 // Do the render
4777 // FIXME (plib)
4778 ps52plib->SetVPointCompat(cvp.pix_width, cvp.pix_height, cvp.view_scale_ppm,
4779 cvp.rotation, cvp.clat, cvp.clon, cvp.chart_scale,
4780 cvp.rv_rect, cvp.GetBBox(), cvp.ref_scale,
4781 GetOCPNCanvasWindow()->GetContentScaleFactor());
4782 ps52plib->PrepareForRender();
4783
4784 ps52plib->RenderObjectToDC(pdc, &rzRules);
4785
4786 // Update the PLIB context after the render operation
4787 UpdatePIObjectPlibContext(pObj, &cobj, &rzRules);
4788 }
4789
4790 return 1;
4791}
4792
4794 wxRect rect, unsigned char* pixbuf) {
4795 // Create a compatible render canvas
4796 render_canvas_parms pb_spec;
4797
4798 pb_spec.depth = BPP;
4799 pb_spec.pb_pitch = ((rect.width * pb_spec.depth / 8));
4800 pb_spec.lclip = rect.x;
4801 pb_spec.rclip = rect.x + rect.width - 1;
4802 pb_spec.pix_buff = pixbuf; // the passed buffer
4803 pb_spec.width = rect.width;
4804 pb_spec.height = rect.height;
4805 pb_spec.x = rect.x;
4806 pb_spec.y = rect.y;
4807#ifdef ocpnUSE_ocpnBitmap
4808 pb_spec.b_revrgb = true;
4809#else
4810 pb_spec.b_revrgb = false;
4811#endif
4812
4813 pb_spec.b_revrgb = false;
4814
4815 // Create and populate a compatible s57 Object
4816 S57Obj cobj;
4817 chart_context ctx;
4818 CreateCompatibleS57Object(pObj, &cobj, &ctx);
4819
4820 S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context;
4821
4822 // Set up object SM rendering constants
4823 sm_parms transform;
4824 toSM(vp->clat, vp->clon, pObj->chart_ref_lat, pObj->chart_ref_lon,
4825 &transform.easting_vp_center, &transform.northing_vp_center);
4826
4827 // Create and populate a minimally compatible object container
4828 ObjRazRules rzRules;
4829 rzRules.obj = &cobj;
4830 rzRules.LUP = pContext->LUP;
4831 rzRules.sm_transform_parms = &transform;
4832 rzRules.child = pContext->ChildRazRules;
4833 rzRules.next = NULL;
4834 rzRules.mps = pContext->MPSRulesList;
4835
4836 ViewPort cvp = CreateCompatibleViewport(*vp);
4837
4838 // If the PlugIn does not support it nativiely, build a fully described
4839 // Geomoetry
4840 if (!(gs_plib_flags & PLIB_CAPS_SINGLEGEO_BUFFER)) {
4841 if (!pObj->geoPtMulti) { // do this only once
4842 PolyTessGeo* tess = (PolyTessGeo*)pObj->pPolyTessGeo;
4843
4844 if (!tess) return 1; // bail on empty data
4845
4846 PolyTriGroup* ptg = new PolyTriGroup;
4847 ptg->tri_prim_head =
4848 tess->Get_PolyTriGroup_head()->tri_prim_head; // tph;
4849 ptg->bsingle_alloc = false;
4850 ptg->data_type = DATA_TYPE_DOUBLE;
4851 tess->Set_PolyTriGroup_head(ptg);
4852
4853 double* pd = (double*)malloc(sizeof(double));
4854 pObj->geoPtMulti = pd; // Hack hack
4855 }
4856 }
4857
4858 if (pContext->LUP) {
4859 // Do the render
4860 // FIXME (plib)
4861 ps52plib->SetVPointCompat(cvp.pix_width, cvp.pix_height, cvp.view_scale_ppm,
4862 cvp.rotation, cvp.clat, cvp.clon, cvp.chart_scale,
4863 cvp.rv_rect, cvp.GetBBox(), cvp.ref_scale,
4864 GetOCPNCanvasWindow()->GetContentScaleFactor());
4865 ps52plib->PrepareForRender();
4866
4867 ps52plib->RenderAreaToDC(pdc, &rzRules, &pb_spec);
4868
4869 // Update the PLIB context after the render operation
4870 UpdatePIObjectPlibContext(pObj, &cobj, &rzRules);
4871 }
4872
4873 return 1;
4874}
4875
4876int PI_PLIBRenderAreaToGL(const wxGLContext& glcc, PI_S57Obj* pObj,
4877 PlugIn_ViewPort* vp, wxRect& render_rect) {
4878#ifdef ocpnUSE_GL
4879 // Create and populate a compatible s57 Object
4880 S57Obj cobj;
4881 chart_context ctx;
4882 CreateCompatibleS57Object(pObj, &cobj, &ctx);
4883
4884 // chart_context *pct = (chart_context *)pObj->m_chart_context;
4885
4886 // If the PlugIn does not support it nativiely, build a fully described
4887 // Geomoetry
4888
4889 if (!(gs_plib_flags & PLIB_CAPS_SINGLEGEO_BUFFER)) {
4890 if (!pObj->geoPtMulti) { // only do this once
4891 PolyTessGeo* tess = (PolyTessGeo*)pObj->pPolyTessGeo;
4892
4893 if (!tess) return 1; // bail on empty data
4894
4895 PolyTriGroup* ptg =
4896 new PolyTriGroup; // this will leak a little, but is POD
4897 ptg->tri_prim_head = tess->Get_PolyTriGroup_head()->tri_prim_head;
4898 ptg->bsingle_alloc = false;
4899 ptg->data_type = DATA_TYPE_DOUBLE;
4900 tess->Set_PolyTriGroup_head(ptg);
4901
4902 // Mark this object using geoPtMulti
4903 // The malloc will get free'ed when the object is deleted.
4904 double* pd = (double*)malloc(sizeof(double));
4905 pObj->geoPtMulti = pd; // Hack hack
4906 }
4907 cobj.auxParm0 = -6; // signal that this object render cannot use VBO
4908 cobj.auxParm1 = -1; // signal that this object render cannot have single
4909 // buffer conversion done
4910 } else { // it is a newer PLugIn, so can do single buffer conversion and VBOs
4911 if (pObj->auxParm0 < 1)
4912 cobj.auxParm0 = -7; // signal that this object render can use a
4913 // persistent VBO for area triangle vertices
4914 }
4915
4916 S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context;
4917
4918 // Set up object SM rendering constants
4919 sm_parms transform;
4920 toSM(vp->clat, vp->clon, pObj->chart_ref_lat, pObj->chart_ref_lon,
4921 &transform.easting_vp_center, &transform.northing_vp_center);
4922
4923 // Create and populate a minimally compatible object container
4924 ObjRazRules rzRules;
4925 rzRules.obj = &cobj;
4926 rzRules.LUP = pContext->LUP;
4927 rzRules.sm_transform_parms = &transform;
4928 rzRules.child = pContext->ChildRazRules;
4929 rzRules.next = NULL;
4930 rzRules.mps = pContext->MPSRulesList;
4931
4932 if (pContext->LUP) {
4933 ViewPort cvp = CreateCompatibleViewport(*vp);
4934
4935 // Do the render
4936 // FIXME (plib)
4937 ps52plib->SetVPointCompat(cvp.pix_width, cvp.pix_height, cvp.view_scale_ppm,
4938 cvp.rotation, cvp.clat, cvp.clon, cvp.chart_scale,
4939 cvp.rv_rect, cvp.GetBBox(), cvp.ref_scale,
4940 GetOCPNCanvasWindow()->GetContentScaleFactor());
4941 ps52plib->PrepareForRender();
4942
4943 ps52plib->RenderAreaToGL(glcc, &rzRules);
4944
4945 // Update the PLIB context after the render operation
4946 UpdatePIObjectPlibContext(pObj, &cobj, &rzRules);
4947 }
4948
4949#endif
4950 return 1;
4951}
4952
4953int PI_PLIBRenderObjectToGL(const wxGLContext& glcc, PI_S57Obj* pObj,
4954 PlugIn_ViewPort* vp, wxRect& render_rect) {
4955 // Create and populate a compatible s57 Object
4956 S57Obj cobj;
4957 chart_context ctx;
4958 CreateCompatibleS57Object(pObj, &cobj, &ctx);
4959
4960 S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context;
4961
4962 // Set up object SM rendering constants
4963 sm_parms transform;
4964 toSM(vp->clat, vp->clon, pObj->chart_ref_lat, pObj->chart_ref_lon,
4965 &transform.easting_vp_center, &transform.northing_vp_center);
4966
4967 // Create and populate a minimally compatible object container
4968 ObjRazRules rzRules;
4969 rzRules.obj = &cobj;
4970 rzRules.LUP = pContext->LUP;
4971 rzRules.sm_transform_parms = &transform;
4972 rzRules.child = pContext->ChildRazRules;
4973 rzRules.next = NULL;
4974 rzRules.mps = pContext->MPSRulesList;
4975
4976 if (pContext->LUP) {
4977 ViewPort cvp = CreateCompatibleViewport(*vp);
4978
4979 // Do the render
4980 // FIXME (plib)
4981 ps52plib->SetVPointCompat(cvp.pix_width, cvp.pix_height, cvp.view_scale_ppm,
4982 cvp.rotation, cvp.clat, cvp.clon, cvp.chart_scale,
4983 cvp.rv_rect, cvp.GetBBox(), cvp.ref_scale,
4984 GetOCPNCanvasWindow()->GetContentScaleFactor());
4985 ps52plib->PrepareForRender();
4986
4987 ps52plib->RenderObjectToGL(glcc, &rzRules);
4988
4989 // Update the PLIB context after the render operation
4990 UpdatePIObjectPlibContext(pObj, &cobj, &rzRules);
4991 }
4992
4993 return 1;
4994}
4995
4996// http File Download Support
4997
4998// OCPN_downloadEvent Implementation
4999
5000OCPN_downloadEvent::OCPN_downloadEvent(wxEventType commandType, int id)
5001 : wxEvent(id, commandType) {
5002 m_stat = OCPN_DL_UNKNOWN;
5003 m_condition = OCPN_DL_EVENT_TYPE_UNKNOWN;
5004 m_b_complete = false;
5005 m_sofarBytes = 0;
5006}
5007
5008OCPN_downloadEvent::~OCPN_downloadEvent() {}
5009
5010wxEvent* OCPN_downloadEvent::Clone() const {
5011 OCPN_downloadEvent* newevent = new OCPN_downloadEvent(*this);
5012 newevent->m_stat = this->m_stat;
5013 newevent->m_condition = this->m_condition;
5014
5015 newevent->m_totalBytes = this->m_totalBytes;
5016 newevent->m_sofarBytes = this->m_sofarBytes;
5017 newevent->m_b_complete = this->m_b_complete;
5018
5019 return newevent;
5020}
5021
5022// const wxEventType wxEVT_DOWNLOAD_EVENT = wxNewEventType();
5023DECL_EXP wxEventType wxEVT_DOWNLOAD_EVENT = wxNewEventType();
5024
5025_OCPN_DLStatus g_download_status;
5026_OCPN_DLCondition g_download_condition;
5027
5028#define DL_EVENT_TIMER 4388
5029
5030class PI_DLEvtHandler : public wxEvtHandler {
5031public:
5034
5035 void onDLEvent(OCPN_downloadEvent& event);
5036 void setBackgroundMode(long ID, wxEvtHandler* handler);
5037 void clearBackgroundMode();
5038 void onTimerEvent(wxTimerEvent& event);
5039
5040 long m_id;
5041 wxTimer m_eventTimer;
5042 wxEvtHandler* m_download_evHandler;
5043
5044 long m_sofarBytes;
5045 long m_totalBytes;
5046};
5047
5048PI_DLEvtHandler::PI_DLEvtHandler() {
5049 g_download_status = OCPN_DL_UNKNOWN;
5050 g_download_condition = OCPN_DL_EVENT_TYPE_UNKNOWN;
5051
5052 m_download_evHandler = NULL;
5053 m_id = -1;
5054 m_sofarBytes = 0;
5055 m_totalBytes = 0;
5056}
5057
5058PI_DLEvtHandler::~PI_DLEvtHandler() {
5059 m_eventTimer.Stop();
5060 Disconnect(
5061 wxEVT_TIMER,
5062 (wxObjectEventFunction)(wxEventFunction)&PI_DLEvtHandler::onTimerEvent);
5063}
5064
5065void PI_DLEvtHandler::onDLEvent(OCPN_downloadEvent& event) {
5066 // qDebug() << "Got Event " << (int)event.getDLEventStatus() <<
5067 // (int)event.getDLEventCondition();
5068
5069 g_download_status = event.getDLEventStatus();
5070 g_download_condition = event.getDLEventCondition();
5071
5072 // This is an END event, happening at the end of BACKGROUND file download
5073 if (m_download_evHandler &&
5074 (OCPN_DL_EVENT_TYPE_END == event.getDLEventCondition())) {
5075 OCPN_downloadEvent ev(wxEVT_DOWNLOAD_EVENT, 0);
5076 ev.setComplete(true);
5077 ev.setTransferred(m_sofarBytes);
5078 ev.setTotal(m_totalBytes);
5079
5080 ev.setDLEventStatus(event.getDLEventStatus());
5081 ev.setDLEventCondition(OCPN_DL_EVENT_TYPE_END);
5082
5083 m_download_evHandler->AddPendingEvent(ev);
5084 m_eventTimer.Stop();
5085#ifdef __ANDROID__
5086 finishAndroidFileDownload();
5087#endif
5088 }
5089
5090 event.Skip();
5091}
5092
5093void PI_DLEvtHandler::setBackgroundMode(long ID, wxEvtHandler* handler) {
5094 m_id = ID;
5095 m_download_evHandler = handler;
5096
5097 m_eventTimer.SetOwner(this, DL_EVENT_TIMER);
5098
5099 Connect(
5100 wxEVT_TIMER,
5101 (wxObjectEventFunction)(wxEventFunction)&PI_DLEvtHandler::onTimerEvent);
5102 m_eventTimer.Start(1000, wxTIMER_CONTINUOUS);
5103}
5104
5105void PI_DLEvtHandler::clearBackgroundMode() {
5106 m_download_evHandler = NULL;
5107 m_eventTimer.Stop();
5108}
5109
5110void PI_DLEvtHandler::onTimerEvent(wxTimerEvent& event) {
5111#ifdef __ANDROID__
5112 // Query the download status, and post to the original requestor
5113 // This method only happens on Background file downloads
5114
5115 wxString sstat;
5116 int stat = queryAndroidFileDownload(m_id, &sstat);
5117
5118 OCPN_downloadEvent ev(wxEVT_DOWNLOAD_EVENT, 0);
5119 long sofarBytes = 0;
5120 long totalBytes = -1;
5121 long state = -1;
5122
5123 if (stat) { // some error
5124 qDebug() << "Error on queryAndroidFileDownload, ending download";
5125 ev.setComplete(true);
5126 ev.setTransferred(sofarBytes);
5127 ev.setTotal(totalBytes);
5128
5129 ev.setDLEventStatus(OCPN_DL_FAILED);
5130 ev.setDLEventCondition(OCPN_DL_EVENT_TYPE_END);
5131 } else {
5132 wxStringTokenizer tk(sstat, _T(";"));
5133 if (tk.HasMoreTokens()) {
5134 wxString token = tk.GetNextToken();
5135 token.ToLong(&state);
5136 token = tk.GetNextToken();
5137 token.ToLong(&sofarBytes);
5138 token = tk.GetNextToken();
5139 token.ToLong(&totalBytes);
5140 }
5141
5142 qDebug() << state << sofarBytes << totalBytes;
5143
5144 m_sofarBytes = sofarBytes;
5145 m_totalBytes = totalBytes;
5146
5147 ev.setTransferred(sofarBytes);
5148 ev.setTotal(totalBytes);
5149
5150 if (state == 16) { // error
5151 qDebug() << "Event OCPN_DL_FAILED/OCPN_DL_EVENT_TYPE_END";
5152 ev.setComplete(true);
5153 ev.setDLEventStatus(OCPN_DL_FAILED);
5154 ev.setDLEventCondition(OCPN_DL_EVENT_TYPE_END);
5155 } else if (state == 8) { // Completed OK
5156 qDebug() << "Event OCPN_DL_NO_ERROR/OCPN_DL_EVENT_TYPE_END";
5157 ev.setComplete(true);
5158 ev.setDLEventStatus(OCPN_DL_NO_ERROR);
5159 ev.setDLEventCondition(OCPN_DL_EVENT_TYPE_END);
5160 } else {
5161 ev.setComplete(false);
5162 ev.setDLEventStatus(OCPN_DL_UNKNOWN);
5163 ev.setDLEventCondition(OCPN_DL_EVENT_TYPE_PROGRESS);
5164 }
5165
5166 // 2;0;148686
5167 }
5168
5169 if (m_download_evHandler) {
5170 // qDebug() << "Sending event on timer...";
5171 m_download_evHandler->AddPendingEvent(ev);
5172 }
5173
5174 // Background download is all done.
5175 if (OCPN_DL_EVENT_TYPE_END == ev.getDLEventCondition()) {
5176 m_eventTimer.Stop();
5177 finishAndroidFileDownload();
5178 }
5179
5180#endif
5181}
5182
5183PI_DLEvtHandler* g_piEventHandler;
5184
5185// Blocking download of single file
5187 const wxString& outputFile,
5188 const wxString& title, const wxString& message,
5189 const wxBitmap& bitmap, wxWindow* parent,
5190 long style, int timeout_secs) {
5191#ifdef __ANDROID__
5192
5193 wxString msg = _T("Downloading file synchronously: ");
5194 msg += url;
5195 msg += _T(" to: ");
5196 msg += outputFile;
5197 wxLogMessage(msg);
5198
5199 // Validate the write location
5200 int vres = validateAndroidWriteLocation(outputFile);
5201 if (vres == 0) // Pending permission dialog
5202 return OCPN_DL_ABORTED;
5203
5204 // Create a single event handler to receive status events
5205 if (!g_piEventHandler) g_piEventHandler = new PI_DLEvtHandler;
5206
5207 // Reset global status indicators
5208 g_download_status = OCPN_DL_UNKNOWN;
5209 g_download_condition = OCPN_DL_EVENT_TYPE_UNKNOWN;
5210
5211 // Create a connection for the expected events from Android Activity
5212 g_piEventHandler->Connect(
5213 wxEVT_DOWNLOAD_EVENT,
5214 (wxObjectEventFunction)(wxEventFunction)&PI_DLEvtHandler::onDLEvent);
5215
5216 long dl_ID = -1;
5217
5218 // Make sure the outputfile is a file URI
5219 wxString fURI = outputFile;
5220 if (!fURI.StartsWith(_T("file://"))) {
5221 fURI.Prepend(_T("file://"));
5222 }
5223
5224 int res = startAndroidFileDownload(url, fURI, g_piEventHandler, &dl_ID);
5225 // Started OK?
5226 if (res) {
5227 finishAndroidFileDownload();
5228 g_piEventHandler->Disconnect(
5229 wxEVT_DOWNLOAD_EVENT,
5230 (wxObjectEventFunction)(wxEventFunction)&PI_DLEvtHandler::onDLEvent);
5231 // delete g_piEventHandler;
5232 return OCPN_DL_FAILED;
5233 }
5234
5235 wxDateTime dl_start_time = wxDateTime::Now();
5236
5237 // Spin, waiting for timeout or event from downstream, and checking status
5238 while (1) {
5239 wxTimeSpan dt = wxDateTime::Now() - dl_start_time;
5240 qDebug() << "Spin.." << dt.GetSeconds().GetLo();
5241
5242 if (dt.GetSeconds() > timeout_secs) {
5243 qDebug() << "USER_TIMOUT";
5244 finishAndroidFileDownload();
5245 g_piEventHandler->Disconnect(
5246 wxEVT_DOWNLOAD_EVENT,
5247 (wxObjectEventFunction)(wxEventFunction)&PI_DLEvtHandler::onDLEvent);
5248 // delete g_piEventHandler;
5249 return (OCPN_DL_USER_TIMEOUT);
5250 }
5251
5252 if (g_download_condition != OCPN_DL_EVENT_TYPE_UNKNOWN) {
5253 if (OCPN_DL_EVENT_TYPE_END == g_download_condition) {
5254 _OCPN_DLStatus ss = g_download_status;
5255 finishAndroidFileDownload();
5256 g_piEventHandler->Disconnect(
5257 wxEVT_DOWNLOAD_EVENT,
5258 (wxObjectEventFunction)(wxEventFunction)&PI_DLEvtHandler::
5259 onDLEvent);
5260 // delete g_piEventHandler;
5261 qDebug() << "RETURN DL_END" << (int)ss;
5262 return ss; // The actual return code
5263 }
5264 }
5265
5266 wxString sstat;
5267 int stat = queryAndroidFileDownload(dl_ID, &sstat);
5268 if (stat) { // some error
5269 qDebug() << "Error on queryAndroidFileDownload";
5270 finishAndroidFileDownload();
5271 g_piEventHandler->Disconnect(
5272 wxEVT_DOWNLOAD_EVENT,
5273 (wxObjectEventFunction)(wxEventFunction)&PI_DLEvtHandler::onDLEvent);
5274 // delete g_piEventHandler;
5275
5276 return OCPN_DL_FAILED; // so abort
5277 }
5278
5279 wxSleep(1);
5280 wxSafeYield();
5281 }
5282
5283#elif defined(OCPN_USE_CURL)
5284 wxFileName tfn = wxFileName::CreateTempFileName(outputFile);
5285 wxFileOutputStream output(tfn.GetFullPath());
5286
5287 wxCurlDownloadDialog ddlg(url, &output, title, message + url, bitmap, parent,
5288 style);
5289 wxCurlDialogReturnFlag ret = ddlg.RunModal();
5290 output.Close();
5291
5293
5294 switch (ret) {
5295 case wxCDRF_SUCCESS: {
5296 if (wxCopyFile(tfn.GetFullPath(), outputFile))
5297 result = OCPN_DL_NO_ERROR;
5298 else
5299 result = OCPN_DL_FAILED;
5300 break;
5301 }
5302 case wxCDRF_FAILED: {
5303 result = OCPN_DL_FAILED;
5304 break;
5305 }
5306 case wxCDRF_USER_ABORTED: {
5307 result = OCPN_DL_ABORTED;
5308 break;
5309 }
5310 default:
5311 wxASSERT(false); // This should never happen because we handle all
5312 // possible cases of ret
5313 }
5314 if (wxFileExists(tfn.GetFullPath())) wxRemoveFile(tfn.GetFullPath());
5315 return result;
5316
5317#else
5318 return OCPN_DL_FAILED;
5319#endif
5320}
5321
5322wxString toUsrDateTimeFormat_Plugin(const wxDateTime date_time,
5324 return ocpn::toUsrDateTimeFormat(date_time, options);
5325}
5326
5327// Non-Blocking download of single file
5329 const wxString& outputFile,
5330 wxEvtHandler* handler,
5331 long* handle) {
5332#ifdef __ANDROID__
5333 wxString msg = _T("Downloading file asynchronously: ");
5334 msg += url;
5335 msg += _T(" to: ");
5336 msg += outputFile;
5337 wxLogMessage(msg);
5338
5339 // Create a single event handler to receive status events
5340
5341 if (!g_piEventHandler) g_piEventHandler = new PI_DLEvtHandler;
5342
5343 long dl_ID = -1;
5344
5345 int res = startAndroidFileDownload(url, outputFile, NULL /*g_piEventHandler*/,
5346 &dl_ID);
5347 // Started OK?
5348 if (res) {
5349 finishAndroidFileDownload();
5350 return OCPN_DL_FAILED;
5351 }
5352
5353 // configure the local event handler for background transfer
5354 g_piEventHandler->setBackgroundMode(dl_ID, handler);
5355
5356 if (handle) *handle = dl_ID;
5357
5358 return OCPN_DL_STARTED;
5359
5360#elif defined(OCPN_USE_CURL)
5361 if (g_pi_manager->m_pCurlThread) // We allow just one download at a time. Do
5362 // we want more? Or at least return some
5363 // other status in this case?
5364 return OCPN_DL_FAILED;
5365 g_pi_manager->m_pCurlThread =
5366 new wxCurlDownloadThread(g_pi_manager, CurlThreadId);
5367 bool http = (url.StartsWith(wxS("http:")) || url.StartsWith(wxS("https:")));
5368 bool keep = false;
5369 if (http && g_pi_manager->m_pCurl &&
5370 dynamic_cast<wxCurlHTTP*>(g_pi_manager->m_pCurl.get())) {
5371 keep = true;
5372 }
5373 if (!keep) {
5374 g_pi_manager->m_pCurl = 0;
5375 }
5376
5377 bool failed = false;
5378 if (!g_pi_manager->HandleCurlThreadError(
5379 g_pi_manager->m_pCurlThread->SetURL(url, g_pi_manager->m_pCurl),
5380 g_pi_manager->m_pCurlThread, url))
5381 failed = true;
5382 if (!failed) {
5383 g_pi_manager->m_pCurl = g_pi_manager->m_pCurlThread->GetCurlSharedPtr();
5384 if (!g_pi_manager->HandleCurlThreadError(
5385 g_pi_manager->m_pCurlThread->SetOutputStream(
5386 new wxFileOutputStream(outputFile)),
5387 g_pi_manager->m_pCurlThread))
5388 failed = true;
5389 }
5390 if (!failed) {
5391 g_pi_manager->m_download_evHandler = handler;
5392 g_pi_manager->m_downloadHandle = handle;
5393
5394 wxCurlThreadError err = g_pi_manager->m_pCurlThread->Download();
5395 if (err != wxCTE_NO_ERROR) {
5396 g_pi_manager->HandleCurlThreadError(
5397 err, g_pi_manager->m_pCurlThread); // shows a message to the user
5398 g_pi_manager->m_pCurlThread->Abort();
5399 failed = true;
5400 }
5401 }
5402
5403 if (!failed) return OCPN_DL_STARTED;
5404
5405 if (g_pi_manager->m_pCurlThread) {
5406 if (g_pi_manager->m_pCurlThread->IsAlive())
5407 g_pi_manager->m_pCurlThread->Abort();
5408 if (g_pi_manager->m_pCurlThread->GetOutputStream())
5409 delete (g_pi_manager->m_pCurlThread->GetOutputStream());
5410 wxDELETE(g_pi_manager->m_pCurlThread);
5411 g_pi_manager->m_download_evHandler = NULL;
5412 g_pi_manager->m_downloadHandle = NULL;
5413 return OCPN_DL_STARTED;
5414 }
5415 g_pi_manager->m_pCurl = 0;
5416 return OCPN_DL_FAILED;
5417
5418#else
5419 return OCPN_DL_FAILED;
5420#endif
5421}
5422
5424#ifdef OCPN_USE_CURL
5425
5426#ifdef __ANDROID__
5427 cancelAndroidFileDownload(handle);
5428 finishAndroidFileDownload();
5429 if (g_piEventHandler) g_piEventHandler->clearBackgroundMode();
5430#else
5431 if (g_pi_manager->m_pCurlThread) {
5432 g_pi_manager->m_pCurlThread->Abort();
5433 delete (g_pi_manager->m_pCurlThread->GetOutputStream());
5434 wxDELETE(g_pi_manager->m_pCurlThread);
5435 g_pi_manager->m_download_evHandler = NULL;
5436 g_pi_manager->m_downloadHandle = NULL;
5437 }
5438 g_pi_manager->m_pCurl = 0;
5439#endif
5440#endif
5441}
5442
5444 const wxString& parameters, wxString& result,
5445 int timeout_secs) {
5446#ifdef __ANDROID__
5447 wxString lparms = parameters;
5448 wxString postResult = doAndroidPOST(url, lparms, timeout_secs * 1000);
5449 if (postResult.IsSameAs(_T("NOK"))) return OCPN_DL_FAILED;
5450
5451 result = postResult;
5452 return OCPN_DL_NO_ERROR;
5453
5454#elif defined(OCPN_USE_CURL)
5455 wxCurlHTTP post;
5456 post.SetOpt(CURLOPT_TIMEOUT, timeout_secs);
5457 size_t res = post.Post(parameters.ToAscii(), parameters.Len(), url);
5458
5459 if (res) {
5460 result = wxString(post.GetResponseBody().c_str(), wxConvUTF8);
5461 return OCPN_DL_NO_ERROR;
5462 } else
5463 result = wxEmptyString;
5464
5465 return OCPN_DL_FAILED;
5466
5467#else
5468 return OCPN_DL_FAILED;
5469#endif
5470}
5471
5473#ifdef __ANDROID__
5474 return androidCheckOnline();
5475#endif
5476
5477#if !defined(__ANDROID__) && defined(OCPN_USE_CURL)
5478 if (wxDateTime::GetTimeNow() >
5479 g_pi_manager->m_last_online_chk + ONLINE_CHECK_RETRY) {
5480 wxCurlHTTP get;
5481 get.Head(_T("http://yahoo.com/"));
5482 g_pi_manager->m_last_online = get.GetResponseCode() > 0;
5483
5484 g_pi_manager->m_last_online_chk = wxDateTime::GetTimeNow();
5485 }
5486 return g_pi_manager->m_last_online;
5487#else
5488 return false;
5489#endif
5490}
5491
5492#if !defined(__ANDROID__) && defined(OCPN_USE_CURL)
5493void PlugInManager::OnEndPerformCurlDownload(wxCurlEndPerformEvent& ev) {
5494 OCPN_downloadEvent event(wxEVT_DOWNLOAD_EVENT, 0);
5495 if (ev.IsSuccessful()) {
5496 event.setDLEventStatus(OCPN_DL_NO_ERROR);
5497 } else {
5498 g_pi_manager->m_pCurl = 0;
5499 event.setDLEventStatus(OCPN_DL_FAILED);
5500 }
5501 event.setDLEventCondition(OCPN_DL_EVENT_TYPE_END);
5502 event.setComplete(true);
5503
5504 if (m_download_evHandler) {
5505 m_download_evHandler->AddPendingEvent(event);
5506 m_download_evHandler = NULL;
5507 m_downloadHandle = NULL;
5508 }
5509
5510 if (m_pCurlThread) {
5511 m_pCurlThread->Wait();
5512 if (!m_pCurlThread->IsAborting()) {
5513 delete (m_pCurlThread->GetOutputStream());
5514 wxDELETE(m_pCurlThread);
5515 }
5516 }
5517}
5518
5519void PlugInManager::OnCurlDownload(wxCurlDownloadEvent& ev) {
5520 OCPN_downloadEvent event(wxEVT_DOWNLOAD_EVENT, 0);
5521 event.setDLEventStatus(OCPN_DL_UNKNOWN);
5522 event.setDLEventCondition(OCPN_DL_EVENT_TYPE_PROGRESS);
5523 event.setTotal(ev.GetTotalBytes());
5524 event.setTransferred(ev.GetDownloadedBytes());
5525 event.setComplete(false);
5526
5527 if (m_download_evHandler) {
5528 m_download_evHandler->AddPendingEvent(event);
5529 }
5530}
5531
5532bool PlugInManager::HandleCurlThreadError(wxCurlThreadError err,
5533 wxCurlBaseThread* p,
5534 const wxString& url) {
5535 switch (err) {
5536 case wxCTE_NO_ERROR:
5537 return true; // ignore this
5538
5539 case wxCTE_NO_RESOURCE:
5540 wxLogError(
5541 wxS("Insufficient resources for correct execution of the program."));
5542 break;
5543
5544 case wxCTE_ALREADY_RUNNING:
5545 wxFAIL; // should never happen!
5546 break;
5547
5548 case wxCTE_INVALID_PROTOCOL:
5549 wxLogError(wxS("The URL '%s' uses an unsupported protocol."),
5550 url.c_str());
5551 break;
5552
5553 case wxCTE_NO_VALID_STREAM:
5554 wxFAIL; // should never happen - the user streams should always be valid!
5555 break;
5556
5557 case wxCTE_ABORTED:
5558 return true; // ignore this
5559
5560 case wxCTE_CURL_ERROR: {
5561 wxString ws = wxS("unknown");
5562 if (p->GetCurlSession())
5563 ws =
5564 wxString(p->GetCurlSession()->GetErrorString().c_str(), wxConvUTF8);
5565 wxLogError(wxS("Network error: %s"), ws.c_str());
5566 } break;
5567 }
5568
5569 // stop the thread
5570 if (p->IsAlive()) p->Abort();
5571
5572 // this is an unrecoverable error:
5573 return false;
5574}
5575#endif
Class AisDecoder and helpers.
Plugin catalog management: Build the runtime catalog, handling downloads as required.
double GetDisplayDIPMult(wxWindow *win)
Get the display scaling factor for DPI-aware rendering.
wxString & GetPrivateDataDir()
Return dir path for opencpn.log, etc., respecting -c cli option.
EventVar plugin_msg
A JSON message should be sent.
Handle messages for blacklisted plugins.
Modal dialog for plugin catalog settings.
ChartCanvas - Main chart display and interaction component.
Definition chcanv.h:153
Manages the chart database and provides access to chart data.
Definition chartdb.h:95
Wrapper class for plugin-based charts.
Definition chartimg.h:392
virtual double GetNearestPreferredScalePPM(double target_scale_ppm)
Find the nearest preferred viewport scale (in pixels/meter) for this chart.
NMEA0183 basic parsing common parts:
const std::vector< DriverPtr > & GetDrivers() const
Wrapper for configuration variables which lives in a wxBaseConfig object.
void Notify() override
Notify all listeners, no data supplied.
Wrapper for global variable, supports notification events when value changes.
Downloader with progress and final message dialogs.
Interface implemented by classes which listens.
Definition observable.h:64
Handle logging and forwarding of incoming n0183/n2k messages.
Definition multiplexer.h:58
Main application frame.
Definition ocpn_frame.h:136
The raw message layer, a singleton.
static NavAddr::Bus GetBusByKey(const std::string &key)
Return bus corresponding to given key.
Provides platform-specific support utilities for OpenCPN.
wxSize getDisplaySize()
Get the display size in logical pixels.
An iterator class for OCPNRegion.
Definition OCPNRegion.h:156
A wrapper class for wxRegion with additional functionality.
Definition OCPNRegion.h:56
Define an action to be performed when a KeyProvider is notified.
Definition observable.h:257
void Init(const KeyProvider &kp, const std::function< void(ObservedEvt &ev)> &action)
Initiate an object yet not listening.
Definition observable.h:295
void Listen(const std::string &key, wxEvtHandler *listener, wxEventType evt)
Set object to send wxEventType ev to listener on changes in key.
Custom event class for OpenCPN's notification system.
std::shared_ptr< const void > GetSharedPtr() const
Gets the event's payload data.
Class representing an S-57 chart object.
double lon_max
Bounding box maximum longitude.
double lon_min
Bounding box minimum longitude.
double lat_max
Bounding box maximum latitude.
int auxParm0
Auxiliary parameter 0.
void * pPolyTessGeo
Tesselated polygon geometry.
double chart_ref_lon
Chart reference longitude.
double * geoPtMulti
Lat/lon pairs for decomposed points.
void * S52_Context
S52 presentation data.
double chart_ref_lat
Chart reference latitude.
char FeatureName[8]
S-57 feature type code (e.g., "DEPARE")
double lat_min
Bounding box minimum latitude.
int Primitive_type
Geometry type (point, line, area)
Represents a line segment element for efficient vector chart rendering.
Extended chart base class with separated text rendering.
virtual wxBitmap & RenderRegionViewOnDCNoText(const PlugIn_ViewPort &VPoint, const wxRegion &Region)
Standard DC rendering without text.
virtual bool RenderRegionViewOnDCTextOnly(wxMemoryDC &dc, const PlugIn_ViewPort &VPoint, const wxRegion &Region)
Standard DC text-only rendering.
virtual void ClearPLIBTextList()
Clears any cached text elements.
virtual int RenderRegionViewOnGLNoText(const wxGLContext &glc, const PlugIn_ViewPort &VPoint, const wxRegion &Region, bool b_use_stencil)
OpenGL rendering without text.
virtual int RenderRegionViewOnGLTextOnly(const wxGLContext &glc, const PlugIn_ViewPort &VPoint, const wxRegion &Region, bool b_use_stencil)
OpenGL text-only rendering.
virtual int RenderRegionViewOnGL(const wxGLContext &glc, const PlugIn_ViewPort &VPoint, const wxRegion &Region, bool b_use_stencil)
OpenGL rendering with combined text and graphics.
virtual ListOfPI_S57Obj * GetLightsObjRuleListVisibleAtLatLon(float lat, float lon, PlugIn_ViewPort *VPoint)
Gets list of visible light objects at specified position.
OpenGL-optimized chart base class for plugin chart types.
virtual int GetNoCOVRTablenPoints(int iTable)
Alternative to GetNoCOVRTablePoints().
virtual int GetNoCOVREntries()
Gets number of no-coverage areas in chart.
virtual int GetNoCOVRTablePoints(int iTable)
Gets number of points in no-coverage area boundary.
virtual wxString CreateObjDescriptions(ListOfPI_S57Obj *obj_list)
Creates description text for chart objects.
virtual float * GetNoCOVRTableHead(int iTable)
Gets coordinate data for no-coverage area boundary.
virtual int RenderRegionViewOnGL(const wxGLContext &glc, const PlugIn_ViewPort &VPoint, const wxRegion &Region, bool b_use_stencil)
Renders chart content using OpenGL.
virtual ListOfPI_S57Obj * GetObjRuleListAtLatLon(float lat, float lon, float select_radius, PlugIn_ViewPort *VPoint)
Gets chart objects near specified position.
Base class for implementing custom chart types in OpenCPN plugins.
virtual void SetColorScheme(int cs, bool bApplyImmediate)
Sets the color scheme for chart display.
virtual bool GetChartExtent(ExtentPI *pext)
Gets the geographic boundaries of the chart.
virtual void chartpix_to_latlong(double pixx, double pixy, double *plat, double *plon)
Converts chart pixel coordinates to geographic coordinates.
virtual double GetNearestPreferredScalePPM(double target_scale_ppm)
Returns the nearest preferred scale value for this chart.
virtual double GetNormalScaleMin(double canvas_scale_factor, bool b_allow_overzoom)
Returns the minimum recommended scale for this chart.
virtual wxBitmap & RenderRegionView(const PlugIn_ViewPort &VPoint, const wxRegion &Region)
Renders a region of the chart for display.
virtual ChartFamilyEnumPI GetChartFamily()
Returns the chart family classification.
virtual wxString GetID()
Returns a unique identifier for the chart.
virtual wxString GetDatumString()
Returns the horizontal geodetic datum of the chart.
virtual wxString GetName()
Returns the chart's name or title.
virtual double GetRasterScaleFactor()
Returns the scale factor for raster chart rendering.
virtual wxDateTime GetEditionDate(void)
Returns the edition date of the chart.
virtual int GetCOVRTablenPoints(int iTable)
Alternative method to get the number of points in a coverage table entry.
virtual bool GetChartBits(wxRect &source, unsigned char *pPix, int sub_samp)
Gets pixel data for a portion of a raster chart.
virtual int GetCOVREntries()
Returns the number of coverage table entries for this chart.
virtual double GetNormalScaleMax(double canvas_scale_factor, int canvas_width)
Returns the maximum recommended scale for this chart.
virtual wxString GetSoundingsDatum()
Returns the vertical datum used for soundings in the chart.
virtual wxString GetExtraInfo()
Returns additional information about the chart.
virtual int GetSize_X()
Gets the width of the chart in pixels.
virtual wxString GetFullPath() const
Returns the full file path of the chart.
virtual wxString GetSE()
Returns the chart's source edition identifier.
virtual bool IsReadyToRender()
Indicates whether the chart is ready for rendering.
virtual int GetNativeScale()
Returns the native scale of the chart.
virtual double GetChartSkew()
Returns the skew/rotation angle of the chart.
virtual wxString GetDepthUnits()
Returns the depth units used in the chart.
virtual int Init(const wxString &full_path, int init_flags)
Initializes a chart instance from a file.
virtual wxString GetFileSearchMask(void)
Returns file pattern(s) for chart files this plugin can handle.
virtual int GetSize_Y()
Gets the height of the chart in pixels.
virtual void latlong_to_chartpix(double lat, double lon, double &pixx, double &pixy)
Converts geographic coordinates to chart pixel coordinates.
virtual void ComputeSourceRectangle(const PlugIn_ViewPort &vp, wxRect *pSourceRect)
Computes the source rectangle for the chart based on a given viewport.
virtual float * GetCOVRTableHead(int iTable)
Returns a pointer to the coverage table data for a specific entry.
virtual double GetChartErrorFactor()
Returns the error factor for the chart.
virtual OcpnProjTypePI GetChartProjection()
Returns the projection type used by the chart.
virtual wxBitmap * GetThumbnail(int tnx, int tny, int cs)
Generates a thumbnail image of the chart.
virtual ChartTypeEnumPI GetChartType()
Returns the chart type identifier.
virtual void GetValidCanvasRegion(const PlugIn_ViewPort &VPoint, wxRegion *pValidRegion)
Determines the valid display area for this chart.
virtual bool AdjustVP(PlugIn_ViewPort &vp_last, PlugIn_ViewPort &vp_proposed)
Adjusts viewport parameters for chart-specific requirements.
virtual int GetCOVRTablePoints(int iTable)
Returns the number of points in a specific coverage table entry.
virtual ChartDepthUnitTypePI GetDepthUnitId()
Returns the depth unit type identifier.
Data for a loaded plugin, including dl-loaded library.
Basic data for a loaded plugin, trivially copyable.
wxString m_plugin_filename
The short file path.
wxString m_plugin_file
The full file path.
int m_cap_flag
PlugIn Capabilities descriptor.
wxString m_common_name
A common name string for the plugin.
std::string Key() const
sort key.
void HandleN0183(std::shared_ptr< const Nmea0183Msg > n0183_msg)
Process incoming NMEA 0183 messages from the message bus.
void HandleSignalK(std::shared_ptr< const SignalkMsg > sK_msg)
Process incoming SignalK messages from the message bus.
Contains view parameters and status information for a chart display viewport.
double view_scale_ppm
Display scale in pixels per meter.
wxRect rv_rect
Rectangle defining the rendered view area.
int pix_width
Viewport width in pixels.
double lon_max
Maximum longitude of the viewport.
double clon
Center longitude of the viewport in decimal degrees.
double lat_max
Maximum latitude of the viewport.
int pix_height
Viewport height in pixels.
double clat
Center latitude of the viewport in decimal degrees.
double skew
Display skew angle in radians.
double rotation
Display rotation angle in radians.
bool bValid
True if this viewport is valid and can be used for rendering.
double lon_min
Minimum longitude of the viewport.
double lat_min
Minimum latitude of the viewport.
int m_projection_type
Chart projection type (PROJECTION_MERCATOR, etc.)
bool b_quilt
True if the viewport is in quilt mode (showing multiple charts)
float chart_scale
Conventional chart displayed scale (e.g., 1:50000)
Handle plugin install from remote repositories and local operations to Uninstall and list plugins.
bool Uninstall(const std::string plugin)
Uninstall an installed and loaded plugin.
const std::vector< PluginMetadata > GetInstalled()
Return list of all installed and loaded plugins.
static std::string ImportedMetadataPath(std::string name)
Return path to imported metadata for given plugin.
static std::string VersionPath(std::string name)
Return path to file containing version for given plugin.
std::vector< std::string > GetInstalldataPlugins()
Return list of installed plugins lower case names, not necessarily loaded.
bool ClearInstallData(const std::string plugin_name)
Remove installation data for not loaded 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.
static std::string FileListPath(std::string name)
Return path to installation manifest for given plugin.
void SetInstalledMetadata(const PluginMetadata &pm)
Set metadata for an installed plugin.
static PluginHandler * GetInstance()
Singleton factory.
void ReloadPluginPanels()
Complete reload from plugins array.
bool LoadAllPlugIns(bool enabled_plugins, bool keep_orphans=false)
Update catalog with imported metadata and load all plugin library files.
void UpdateManagedPlugins(bool keep_orphans)
Update all managed plugins i.
EventVar evt_pluglist_change
Notified without data when the GetPlugInArray() list is changed.
static std::string GetPluginVersion(const PlugInData pd, std::function< const PluginMetadata(const std::string &)> get_metadata)
Return version string for a plugin, possibly with an "Imported" suffix.
void SortPlugins(int(*cmp_func)(PlugInContainer **, PlugInContainer **))
Sort GetPluginArray().
static void UpdatePlugin(PlugInContainer *plugin, const PluginMetadata &md)
Update PlugInContainer status using data from PluginMetadata and manifest.
void SetEnabled(const wxString &common_name, bool enabled)
Update enabled/disabled state for plugin with given name.
static PluginMetadata MetadataByName(const std::string &name)
Find metadata for given plugin.
bool DeactivatePlugIn(PlugInContainer *pic)
Deactivate given plugin.
bool UpdatePlugIns()
Update the GetPlugInArray() list by reloading all plugins from disk.
void ShowPreferencesDialog(const PlugInData &pd, wxWindow *parent)
Display the preferences dialog for a plugin.
const ArrayOfPlugIns * GetPlugInArray()
Return list of currently loaded plugins.
PluginPanel(wxPanel *parent, wxWindowID id, const wxPoint &pos, const wxSize &size, const PlugInData plugin)
An entry in the list of plugins presented by Options | Plugins.
KeyProvider wrapper for a plain key string.
std::string GetKey() const override
Get the Key object from the Key Provider.
EventVar json_msg
Notified with message targeting all plugins.
Definition routeman.h:260
EventVar json_leg_info
Notified with a shared_ptr<ActiveLegDat>, leg info to all plugins.
Definition routeman.h:263
EventVar on_message_sent
Notified when a message available as GetString() is sent to garmin.
Definition routeman.h:266
A parsed SignalK message over ipv4.
Modal dialog, displays available updates (possibly just one) and lets user select and eventually conf...
Definition update_mgr.h:41
ViewPort - Core geographic projection and coordinate transformation engine.
Definition viewport.h:81
double view_scale_ppm
Requested view scale in physical pixels per meter (ppm), before applying projections.
Definition viewport.h:229
double ref_scale
The nominal scale of the "reference chart" for this view.
Definition viewport.h:246
int pix_height
Height of the viewport in physical pixels.
Definition viewport.h:258
void SetBoxes(void)
Computes the bounding box coordinates for the current viewport.
Definition viewport.cpp:823
double rotation
Rotation angle of the viewport in radians.
Definition viewport.h:239
int pix_width
Width of the viewport in physical pixels.
Definition viewport.h:256
double skew
Angular distortion (shear transform) applied to the viewport in radians.
Definition viewport.h:237
double clon
Center longitude of the viewport in degrees.
Definition viewport.h:224
double clat
Center latitude of the viewport in degrees.
Definition viewport.h:222
double chart_scale
Chart scale denominator (e.g., 50000 for a 1:50000 scale).
Definition viewport.h:244
Invokes client browser on plugin info_url when clicked.
WebsiteButton(wxWindow *parent, const char *url)
Invokes client browser on plugin info_url when clicked.
Device context class that can use either wxDC or OpenGL for drawing.
Definition ocpndc.h:64
Floating toolbar dialog for OpenCPN.
Definition toolbar.h:386
virtual void PrepareContextMenu(int canvasIndex)
Prepares plugin context menu items.
virtual bool RenderOverlayMultiCanvas(wxDC &dc, PlugIn_ViewPort *vp, int canvasIndex)
Renders plugin overlay graphics in standard DC mode for multi-canvas support.
virtual bool RenderGLOverlayMultiCanvas(wxGLContext *pcontext, PlugIn_ViewPort *vp, int canvasIndex)
Renders plugin overlay graphics in OpenGL mode for multi-canvas support.
virtual bool RenderGLOverlayMultiCanvas(wxGLContext *pcontext, PlugIn_ViewPort *vp, int canvasIndex, int priority)
Renders plugin overlay graphics in OpenGL mode with priority control.
virtual bool RenderOverlayMultiCanvas(wxDC &dc, PlugIn_ViewPort *vp, int canvas_ix, int priority)
Renders plugin overlay graphics in standard DC mode with priority control.
virtual bool RenderOverlay(wxDC &dc, PlugIn_ViewPort *vp)
Renders plugin overlay graphics in standard DC mode for single canvas.
virtual bool RenderGLOverlay(wxGLContext *pcontext, PlugIn_ViewPort *vp)
Renders plugin overlay graphics in OpenGL mode for single canvas.
Base class for OpenCPN plugins.
virtual void OnCloseToolboxPanel(int page_sel, int ok_apply_cancel)
Handles preference page closure.
virtual void SetCurrentViewPort(PlugIn_ViewPort &vp)
Notifies plugin of viewport changes.
virtual void UpdateAuiStatus(void)
Updates AUI manager status.
virtual void SetColorScheme(PI_ColorScheme cs)
Updates plugin color scheme.
virtual wxArrayString GetDynamicChartClassNameArray(void)
Returns array of dynamically loaded chart class names.
virtual int GetPlugInVersionMajor()
Returns the major version number of the plugin itself.
virtual bool RenderOverlay(wxMemoryDC *pmdc, PlugIn_ViewPort *vp)
Render plugin overlay graphics using standard device context.
virtual void ProcessParentResize(int x, int y)
Handles parent window resize events.
virtual int GetPlugInVersionMinor()
Returns the minor version number of the plugin itself.
The JSON parser.
Definition jsonreader.h:50
int Parse(const wxString &doc, wxJSONValue *val)
Parse the JSON document.
The JSON value class implementation.
Definition jsonval.h:84
The JSON document writer.
Definition jsonwriter.h:50
void Write(const wxJSONValue &value, wxString &str)
Write the JSONvalue object to a JSON text.
NMEA0183 over IP driver.
NMEA0183 serial driver.
DriverPtr & FindDriver(const std::vector< DriverPtr > &drivers, const std::string &iface, const NavAddr::Bus _bus)
Search list of drivers for a driver with given interface string.
Driver registration container, a singleton.
Raw messages layer, supports sending and recieving navmsg messages.
Enhanced logging interface on top of wx/log.h.
Multiplexer class and helpers.
bool replace(std::string &str, const std::string &from, const std::string &to)
Perform in place substitution in str, replacing "from" with "to".
std::string lookup_tarball(const char *uri)
Get path to tarball in cache for given filename.
std::string tolower(const std::string &input)
Return copy of s with all characters converted to lower case.
std::vector< std::string > split(const char *token_string, const std::string &delimiter)
Return vector of items in s separated by delimiter.
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.
Notify()/Listen() configuration variable wrapper.
Global variables Listen()/Notify() wrapper.
enum _PI_DisCat PI_DisCat
Display categories for S52 chart features.
#define INSTALLS_PLUGIN_CHART
Plugin provides new chart type for standard (non-GL) view.
enum _PI_DisPrio PI_DisPrio
Display priority levels for S52 chart objects.
#define PLIB_CAPS_OBJCATMUTATE
Support for object category mutation Allows dynamic changes to object display categories.
#define INSTALLS_CONTEXTMENU_ITEMS
Plugin will add items to chart context menu.
#define WANTS_LATE_INIT
Delay full plugin initialization until system is ready.
_OCPN_DLCondition
Event types for HTTP file download operations.
@ OCPN_DL_EVENT_TYPE_UNKNOWN
Unknown event type.
@ OCPN_DL_EVENT_TYPE_PROGRESS
Download progress update.
@ OCPN_DL_EVENT_TYPE_END
Download has completed.
PI_ColorScheme
Enumeration of color schemes.
#define PLIB_CAPS_SINGLEGEO_BUFFER
Support for single geometry buffers Allows combining multiple geometries into a single buffer.
#define PLIB_CAPS_LINE_BUFFER
Support for line vertex buffers Enables batched line rendering using vertex buffers.
_OCPN_DLStatus
Status codes for HTTP file download operations.
@ OCPN_DL_NO_ERROR
Download completed successfully.
@ OCPN_DL_USER_TIMEOUT
Download timed out waiting for user action.
@ OCPN_DL_STARTED
Download has begun but not yet complete.
@ OCPN_DL_FAILED
Download failed (general error)
@ OCPN_DL_UNKNOWN
Unknown or uninitialized status.
@ OCPN_DL_ABORTED
Download was cancelled by user.
@ PI_CHART_FAMILY_VECTOR
Vector chart formats (S-57, CM93, etc.)
#define INSTALLS_TOOLBOX_PAGE
Plugin will add pages to the toolbox/settings dialog.
enum _PI_LUPname PI_LUPname
Name identifiers for S57 lookup table sets.
#define USES_AUI_MANAGER
Plugin uses wxAuiManager for window management.
#define PLIB_CAPS_OBJSEGLIST
Support for object segment lists Enables breaking complex geometries into optimized segments.
#define INSTALLS_PLUGIN_CHART_GL
Plugin provides new chart type for OpenGL view.
#define WANTS_ONPAINT_VIEWPORT
Receive callbacks during chart viewport painting.
#define WANTS_PREFERENCES
Plugin will add page(s) to global preferences dialog.
#define WANTS_OVERLAY_CALLBACK
Receive callbacks to render custom overlay graphics on the chart.
Definition ocpn_plugin.h:87
wxWindow * GetOCPNCanvasWindow()
Gets OpenCPN's main canvas window.
double OCPN_GetDisplayContentScaleFactor()
Gets content scaling factor for current display.
double OCPN_GetWinDIPScaleFactor()
Gets Windows-specific DPI scaling factor.
Miscellaneous utilities, many of which string related.
void SendNMEASentenceToAllPlugIns(const wxString &sentence)
Distribute a NMEA 0183 sentence to all plugins that have registered interest by setting the WANTS_NME...
Tools to send data to plugins.
PLugin remote repositories installation and Uninstall/list operations.
Low level code to load plugins from disk, notably the PluginLoader class.
PluginStatus
@ Ghost
Managed, shadowing another (packaged?) plugin.
@ Unmanaged
Unmanaged, probably a package.
@ Managed
Managed by installer.
@ System
One of the four system plugins, unmanaged.
Plugin installation and data paths support.
wxString toSDMM_PlugIn(int NEflag, double a, bool hi_precision)
Convert decimal degrees to a formatted string.
bool PI_GetObjectRenderBox(PI_S57Obj *pObj, double *lat_min, double *lat_max, double *lon_min, double *lon_max)
Gets geographic bounding box of S57 object.
void PI_PLIBFreeContext(void *pContext)
Frees S52 PLIB context.
_OCPN_DLStatus OCPN_postDataHttp(const wxString &url, const wxString &parameters, wxString &result, int timeout_secs)
Performs HTTP POST request.
int PI_GetPLIBSymbolStyle()
Gets configured S52 symbol style.
void OCPN_cancelDownloadFileBackground(long handle)
Cancels a background download.
wxString GetPlugInPath(opencpn_plugin *pplugin)
Gets the installation path for a specific plugin.
int PI_GetPLIBStateHash()
Gets hash value representing current PLIB state.
int PI_PLIBRenderAreaToDC(wxDC *pdc, PI_S57Obj *pObj, PlugIn_ViewPort *vp, wxRect rect, unsigned char *pixbuf)
Renders an S57 area object using standard device context.
wxColour GetBaseGlobalColor(wxString colorName)
Gets a global system color.
wxString PI_GetPLIBColorScheme()
Gets current color scheme used by S52 PLIB.
double PI_GetPLIBMarinerSafetyContour()
Gets configured safety contour depth.
bool PI_PLIBObjectRenderCheck(PI_S57Obj *pObj, PlugIn_ViewPort *vp)
Checks if object should be rendered.
wxString GetWritableDocumentsDir(void)
Returns the platform-specific default documents directory.
std::vector< const PlugInData * > GetInstalled()
Return sorted list of all installed plugins.
int PI_GetPLIBDepthUnitInt()
Gets configured depth unit for S52 display.
bool PI_PLIBSetContext(PI_S57Obj *pObj)
Sets S52 PLIB rendering context for an object.
void PI_PLIBSetLineFeaturePriority(PI_S57Obj *pObj, int prio)
Sets rendering priority for line feature.
PI_DisCat PI_GetObjectDisplayCategory(PI_S57Obj *pObj)
Gets display category for object.
void PI_PLIBPrepareForNewRender(void)
Prepares PLIB for new rendering pass.
PI_LUPname PI_GetObjectLUPName(PI_S57Obj *pObj)
Gets Look-Up Table (LUP) name for object.
int OCPNMessageBox_PlugIn(wxWindow *parent, const wxString &message, const wxString &caption, int style, int x, int y)
Shows a message box dialog.
int PI_PLIBRenderObjectToGL(const wxGLContext &glcc, PI_S57Obj *pObj, PlugIn_ViewPort *vp, wxRect &render_rect)
Renders any S57 object using OpenGL.
int PI_PLIBRenderAreaToGL(const wxGLContext &glcc, PI_S57Obj *pObj, PlugIn_ViewPort *vp, wxRect &render_rect)
Renders an S57 area object using OpenGL.
_OCPN_DLStatus OCPN_downloadFileBackground(const wxString &url, const wxString &outputFile, wxEvtHandler *handler, long *handle)
Asynchronously downloads a file in the background.
wxString toUsrDateTimeFormat_Plugin(const wxDateTime date_time, const DateTimeFormatOptions &options)
Format a date/time to a localized string representation, conforming to the global date/time format an...
int PI_PLIBRenderObjectToDC(wxDC *pdc, PI_S57Obj *pObj, PlugIn_ViewPort *vp)
Renders an S57 object using standard device context.
void PI_UpdateContext(PI_S57Obj *pObj)
Updates rendering context for S57 object.
_OCPN_DLStatus OCPN_downloadFile(const wxString &url, const wxString &outputFile, const wxString &title, const wxString &message, const wxBitmap &bitmap, wxWindow *parent, long style, int timeout_secs)
Synchronously download a file with progress dialog.
wxString * GetpPlugInLocation()
Gets plugins directory location.
int PI_GetPLIBBoundaryStyle()
Gets configured S52 boundary style.
wxString GetOCPN_ExePath(void)
Gets OpenCPN executable path.
bool OCPN_isOnline()
Checks internet connectivity.
void PI_PLIBSetRenderCaps(unsigned int flags)
Sets rendering capability flags.
wxString g_locale
Global locale setting for OpenCPN UI.
Definition ocpn_app.cpp:596
PI_DisPrio PI_GetObjectDisplayPriority(PI_S57Obj *pObj)
Gets display priority for object.
double g_display_size_mm
The width of the physical screen in millimeters.
Definition ocpn_app.cpp:394
PlugInManager and helper classes – Mostly gui parts (dialogs) and plugin API stuff.
Configuration options for date and time formatting.
Plugin metadata, reflects the xml format directly.
std::string to_string()
Return printable XML representation.
Versions uses a modified semantic versioning scheme: major.minor.revision.post-tag+build.
static SemanticVersion parse(std::string s)
Parse a version string, sets major == -1 on errors.
Geographic extent structure defining a bounding box.
double NLAT
Northern latitude boundary in decimal degrees.
double WLON
Western longitude boundary in decimal degrees.
double SLAT
Southern latitude boundary in decimal degrees.
double ELON
Eastern longitude boundary in decimal degrees.
SVG utilities.