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