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