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