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