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