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