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