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