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