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