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"
93#include "observable/configvar.h"
94#include "observable/global_var.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 (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<wxString> 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 auto plugin_array = PluginLoader::GetInstance()->GetPlugInArray();
1702 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
1703 PlugInContainer* pic = plugin_array->Item(i);
1704 if (pic->m_enabled && pic->m_init_state)
1705 pic->m_pplugin->SetColorScheme((PI_ColorScheme)cs);
1706 }
1707}
1708
1709void PlugInManager::PrepareAllPluginContextMenus() {
1710 int canvasIndex = top_frame::Get()->GetCanvasIndexUnderMouse();
1711 if (canvasIndex < 0) return;
1712
1713 auto plugin_array = PluginLoader::GetInstance()->GetPlugInArray();
1714 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
1715 PlugInContainer* pic = plugin_array->Item(i);
1716 if (pic->m_enabled && pic->m_init_state) {
1718 switch (pic->m_api_version) {
1719 case 116:
1720 case 117:
1721 case 118:
1722 case 119:
1723 case 120:
1724 case 121: {
1725 opencpn_plugin_116* ppi =
1726 dynamic_cast<opencpn_plugin_116*>(pic->m_pplugin);
1727 if (ppi) ppi->PrepareContextMenu(canvasIndex);
1728 break;
1729 }
1730 default:
1731 break;
1732 }
1733 }
1734 }
1735 }
1736}
1737
1738void PlugInManager::SendSKConfigToAllPlugIns() {
1739 // Send the current ownship MMSI, encoded as sK, to all PlugIns
1740 nlohmann::json v;
1741 v["self"] = g_ownshipMMSI_SK;
1742 std::string out = v.dump();
1743 SendMessageToAllPlugins("OCPN_CORE_SIGNALK", out);
1744}
1745
1746void PlugInManager::SendBaseConfigToAllPlugIns() {
1747 // Send the current run-time configuration to all PlugIns
1748 nlohmann::json v;
1749 v["OpenCPN Version Major"] = VERSION_MAJOR;
1750 v["OpenCPN Version Minor"] = VERSION_MINOR;
1751 v["OpenCPN Version Patch"] = VERSION_PATCH;
1752 v["OpenCPN Version Date"] = VERSION_DATE;
1753 v["OpenCPN Version Full"] = VERSION_FULL;
1754
1755 // Some useful display metrics
1756 if (g_MainToolbar) {
1757 v["OpenCPN Toolbar Width"] = g_MainToolbar->GetToolbarRect().width;
1758 v["OpenCPN Toolbar Height"] = g_MainToolbar->GetToolbarRect().height;
1759 v["OpenCPN Toolbar PosnX"] = g_MainToolbar->GetToolbarRect().x;
1760 v["OpenCPN Toolbar PosnY"] = g_MainToolbar->GetToolbarRect().y;
1761 }
1762
1763 // Some rendering parameters
1764 v["OpenCPN Zoom Mod Vector"] = g_chart_zoom_modifier_vector;
1765 v["OpenCPN Zoom Mod Other"] = g_chart_zoom_modifier_raster;
1766 v["OpenCPN Scale Factor Exp"] =
1767 g_Platform->GetChartScaleFactorExp(g_ChartScaleFactor);
1768 v["OpenCPN Display Width"] = (int)g_display_size_mm;
1769 v["OpenCPN Content Scale Factor"] = OCPN_GetDisplayContentScaleFactor();
1770 v["OpenCPN Display DIP Scale Factor"] = OCPN_GetWinDIPScaleFactor();
1771
1772 std::string out = v.dump();
1773 SendMessageToAllPlugins("OpenCPN Config", out);
1774}
1775
1776void PlugInManager::SendS52ConfigToAllPlugIns(bool bReconfig) {
1777 // Send the current run-time configuration to all PlugIns
1778 nlohmann::json v;
1779 v["OpenCPN Version Major"] = VERSION_MAJOR;
1780 v["OpenCPN Version Minor"] = VERSION_MINOR;
1781 v["OpenCPN Version Patch"] = VERSION_PATCH;
1782 v["OpenCPN Version Date"] = VERSION_DATE;
1783 v["OpenCPN Version Full"] = VERSION_FULL;
1784
1785 // S52PLIB state
1786 if (ps52plib) {
1787 // v["OpenCPN S52PLIB ShowText"] = ps52plib->GetShowS57Text();
1788 // v["OpenCPN S52PLIB ShowSoundings"] =
1789 // ps52plib->GetShowSoundings(); v["OpenCPN S52PLIB ShowLights"]
1790 // = !ps52plib->GetLightsOff();
1791 v["OpenCPN S52PLIB ShowAnchorConditions"] = ps52plib->GetAnchorOn();
1792 v["OpenCPN S52PLIB ShowQualityOfData"] = ps52plib->GetQualityOfData();
1793 // v["OpenCPN S52PLIB DisplayCategory"] =
1794 // ps52plib->GetDisplayCategory();
1795
1796 // Global parameters
1797 v["OpenCPN S52PLIB MetaDisplay"] = ps52plib->m_bShowMeta;
1798 v["OpenCPN S52PLIB DeclutterText"] = ps52plib->m_bDeClutterText;
1799 v["OpenCPN S52PLIB ShowNationalText"] = ps52plib->m_bShowNationalTexts;
1800 v["OpenCPN S52PLIB ShowImportantTextOnly"] =
1801 ps52plib->m_bShowS57ImportantTextOnly;
1802 v["OpenCPN S52PLIB UseSCAMIN"] = ps52plib->m_bUseSCAMIN;
1803 v["OpenCPN S52PLIB UseSUPER_SCAMIN"] = ps52plib->m_bUseSUPER_SCAMIN;
1804 v["OpenCPN S52PLIB SymbolStyle"] = ps52plib->m_nSymbolStyle;
1805 v["OpenCPN S52PLIB BoundaryStyle"] = ps52plib->m_nBoundaryStyle;
1806 v["OpenCPN S52PLIB ColorShades"] = S52_getMarinerParam(S52_MAR_TWO_SHADES);
1807 v["OpenCPN S52PLIB Safety Depth"] =
1808 (double)S52_getMarinerParam(S52_MAR_SAFETY_DEPTH);
1809 v["OpenCPN S52PLIB Shallow Contour"] =
1810 (double)S52_getMarinerParam(S52_MAR_SHALLOW_CONTOUR);
1811 v["OpenCPN S52PLIB Deep Contour"] =
1812 (double)S52_getMarinerParam(S52_MAR_DEEP_CONTOUR);
1813 }
1814
1815 // Notify plugins that S52PLIB may have reconfigured global options
1816 v["OpenCPN S52PLIB GlobalReconfig"] = bReconfig;
1817
1818 const std::string out = v.dump();
1819 SendMessageToAllPlugins("OpenCPN Config", out);
1820}
1821
1822void PlugInManager::NotifyAuiPlugIns() {
1823 auto plugin_array = PluginLoader::GetInstance()->GetPlugInArray();
1824 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
1825 PlugInContainer* pic = plugin_array->Item(i);
1826 if (pic->m_enabled && pic->m_init_state &&
1828 pic->m_pplugin->UpdateAuiStatus();
1829 }
1830}
1831
1832int PlugInManager::AddToolbarTool(wxString label, wxBitmap* bitmap,
1833 wxBitmap* bmpRollover, wxItemKind kind,
1834 wxString shortHelp, wxString longHelp,
1835 wxObject* clientData, int position,
1836 int tool_sel, opencpn_plugin* pplugin) {
1838 pttc->label = label;
1839
1840 if (!bitmap->IsOk()) {
1841 ocpnStyle::Style* style = g_StyleManager->GetCurrentStyle();
1842 pttc->bitmap_day = new wxBitmap(style->GetIcon("default_pi"));
1843 } else {
1844 // Force a non-reference copy of the bitmap from the PlugIn
1845 pttc->bitmap_day = new wxBitmap(*bitmap);
1846 pttc->bitmap_day->UnShare();
1847 }
1848
1849 if (!bmpRollover->IsOk()) {
1850 ocpnStyle::Style* style = g_StyleManager->GetCurrentStyle();
1851 pttc->bitmap_Rollover_day = new wxBitmap(style->GetIcon("default_pi"));
1852 } else {
1853 // Force a non-reference copy of the bitmap from the PlugIn
1854 pttc->bitmap_Rollover_day = new wxBitmap(*bmpRollover);
1855 pttc->bitmap_Rollover_day->UnShare();
1856 }
1857
1858 pttc->bitmap_dusk = BuildDimmedToolBitmap(pttc->bitmap_day, 128);
1859 pttc->bitmap_night = BuildDimmedToolBitmap(pttc->bitmap_day, 32);
1860 pttc->bitmap_Rollover_dusk =
1861 BuildDimmedToolBitmap(pttc->bitmap_Rollover_day, 128);
1862 pttc->bitmap_Rollover_night =
1863 BuildDimmedToolBitmap(pttc->bitmap_Rollover_day, 32);
1864
1865 pttc->kind = kind;
1866 pttc->shortHelp = shortHelp;
1867 pttc->longHelp = longHelp;
1868 pttc->clientData = clientData;
1869 pttc->position = position;
1870 pttc->m_pplugin = pplugin;
1871 pttc->tool_sel = tool_sel;
1872 pttc->b_viz = true;
1873 pttc->b_toggle = false;
1874 pttc->id = m_plugin_tool_id_next;
1875
1876 m_PlugInToolbarTools.Add(pttc);
1877
1878 m_plugin_tool_id_next++;
1879
1880 return pttc->id;
1881}
1882
1883int PlugInManager::AddToolbarTool(wxString label, wxString SVGfile,
1884 wxString SVGRolloverfile,
1885 wxString SVGToggledfile, wxItemKind kind,
1886 wxString shortHelp, wxString longHelp,
1887 wxObject* clientData, int position,
1888 int tool_sel, opencpn_plugin* pplugin) {
1890 pttc->label = label;
1891
1892 pttc->pluginNormalIconSVG = SVGfile;
1893 pttc->pluginRolloverIconSVG = SVGRolloverfile;
1894 pttc->pluginToggledIconSVG = SVGToggledfile;
1895
1896 // Build a set of bitmaps based on the generic "puzzle piece" icon,
1897 // In case there is some problem with the SVG file(s) specified.
1898 ocpnStyle::Style* style = g_StyleManager->GetCurrentStyle();
1899 pttc->bitmap_day = new wxBitmap(style->GetIcon("default_pi"));
1900 pttc->bitmap_dusk = BuildDimmedToolBitmap(pttc->bitmap_day, 128);
1901 pttc->bitmap_night = BuildDimmedToolBitmap(pttc->bitmap_day, 32);
1902 pttc->bitmap_Rollover_day = new wxBitmap(*pttc->bitmap_day);
1903 pttc->bitmap_Rollover_dusk =
1904 BuildDimmedToolBitmap(pttc->bitmap_Rollover_day, 128);
1905 pttc->bitmap_Rollover_night =
1906 BuildDimmedToolBitmap(pttc->bitmap_Rollover_day, 32);
1907
1908 pttc->kind = kind;
1909 pttc->shortHelp = shortHelp;
1910 pttc->longHelp = longHelp;
1911 pttc->clientData = clientData;
1912 pttc->position = position;
1913 pttc->m_pplugin = pplugin;
1914 pttc->tool_sel = tool_sel;
1915 pttc->b_viz = true;
1916 pttc->b_toggle = false;
1917 pttc->id = m_plugin_tool_id_next;
1918
1919 m_PlugInToolbarTools.Add(pttc);
1920
1921 m_plugin_tool_id_next++;
1922
1923 return pttc->id;
1924}
1925
1926void PlugInManager::RemoveToolbarTool(int tool_id) {
1927 for (unsigned int i = 0; i < m_PlugInToolbarTools.GetCount(); i++) {
1928 PlugInToolbarToolContainer* pttc = m_PlugInToolbarTools[i];
1929 {
1930 if (pttc->id == tool_id) {
1931 m_PlugInToolbarTools.Remove(pttc);
1932 delete pttc;
1933 break;
1934 }
1935 }
1936 }
1937 m_parent->RequestNewToolbars();
1938}
1939
1940void PlugInManager::SetToolbarToolViz(int item, bool viz) {
1941 for (unsigned int i = 0; i < m_PlugInToolbarTools.GetCount(); i++) {
1942 PlugInToolbarToolContainer* pttc = m_PlugInToolbarTools[i];
1943 {
1944 if (pttc->id == item) {
1945 pttc->b_viz = viz;
1946 m_parent->RequestNewToolbars(); // Apply the change
1947 break;
1948 }
1949 }
1950 }
1951}
1952
1953void PlugInManager::SetToolbarItemState(int item, bool toggle) {
1954 for (unsigned int i = 0; i < m_PlugInToolbarTools.GetCount(); i++) {
1955 PlugInToolbarToolContainer* pttc = m_PlugInToolbarTools[i];
1956 {
1957 if (pttc->id == item) {
1958 pttc->b_toggle = toggle;
1959 m_parent->SetMasterToolbarItemState(item, toggle);
1960 break;
1961 }
1962 }
1963 }
1964}
1965
1966void PlugInManager::SetToolbarItemBitmaps(int item, wxBitmap* bitmap,
1967 wxBitmap* bmpRollover) {
1968 for (unsigned int i = 0; i < m_PlugInToolbarTools.GetCount(); i++) {
1969 PlugInToolbarToolContainer* pttc = m_PlugInToolbarTools[i];
1970 {
1971 if (pttc->id == item) {
1972 delete pttc->bitmap_day;
1973 delete pttc->bitmap_dusk;
1974 delete pttc->bitmap_night;
1975 delete pttc->bitmap_Rollover_day;
1976
1977 if (!bitmap->IsOk()) {
1978 ocpnStyle::Style* style = g_StyleManager->GetCurrentStyle();
1979 pttc->bitmap_day = new wxBitmap(style->GetIcon("default_pi"));
1980 } else {
1981 // Force a non-reference copy of the bitmap from the PlugIn
1982 pttc->bitmap_day = new wxBitmap(*bitmap);
1983 pttc->bitmap_day->UnShare();
1984 }
1985
1986 if (!bmpRollover->IsOk()) {
1987 ocpnStyle::Style* style = g_StyleManager->GetCurrentStyle();
1988 pttc->bitmap_Rollover_day =
1989 new wxBitmap(style->GetIcon("default_pi"));
1990 } else {
1991 // Force a non-reference copy of the bitmap from the PlugIn
1992 pttc->bitmap_Rollover_day = new wxBitmap(*bmpRollover);
1993 pttc->bitmap_Rollover_day->UnShare();
1994 }
1995
1996 pttc->bitmap_dusk = BuildDimmedToolBitmap(pttc->bitmap_day, 128);
1997 pttc->bitmap_night = BuildDimmedToolBitmap(pttc->bitmap_day, 32);
1998
1999 m_parent->SetToolbarItemBitmaps(item, pttc->bitmap_day,
2000 pttc->bitmap_Rollover_day);
2001 break;
2002 }
2003 }
2004 }
2005}
2006
2007void PlugInManager::SetToolbarItemBitmaps(int item, wxString SVGfile,
2008 wxString SVGfileRollover,
2009 wxString SVGfileToggled) {
2010 for (unsigned int i = 0; i < m_PlugInToolbarTools.GetCount(); i++) {
2011 PlugInToolbarToolContainer* pttc = m_PlugInToolbarTools[i];
2012 {
2013 if (pttc->id == item) {
2014 pttc->pluginNormalIconSVG = SVGfile;
2015 pttc->pluginRolloverIconSVG = SVGfileRollover;
2016 pttc->pluginToggledIconSVG = SVGfileToggled;
2017 m_parent->SetToolbarItemSVG(item, pttc->pluginNormalIconSVG,
2018 pttc->pluginRolloverIconSVG,
2019 pttc->pluginToggledIconSVG);
2020 break;
2021 }
2022 }
2023 }
2024}
2025
2026opencpn_plugin* PlugInManager::FindToolOwner(const int id) {
2027 for (unsigned int i = 0; i < m_PlugInToolbarTools.GetCount(); i++) {
2028 PlugInToolbarToolContainer* pc = m_PlugInToolbarTools[i];
2029 if (id == pc->id) return pc->m_pplugin;
2030 }
2031
2032 return NULL;
2033}
2034
2035wxString PlugInManager::GetToolOwnerCommonName(const int id) {
2036 opencpn_plugin* ppi = FindToolOwner(id);
2037 if (ppi) {
2038 auto plugin_array = PluginLoader::GetInstance()->GetPlugInArray();
2039 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
2040 PlugInContainer* pic = plugin_array->Item(i);
2041 if (pic && (pic->m_pplugin == ppi)) return pic->m_common_name;
2042 }
2043 }
2044
2045 return wxEmptyString;
2046}
2047
2048wxString PlugInManager::GetLastError() { return m_last_error_string; }
2049
2050wxBitmap* PlugInManager::BuildDimmedToolBitmap(wxBitmap* pbmp_normal,
2051 unsigned char dim_ratio) {
2052 wxImage img_dup = pbmp_normal->ConvertToImage();
2053
2054 if (!img_dup.IsOk()) return NULL;
2055
2056 if (dim_ratio < 200) {
2057 // Create a dimmed version of the image/bitmap
2058 int gimg_width = img_dup.GetWidth();
2059 int gimg_height = img_dup.GetHeight();
2060
2061 double factor = (double)(dim_ratio) / 256.0;
2062
2063 for (int iy = 0; iy < gimg_height; iy++) {
2064 for (int ix = 0; ix < gimg_width; ix++) {
2065 if (!img_dup.IsTransparent(ix, iy)) {
2066 wxImage::RGBValue rgb(img_dup.GetRed(ix, iy),
2067 img_dup.GetGreen(ix, iy),
2068 img_dup.GetBlue(ix, iy));
2069 wxImage::HSVValue hsv = wxImage::RGBtoHSV(rgb);
2070 hsv.value = hsv.value * factor;
2071 wxImage::RGBValue nrgb = wxImage::HSVtoRGB(hsv);
2072 img_dup.SetRGB(ix, iy, nrgb.red, nrgb.green, nrgb.blue);
2073 }
2074 }
2075 }
2076 }
2077 // Make a bitmap
2078 wxBitmap* ptoolBarBitmap;
2079
2080#ifdef __WXMSW__
2081 wxBitmap tbmp(img_dup.GetWidth(), img_dup.GetHeight(), -1);
2082 wxMemoryDC dwxdc;
2083 dwxdc.SelectObject(tbmp);
2084
2085 ptoolBarBitmap = new wxBitmap(img_dup, (wxDC&)dwxdc);
2086#else
2087 ptoolBarBitmap = new wxBitmap(img_dup);
2088#endif
2089
2090 // store it
2091 return ptoolBarBitmap;
2092}
2093
2094wxArrayString PlugInManager::GetPlugInChartClassNameArray() {
2095 wxArrayString array;
2096 auto plugin_array = PluginLoader::GetInstance()->GetPlugInArray();
2097 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
2098 PlugInContainer* pic = plugin_array->Item(i);
2099 if (pic && pic->m_enabled && pic->m_init_state &&
2102 wxArrayString carray = pic->m_pplugin->GetDynamicChartClassNameArray();
2103
2104 for (unsigned int j = 0; j < carray.GetCount(); j++) {
2105 array.Add(carray[j]);
2106 }
2107 }
2108 }
2109
2110 // Scrub the list for duplicates
2111 // Corrects a flaw in BSB4 and NVC PlugIns
2112 unsigned int j = 0;
2113 while (j < array.GetCount()) {
2114 wxString test = array[j];
2115 unsigned int k = j + 1;
2116 while (k < array.GetCount()) {
2117 if (test == array[k]) {
2118 array.RemoveAt(k);
2119 j = -1;
2120 break;
2121 } else
2122 k++;
2123 }
2124
2125 j++;
2126 }
2127
2128 return array;
2129}
2130
2131opencpn_plugin* PlugInManager::GetProvidingPlugin(
2132 const wxString& ChartClassName) {
2133 opencpn_plugin* plugin = nullptr;
2134 auto plugin_array = PluginLoader::GetInstance()->GetPlugInArray();
2135 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
2136 PlugInContainer* pic = plugin_array->Item(i);
2137 if (pic && pic->m_enabled && pic->m_init_state &&
2140 wxArrayString carray = pic->m_pplugin->GetDynamicChartClassNameArray();
2141
2142 for (unsigned int j = 0; j < carray.GetCount(); j++) {
2143 if (carray[j].IsSameAs(ChartClassName)) {
2144 plugin = pic->m_pplugin;
2145 break;
2146 }
2147 }
2148 if (plugin) break;
2149 }
2150 }
2151 return plugin;
2152}
2153
2154//-------------------------------------------------------------------------------
2155// PluginListPanel & PluginPanel Implementation
2156//-------------------------------------------------------------------------------
2157
2158#define DISABLED_SETTINGS_MSG \
2159 _("These settings might destabilize OpenCPN and are by default disabled." \
2160 " To despite the dangers enable them manually add a CatalogExpert=1" \
2161 " line in the [PlugIns] section in the configuration file.")
2162
2163/*
2164 * Panel with buttons to control plugin catalog management.
2165 */
2166CatalogMgrPanel::CatalogMgrPanel(wxWindow* parent)
2167 : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize),
2168 m_parent(parent) {
2169 wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL);
2170 SetSizer(topSizer);
2171
2172 topSizer->Add(new wxStaticLine(this), 0, wxGROW | wxLEFT | wxRIGHT, 4);
2173
2174 wxStaticBox* itemStaticBoxSizer4Static =
2175 new wxStaticBox(this, wxID_ANY, _("Plugin Catalog"));
2176 wxStaticBoxSizer* itemStaticBoxSizer4 =
2177 new wxStaticBoxSizer(itemStaticBoxSizer4Static, wxVERTICAL);
2178 topSizer->Add(itemStaticBoxSizer4, 1, wxEXPAND | wxALL, 2);
2179
2180#ifndef __ANDROID__
2181 // First line
2182 m_catalogText = new wxStaticText(this, wxID_STATIC, "");
2183 itemStaticBoxSizer4->Add(m_catalogText,
2184 wxSizerFlags().Border().Proportion(1));
2185 m_catalogText->SetLabel(GetCatalogText(false));
2186
2187 // Next line
2188 wxBoxSizer* rowSizer2 = new wxBoxSizer(wxHORIZONTAL);
2189 itemStaticBoxSizer4->Add(rowSizer2,
2190 wxSizerFlags().Expand().Border().Proportion(1));
2191
2192 m_updateButton = new wxButton(this, wxID_ANY, _("Update Plugin Catalog"),
2193 wxDefaultPosition, wxDefaultSize, 0);
2194 rowSizer2->Add(m_updateButton, 0, wxALIGN_LEFT);
2195 m_updateButton->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
2196 &CatalogMgrPanel::OnUpdateButton, this);
2197 rowSizer2->AddSpacer(4 * GetCharWidth());
2198 m_tarballButton = new wxButton(this, wxID_ANY, _("Import plugin..."),
2199 wxDefaultPosition, wxDefaultSize, 0);
2200 rowSizer2->Add(m_tarballButton, 0, wxALIGN_LEFT | wxLEFT, 2 * GetCharWidth());
2201 m_tarballButton->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
2202 &CatalogMgrPanel::OnTarballButton, this);
2203
2204 rowSizer2->AddSpacer(4 * GetCharWidth());
2205 m_adv_button = new wxButton(this, wxID_ANY, _("Settings..."),
2206 wxDefaultPosition, wxDefaultSize, 0);
2207 obs::ConfigVar<bool> expert("/PlugIns", "CatalogExpert", pConfig);
2208 if (expert.Get(false)) {
2209 m_adv_button->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
2210 &CatalogMgrPanel::OnPluginSettingsButton, this);
2211 } else {
2212 m_adv_button->Bind(wxEVT_COMMAND_BUTTON_CLICKED, [&](wxCommandEvent&) {
2213 wxMessageBox(DISABLED_SETTINGS_MSG, _("Disabled"));
2214 });
2215 }
2216 rowSizer2->AddSpacer(4 * GetCharWidth());
2217 rowSizer2->Add(m_adv_button, 0, wxALIGN_LEFT);
2218
2219 SetUpdateButtonLabel();
2220
2221 // Next line
2222 wxBoxSizer* rowSizer3 = new wxBoxSizer(wxHORIZONTAL);
2223 itemStaticBoxSizer4->Add(rowSizer3, 0, wxEXPAND | wxALL, 4);
2224
2225 SetMinSize(wxSize(m_parent->GetClientSize().x - (4 * GetCharWidth()), -1));
2226 Fit();
2227
2228 obs::GlobalVar<wxString> catalog(&g_catalog_channel);
2229 wxDEFINE_EVENT(EVT_CATALOG_CHANGE, wxCommandEvent);
2230 catalog_listener.Listen(catalog, this, EVT_CATALOG_CHANGE);
2231 Bind(EVT_CATALOG_CHANGE, [&](wxCommandEvent&) { SetUpdateButtonLabel(); });
2232
2233#else // __ANDROID__
2234 SetBackgroundColour(wxColour(0x7c, 0xb0, 0xe9)); // light blue
2235 obs::ConfigVar<bool> expert("/PlugIns", "CatalogExpert", pConfig);
2236 if (!expert.Get(false)) {
2237 m_updateButton =
2238 new wxButton(this, wxID_ANY, _("Update Plugin Catalog: master"),
2239 wxDefaultPosition, wxDefaultSize, 0);
2240 itemStaticBoxSizer4->Add(m_updateButton, 0, wxALIGN_LEFT);
2241 m_updateButton->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
2242 &CatalogMgrPanel::OnUpdateButton, this);
2243 SetUpdateButtonLabel();
2244 m_tarballButton = NULL;
2245 m_adv_button = NULL;
2246 } else {
2247 // First line
2248 m_catalogText = new wxStaticText(this, wxID_STATIC, GetCatalogText(false));
2249 itemStaticBoxSizer4->Add(m_catalogText,
2250 wxSizerFlags().Border(wxALL, 5).Proportion(1));
2251 // m_catalogText->SetLabel(GetCatalogText(false));
2252
2253 m_updateButton = new wxButton(
2254 this, wxID_ANY, "Update Plugin Catalog:master ",
2255 wxDefaultPosition, wxDefaultSize, 0);
2256 itemStaticBoxSizer4->Add(m_updateButton, 0, wxALIGN_LEFT | wxTOP, 5);
2257 m_updateButton->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
2258 &CatalogMgrPanel::OnUpdateButton, this);
2259 SetUpdateButtonLabel();
2260
2261 // Next line
2262 m_adv_button = new wxButton(this, wxID_ANY, _("Settings..."),
2263 wxDefaultPosition, wxDefaultSize, 0);
2264 itemStaticBoxSizer4->Add(m_adv_button, 0, wxALIGN_LEFT | wxTOP,
2265 GetCharWidth());
2266 m_adv_button->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
2267 &CatalogMgrPanel::OnPluginSettingsButton, this);
2268
2269 // Next line
2270 m_tarballButton = new wxButton(this, wxID_ANY, _("Import plugin..."),
2271 wxDefaultPosition, wxDefaultSize, 0);
2272 itemStaticBoxSizer4->Add(m_tarballButton, 0, wxALIGN_LEFT | wxALL,
2273 2 * GetCharWidth());
2274 m_tarballButton->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
2275 &CatalogMgrPanel::OnTarballButton, this);
2276 }
2277
2278#endif
2279}
2280
2281CatalogMgrPanel::~CatalogMgrPanel() {
2282 m_updateButton->Unbind(wxEVT_COMMAND_BUTTON_CLICKED,
2283 &CatalogMgrPanel::OnUpdateButton, this);
2284 if (m_tarballButton)
2285 m_tarballButton->Unbind(wxEVT_COMMAND_BUTTON_CLICKED,
2286 &CatalogMgrPanel::OnTarballButton, this);
2287}
2288
2289static const char* const DOWNLOAD_REPO_PROTO =
2290 "https://raw.githubusercontent.com/OpenCPN/plugins/@branch@/"
2291 "ocpn-plugins.xml";
2292
2293void CatalogMgrPanel::OnUpdateButton(wxCommandEvent& event) {
2294 // Craft the url
2295 std::string catalog(g_catalog_channel == "" ? "master" : g_catalog_channel);
2296 std::string url(g_catalog_custom_url);
2297 if (catalog != "custom") {
2298 url = std::string(DOWNLOAD_REPO_PROTO);
2299 ocpn::replace(url, "@branch@", catalog);
2300 }
2301 // Download to a temp file
2302 std::string filePath =
2303 wxFileName::CreateTempFileName("ocpn_dl").ToStdString();
2304
2305 auto catalogHdlr = CatalogHandler::GetInstance();
2306
2307 g_Platform->ShowBusySpinner();
2308 auto status = catalogHdlr->DownloadCatalog(filePath, url);
2309 g_Platform->HideBusySpinner();
2310
2311 std::string message;
2312 if (status != CatalogHandler::ServerStatus::OK) {
2313 message = _("Cannot download data from url");
2314 OCPNMessageBox(this, message, _("OpenCPN Catalog update"),
2315 wxICON_ERROR | wxOK);
2316 return;
2317 }
2318
2319 // TODO Validate xml using xsd here....
2320#ifdef __ANDROID__
2321 if (!AndroidSecureCopyFile(wxString(filePath.c_str()),
2322 g_Platform->GetPrivateDataDir() +
2323 wxFileName::GetPathSeparator() +
2324 "ocpn-plugins.xml")) {
2325 OCPNMessageBox(this, _("Unable to copy catalog file"),
2326 _("OpenCPN Catalog update"), wxICON_ERROR | wxOK);
2327 return;
2328 }
2329#else
2330 // Copy the downloaded file to proper local location
2331 if (!wxCopyFile(wxString(filePath.c_str()),
2332 g_Platform->GetPrivateDataDir() +
2333 wxFileName::GetPathSeparator() + "ocpn-plugins.xml")) {
2334 OCPNMessageBox(this, _("Unable to copy catalog file"),
2335 _("OpenCPN Catalog update"), wxICON_ERROR | wxOK);
2336 return;
2337 }
2338#endif
2339
2340 // If this is the "master" catalog, also copy to plugin cache
2341 if (catalog == "master") {
2342 if (!ocpn::store_metadata(filePath.c_str())) {
2343 OCPNMessageBox(this, _("Unable to copy catalog file to cache"),
2344 _("OpenCPN Catalog update"), wxICON_ERROR | wxOK);
2345 return;
2346 }
2347 }
2348
2349 // Record in the config file the name of the catalog downloaded
2350 pConfig->SetPath("/PlugIns/");
2351 pConfig->Write("LatestCatalogDownloaded", catalog.c_str());
2352 pConfig->Flush();
2353
2354 // Reset the PluginHandler catalog file source.
2355 // This will case the Handler to find, load, and parse the just-downloaded
2356 // catalog as copied to g_Platform->GetPrivateDataDir()...
2357 auto pluginHandler = PluginHandler::GetInstance();
2358 pluginHandler->setMetadata("");
2359
2360 // Also clear the cached values in the CatalogHandler, forcing
2361 // a reload and parse of the catalog.
2362 auto cataloghdlr = CatalogHandler::GetInstance();
2363 cataloghdlr->ClearCatalogData();
2364
2365 // Reload all plugins, which will also update the status fields
2366 LoadAllPlugIns(false);
2367
2368 // Update this Panel, and the entire list.
2369#ifndef __ANDROID__
2370 m_catalogText->SetLabel(GetCatalogText(true));
2371#endif
2372 if (m_PluginListPanel) m_PluginListPanel->ReloadPluginPanels();
2373 OCPNMessageBox(this, _("Catalog update successful"),
2374 _("OpenCPN Catalog update"), wxICON_INFORMATION | wxOK);
2375}
2376
2377void CatalogMgrPanel::OnPluginSettingsButton(wxCommandEvent& event) {
2378 auto dialog = new CatalogSettingsDialog(this);
2379
2380#ifdef __ANDROID__
2381 androidDisableRotation();
2382#endif
2383
2384 dialog->ShowModal();
2385
2386#ifdef __ANDROID__
2387 androidEnableRotation();
2388#endif
2389}
2390
2391void CatalogMgrPanel::OnTarballButton(wxCommandEvent& event) {
2392 // Present a file selector dialog to get the file name..
2393 wxString path;
2394 int response = g_Platform->DoFileSelectorDialog(
2395 this, &path, _("Select tarball file"), GetImportInitDir(), "",
2396 "tar files (*.tar.gz)|*.tar.gz|All Files (*.*)|*.*");
2397
2398 if (response != wxID_OK) {
2399 return;
2400 }
2401 auto handler = PluginHandler::GetInstance();
2402 PluginMetadata metadata;
2403 bool ok = handler->ExtractMetadata(path.ToStdString(), metadata);
2404 if (!ok) {
2405 OCPNMessageBox(
2406 this,
2407 _("Error extracting metadata from tarball (missing metadata.xml?)"),
2408 _("OpenCPN Plugin Import Error"));
2409 return;
2410 }
2411 if (!PluginHandler::IsCompatible(metadata)) {
2412 OCPNMessageBox(this, _("Incompatible import plugin detected."),
2413 _("OpenCPN Plugin Import Error"));
2414 handler->Uninstall(metadata.name);
2415 return;
2416 }
2417 UninstallPlugin(metadata.name);
2418 ok = handler->InstallPlugin(metadata, path.ToStdString());
2419 if (!ok) {
2420 OCPNMessageBox(this, _("Error extracting import plugin tarball."),
2421 _("OpenCPN Plugin Import Error"));
2422 return;
2423 }
2424 metadata.is_imported = true;
2425 auto metadata_path = PluginHandler::ImportedMetadataPath(metadata.name);
2426 std::ofstream file(metadata_path);
2427 file << metadata.to_string();
2428 if (!file.good()) {
2429 WARNING_LOG << "Error saving metadata file: " << metadata_path
2430 << " for imported plugin: " << metadata.name;
2431 }
2432 LoadAllPlugIns(false, true);
2434 m_PluginListPanel->ReloadPluginPanels();
2435 wxString ws(_("Plugin"));
2436 ws += metadata.name + _(" successfully imported");
2437 OCPNMessageBox(wxTheApp->GetTopWindow(), ws, _("Installation complete"),
2438 wxICON_INFORMATION | wxOK | wxCENTRE);
2439}
2440
2441wxString CatalogMgrPanel::GetCatalogText(bool updated) {
2442 wxString catalog;
2443 catalog = updated ? _("Active Catalog") : _("Last Catalog");
2444 catalog += ": ";
2445
2446 // Check the config file to learn what was the last catalog downloaded.
2447 pConfig->SetPath("/PlugIns/");
2448 wxString latestCatalog = pConfig->Read("LatestCatalogDownloaded", "default");
2449 catalog += latestCatalog;
2450
2451#ifndef __ANDROID__
2452 // Get the version from the currently active catalog, by which we mean
2453 // the latest catalog parsed.
2454 auto pluginHandler = PluginHandler::GetInstance();
2455 std::string date = pluginHandler->GetCatalogData()->date;
2456
2457 catalog += wxString(" ") + _("Last change: ") + " " + date;
2458 if (!updated) catalog += " : " + _("Please Update Plugin Catalog.");
2459#endif
2460
2461 return catalog;
2462}
2463
2464void CatalogMgrPanel::SetUpdateButtonLabel() {
2465 wxString label = _("Update Plugin Catalog");
2466 label += ": ";
2467 label += g_catalog_channel == "" ? "master" : g_catalog_channel;
2468 m_updateButton->SetLabel(label);
2469 Layout();
2470}
2471
2472wxString CatalogMgrPanel::GetImportInitDir() {
2473 // Check the config file for the last Import path.
2474 pConfig->SetPath("/PlugIns/");
2475 wxString lastImportDir;
2476 lastImportDir =
2477 pConfig->Read("LatestImportDir", g_Platform->GetWritableDocumentsDir());
2478 if (wxDirExists(lastImportDir)) {
2479 return lastImportDir;
2480 }
2481 return (g_Platform->GetWritableDocumentsDir());
2482}
2483
2484BEGIN_EVENT_TABLE(PluginListPanel, wxScrolledWindow)
2485// EVT_BUTTON( ID_CMD_BUTTON_PERFORM_ACTION,
2486// PluginListPanel::OnPluginPanelAction )
2487END_EVENT_TABLE()
2488
2489PluginListPanel::PluginListPanel(wxWindow* parent, wxWindowID id,
2490 const wxPoint& pos, const wxSize& size)
2491 : wxScrolledWindow(parent, id, pos, size, wxTAB_TRAVERSAL | wxVSCROLL),
2492 m_PluginSelected(0) {
2493 m_is_loading.clear();
2494 SetSizer(new wxBoxSizer(wxVERTICAL));
2495 ReloadPluginPanels();
2496}
2497
2498void PluginListPanel::SelectByName(wxString& name) {
2499 for (auto it = GetChildren().GetFirst(); it; it = it->GetNext()) {
2500 auto pluginPanel = dynamic_cast<PluginPanel*>(it->GetData());
2501 if (pluginPanel) {
2502 if (pluginPanel->GetPluginPtr()->m_common_name.IsSameAs(name)) {
2503 pluginPanel->SetSelected(true);
2504 pluginPanel->Layout();
2505 SelectPlugin(pluginPanel);
2506 break;
2507 }
2508 }
2509 }
2510}
2511
2513std::vector<const PlugInData*> GetInstalled() {
2514 std::vector<const PlugInData*> result;
2515 auto loader = PluginLoader::GetInstance();
2516 for (size_t i = 0; i < loader->GetPlugInArray()->GetCount(); i++) {
2517 auto const item = loader->GetPlugInArray()->Item(i);
2518 if (item->m_managed_metadata.name.empty()) {
2519 const auto name = item->m_common_name.ToStdString();
2520 item->m_managed_metadata = PluginLoader::MetadataByName(name);
2521 }
2522 PluginLoader::UpdatePlugin(item, item->m_managed_metadata);
2523 result.push_back(item);
2524 }
2525 auto compare = [](const PlugInData* lhs, const PlugInData* rhs) {
2526 std::string slhs, srhs;
2527 for (auto& cl : lhs->Key()) slhs += toupper(cl);
2528 for (auto& cr : rhs->Key()) srhs += toupper(cr);
2529 return slhs.compare(srhs) < 0;
2530 };
2531 std::sort(result.begin(), result.end(), compare);
2532 return result;
2533}
2534
2535/* Is plugin with given name present in loaded or safe list if installed? */
2536static bool IsPluginLoaded(const std::string& name) {
2537 if (safe_mode::GetMode()) {
2539 auto found =
2540 std::find(installed.begin(), installed.end(), ocpn::tolower(name));
2541 return found != installed.end();
2542 } else {
2543 auto loaded = PluginLoader::GetInstance()->GetPlugInArray();
2544 for (size_t i = 0; i < loaded->GetCount(); i++) {
2545 if (loaded->Item(i)->m_common_name.ToStdString() == name) return true;
2546 }
2547 return false;
2548 }
2549}
2550
2552 if (m_is_loading.test_and_set()) {
2553 // recursive call...
2554 DEBUG_LOG << "LoadAllPlugins: recursive invocation";
2555 return;
2556 }
2557
2558 auto plugins = PluginLoader::GetInstance()->GetPlugInArray();
2559 m_PluginItems.Clear();
2560
2561 wxWindowList kids = GetChildren();
2562 for (unsigned int i = 0; i < kids.GetCount(); i++) {
2563 wxWindowListNode* node = kids.Item(i);
2564 wxWindow* win = node->GetData();
2565 PluginPanel* pp = dynamic_cast<PluginPanel*>(win);
2566 if (pp) win->Destroy();
2567 }
2568 GetSizer()->Clear();
2569
2570 Hide();
2571 m_PluginSelected = 0;
2572
2573 if (safe_mode::GetMode()) {
2576 for (const auto& name : installed) AddPlugin(name);
2577 } else {
2578 /* The catalog entries. */
2579 auto available = getCompatiblePlugins();
2580
2581 /* Remove those which are loaded or in safe list. */
2582 auto predicate = [](const PluginMetadata& md) {
2583 return IsPluginLoaded(md.name);
2584 };
2585 auto end = std::remove_if(available.begin(), available.end(), predicate);
2586 available.erase(end, available.end());
2587
2588 // Sort on case-insensitive name
2589 struct CompSort {
2590 bool operator()(const PluginMetadata& lhs,
2591 const PluginMetadata rhs) const {
2592 std::string slhs, srhs;
2593 for (auto& cl : lhs.name) slhs += toupper(cl);
2594 for (auto& cr : rhs.name) srhs += toupper(cr);
2595 return slhs.compare(srhs) < 0;
2596 }
2597 } comp_sort;
2598
2599 std::set<PluginMetadata, CompSort> unique_sorted_entries(comp_sort);
2600 for (const auto& p : available) unique_sorted_entries.insert(p);
2601
2602 // Build the list of panels.
2603
2604 // Add Installed and active plugins
2605 for (const auto& p : GetInstalled())
2606 if (p->m_enabled) AddPlugin(*p);
2607
2608 // Add Installed and inactive plugins
2609 for (const auto& p : GetInstalled())
2610 if (!p->m_enabled) AddPlugin(*p);
2611
2612 // Add available plugins, sorted
2613 for (const auto& p : unique_sorted_entries) AddPlugin(PlugInData(p));
2614 }
2615
2616 Show();
2617 Layout();
2618 Refresh(true);
2619 Scroll(0, 0);
2620
2621 m_is_loading.clear();
2622}
2623
2624void PluginListPanel::AddPlugin(const std::string& name) {
2625 auto panel = new PluginPanel(this, name);
2626 DimeControl(panel);
2627 panel->SetSelected(false);
2628 GetSizer()->Add(panel, 0, wxEXPAND);
2629 m_PluginItems.Add(panel);
2630 m_pluginSpacer = g_Platform->GetDisplayDPmm() * 1.0;
2631 GetSizer()->AddSpacer(m_pluginSpacer);
2632}
2633
2634void PluginListPanel::AddPlugin(const PlugInData& pic) {
2635 auto pPluginPanel =
2636 new PluginPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, pic);
2637 pPluginPanel->SetSelected(false);
2638 GetSizer()->Add(pPluginPanel, 0, wxEXPAND);
2639 m_PluginItems.Add(pPluginPanel);
2640
2641 m_pluginSpacer = g_Platform->GetDisplayDPmm() * 1.0;
2642 GetSizer()->AddSpacer(m_pluginSpacer);
2643
2644 // wxStaticLine* itemStaticLine = new wxStaticLine( m_panel, wxID_ANY,
2645 // wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
2646 // m_pitemBoxSizer01->Add( itemStaticLine, wxSizerFlags().Expand());
2647}
2648
2649// When a child Panel is selected, its size grows to include "Preferences"
2650// and Enable" buttons. As a consequence, the vertical size of the
2651// ListPanel grows as well.Calculate and add a spacer to bottom of
2652// ListPanel so that initial ListPanel minimum size calculations account
2653// for selected Panel size growth. Sadly, this does not work right on wxQt.
2654// So, just punt for now...
2655int PluginListPanel::ComputePluginSpace(ArrayOfPluginPanel plugins,
2656 wxBoxSizer* sizer) {
2657 int max_dy = 0;
2658 for (size_t i = 0; i < plugins.GetCount(); i++) {
2659 auto panel = plugins.Item(i);
2660 bool was_selected = panel->GetSelected();
2661 panel->SetSelected(false);
2662 sizer->Layout();
2663 wxSize unselected = panel->GetSize();
2664
2665 panel->SetSelected(true); // switch to selected, a bit bigger
2666 sizer->Layout();
2667 wxSize selected = panel->GetSize();
2668
2669 int dy = selected.GetHeight() - unselected.GetHeight();
2670 max_dy = wxMax(max_dy, dy);
2671 panel->SetSelected(was_selected);
2672 }
2673 return max_dy;
2674}
2675
2676PluginListPanel::~PluginListPanel() {}
2677
2678void PluginListPanel::UpdateSelections() {
2679 for (unsigned int i = 0; i < m_PluginItems.GetCount(); i++) {
2680 PluginPanel* pPluginPanel = m_PluginItems[i];
2681 if (pPluginPanel) {
2682 pPluginPanel->SetSelected(pPluginPanel->GetSelected());
2683 }
2684 }
2685}
2686
2687void PluginListPanel::SelectPlugin(PluginPanel* pi) {
2688 int xs, ys;
2689 GetViewStart(&xs, &ys);
2690 Scroll(0, 0);
2691
2692 if (m_PluginSelected) {
2693 m_PluginSelected->SetSelected(false);
2694 m_PluginSelected->Layout();
2695 }
2696
2697 if (pi == NULL) m_PluginSelected->SetSelected(false);
2698
2699 m_PluginSelected = pi;
2700
2701 GetSizer()->Layout();
2702 Refresh(false);
2703 wxSize size = GetBestVirtualSize();
2704 SetVirtualSize(size);
2705
2706 // Measure, and ensure that the selected item is fully visible in the
2707 // vertical scroll box.
2708 int htop = 0;
2709 for (unsigned int i = 0; i < m_PluginItems.GetCount(); i++) {
2710 PluginPanel* pPluginPanel = m_PluginItems[i];
2711 int yd = pPluginPanel->GetSize().y;
2712 htop += yd;
2713 htop += m_pluginSpacer;
2714 if (pPluginPanel == pi) {
2715 int piBottom = htop - (ys * g_options->GetScrollRate());
2716 if (piBottom > GetClientSize().y) {
2717 ys += (piBottom - GetClientSize().y) / g_options->GetScrollRate();
2718 }
2719 break;
2720 }
2721 }
2722
2723 Scroll(xs, ys);
2724}
2725
2726void PluginListPanel::MoveUp(PluginPanel* pi) {
2727 int pos = m_PluginItems.Index(pi);
2728 if (pos == 0) // The first one can't be moved further up
2729 return;
2730 m_PluginItems.RemoveAt(pos);
2731 // m_pitemBoxSizer01->Remove( pos * 2 + 1 );
2732 // m_pitemBoxSizer01->Remove( pos * 2 );
2733 m_PluginItems.Insert(pi, pos - 1);
2734 wxStaticLine* itemStaticLine = new wxStaticLine(
2735 this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL);
2736 // m_pitemBoxSizer01->Insert( (pos - 1) * 2, itemStaticLine, 0,
2737 // wxEXPAND|wxALL, 0 ); m_pitemBoxSizer01->Insert( (pos - 1) * 2, pi, 0,
2738 // wxEXPAND|wxALL, 0 );
2739
2740 m_PluginSelected = pi;
2741
2742 GetSizer()->Layout();
2743 m_parent->Layout();
2744 Refresh(true);
2745}
2746
2747void PluginListPanel::MoveDown(PluginPanel* pi) {
2748 int pos = m_PluginItems.Index(pi);
2749 if (pos == (int)m_PluginItems.Count() -
2750 1) // The last one can't be moved further down
2751 return;
2752 m_PluginItems.RemoveAt(pos);
2753 // m_pitemBoxSizer01->Remove( pos * 2 + 1 );
2754 // m_pitemBoxSizer01->Remove( pos * 2 );
2755 m_PluginItems.Insert(pi, pos + 1);
2756 wxStaticLine* itemStaticLine = new wxStaticLine(
2757 this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL);
2758 // m_pitemBoxSizer01->Insert( (pos + 1) * 2 - 1, itemStaticLine, 0,
2759 // wxEXPAND|wxALL, 0 ); m_pitemBoxSizer01->Insert( (pos + 1) * 2, pi, 0,
2760 // wxEXPAND|wxALL, 0 );
2761
2762 m_PluginSelected = pi;
2763
2764 GetSizer()->Layout();
2765 m_parent->Layout();
2766 Refresh(false);
2767}
2768
2769static bool canUninstall(std::string name) {
2770 PluginHandler* pluginHandler = PluginHandler::GetInstance();
2771 // std::transform(name.begin(), name.end(), name.begin(), ::tolower);
2772
2773 for (auto plugin : pluginHandler->GetInstalled()) {
2774 if (plugin.name == name) {
2775 if (safe_mode::GetMode())
2776 return true;
2777 else
2778 return !plugin.readonly;
2779 }
2780 }
2781 return false;
2782}
2783
2784PluginPanel::PluginPanel(wxPanel* parent, const std::string& name)
2785 : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize,
2786 wxBORDER_NONE),
2787 m_is_safe_panel(true) {
2788 m_PluginListPanel = dynamic_cast<PluginListPanel*>(parent);
2789 wxASSERT(m_PluginListPanel != 0);
2790 wxBoxSizer* top_sizer = new wxBoxSizer(wxVERTICAL);
2791 SetSizer(top_sizer);
2792 wxBoxSizer* top_horizontal = new wxBoxSizer(wxHORIZONTAL);
2793 top_sizer->Add(top_horizontal, 0, wxEXPAND);
2794
2795 double iconSize = GetCharWidth() * 4;
2796 double dpi_mult = g_Platform->GetDisplayDIPMult(this);
2797 int icon_scale = iconSize * dpi_mult;
2798 ocpnStyle::Style* style = g_StyleManager->GetCurrentStyle();
2799 wxBitmap bitmap(style->GetIcon("default_pi", icon_scale, icon_scale));
2800 m_itemStaticBitmap = new wxStaticBitmap(this, wxID_ANY, bitmap);
2801 top_horizontal->Add(m_itemStaticBitmap, 0, wxEXPAND | wxALL, 10);
2802
2803 m_pName = new wxStaticText(this, wxID_ANY, name);
2804 top_horizontal->Add(m_pName, wxID_ANY, wxALIGN_CENTER_VERTICAL);
2805 m_pVersion = new wxStaticText(this, wxID_ANY, "");
2806 top_horizontal->Add(m_pVersion);
2807 m_pVersion->Hide();
2808
2809 m_pButtons = new wxBoxSizer(wxHORIZONTAL);
2810 top_horizontal->Add(m_pButtons);
2811 m_info_btn = new WebsiteButton(this, "https:\\opencpn.org");
2812 top_horizontal->Add(m_info_btn);
2813 m_pButtonUninstall = new wxButton(this, wxID_ANY, _("Uninstall"),
2814 wxDefaultPosition, wxDefaultSize, 0);
2815 top_horizontal->Add(m_pButtonUninstall, 0, wxALIGN_CENTER_VERTICAL | wxALL,
2816 2);
2817 auto uninstall = [&](wxCommandEvent ev) {
2818 auto n = m_pName->GetLabel().ToStdString();
2819 int result = OCPNMessageBox(
2820 wxTheApp->GetTopWindow(), std::string(_("Uninstall plugin ")) + n + "?",
2821 _("Un-Installation"), wxICON_QUESTION | wxOK | wxCANCEL);
2822 if (result != wxID_OK) return;
2824 m_PluginListPanel->ReloadPluginPanels();
2825 };
2826 m_pButtonUninstall->Bind(wxEVT_COMMAND_BUTTON_CLICKED, uninstall);
2827}
2828
2829BEGIN_EVENT_TABLE(PluginPanel, wxPanel)
2830EVT_PAINT(PluginPanel::OnPaint)
2831END_EVENT_TABLE()
2832
2833PluginPanel::PluginPanel(wxPanel* parent, wxWindowID id, const wxPoint& pos,
2834 const wxSize& size, const PlugInData plugin)
2835 : wxPanel(parent, id, pos, size, wxBORDER_NONE),
2836 m_plugin(plugin),
2837 m_is_safe_panel(false) {
2838 m_PluginListPanel = (PluginListPanel*)parent; //->GetParent();
2839 m_PluginListPanel = dynamic_cast<PluginListPanel*>(parent /*->GetParent()*/);
2840 wxASSERT(m_PluginListPanel != 0);
2841
2842 m_bSelected = false;
2843 m_penWidthUnselected = g_Platform->GetDisplayDPmm() * .25;
2844 m_penWidthSelected = g_Platform->GetDisplayDPmm() * .5;
2845
2846 wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL);
2847 SetSizer(topSizer);
2848
2849 wxBoxSizer* itemBoxSizer01 = new wxBoxSizer(wxHORIZONTAL);
2850 topSizer->Add(itemBoxSizer01, 0, wxEXPAND);
2851 Bind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this);
2852 Bind(wxEVT_LEFT_UP, &PluginPanel::OnPluginSelectedUp, this);
2853
2854 double iconSize = GetCharWidth() * 4;
2855 double dpi_mult = g_Platform->GetDisplayDIPMult(this);
2856 int icon_scale = iconSize * dpi_mult;
2857
2858 wxImage plugin_icon;
2859 ocpnStyle::Style* style = g_StyleManager->GetCurrentStyle();
2860 if (m_plugin.m_bitmap.IsOk()) {
2861 plugin_icon = m_plugin.m_bitmap.ConvertToImage();
2862 }
2863 wxBitmap bitmap;
2864 if (m_plugin.m_status == PluginStatus::ManagedInstallAvailable) {
2865 wxFileName path(g_Platform->GetSharedDataDir(), "packageBox.svg");
2866 path.AppendDir("uidata");
2867 path.AppendDir("traditional"); // FIXME(leamas) cache it.
2868 bitmap = LoadSVG(path.GetFullPath(), icon_scale, icon_scale);
2869 } else if (plugin_icon.IsOk()) {
2870 int nowSize = plugin_icon.GetWidth();
2871 plugin_icon.Rescale(icon_scale, icon_scale, wxIMAGE_QUALITY_HIGH);
2872 bitmap = wxBitmap(plugin_icon);
2873 } else {
2874 bitmap = wxBitmap(style->GetIcon("default_pi", icon_scale, icon_scale));
2875 }
2876 m_itemStaticBitmap = new wxStaticBitmap(this, wxID_ANY, bitmap);
2877
2878 itemBoxSizer01->Add(m_itemStaticBitmap, 0, wxEXPAND | wxALL, 10);
2879 m_itemStaticBitmap->Bind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected,
2880 this);
2881 m_itemStaticBitmap->Bind(wxEVT_LEFT_UP, &PluginPanel::OnPluginSelectedUp,
2882 this);
2883
2884 wxBoxSizer* itemBoxSizer02 = new wxBoxSizer(wxVERTICAL);
2885 itemBoxSizer01->Add(itemBoxSizer02, 1, wxEXPAND | wxALL, 0);
2886
2887 // Calculate character width available
2888 int nChars = g_options->GetSize().x / GetCharWidth();
2889 bool bCompact = false;
2890 if (nChars < 60) // Arbitrary, detecting mobile devices in portrait mode.
2891 bCompact = true;
2892
2893 if (bCompact) {
2894 // Might need to shorten the Plugin name string
2895 wxString nameString = m_plugin.m_common_name;
2896 int maxWidth = g_Platform->getDisplaySize().x * 3 / 10;
2897 wxScreenDC dc;
2898 int nameWidth;
2899 dc.GetTextExtent(m_plugin.m_common_name, &nameWidth, NULL);
2900 if (nameWidth > maxWidth) {
2901 nameString = wxControl::Ellipsize(m_plugin.m_common_name, dc,
2902 wxELLIPSIZE_END, maxWidth);
2903 }
2904 m_pName = new wxStaticText(this, wxID_ANY, nameString);
2905 m_pName->Bind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this);
2906 m_pName->Bind(wxEVT_LEFT_UP, &PluginPanel::OnPluginSelectedUp, this);
2907 itemBoxSizer02->Add(m_pName, 0, /*wxEXPAND|*/ wxALL, 5);
2908
2909 wxFlexGridSizer* sl1 = new wxFlexGridSizer(2, 0, 0);
2910 sl1->AddGrowableCol(1);
2911 itemBoxSizer02->Add(sl1, 0, wxEXPAND);
2912
2913 m_pVersion = new wxStaticText(this, wxID_ANY, "X.YY.ZZ.AA");
2914 sl1->Add(m_pVersion, 0, /*wxEXPAND|*/ wxALL, 5);
2915 if (m_plugin.m_status == PluginStatus::ManagedInstallAvailable) {
2916 m_pVersion->Hide();
2917 }
2918 m_pVersion->Bind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this);
2919 m_pVersion->Bind(wxEVT_LEFT_UP, &PluginPanel::OnPluginSelectedUp, this);
2920
2921 m_cbEnable = new wxCheckBox(this, wxID_ANY, _("Enabled"));
2922 sl1->Add(m_cbEnable, 1, wxALIGN_RIGHT | wxTOP, 5);
2923 m_cbEnable->Bind(wxEVT_CHECKBOX, &PluginPanel::OnPluginEnableToggle, this);
2924
2925 // Might need to shorten the Plugin description string
2926 wxString descriptionString = m_plugin.m_short_description;
2927 int maxDescriptionWidth = g_Platform->getDisplaySize().x - (iconSize * 4);
2928 int descriptionWidth;
2929 dc.GetTextExtent(m_plugin.m_short_description, &descriptionWidth, NULL);
2930 if (descriptionWidth > maxDescriptionWidth)
2931 descriptionString =
2932 wxControl::Ellipsize(m_plugin.m_short_description, dc,
2933 wxELLIPSIZE_END, maxDescriptionWidth);
2934
2935 // This invocation has the effect of setting the minimum width of the
2936 // descriptor field.
2937 m_pDescription =
2938 new wxStaticText(this, wxID_ANY, descriptionString, wxDefaultPosition,
2939 wxSize(maxDescriptionWidth, -1), wxST_NO_AUTORESIZE);
2940 itemBoxSizer02->Add(m_pDescription, 0, wxEXPAND | wxALL, 5);
2941 m_pDescription->Bind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this);
2942 m_pDescription->Bind(wxEVT_LEFT_UP, &PluginPanel::OnPluginSelectedUp, this);
2943
2944 } else {
2945 wxFlexGridSizer* itemBoxSizer03 = new wxFlexGridSizer(4, 0, 0);
2946 itemBoxSizer03->AddGrowableCol(2);
2947 itemBoxSizer02->Add(itemBoxSizer03, 0, wxEXPAND);
2948
2949 wxString nameString = m_plugin.m_common_name;
2950 m_pName = new wxStaticText(this, wxID_ANY, nameString);
2951 m_pName->Bind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this);
2952 m_pName->Bind(wxEVT_LEFT_UP, &PluginPanel::OnPluginSelectedUp, this);
2953
2954 // Avoid known bug in wxGTK3
2955#ifndef __WXGTK3__
2956 wxFont font = GetFont();
2957 font.SetWeight(wxFONTWEIGHT_BOLD);
2958 m_pName->SetFont(font);
2959#endif
2960
2961 itemBoxSizer03->Add(m_pName, 0, /*wxEXPAND|*/ wxALL, 10);
2962
2963 m_pVersion = new wxStaticText(this, wxID_ANY, "X.YY.ZZ.AA");
2964 itemBoxSizer03->Add(m_pVersion, 0, /*wxEXPAND|*/ wxALL, 10);
2965 if (m_plugin.m_status == PluginStatus::ManagedInstallAvailable ||
2966 m_plugin.m_status == PluginStatus::System ||
2967 (m_plugin.m_status == PluginStatus::Unmanaged &&
2968 !m_plugin.m_managed_metadata.is_orphan)) {
2969 m_pVersion->Hide();
2970 }
2971 m_pVersion->Bind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this);
2972 m_pVersion->Bind(wxEVT_LEFT_UP, &PluginPanel::OnPluginSelectedUp, this);
2973
2974 m_cbEnable = new wxCheckBox(this, wxID_ANY, _("Enabled"));
2975 itemBoxSizer03->Add(m_cbEnable, 1, wxALIGN_RIGHT | wxTOP, 10);
2976 m_cbEnable->Bind(wxEVT_CHECKBOX, &PluginPanel::OnPluginEnableToggle, this);
2977
2978 itemBoxSizer03->Add(5 * GetCharWidth(), 1, 0, wxALIGN_RIGHT | wxTOP, 10);
2979
2980 m_pDescription = new wxStaticText(
2981 this, wxID_ANY, m_plugin.m_short_description, wxDefaultPosition,
2982 wxSize(-1, -1) /*, wxST_NO_AUTORESIZE*/);
2983 itemBoxSizer02->Add(m_pDescription, 1, wxEXPAND | wxALL, 5);
2984 m_pDescription->Bind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this);
2985 m_pDescription->Bind(wxEVT_LEFT_UP, &PluginPanel::OnPluginSelectedUp, this);
2986 }
2987
2988 if (!bCompact) {
2989 m_info_btn = new WebsiteButton(this, "https:\\opencpn.org");
2990 m_info_btn->Hide();
2991 itemBoxSizer02->Add(m_info_btn, 0);
2992
2993 m_pButtons = new wxBoxSizer(wxHORIZONTAL);
2994 itemBoxSizer02->Add(m_pButtons, 0, /*wxEXPAND|*/ wxALL, 0);
2995 m_pButtonPreferences = new wxButton(this, wxID_ANY, _("Preferences"),
2996 wxDefaultPosition, wxDefaultSize, 0);
2997 m_pButtons->Add(m_pButtonPreferences, 0, wxALIGN_LEFT | wxALL, 2);
2998
2999 m_pButtons->AddSpacer(3 * GetCharWidth());
3000
3001 m_pButtonAction =
3002 new wxButton(this, wxID_ANY, "Upgrade to Version XX.XX.XX",
3003 wxDefaultPosition, wxDefaultSize, 0);
3004 m_pButtons->Add(m_pButtonAction, 0, wxALIGN_LEFT | wxALL, 2);
3005
3006 m_pButtonUninstall = new wxButton(this, wxID_ANY, _("Uninstall"),
3007 wxDefaultPosition, wxDefaultSize, 0);
3008 m_pButtons->Add(m_pButtonUninstall, 0, wxALIGN_LEFT | wxALL, 2);
3009 } else {
3010 m_pButtons = new wxBoxSizer(wxVERTICAL);
3011 itemBoxSizer02->Add(m_pButtons, 0, /*wxEXPAND|*/ wxALL, 0);
3012
3013 wxBoxSizer* tline = new wxBoxSizer(wxHORIZONTAL);
3014 m_pButtons->Add(tline, 0, wxALL, 2);
3015
3016 m_pButtonPreferences = new wxButton(this, wxID_ANY, _("Preferences"),
3017 wxDefaultPosition, wxDefaultSize, 0);
3018 tline->Add(m_pButtonPreferences, 0, wxALIGN_LEFT | wxALL, 0);
3019
3020 tline->AddSpacer(3 * GetCharWidth());
3021
3022 m_info_btn = new WebsiteButton(this, "https:\\opencpn.org");
3023 m_info_btn->Hide();
3024 tline->Add(m_info_btn, 0);
3025
3026 m_pButtonAction =
3027 new wxButton(this, wxID_ANY, "Upgrade to Version XX.XX.XX",
3028 wxDefaultPosition, wxDefaultSize);
3029 m_pButtons->Add(m_pButtonAction, 0, wxALIGN_LEFT | wxALL, 2);
3030
3031 m_pButtonUninstall = new wxButton(this, wxID_ANY, _("Uninstall"),
3032 wxDefaultPosition, wxDefaultSize, 0);
3033 m_pButtons->Add(m_pButtonUninstall, 0, wxALIGN_LEFT | wxALL, 2);
3034 }
3035
3036 wxBitmap statusBitmap;
3037 const auto stat = m_plugin.m_status;
3038 auto icon_name = icon_by_status.at(stat);
3039 if (stat == PluginStatus::Imported &&
3040 IsUpdateAvailable(m_plugin.m_managed_metadata)) {
3041 icon_name =
3042 icon_by_status.at(PluginStatus::ManagedInstalledUpdateAvailable);
3043 }
3044
3045 wxFileName path(g_Platform->GetSharedDataDir(), icon_name);
3046 path.AppendDir("uidata");
3047 path.AppendDir("traditional");
3048 bool ok = false;
3049 int bmsize = GetCharWidth() * 3 * dpi_mult;
3050 if (path.IsFileReadable()) {
3051 statusBitmap = LoadSVG(path.GetFullPath(), bmsize, bmsize);
3052 ok = statusBitmap.IsOk();
3053 }
3054 if (!ok) {
3055 auto style = g_StyleManager->GetCurrentStyle();
3056 statusBitmap = wxBitmap(style->GetIcon("default_pi", bmsize, bmsize));
3057 wxLogMessage("Icon: %s not found.", path.GetFullPath());
3058 }
3059
3060 m_itemStatusIconBitmap = new wxStaticBitmap(this, wxID_ANY, statusBitmap);
3061 m_itemStatusIconBitmap->SetToolTip(message_by_status(stat));
3062 m_itemStatusIconBitmap->Bind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected,
3063 this);
3064 m_itemStatusIconBitmap->Bind(wxEVT_LEFT_UP, &PluginPanel::OnPluginSelectedUp,
3065 this);
3066
3067 itemBoxSizer01->Add(m_itemStatusIconBitmap, 0, wxEXPAND | wxALL, 20);
3068
3069 itemBoxSizer02->AddSpacer(GetCharWidth());
3070
3071 m_pButtonPreferences->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
3072 &PluginPanel::OnPluginPreferences, this);
3073 m_pButtonUninstall->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
3074 &PluginPanel::OnPluginUninstall, this);
3075 m_pButtonAction->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
3076 &PluginPanel::OnPluginAction, this);
3077
3078 SetSelected(m_bSelected);
3079 SetAutoLayout(true);
3080 Fit();
3081}
3082
3083PluginPanel::PluginPanel(wxPanel* parent, wxWindowID id, const wxPoint& pos,
3084 const wxSize& size, PluginMetadata md)
3085 : PluginPanel(parent, id, pos, size, PlugInData(md)) {};
3086
3087PluginPanel::~PluginPanel() {
3088 Unbind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this);
3089 if (m_is_safe_panel) return;
3090 m_itemStaticBitmap->Unbind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected,
3091 this);
3092 m_pName->Unbind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this);
3093 m_pVersion->Unbind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this);
3094 m_pDescription->Unbind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this);
3095 if (m_pButtonAction) {
3096 m_pButtonAction->Unbind(wxEVT_COMMAND_BUTTON_CLICKED,
3097 &PluginPanel::OnPluginAction, this);
3098 }
3099 m_pButtonPreferences->Unbind(wxEVT_COMMAND_BUTTON_CLICKED,
3100 &PluginPanel::OnPluginPreferences, this);
3101 m_cbEnable->Unbind(wxEVT_COMMAND_BUTTON_CLICKED,
3102 &PluginPanel::OnPluginEnableToggle, this);
3103}
3104
3105void PluginPanel::SetActionLabel(wxString& label) {
3106 m_pButtonAction->SetLabel(label);
3107 Refresh();
3108}
3109
3110static wxStopWatch swclick;
3111static int downx, downy;
3112
3113void PluginPanel::OnPluginSelected(wxMouseEvent& event) {
3114#ifdef __ANDROID__
3115 swclick.Start();
3116 event.GetPosition(&downx, &downy);
3117#else
3118 DoPluginSelect();
3119#endif
3120}
3121
3122void PluginPanel::OnPluginSelectedUp(wxMouseEvent& event) {
3123#ifdef __ANDROID__
3124 qDebug() << swclick.Time();
3125 if (swclick.Time() < 200) {
3126 int upx, upy;
3127 event.GetPosition(&upx, &upy);
3128 if ((fabs(upx - downx) < GetCharWidth()) &&
3129 (fabs(upy - downy) < GetCharWidth())) {
3130 DoPluginSelect();
3131 }
3132 }
3133 swclick.Start();
3134#endif
3135}
3136
3137void PluginPanel::DoPluginSelect() {
3138 if (m_plugin.m_status == PluginStatus::ManagedInstallAvailable) {
3139 // auto dialog = dynamic_cast<PluginListPanel*>(GetParent());
3140 // auto dialog = dynamic_cast<PluginListPanel*>(m_parent);
3141 // wxASSERT(dialog != 0);
3142
3143 // Install the new plugin, auto-enabling as a convenience measure.
3144 run_update_dialog(m_PluginListPanel, &m_plugin, false, 0, true);
3145 } else if (m_bSelected) {
3146 SetSelected(false);
3147 m_PluginListPanel->SelectPlugin(NULL);
3148 } else {
3149 SetSelected(true);
3150 m_PluginListPanel->SelectPlugin(this);
3151 }
3152}
3153
3158static PluginMetadata GetMetadataByName(const std::string& name) {
3159 auto plugins = PluginHandler::GetInstance()->GetInstalled();
3160 auto predicate = [name](const PluginMetadata& pm) { return pm.name == name; };
3161 auto found = std::find_if(plugins.begin(), plugins.end(), predicate);
3162 if (found == plugins.end()) {
3163 wxLogDebug("Cannot find metadata for %s", name.c_str());
3164 }
3165 return found != plugins.end() ? *found : PluginMetadata();
3166}
3167
3168void PluginPanel::SetSelected(bool selected) {
3169 m_bSelected = selected;
3170
3171 m_pVersion->SetLabel(
3172 PluginLoader::GetPluginVersion(m_plugin, GetMetadataByName));
3173 if (selected) {
3174 SetBackgroundColour(GetDialogColor(DLG_SELECTED_BACKGROUND));
3175 m_pButtons->Show(true);
3176 bool unInstallPossible = canUninstall(m_plugin.m_common_name.ToStdString());
3177
3178 // Directly mark Legacy and system plugins as "not uninstallable"
3179 if (m_plugin.m_status == PluginStatus::LegacyUpdateAvailable ||
3180 m_plugin.m_status == PluginStatus::Unmanaged ||
3181 m_plugin.m_status == PluginStatus::System)
3182 unInstallPossible = false;
3183
3184 // Orphan plugins can usually be uninstalled, at best effort.
3185 if (m_plugin.m_managed_metadata.is_orphan) unInstallPossible = true;
3186
3187 m_pButtonUninstall->Show(unInstallPossible);
3188
3189 if (m_plugin.m_managed_metadata.info_url.size()) {
3190 m_info_btn->SetURL(m_plugin.m_managed_metadata.info_url.c_str());
3191 m_info_btn->Show();
3192 }
3193
3194 m_cbEnable->Show(true);
3195
3196 // Configure the "Action" button
3197 wxString label;
3198 SemanticVersion newVersion;
3199 switch (m_plugin.m_status) {
3200 case PluginStatus::LegacyUpdateAvailable:
3201 label = _("Upgrade to Version ");
3202 label += wxString(m_plugin.m_managed_metadata.version.c_str());
3203 m_action = ActionVerb::UPGRADE_TO_MANAGED_VERSION;
3204 m_pButtonAction->Enable();
3205 break;
3206
3207 case PluginStatus::ManagedInstallAvailable:
3208 label = _("Install...");
3209 m_action = ActionVerb::INSTALL_MANAGED_VERSION;
3210 m_pButtonAction->Enable();
3211 break;
3212
3213 case PluginStatus::ManagedInstalledUpdateAvailable:
3214 label = _("Update to ");
3215 label += wxString(m_plugin.m_managed_metadata.version.c_str());
3216 m_action = ActionVerb::UPGRADE_INSTALLED_MANAGED_VERSION;
3217 m_pButtonAction->Enable();
3218 break;
3219
3220 case PluginStatus::ManagedInstalledCurrentVersion:
3221 label = _("Reinstall");
3222 m_action = ActionVerb::REINSTALL_MANAGED_VERSION;
3223 m_pButtonAction->Enable();
3224 break;
3225
3226 case PluginStatus::ManagedInstalledDowngradeAvailable:
3227 label = _("Downgrade");
3228 m_action = ActionVerb::DOWNGRADE_INSTALLED_MANAGED_VERSION;
3229 m_pButtonAction->Enable();
3230 break;
3231
3232 case PluginStatus::Imported:
3233 if (IsUpdateAvailable(m_plugin.m_managed_metadata)) {
3234 label = _("Update");
3235 m_action = ActionVerb::UPDATE_IMPORTED_VERSION;
3236 } else {
3237 m_pButtonAction->Hide();
3238 m_action = ActionVerb::NOP;
3239 }
3240 break;
3241
3243 m_action = ActionVerb::NOP;
3244 m_pButtonAction->Hide();
3245 break;
3246
3248 m_action = ActionVerb::NOP;
3249 m_pButtonAction->Hide();
3250 break;
3251
3252 default:
3253 label = "TBD";
3254 m_action = ActionVerb::NOP;
3255 break;
3256 }
3257 SetActionLabel(label);
3258 Layout();
3259 } else {
3260 SetBackgroundColour(GetDialogColor(DLG_UNSELECTED_BACKGROUND));
3261 // m_pDescription->SetLabel( m_pPlugin->m_short_description );
3262#ifndef __WXQT__
3263 // m_pButtons->Show(false);
3264#else
3265 // m_pButtons->Show(true);
3266#endif
3267 //();
3268
3269 m_pButtons->Show(false);
3270 m_info_btn->Hide();
3271
3272 if (m_plugin.m_status == PluginStatus::ManagedInstallAvailable)
3273 m_cbEnable->Show(false);
3274
3275 Layout();
3276 }
3277
3278 // m_pButtons->Show(selected); // For most platforms, show buttons if
3279 // selected m_pButtonsUpDown->Show(selected);
3280#ifdef __ANDROID__
3281 // Some Android devices (e.g. Kyocera) have trouble with wxBitmapButton...
3282 // m_pButtonsUpDown->Show(false);
3283 // m_pButtons->Show(true); // Always enable buttons for Android
3284#endif
3285
3286 Layout();
3287
3288 if (selected) {
3289 SetBackgroundColour(GetDialogColor(DLG_SELECTED_BACKGROUND));
3290 } else {
3291 SetBackgroundColour(GetDialogColor(DLG_UNSELECTED_BACKGROUND));
3292 }
3293
3294 SetEnabled(m_plugin.m_enabled);
3295
3296#ifdef __ANDROID__
3297 // Android (wxQT) sizers have troubles...
3298 // So we set some layout factors to avoid re-sizing on select/deselect.
3299 // m_rgSizer->Show(true);
3300 // m_pButtons->Show(true);
3301 // m_pButtonAction->Hide();
3302 // m_pButtonUninstall->Hide();
3303
3304 Fit();
3305 // m_PluginListPanel->m_pitemBoxSizer01->Layout();
3306#endif
3307}
3308
3309void PluginPanel::OnPaint(wxPaintEvent& event) {
3310 wxPaintDC dc(this);
3311
3312 int penWidth = m_penWidthUnselected;
3313 wxColour color = GetDialogColor(DLG_UNSELECTED_BACKGROUND);
3314 wxColour border = GetDialogColor(DLG_UNSELECTED_ACCENT);
3315
3316 if (m_bSelected) {
3317 penWidth = m_penWidthSelected;
3320 }
3321
3322 wxBrush b(color, wxBRUSHSTYLE_SOLID);
3323 dc.SetBrush(b);
3324 dc.SetPen(wxPen(border, penWidth));
3325
3326 dc.DrawRoundedRectangle(5, 5, GetSize().x - 10, GetSize().y - 10, 5);
3327}
3328
3329void PluginPanel::OnPluginPreferences(wxCommandEvent& event) {
3330 if (m_plugin.m_enabled && m_plugin.m_init_state &&
3331 (m_plugin.m_cap_flag & WANTS_PREFERENCES)) {
3332#ifdef __ANDROID__
3333 androidDisableRotation();
3334 PluginLoader::GetInstance()->ShowPreferencesDialog(m_plugin,
3335 GetGrandParent());
3336 // GrandParent will be the entire list panel, not the plugin panel.
3337 // Ensures better centering on small screens
3338#else
3339 PluginLoader::GetInstance()->ShowPreferencesDialog(m_plugin, this);
3340#endif
3341 }
3342}
3343
3344void PluginPanel::OnPluginEnableToggle(wxCommandEvent& event) {
3345 g_Platform->ShowBusySpinner();
3346 SetEnabled(event.IsChecked());
3347 m_pVersion->SetLabel(
3348 PluginLoader::GetPluginVersion(m_plugin, GetMetadataByName));
3349 if (m_plugin.m_status == PluginStatus::System) {
3350 // Force pluginmanager to reload all panels. Not kosher --
3351 // the EventVar should really only be notified from within PluginLoader.
3352 PluginLoader::GetInstance()->evt_pluglist_change.Notify();
3353 }
3354 g_Platform->HideBusySpinner();
3355}
3356
3357void PluginPanel::OnPluginUninstall(wxCommandEvent& event) {
3358 m_action = ActionVerb::UNINSTALL_MANAGED_VERSION;
3359
3360 // Chain up to the utility event handler
3361 wxCommandEvent actionEvent(wxEVT_COMMAND_BUTTON_CLICKED);
3362 actionEvent.SetId(ID_CMD_BUTTON_PERFORM_ACTION);
3363 actionEvent.SetClientData(this);
3364 g_pi_manager->GetUtilHandler()->AddPendingEvent(actionEvent);
3365}
3366
3367void PluginPanel::OnPluginAction(wxCommandEvent& event) {
3368 // Chain up to the utility event handler
3369 wxCommandEvent actionEvent(wxEVT_COMMAND_BUTTON_CLICKED);
3370 actionEvent.SetId(ID_CMD_BUTTON_PERFORM_ACTION);
3371 actionEvent.SetClientData(this);
3372 g_pi_manager->GetUtilHandler()->AddPendingEvent(actionEvent);
3373
3374 return;
3375}
3376
3377static void SetWindowFontStyle(wxWindow* window, wxFontStyle style) {
3378 auto font = window->GetFont();
3379 font.SetStyle(style);
3380 window->SetFont(font);
3381}
3382
3383void PluginPanel::SetEnabled(bool enabled) {
3384 if (m_is_safe_panel) return;
3385 PluginLoader::GetInstance()->SetEnabled(m_plugin.m_common_name, enabled);
3386 PluginLoader::GetInstance()->UpdatePlugIns();
3387 if (enabled) NotifySetupOptionsPlugin(&m_plugin);
3388 if (!enabled && !m_bSelected) {
3389 SetWindowFontStyle(m_pName, wxFONTSTYLE_ITALIC);
3390 SetWindowFontStyle(m_pVersion, wxFONTSTYLE_ITALIC);
3391 SetWindowFontStyle(m_pDescription, wxFONTSTYLE_ITALIC);
3392#ifdef x__ANDROID__
3393 m_pName->Disable();
3394 m_pVersion->Disable();
3395 m_pDescription->Disable();
3396#endif
3397 } else {
3398 SetWindowFontStyle(m_pName, wxFONTSTYLE_NORMAL);
3399 SetWindowFontStyle(m_pVersion, wxFONTSTYLE_NORMAL);
3400 SetWindowFontStyle(m_pDescription, wxFONTSTYLE_NORMAL);
3401#ifdef x__ANDROID__
3402 m_pName->Enable();
3403 m_pVersion->Enable();
3404 m_pDescription->Enable();
3405#endif
3406 }
3407
3408#ifdef __ANDROID__
3409 m_pName->Enable(enabled || m_bSelected);
3410 m_pVersion->Enable(enabled || m_bSelected);
3411 m_pDescription->Enable(enabled || m_bSelected);
3412#endif
3413
3414 if (m_bSelected) {
3415 wxString description = m_plugin.m_long_description;
3416 if (description.IsEmpty())
3417 description = wxString(m_plugin.m_managed_metadata.description.c_str());
3418
3419 PanelHardBreakWrapper wrapper(this, description,
3420 g_options->GetSize().x * 7 / 10);
3421 m_pDescription->SetLabel(wrapper.GetWrapped());
3422 if (m_plugin.m_managed_metadata.info_url.size()) {
3423 m_info_btn->SetURL(m_plugin.m_managed_metadata.info_url.c_str());
3424 m_info_btn->Show();
3425 }
3426 } else {
3427 wxString description = m_plugin.m_short_description;
3428 if (description.IsEmpty())
3429 description = wxString(m_plugin.m_managed_metadata.summary.c_str());
3430 PanelHardBreakWrapper wrapper(this, description,
3431 g_options->GetSize().x * 7 / 10);
3432 m_pDescription->SetLabel(wrapper.GetWrapped());
3433 }
3434
3435 m_pButtonPreferences->Enable(enabled &&
3436 (m_plugin.m_cap_flag & WANTS_PREFERENCES));
3437 m_cbEnable->SetValue(enabled);
3438}
3439
3440void PluginPanel::OnPluginUp(wxCommandEvent& event) {
3441 m_PluginListPanel->MoveUp(this);
3442}
3443
3444void PluginPanel::OnPluginDown(wxCommandEvent& event) {
3445 m_PluginListPanel->MoveDown(this);
3446}
3447
3449WebsiteButton::WebsiteButton(wxWindow* parent, const char* url)
3450 : wxPanel(parent), m_url(url) {
3451 auto vbox = new wxBoxSizer(wxVERTICAL);
3452 auto button = new wxButton(this, wxID_ANY, _("Website"));
3453 button->Enable(strlen(url) > 0);
3454 vbox->Add(button);
3455 SetSizer(vbox);
3456 Bind(wxEVT_COMMAND_BUTTON_CLICKED,
3457 [=](wxCommandEvent&) { wxLaunchDefaultBrowser(m_url); });
3458}
3459
3460// ----------------------------------------------------------------------------
3461// PlugInChartBase Implmentation
3462// This class is the base class for Plug-able chart types
3463// ----------------------------------------------------------------------------
3464
3465PlugInChartBase::PlugInChartBase() { m_Chart_Error_Factor = 0.; }
3466
3467PlugInChartBase::~PlugInChartBase() {}
3468
3469wxString PlugInChartBase::GetFileSearchMask() { return ""; }
3470
3471int PlugInChartBase::Init(const wxString& name, int init_flags) { return 0; }
3472
3473// Accessors
3474
3475double PlugInChartBase::GetNormalScaleMin(double canvas_scale_factor,
3476 bool b_allow_overzoom) {
3477 return 1.0;
3478}
3479
3480double PlugInChartBase::GetNormalScaleMax(double canvas_scale_factor,
3481 int canvas_width) {
3482 return 2.0e7;
3483}
3484
3485bool PlugInChartBase::GetChartExtent(ExtentPI* pext) { return false; }
3486
3488 const wxRegion& Region) {
3489 return wxNullBitmap;
3490}
3491
3493 PlugIn_ViewPort& vp_proposed) {
3494 return false;
3495}
3496
3498 wxRegion* pValidRegion) {}
3499
3500void PlugInChartBase::SetColorScheme(int cs, bool bApplyImmediate) {}
3501
3502double PlugInChartBase::GetNearestPreferredScalePPM(double target_scale_ppm) {
3503 return 1.0;
3504}
3505
3506wxBitmap* PlugInChartBase::GetThumbnail(int tnx, int tny, int cs) {
3507 return NULL;
3508}
3509
3511 wxRect* pSourceRect) {}
3512
3514
3515bool PlugInChartBase::GetChartBits(wxRect& source, unsigned char* pPix,
3516 int sub_samp) {
3517 return false;
3518}
3519
3521
3523
3524void PlugInChartBase::latlong_to_chartpix(double lat, double lon, double& pixx,
3525 double& pixy) {}
3526
3527void PlugInChartBase::chartpix_to_latlong(double pixx, double pixy,
3528 double* plat, double* plon) {}
3529
3530// ----------------------------------------------------------------------------
3531// PlugInChartBaseGL Implementation
3532//
3533// ----------------------------------------------------------------------------
3534
3535PlugInChartBaseGL::PlugInChartBaseGL() {}
3536
3537PlugInChartBaseGL::~PlugInChartBaseGL() {}
3538
3540 const PlugIn_ViewPort& VPoint,
3541 const wxRegion& Region,
3542 bool b_use_stencil) {
3543 return 0;
3544}
3545
3547 float lat, float lon, float select_radius, PlugIn_ViewPort* VPoint) {
3548 return NULL;
3549}
3550
3551wxString PlugInChartBaseGL::CreateObjDescriptions(ListOfPI_S57Obj* obj_list) {
3552 return "";
3553}
3554
3556
3557int PlugInChartBaseGL::GetNoCOVRTablePoints(int iTable) { return 0; }
3558
3559int PlugInChartBaseGL::GetNoCOVRTablenPoints(int iTable) { return 0; }
3560
3561float* PlugInChartBaseGL::GetNoCOVRTableHead(int iTable) { return 0; }
3562
3563// ----------------------------------------------------------------------------
3564// PlugInChartBaseExtended Implementation
3565//
3566// ----------------------------------------------------------------------------
3567
3568PlugInChartBaseExtended::PlugInChartBaseExtended() {}
3569
3570PlugInChartBaseExtended::~PlugInChartBaseExtended() {}
3571
3573 const PlugIn_ViewPort& VPoint,
3574 const wxRegion& Region,
3575 bool b_use_stencil) {
3576 return 0;
3577}
3578
3580 const wxGLContext& glc, const PlugIn_ViewPort& VPoint,
3581 const wxRegion& Region, bool b_use_stencil) {
3582 return 0;
3583}
3584
3586 const wxGLContext& glc, const PlugIn_ViewPort& VPoint,
3587 const wxRegion& Region, bool b_use_stencil) {
3588 return 0;
3589}
3590
3592 const PlugIn_ViewPort& VPoint, const wxRegion& Region) {
3593 return wxNullBitmap;
3594}
3595
3597 wxMemoryDC& dc, const PlugIn_ViewPort& VPoint, const wxRegion& Region) {
3598 return false;
3599}
3600
3601ListOfPI_S57Obj* PlugInChartBaseExtended::GetObjRuleListAtLatLon(
3602 float lat, float lon, float select_radius, PlugIn_ViewPort* VPoint) {
3603 return NULL;
3604}
3605
3606wxString PlugInChartBaseExtended::CreateObjDescriptions(
3607 ListOfPI_S57Obj* obj_list) {
3608 return "";
3609}
3610
3611int PlugInChartBaseExtended::GetNoCOVREntries() { return 0; }
3612
3613int PlugInChartBaseExtended::GetNoCOVRTablePoints(int iTable) { return 0; }
3614
3615int PlugInChartBaseExtended::GetNoCOVRTablenPoints(int iTable) { return 0; }
3616
3617float* PlugInChartBaseExtended::GetNoCOVRTableHead(int iTable) { return 0; }
3618
3620
3621// ----------------------------------------------------------------------------
3622// PlugInChartBaseExtendedPlus2 Implementation
3623//
3624// ----------------------------------------------------------------------------
3625
3626PlugInChartBaseExtendedPlus2::PlugInChartBaseExtendedPlus2() {}
3627
3628PlugInChartBaseExtendedPlus2::~PlugInChartBaseExtendedPlus2() {}
3629
3630ListOfPI_S57Obj*
3631PlugInChartBaseExtendedPlus2::GetLightsObjRuleListVisibleAtLatLon(
3632 float lat, float lon, PlugIn_ViewPort* VPoint) {
3633 return NULL;
3634}
3635
3636// ----------------------------------------------------------------------------
3637// PlugInChartBaseGLPlus2 Implementation
3638//
3639// ----------------------------------------------------------------------------
3640
3641PlugInChartBaseGLPlus2::PlugInChartBaseGLPlus2() {}
3642
3643PlugInChartBaseGLPlus2::~PlugInChartBaseGLPlus2() {}
3644
3646 float lat, float lon, PlugIn_ViewPort* VPoint) {
3647 return NULL;
3648}
3649
3650// ----------------------------------------------------------------------------
3651// ChartPlugInWrapper Implementation
3652// This class is a wrapper/interface to PlugIn charts(PlugInChartBase)
3653// ----------------------------------------------------------------------------
3654
3655ChartPlugInWrapper::ChartPlugInWrapper() {}
3656
3657ChartPlugInWrapper::ChartPlugInWrapper(const wxString& chart_class) {
3658 m_ppo = ::wxCreateDynamicObject(chart_class);
3659 m_ppicb = dynamic_cast<PlugInChartBase*>(m_ppo);
3660}
3661
3662ChartPlugInWrapper::~ChartPlugInWrapper() {
3663 if (m_ppicb) delete m_ppicb;
3664}
3665
3666wxString ChartPlugInWrapper::GetFileSearchMask() {
3667 if (m_ppicb)
3668 return m_ppicb->GetFileSearchMask();
3669 else
3670 return "";
3671}
3672
3673InitReturn ChartPlugInWrapper::Init(const wxString& name,
3674 ChartInitFlag init_flags) {
3675 if (m_ppicb) {
3676 wxWindow* pa = wxWindow::FindFocus();
3677
3678 InitReturn ret_val = (InitReturn)m_ppicb->Init(name, (int)init_flags);
3679
3680 // Here we transcribe all the required wrapped member elements up into
3681 // the chartbase object which is the parent of this class
3682 if (ret_val == INIT_OK) {
3683 m_FullPath = m_ppicb->GetFullPath();
3684 m_ChartType = (ChartTypeEnum)m_ppicb->GetChartType();
3685 m_ChartFamily = (ChartFamilyEnum)m_ppicb->GetChartFamily();
3686 m_projection = (OcpnProjType)m_ppicb->GetChartProjection();
3687 m_EdDate = m_ppicb->GetEditionDate();
3688 m_Name = m_ppicb->GetName();
3689 m_ID = m_ppicb->GetID();
3690 m_DepthUnits = m_ppicb->GetDepthUnits();
3691 m_SoundingsDatum = m_ppicb->GetSoundingsDatum();
3692 m_datum_str = m_ppicb->GetDatumString();
3693 m_SE = m_ppicb->GetSE();
3694 m_EdDate = m_ppicb->GetEditionDate();
3695 m_ExtraInfo = m_ppicb->GetExtraInfo();
3696 Chart_Error_Factor = m_ppicb->GetChartErrorFactor();
3697 m_depth_unit_id = (ChartDepthUnitType)m_ppicb->GetDepthUnitId();
3698 m_Chart_Skew = m_ppicb->GetChartSkew();
3699 m_Chart_Scale = m_ppicb->GetNativeScale();
3700
3701 // We estimate ppm_avg as needed by raster texture cache logic...
3702 // This number works for average BSB charts, scanned with average
3703 // resolution
3704 m_ppm_avg = 10000. / m_ppicb->GetNativeScale(); // fallback value
3705
3706 // Calcuculate a "better" ppm from the chart geo extent and raster size.
3707 if ((fabs(m_Chart_Skew) < .01) &&
3708 (CHART_FAMILY_RASTER == m_ChartFamily)) {
3709 Extent extent;
3710 if (GetChartExtent(&extent)) {
3711 double lon_range = extent.ELON - extent.WLON;
3712 if ((lon_range > 0) &&
3713 (lon_range < 90.0)) // Be safe about IDL crossing and huge charts
3714 m_ppm_avg = GetSize_X() / (lon_range * 1852 * 60);
3715 }
3716 }
3717
3718 m_overlayENC = false;
3719 if (m_ChartFamily == (ChartFamilyEnum)PI_CHART_FAMILY_VECTOR) {
3720 wxCharBuffer buf = m_FullPath.ToUTF8();
3721 m_overlayENC = s57chart::IsCellOverlayType(buf.data());
3722 }
3723
3724 bReadyToRender = m_ppicb->IsReadyToRender();
3725 } else {
3726 // Mark the chart as unable to render
3727 m_ChartType = CHART_TYPE_UNKNOWN;
3728 m_ChartFamily = CHART_FAMILY_UNKNOWN;
3729 }
3730
3731 // PlugIn may invoke wxExecute(), which steals the keyboard focus
3732 // So take it back
3733 auto pc = dynamic_cast<ChartCanvas*>(pa);
3734 if (pc) pc->SetFocus();
3735
3736 return ret_val;
3737 } else
3738 return INIT_FAIL_REMOVE;
3739}
3740
3741// Accessors
3742int ChartPlugInWrapper::GetCOVREntries() {
3743 if (m_ppicb)
3744 return m_ppicb->GetCOVREntries();
3745 else
3746 return 0;
3747}
3748
3749int ChartPlugInWrapper::GetCOVRTablePoints(int iTable) {
3750 if (m_ppicb)
3751 return m_ppicb->GetCOVRTablePoints(iTable);
3752 else
3753 return 0;
3754}
3755
3756int ChartPlugInWrapper::GetCOVRTablenPoints(int iTable) {
3757 if (m_ppicb)
3758 return m_ppicb->GetCOVRTablenPoints(iTable);
3759 else
3760 return 0;
3761}
3762
3763float* ChartPlugInWrapper::GetCOVRTableHead(int iTable) {
3764 if (m_ppicb)
3765 return m_ppicb->GetCOVRTableHead(iTable);
3766 else
3767 return 0;
3768}
3769
3770// TODO
3771// PlugIn chart types do not properly support NoCovr Regions
3772// Proper fix is to update PlugIn Chart Type API
3773// Derive an extended PlugIn chart class from existing class,
3774// and use some kind of RTTI to figure out which class to call.
3775int ChartPlugInWrapper::GetNoCOVREntries() {
3776 if (m_ppicb) {
3777 PlugInChartBaseGL* ppicbgl = dynamic_cast<PlugInChartBaseGL*>(m_ppicb);
3778 if (ppicbgl) {
3779 return ppicbgl->GetNoCOVREntries();
3780 }
3781 }
3782 return 0;
3783}
3784
3785int ChartPlugInWrapper::GetNoCOVRTablePoints(int iTable) {
3786 if (m_ppicb) {
3787 PlugInChartBaseGL* ppicbgl = dynamic_cast<PlugInChartBaseGL*>(m_ppicb);
3788 if (ppicbgl) {
3789 return ppicbgl->GetNoCOVRTablePoints(iTable);
3790 }
3791 }
3792 return 0;
3793}
3794
3795int ChartPlugInWrapper::GetNoCOVRTablenPoints(int iTable) {
3796 if (m_ppicb) {
3797 PlugInChartBaseGL* ppicbgl = dynamic_cast<PlugInChartBaseGL*>(m_ppicb);
3798 if (ppicbgl) {
3799 return ppicbgl->GetNoCOVRTablenPoints(iTable);
3800 }
3801 }
3802 return 0;
3803}
3804
3805float* ChartPlugInWrapper::GetNoCOVRTableHead(int iTable) {
3806 if (m_ppicb) {
3807 PlugInChartBaseGL* ppicbgl = dynamic_cast<PlugInChartBaseGL*>(m_ppicb);
3808 if (ppicbgl) {
3809 return ppicbgl->GetNoCOVRTableHead(iTable);
3810 }
3811 }
3812 return 0;
3813}
3814
3815bool ChartPlugInWrapper::GetChartExtent(Extent* pext) {
3816 if (m_ppicb) {
3817 ExtentPI xpi;
3818 if (m_ppicb->GetChartExtent(&xpi)) {
3819 pext->NLAT = xpi.NLAT;
3820 pext->SLAT = xpi.SLAT;
3821 pext->ELON = xpi.ELON;
3822 pext->WLON = xpi.WLON;
3823
3824 return true;
3825 } else
3826 return false;
3827 } else
3828 return false;
3829}
3830
3831ThumbData* ChartPlugInWrapper::GetThumbData(int tnx, int tny, float lat,
3832 float lon) {
3833 if (m_ppicb) {
3834 // Create the bitmap if needed, doing a deep copy from the Bitmap owned
3835 // by the PlugIn Chart
3836 if (!pThumbData->pDIBThumb) {
3837 wxBitmap* pBMPOwnedByChart =
3838 m_ppicb->GetThumbnail(tnx, tny, m_global_color_scheme);
3839 if (pBMPOwnedByChart) {
3840 wxImage img = pBMPOwnedByChart->ConvertToImage();
3841 pThumbData->pDIBThumb = new wxBitmap(img);
3842 } else
3843 pThumbData->pDIBThumb = NULL;
3844 }
3845
3846 pThumbData->Thumb_Size_X = tnx;
3847 pThumbData->Thumb_Size_Y = tny;
3848
3849 /*
3850 // Plot the supplied Lat/Lon on the thumbnail
3851 int divx = m_ppicb->Size_X / tnx;
3852 int divy = m_ppicb->Size_Y / tny;
3853
3854 int div_factor = __min(divx, divy);
3855
3856 int pixx, pixy;
3857
3858
3859 // Using a temporary synthetic ViewPort and source rectangle,
3860 // calculate the ships position on the thumbnail
3861 ViewPort tvp;
3862 tvp.pix_width = tnx;
3863 tvp.pix_height = tny;
3864 tvp.view_scale_ppm = GetPPM() / div_factor;
3865 wxRect trex = Rsrc;
3866 Rsrc.x = 0;
3867 Rsrc.y = 0;
3868 latlong_to_pix_vp(lat, lon, pixx, pixy, tvp);
3869 Rsrc = trex;
3870
3871 pThumbData->ShipX = pixx;// / div_factor;
3872 pThumbData->ShipY = pixy;// / div_factor;
3873 */
3874 pThumbData->ShipX = 0;
3875 pThumbData->ShipY = 0;
3876
3877 return pThumbData;
3878 } else
3879 return NULL;
3880}
3881
3882ThumbData* ChartPlugInWrapper::GetThumbData() { return pThumbData; }
3883
3884bool ChartPlugInWrapper::UpdateThumbData(double lat, double lon) {
3885 return true;
3886}
3887
3888double ChartPlugInWrapper::GetNormalScaleMin(double canvas_scale_factor,
3889 bool b_allow_overzoom) {
3890 if (m_ppicb)
3891 return m_ppicb->GetNormalScaleMin(canvas_scale_factor, b_allow_overzoom);
3892 else
3893 return 1.0;
3894}
3895
3896double ChartPlugInWrapper::GetNormalScaleMax(double canvas_scale_factor,
3897 int canvas_width) {
3898 if (m_ppicb)
3899 return m_ppicb->GetNormalScaleMax(canvas_scale_factor, canvas_width);
3900 else
3901 return 2.0e7;
3902}
3903
3904/* RectRegion:
3905 * This is the Screen region desired to be updated. Will
3906 * be either 1 rectangle(full screen) or two rectangles (panning with FBO
3907 * accelerated pan logic)
3908 *
3909 * Region:
3910 * This is the LLRegion describing the quilt active region
3911 * for this chart.
3912 *
3913 * So, Actual rendering area onscreen should be clipped to the
3914 * intersection of the two regions.
3915 */
3916
3917// Render helpers
3918void RenderRotateToViewPort(const ViewPort& VPoint) {
3919#ifndef USE_ANDROID_GLES2
3920 float xt = VPoint.pix_width / 2.0, yt = VPoint.pix_height / 2.0;
3921 glTranslatef(xt, yt, 0);
3922 glRotatef(VPoint.rotation * 180. / PI, 0, 0, 1);
3923 glTranslatef(-xt, -yt, 0);
3924#endif
3925}
3926
3927void UndoRenderRotateToViewPort(const ViewPort& VPoint) {
3928#ifndef USE_ANDROID_GLES2
3929 float xt = VPoint.pix_width / 2.0, yt = VPoint.pix_height / 2.0;
3930 glTranslatef(xt, yt, 0);
3931 glRotatef(-VPoint.rotation * 180. / PI, 0, 0, 1);
3932 glTranslatef(-xt, -yt, 0);
3933#endif
3934}
3935
3936bool ChartPlugInWrapper::RenderRegionViewOnGL(const wxGLContext& glc,
3937 const ViewPort& VPoint,
3938 const OCPNRegion& RectRegion,
3939 const LLRegion& Region) {
3940#ifdef ocpnUSE_GL
3941 if (m_ppicb) {
3942 ViewPort vp = VPoint; // non-const copy
3943
3944 gs_plib_flags = 0; // reset the CAPs flag
3945 PlugInChartBaseGL* ppicb_gl = dynamic_cast<PlugInChartBaseGL*>(m_ppicb);
3946 PlugInChartBaseExtended* ppicb_x =
3947 dynamic_cast<PlugInChartBaseExtended*>(m_ppicb);
3948 if (!Region.Empty() && (ppicb_gl || ppicb_x)) {
3949 wxRegion* r = RectRegion.GetNew_wxRegion();
3950 for (OCPNRegionIterator upd(RectRegion); upd.HaveRects();
3951 upd.NextRect()) {
3952 LLRegion chart_region = vp.GetLLRegion(upd.GetRect());
3953 chart_region.Intersect(Region);
3954
3955 if (!chart_region.Empty()) {
3956 ViewPort cvp = glChartCanvas::ClippedViewport(VPoint, chart_region);
3957
3958 glChartCanvas::SetClipRect(cvp, upd.GetRect(), false);
3959
3960 // ps52plib->m_last_clip_rect = upd.GetRect();
3961
3962#ifndef USE_ANDROID_GLES2
3963// glPushMatrix(); // Adjust for rotation
3964#endif
3965 RenderRotateToViewPort(VPoint);
3966
3967 PlugIn_ViewPort pivp = CreatePlugInViewport(cvp);
3968 if (ppicb_x)
3969 ppicb_x->RenderRegionViewOnGL(glc, pivp, *r,
3970 glChartCanvas::s_b_useStencil);
3971 else if (ppicb_gl)
3972 ppicb_gl->RenderRegionViewOnGL(glc, pivp, *r,
3973 glChartCanvas::s_b_useStencil);
3974 UndoRenderRotateToViewPort(VPoint);
3975
3976#ifndef USE_ANDROID_GLES2
3977// glPopMatrix();
3978#endif
3979 glChartCanvas::DisableClipRegion();
3980
3981 }
3982 } // for
3983 delete r;
3984 }
3985 } else
3986 return false;
3987#endif
3988 return true;
3989}
3990
3991// int indexrr;
3992
3993bool ChartPlugInWrapper::RenderRegionViewOnGLNoText(
3994 const wxGLContext& glc, const ViewPort& VPoint,
3995 const OCPNRegion& RectRegion, const LLRegion& Region) {
3996#ifdef ocpnUSE_GL
3997 if (m_ppicb) {
3998 // printf("\nCPIW::RRVOGLNT %d %d \n", indexrr++, m_Chart_Scale);
3999
4000 gs_plib_flags = 0; // reset the CAPs flag
4001 PlugInChartBaseExtended* ppicb_x =
4002 dynamic_cast<PlugInChartBaseExtended*>(m_ppicb);
4003 PlugInChartBaseGL* ppicb = dynamic_cast<PlugInChartBaseGL*>(m_ppicb);
4004 if (!Region.Empty() && ppicb_x) {
4005 // Start with a clean slate
4006 glChartCanvas::SetClipRect(VPoint, VPoint.rv_rect, false);
4007 glChartCanvas::DisableClipRegion();
4008
4009 // Apply rotation to this chart
4010 RenderRotateToViewPort(VPoint);
4011
4012 PlugIn_ViewPort pivp = CreatePlugInViewport(VPoint);
4013 wxRegion* r = RectRegion.GetNew_wxRegion();
4014
4015 ppicb_x->RenderRegionViewOnGLNoText(glc, pivp, *r,
4016 glChartCanvas::s_b_useStencil);
4017
4018 // Undo rotation
4019 UndoRenderRotateToViewPort(VPoint);
4020
4021 delete r;
4022 }
4023
4024 else if (!Region.Empty() &&
4025 ppicb) // Legacy Vector GL Plugin chart (e.g.S63)
4026 {
4027 ViewPort vp = VPoint; // non-const copy
4028 wxRegion* r = RectRegion.GetNew_wxRegion();
4029 for (OCPNRegionIterator upd(RectRegion); upd.HaveRects();
4030 upd.NextRect()) {
4031 LLRegion chart_region = vp.GetLLRegion(upd.GetRect());
4032 chart_region.Intersect(Region);
4033
4034 if (!chart_region.Empty()) {
4035 ViewPort cvp = glChartCanvas::ClippedViewport(VPoint, chart_region);
4036
4037 glChartCanvas::SetClipRect(cvp, upd.GetRect(), false);
4038
4039 RenderRotateToViewPort(VPoint);
4040
4041 PlugIn_ViewPort pivp = CreatePlugInViewport(cvp);
4042 ppicb->RenderRegionViewOnGL(glc, pivp, *r,
4043 glChartCanvas::s_b_useStencil);
4044
4045 // Undo rotation
4046 UndoRenderRotateToViewPort(VPoint);
4047
4048 glChartCanvas::DisableClipRegion();
4049
4050 }
4051 } // for
4052 delete r;
4053 }
4054
4055 } else
4056 return false;
4057#endif
4058 return true;
4059}
4060
4061bool ChartPlugInWrapper::RenderRegionViewOnGLTextOnly(
4062 const wxGLContext& glc, const ViewPort& VPoint, const OCPNRegion& Region) {
4063#ifdef ocpnUSE_GL
4064 if (m_ppicb) {
4065 gs_plib_flags = 0; // reset the CAPs flag
4066 PlugInChartBaseExtended* ppicb_x =
4067 dynamic_cast<PlugInChartBaseExtended*>(m_ppicb);
4068 if (!Region.Empty() && ppicb_x) {
4069 wxRegion* r = Region.GetNew_wxRegion();
4070 for (OCPNRegionIterator upd(Region); upd.HaveRects(); upd.NextRect()) {
4071#ifndef USE_ANDROID_GLES2
4072// glPushMatrix(); // Adjust for rotation
4073#endif
4074 RenderRotateToViewPort(VPoint);
4075
4076 PlugIn_ViewPort pivp = CreatePlugInViewport(VPoint);
4077 ppicb_x->RenderRegionViewOnGLTextOnly(glc, pivp, *r,
4078 glChartCanvas::s_b_useStencil);
4079 UndoRenderRotateToViewPort(VPoint);
4080
4081#ifndef USE_ANDROID_GLES2
4082// glPopMatrix();
4083#endif
4084
4085 } // for
4086 delete r;
4087 }
4088 } else
4089 return false;
4090#endif
4091 return true;
4092}
4093
4094bool ChartPlugInWrapper::RenderRegionViewOnDC(wxMemoryDC& dc,
4095 const ViewPort& VPoint,
4096 const OCPNRegion& Region) {
4097 if (m_ppicb) {
4098 gs_plib_flags = 0; // reset the CAPs flag
4099 PlugIn_ViewPort pivp = CreatePlugInViewport(VPoint);
4100 if (Region.IsOk()) {
4101 wxRegion* r = Region.GetNew_wxRegion();
4102 if (!m_overlayENC)
4103 dc.SelectObject(m_ppicb->RenderRegionView(pivp, *r));
4104 else {
4105 wxBitmap& obmp = m_ppicb->RenderRegionView(pivp, *r);
4106
4107 // Create a mask to remove the NODTA areas from overlay cells.
4108 wxColour nodat = GetGlobalColor(_T ( "NODTA" ));
4109 wxColour nodat_sub = nodat;
4110
4111#ifdef ocpnUSE_ocpnBitmap
4112 nodat_sub = wxColour(nodat.Blue(), nodat.Green(), nodat.Red());
4113#endif
4114 m_pMask = new wxMask(obmp, nodat_sub);
4115 obmp.SetMask(m_pMask);
4116
4117 dc.SelectObject(obmp);
4118 }
4119
4120 delete r;
4121 return true;
4122 } else
4123 return false;
4124 } else
4125 return false;
4126}
4127
4128bool ChartPlugInWrapper::RenderRegionViewOnDCNoText(wxMemoryDC& dc,
4129 const ViewPort& VPoint,
4130 const OCPNRegion& Region) {
4131 if (m_ppicb) {
4132 gs_plib_flags = 0; // reset the CAPs flag
4133 PlugIn_ViewPort pivp = CreatePlugInViewport(VPoint);
4134
4136 dynamic_cast<PlugInChartBaseExtended*>(m_ppicb);
4137 PlugInChartBase* ppicb = dynamic_cast<PlugInChartBase*>(m_ppicb);
4138
4139 if (Region.IsOk() && (pCBx || ppicb)) {
4140 wxRegion* r = Region.GetNew_wxRegion();
4141
4142 if (pCBx)
4143 dc.SelectObject(pCBx->RenderRegionViewOnDCNoText(pivp, *r));
4144 else if (ppicb)
4145 dc.SelectObject(ppicb->RenderRegionView(pivp, *r));
4146
4147 delete r;
4148 return true;
4149 } else
4150 return false;
4151 } else
4152 return false;
4153}
4154
4155bool ChartPlugInWrapper::RenderRegionViewOnDCTextOnly(
4156 wxMemoryDC& dc, const ViewPort& VPoint, const OCPNRegion& Region) {
4157 if (m_ppicb) {
4158 bool ret_val = false;
4159 gs_plib_flags = 0; // reset the CAPs flag
4160 PlugIn_ViewPort pivp = CreatePlugInViewport(VPoint);
4161 if (Region.IsOk()) {
4162 wxRegion* r = Region.GetNew_wxRegion();
4163
4165 dynamic_cast<PlugInChartBaseExtended*>(m_ppicb);
4166 if (pCBx) ret_val = pCBx->RenderRegionViewOnDCTextOnly(dc, pivp, *r);
4167
4168 delete r;
4169 return ret_val;
4170 } else
4171 return false;
4172 } else
4173 return false;
4174}
4175
4176void ChartPlugInWrapper::ClearPLIBTextList() {
4177 if (m_ppicb) {
4179 dynamic_cast<PlugInChartBaseExtended*>(m_ppicb);
4180 if (pCBx) pCBx->ClearPLIBTextList();
4181 }
4182}
4183
4184bool ChartPlugInWrapper::AdjustVP(ViewPort& vp_last, ViewPort& vp_proposed) {
4185 if (m_ppicb) {
4186 PlugIn_ViewPort pivp_last = CreatePlugInViewport(vp_last);
4187 PlugIn_ViewPort pivp_proposed = CreatePlugInViewport(vp_proposed);
4188 return m_ppicb->AdjustVP(pivp_last, pivp_proposed);
4189 } else
4190 return false;
4191}
4192
4193void ChartPlugInWrapper::GetValidCanvasRegion(const ViewPort& VPoint,
4194 OCPNRegion* pValidRegion) {
4195 if (m_ppicb) {
4196 PlugIn_ViewPort pivp = CreatePlugInViewport(VPoint);
4197 // currently convert using wxRegion,
4198 // this should be changed as wxRegion is proven unstable/buggy on various
4199 // platforms
4200 wxRegion region;
4201 m_ppicb->GetValidCanvasRegion(pivp, &region);
4202 *pValidRegion = OCPNRegion(region);
4203 }
4204
4205 return;
4206}
4207
4208void ChartPlugInWrapper::SetColorScheme(ColorScheme cs, bool bApplyImmediate) {
4209 if (m_ppicb) {
4210 m_ppicb->SetColorScheme(cs, bApplyImmediate);
4211 }
4212 m_global_color_scheme = cs;
4213 // Force a new thumbnail
4214 if (pThumbData) pThumbData->pDIBThumb = NULL;
4215}
4216
4218 double target_scale_ppm) {
4219 if (m_ppicb)
4220 return m_ppicb->GetNearestPreferredScalePPM(target_scale_ppm);
4221 else
4222 return 1.0;
4223}
4224
4225void ChartPlugInWrapper::ComputeSourceRectangle(const ViewPort& VPoint,
4226 wxRect* pSourceRect) {
4227 if (m_ppicb) {
4228 PlugIn_ViewPort pivp = CreatePlugInViewport(VPoint);
4229 m_ppicb->ComputeSourceRectangle(pivp, pSourceRect);
4230 }
4231}
4232
4233double ChartPlugInWrapper::GetRasterScaleFactor(const ViewPort& vp) {
4234 if (m_ppicb) {
4235 return (wxRound(100000 * GetPPM() / vp.view_scale_ppm)) / 100000.;
4236 } else
4237 return 1.0;
4238}
4239
4240bool ChartPlugInWrapper::GetChartBits(wxRect& source, unsigned char* pPix,
4241 int sub_samp) {
4242 wxCriticalSectionLocker locker(m_critSect);
4243
4244 if (m_ppicb)
4245
4246 return m_ppicb->GetChartBits(source, pPix, sub_samp);
4247 else
4248 return false;
4249}
4250
4251int ChartPlugInWrapper::GetSize_X() {
4252 if (m_ppicb)
4253 return m_ppicb->GetSize_X();
4254 else
4255 return 1;
4256}
4257
4258int ChartPlugInWrapper::GetSize_Y() {
4259 if (m_ppicb)
4260 return m_ppicb->GetSize_Y();
4261 else
4262 return 1;
4263}
4264
4265void ChartPlugInWrapper::latlong_to_chartpix(double lat, double lon,
4266 double& pixx, double& pixy) {
4267 if (m_ppicb) m_ppicb->latlong_to_chartpix(lat, lon, pixx, pixy);
4268}
4269
4270void ChartPlugInWrapper::chartpix_to_latlong(double pixx, double pixy,
4271 double* plat, double* plon) {
4272 if (m_ppicb) m_ppicb->chartpix_to_latlong(pixx, pixy, plat, plon);
4273}
4274
4275//----------------------------------------------------------------------------------------------------------
4276// The PlugIn CallBack API Implementation
4277// The definitions of this API are found in ocpn_plugin.h
4278//----------------------------------------------------------------------------------------------------------
4279
4280/* API 1.11 */
4281
4282/* API 1.11 adds some more common functions to avoid unnecessary code
4283 * duplication */
4284
4285wxString toSDMM_PlugIn(int NEflag, double a, bool hi_precision) {
4286 return toSDMM(NEflag, a, hi_precision);
4287}
4288
4289wxColour GetBaseGlobalColor(wxString colorName) {
4290 return GetGlobalColor(colorName);
4291}
4292
4293int OCPNMessageBox_PlugIn(wxWindow* parent, const wxString& message,
4294 const wxString& caption, int style, int x, int y) {
4295 return OCPNMessageBox(parent, message, caption, style, 100, x, y);
4296}
4297
4298wxString GetOCPN_ExePath() { return g_Platform->GetExePath(); }
4299
4300wxString* GetpPlugInLocation() { return g_Platform->GetPluginDirPtr(); }
4301
4303 return g_Platform->GetWritableDocumentsDir();
4304}
4305
4306wxString GetPlugInPath(opencpn_plugin* pplugin) {
4307 wxString ret_val;
4308 auto loader = PluginLoader::GetInstance();
4309 for (unsigned int i = 0; i < loader->GetPlugInArray()->GetCount(); i++) {
4310 PlugInContainer* pic = loader->GetPlugInArray()->Item(i);
4311 if (pic->m_pplugin == pplugin) {
4312 ret_val = pic->m_plugin_file;
4313 break;
4314 }
4315 }
4316 return ret_val;
4317}
4318
4319// API 1.11 Access to Vector PlugIn charts
4320
4321ListOfPI_S57Obj* PlugInManager::GetPlugInObjRuleListAtLatLon(
4322 ChartPlugInWrapper* target, float zlat, float zlon, float SelectRadius,
4323 const ViewPort& vp) {
4324 ListOfPI_S57Obj* list = NULL;
4325 if (target) {
4326 PlugInChartBaseGL* picbgl =
4327 dynamic_cast<PlugInChartBaseGL*>(target->GetPlugInChart());
4328 if (picbgl) {
4329 PlugIn_ViewPort pi_vp = CreatePlugInViewport(vp);
4330 list = picbgl->GetObjRuleListAtLatLon(zlat, zlon, SelectRadius, &pi_vp);
4331
4332 return list;
4333 }
4335 dynamic_cast<PlugInChartBaseExtended*>(target->GetPlugInChart());
4336 if (picbx) {
4337 PlugIn_ViewPort pi_vp = CreatePlugInViewport(vp);
4338 list = picbx->GetObjRuleListAtLatLon(zlat, zlon, SelectRadius, &pi_vp);
4339
4340 return list;
4341 } else
4342 return list;
4343 } else
4344 return list;
4345}
4346
4347wxString PlugInManager::CreateObjDescriptions(ChartPlugInWrapper* target,
4348 ListOfPI_S57Obj* rule_list) {
4349 wxString ret_str;
4350 if (target) {
4351 PlugInChartBaseGL* picbgl =
4352 dynamic_cast<PlugInChartBaseGL*>(target->GetPlugInChart());
4353 if (picbgl) {
4354 ret_str = picbgl->CreateObjDescriptions(rule_list);
4355 } else {
4357 dynamic_cast<PlugInChartBaseExtended*>(target->GetPlugInChart());
4358 if (picbx) {
4359 ret_str = picbx->CreateObjDescriptions(rule_list);
4360 }
4361 }
4362 }
4363 return ret_str;
4364}
4365
4366// API 1.11 Access to S52 PLIB
4368 return ""; // ps52plib->GetPLIBColorScheme()
4369}
4370
4372 if (ps52plib)
4373 return ps52plib->m_nDepthUnitDisplay;
4374 else
4375 return 0;
4376}
4377
4379 if (ps52plib)
4380 return ps52plib->m_nSymbolStyle;
4381 else
4382 return 0;
4383}
4384
4386 if (ps52plib)
4387 return ps52plib->m_nBoundaryStyle;
4388 else
4389 return 0;
4390}
4391
4393 if (ps52plib) {
4394 // Create and populate a compatible s57 Object
4395 S57Obj cobj;
4396 chart_context ctx;
4397 CreateCompatibleS57Object(pObj, &cobj, &ctx);
4398
4399 ViewPort cvp = CreateCompatibleViewport(*vp);
4400
4401 S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context;
4402
4403 // Create and populate a minimally compatible object container
4404 ObjRazRules rzRules;
4405 rzRules.obj = &cobj;
4406 rzRules.LUP = pContext->LUP;
4407 rzRules.sm_transform_parms = 0;
4408 rzRules.child = NULL;
4409 rzRules.next = NULL;
4410
4411 if (pContext->LUP) {
4412 ps52plib->SetVPointCompat(
4413 cvp.pix_width, cvp.pix_height, cvp.view_scale_ppm, cvp.rotation,
4414 cvp.clat, cvp.clon, cvp.chart_scale, cvp.rv_rect, cvp.GetBBox(),
4415 cvp.ref_scale, GetOCPNCanvasWindow()->GetContentScaleFactor());
4416 ps52plib->PrepareForRender();
4417
4418 return ps52plib->ObjectRenderCheck(&rzRules);
4419 } else
4420 return false;
4421 } else
4422 return false;
4423}
4424
4426 if (ps52plib)
4427 return ps52plib->GetStateHash();
4428 else
4429 return 0;
4430}
4431
4432void CreateCompatibleS57Object(PI_S57Obj* pObj, S57Obj* cobj,
4433 chart_context* pctx) {
4434 strncpy(cobj->FeatureName, pObj->FeatureName, 8);
4435 cobj->Primitive_type = (GeoPrim_t)pObj->Primitive_type;
4436 cobj->att_array = pObj->att_array;
4437 cobj->attVal = pObj->attVal;
4438 cobj->n_attr = pObj->n_attr;
4439
4440 cobj->x = pObj->x;
4441 cobj->y = pObj->y;
4442 cobj->z = pObj->z;
4443 cobj->npt = pObj->npt;
4444
4445 cobj->iOBJL = pObj->iOBJL;
4446 cobj->Index = pObj->Index;
4447
4448 cobj->geoPt = (pt*)pObj->geoPt;
4449 cobj->geoPtz = pObj->geoPtz;
4450 cobj->geoPtMulti = pObj->geoPtMulti;
4451
4452 cobj->m_lat = pObj->m_lat;
4453 cobj->m_lon = pObj->m_lon;
4454
4455 cobj->m_DisplayCat = (DisCat)pObj->m_DisplayCat;
4456 cobj->x_rate = pObj->x_rate;
4457 cobj->y_rate = pObj->y_rate;
4458 cobj->x_origin = pObj->x_origin;
4459 cobj->y_origin = pObj->y_origin;
4460
4461 cobj->Scamin = pObj->Scamin;
4462 cobj->nRef = pObj->nRef;
4463 cobj->bIsAton = pObj->bIsAton;
4464 cobj->bIsAssociable = pObj->bIsAssociable;
4465
4466 cobj->m_n_lsindex = pObj->m_n_lsindex;
4467 cobj->m_lsindex_array = pObj->m_lsindex_array;
4468 cobj->m_n_edge_max_points = pObj->m_n_edge_max_points;
4469
4470 if (gs_plib_flags & PLIB_CAPS_OBJSEGLIST) {
4471 cobj->m_ls_list_legacy =
4473 pObj->m_ls_list; // note the cast, assumes in-sync layout
4474 } else
4475 cobj->m_ls_list_legacy = 0;
4476 cobj->m_ls_list = 0;
4477
4478 if (gs_plib_flags & PLIB_CAPS_OBJCATMUTATE)
4479 cobj->m_bcategory_mutable = pObj->m_bcategory_mutable;
4480 else
4481 cobj->m_bcategory_mutable = true; // assume all objects are mutable
4482
4483 cobj->m_DPRI = -1; // default is unassigned, fixed at render time
4484 if (gs_plib_flags & PLIB_CAPS_OBJCATMUTATE) {
4485 if (pObj->m_DPRI == -1) {
4486 S52PLIB_Context* pCtx = (S52PLIB_Context*)pObj->S52_Context;
4487 if (pCtx->LUP) cobj->m_DPRI = pCtx->LUP->DPRI - '0';
4488 } else
4489 cobj->m_DPRI = pObj->m_DPRI;
4490 }
4491
4492 cobj->pPolyTessGeo = (PolyTessGeo*)pObj->pPolyTessGeo;
4493 cobj->m_chart_context = (chart_context*)pObj->m_chart_context;
4494
4495 if (pObj->auxParm3 != 1234) {
4496 pObj->auxParm3 = 1234;
4497 pObj->auxParm0 = -99;
4498 }
4499
4500 cobj->auxParm0 = pObj->auxParm0;
4501 cobj->auxParm1 = 0;
4502 cobj->auxParm2 = 0;
4503 cobj->auxParm3 = 0;
4504
4505 S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context;
4506
4507 if (pContext->bBBObj_valid)
4508 // this is ugly because plugins still use BoundingBox
4509 cobj->BBObj.Set(pContext->BBObj.GetMinY(), pContext->BBObj.GetMinX(),
4510 pContext->BBObj.GetMaxY(), pContext->BBObj.GetMaxX());
4511
4512 cobj->CSrules = pContext->CSrules;
4513 cobj->bCS_Added = pContext->bCS_Added;
4514
4515 cobj->FText = pContext->FText;
4516 cobj->bFText_Added = pContext->bFText_Added;
4517 cobj->rText = pContext->rText;
4518
4519 cobj->bIsClone = true; // Protect cloned object pointers in S57Obj dtor
4520
4521 if (pctx) {
4522 cobj->m_chart_context = pctx;
4523 chart_context* ppctx = (chart_context*)pObj->m_chart_context;
4524
4525 if (ppctx) {
4526 cobj->m_chart_context->m_pvc_hash = ppctx->m_pvc_hash;
4527 cobj->m_chart_context->m_pve_hash = ppctx->m_pve_hash;
4528 cobj->m_chart_context->ref_lat = ppctx->ref_lat;
4529 cobj->m_chart_context->ref_lon = ppctx->ref_lon;
4530 cobj->m_chart_context->pFloatingATONArray = ppctx->pFloatingATONArray;
4531 cobj->m_chart_context->pRigidATONArray = ppctx->pRigidATONArray;
4532 cobj->m_chart_context->safety_contour = ppctx->safety_contour;
4533 cobj->m_chart_context->vertex_buffer = ppctx->vertex_buffer;
4534 }
4535 cobj->m_chart_context->chart =
4536 0; // note bene, this is always NULL for a PlugIn chart
4537 cobj->m_chart_context->chart_type = S52_CHART_TYPE_PLUGIN;
4538 }
4539}
4540
4542 S52PLIB_Context* ctx;
4543 if (!pObj->S52_Context) {
4544 ctx = new S52PLIB_Context;
4545 pObj->S52_Context = ctx;
4546 }
4547
4548 ctx = (S52PLIB_Context*)pObj->S52_Context;
4549
4550 S57Obj cobj;
4551 CreateCompatibleS57Object(pObj, &cobj, NULL);
4552
4553 LUPname LUP_Name = PAPER_CHART;
4554
4555 // Force a re-evaluation of CS rules
4556 ctx->CSrules = NULL;
4557 ctx->bCS_Added = false;
4558
4559 // Clear the rendered text cache
4560 if (ctx->bFText_Added) {
4561 ctx->bFText_Added = false;
4562 delete ctx->FText;
4563 ctx->FText = NULL;
4564 }
4565
4566 // Reset object selection box
4567 ctx->bBBObj_valid = true;
4568 ctx->BBObj.SetMin(pObj->lon_min, pObj->lat_min);
4569 ctx->BBObj.SetMax(pObj->lon_max, pObj->lat_max);
4570
4571 // This is where Simplified or Paper-Type point features are selected
4572 switch (cobj.Primitive_type) {
4573 case GEO_POINT:
4574 case GEO_META:
4575 case GEO_PRIM:
4576
4577 if (PAPER_CHART == ps52plib->m_nSymbolStyle)
4578 LUP_Name = PAPER_CHART;
4579 else
4580 LUP_Name = SIMPLIFIED;
4581
4582 break;
4583
4584 case GEO_LINE:
4585 LUP_Name = LINES;
4586 break;
4587
4588 case GEO_AREA:
4589 if (PLAIN_BOUNDARIES == ps52plib->m_nBoundaryStyle)
4590 LUP_Name = PLAIN_BOUNDARIES;
4591 else
4592 LUP_Name = SYMBOLIZED_BOUNDARIES;
4593
4594 break;
4595 }
4596
4597 LUPrec* lup = ps52plib->S52_LUPLookup(LUP_Name, cobj.FeatureName, &cobj);
4598 ctx->LUP = lup;
4599
4600 // Convert LUP to rules set
4601 ps52plib->_LUP2rules(lup, &cobj);
4602
4603 ctx->MPSRulesList = NULL;
4604
4605 return true;
4606}
4607
4609 S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context;
4610 if (pContext) {
4611 pContext->bBBObj_valid = true;
4612 pContext->BBObj.SetMin(pObj->lon_min, pObj->lat_min);
4613 pContext->BBObj.SetMax(pObj->lon_max, pObj->lat_max);
4614 }
4615}
4616
4617void UpdatePIObjectPlibContext(PI_S57Obj* pObj, S57Obj* cobj,
4618 ObjRazRules* rzRules) {
4619 // Update the PLIB context after the render operation
4620 S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context;
4621
4622 pContext->CSrules = cobj->CSrules;
4623 pContext->bCS_Added = cobj->bCS_Added;
4624
4625 pContext->FText = cobj->FText;
4626 pContext->bFText_Added = cobj->bFText_Added;
4627 pContext->rText = cobj->rText;
4628
4629 if (cobj->BBObj.GetValid()) {
4630 // ugly as plugins still use BoundingBox
4631 pContext->BBObj =
4632 BoundingBox(cobj->BBObj.GetMinLon(), cobj->BBObj.GetMinLat(),
4633 cobj->BBObj.GetMaxLon(), cobj->BBObj.GetMaxLat());
4634 pContext->bBBObj_valid = true;
4635 }
4636
4637 // Render operation may have promoted the object's display category
4638 // (e.g.WRECKS)
4639 pObj->m_DisplayCat = (PI_DisCat)cobj->m_DisplayCat;
4640
4641 if (gs_plib_flags & PLIB_CAPS_OBJCATMUTATE) pObj->m_DPRI = cobj->m_DPRI;
4642
4643 pContext->ChildRazRules = rzRules->child;
4644 pContext->MPSRulesList = rzRules->mps;
4645
4646 pObj->auxParm0 = cobj->auxParm0;
4647}
4648
4649bool PI_GetObjectRenderBox(PI_S57Obj* pObj, double* lat_min, double* lat_max,
4650 double* lon_min, double* lon_max) {
4651 S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context;
4652 if (pContext) {
4653 if (lat_min) *lat_min = pContext->BBObj.GetMinY();
4654 if (lat_max) *lat_max = pContext->BBObj.GetMaxY();
4655 if (lon_min) *lon_min = pContext->BBObj.GetMinX();
4656 if (lon_max) *lon_max = pContext->BBObj.GetMaxX();
4657 return pContext->bBBObj_valid;
4658 } else
4659 return false;
4660}
4661
4663 S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context;
4664 if (pContext) {
4665 LUPrec* lup = pContext->LUP;
4666 if (lup) return (PI_LUPname)(lup->TNAM);
4667 }
4668 return (PI_LUPname)(-1);
4669}
4670
4672 S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context;
4673 if (pContext) {
4674 LUPrec* lup = pContext->LUP;
4675 if (lup) return (PI_DisPrio)(lup->DPRI);
4676 }
4677
4678 return (PI_DisPrio)(-1);
4679}
4680
4682 S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context;
4683 if (pContext) {
4684 LUPrec* lup = pContext->LUP;
4685 if (lup) return (PI_DisCat)(lup->DISC);
4686 }
4687 return (PI_DisCat)(-1);
4688}
4690 return S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR);
4691}
4692
4694 // Create and populate a compatible s57 Object
4695 S57Obj cobj;
4696 chart_context ctx;
4697 CreateCompatibleS57Object(pObj, &cobj, &ctx);
4698
4699 S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context;
4700
4701 // Create and populate a minimally compatible object container
4702 ObjRazRules rzRules;
4703 rzRules.obj = &cobj;
4704 rzRules.LUP = pContext->LUP;
4705 rzRules.sm_transform_parms = 0;
4706 rzRules.child = NULL;
4707 rzRules.next = NULL;
4708 rzRules.mps = pContext->MPSRulesList;
4709
4710 if (pContext->LUP) {
4711 ps52plib->SetLineFeaturePriority(&rzRules, prio);
4712
4713 // Update the PLIB context after the render operation
4714 UpdatePIObjectPlibContext(pObj, &cobj, &rzRules);
4715 }
4716}
4717
4719 if (ps52plib) {
4720 ps52plib->PrepareForRender();
4721 ps52plib->ClearTextList();
4722
4723 if (gs_plib_flags & PLIB_CAPS_LINE_BUFFER)
4724 ps52plib->EnableGLLS(true); // Newer PlugIns can use GLLS
4725 else
4726 ps52plib->EnableGLLS(false); // Older cannot
4727 }
4728}
4729
4730void PI_PLIBSetRenderCaps(unsigned int flags) { gs_plib_flags = flags; }
4731
4732void PI_PLIBFreeContext(void* pContext) {
4733 S52PLIB_Context* pctx = (S52PLIB_Context*)pContext;
4734
4735 if (pctx->ChildRazRules) {
4736 ObjRazRules* ctop = pctx->ChildRazRules;
4737 while (ctop) {
4738 delete ctop->obj;
4739
4740 if (ps52plib) ps52plib->DestroyLUP(ctop->LUP);
4741
4742 ObjRazRules* cnxx = ctop->next;
4743 delete ctop;
4744 ctop = cnxx;
4745 }
4746 }
4747
4748 if (pctx->MPSRulesList) {
4749 if (ps52plib && pctx->MPSRulesList->cs_rules) {
4750 for (unsigned int i = 0; i < pctx->MPSRulesList->cs_rules->GetCount();
4751 i++) {
4752 Rules* top = pctx->MPSRulesList->cs_rules->Item(i);
4753 ps52plib->DestroyRulesChain(top);
4754 }
4755 delete pctx->MPSRulesList->cs_rules;
4756 }
4757 free(pctx->MPSRulesList);
4758 }
4759
4760 delete pctx->FText;
4761
4762 delete pctx;
4763}
4764
4766 // Create and populate a compatible s57 Object
4767 S57Obj cobj;
4768 chart_context ctx;
4769 CreateCompatibleS57Object(pObj, &cobj, &ctx);
4770
4771 S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context;
4772
4773 // Set up object SM rendering constants
4774 sm_parms transform;
4775 toSM(vp->clat, vp->clon, pObj->chart_ref_lat, pObj->chart_ref_lon,
4776 &transform.easting_vp_center, &transform.northing_vp_center);
4777
4778 // Create and populate a minimally compatible object container
4779 ObjRazRules rzRules;
4780 rzRules.obj = &cobj;
4781 rzRules.LUP = pContext->LUP;
4782 rzRules.sm_transform_parms = &transform;
4783 rzRules.child = pContext->ChildRazRules;
4784 rzRules.next = NULL;
4785 rzRules.mps = pContext->MPSRulesList;
4786
4787 if (pContext->LUP) {
4788 ViewPort cvp = CreateCompatibleViewport(*vp);
4789
4790 // Do the render
4791 // FIXME (plib)
4792 ps52plib->SetVPointCompat(cvp.pix_width, cvp.pix_height, cvp.view_scale_ppm,
4793 cvp.rotation, cvp.clat, cvp.clon, cvp.chart_scale,
4794 cvp.rv_rect, cvp.GetBBox(), cvp.ref_scale,
4795 GetOCPNCanvasWindow()->GetContentScaleFactor());
4796 ps52plib->PrepareForRender();
4797
4798 ps52plib->RenderObjectToDC(pdc, &rzRules);
4799
4800 // Update the PLIB context after the render operation
4801 UpdatePIObjectPlibContext(pObj, &cobj, &rzRules);
4802 }
4803
4804 return 1;
4805}
4806
4808 wxRect rect, unsigned char* pixbuf) {
4809 // Create a compatible render canvas
4810 render_canvas_parms pb_spec;
4811
4812 pb_spec.depth = BPP;
4813 pb_spec.pb_pitch = ((rect.width * pb_spec.depth / 8));
4814 pb_spec.lclip = rect.x;
4815 pb_spec.rclip = rect.x + rect.width - 1;
4816 pb_spec.pix_buff = pixbuf; // the passed buffer
4817 pb_spec.width = rect.width;
4818 pb_spec.height = rect.height;
4819 pb_spec.x = rect.x;
4820 pb_spec.y = rect.y;
4821#ifdef ocpnUSE_ocpnBitmap
4822 pb_spec.b_revrgb = true;
4823#else
4824 pb_spec.b_revrgb = false;
4825#endif
4826
4827 pb_spec.b_revrgb = false;
4828
4829 // Create and populate a compatible s57 Object
4830 S57Obj cobj;
4831 chart_context ctx;
4832 CreateCompatibleS57Object(pObj, &cobj, &ctx);
4833
4834 S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context;
4835
4836 // Set up object SM rendering constants
4837 sm_parms transform;
4838 toSM(vp->clat, vp->clon, pObj->chart_ref_lat, pObj->chart_ref_lon,
4839 &transform.easting_vp_center, &transform.northing_vp_center);
4840
4841 // Create and populate a minimally compatible object container
4842 ObjRazRules rzRules;
4843 rzRules.obj = &cobj;
4844 rzRules.LUP = pContext->LUP;
4845 rzRules.sm_transform_parms = &transform;
4846 rzRules.child = pContext->ChildRazRules;
4847 rzRules.next = NULL;
4848 rzRules.mps = pContext->MPSRulesList;
4849
4850 ViewPort cvp = CreateCompatibleViewport(*vp);
4851
4852 // If the PlugIn does not support it nativiely, build a fully described
4853 // Geomoetry
4854 if (!(gs_plib_flags & PLIB_CAPS_SINGLEGEO_BUFFER)) {
4855 if (!pObj->geoPtMulti) { // do this only once
4856 PolyTessGeo* tess = (PolyTessGeo*)pObj->pPolyTessGeo;
4857
4858 if (!tess) return 1; // bail on empty data
4859
4860 PolyTriGroup* ptg = new PolyTriGroup;
4861 ptg->tri_prim_head =
4862 tess->Get_PolyTriGroup_head()->tri_prim_head; // tph;
4863 ptg->bsingle_alloc = false;
4864 ptg->data_type = DATA_TYPE_DOUBLE;
4865 tess->Set_PolyTriGroup_head(ptg);
4866
4867 double* pd = (double*)malloc(sizeof(double));
4868 pObj->geoPtMulti = pd; // Hack hack
4869 }
4870 }
4871
4872 if (pContext->LUP) {
4873 // Do the render
4874 // FIXME (plib)
4875 ps52plib->SetVPointCompat(cvp.pix_width, cvp.pix_height, cvp.view_scale_ppm,
4876 cvp.rotation, cvp.clat, cvp.clon, cvp.chart_scale,
4877 cvp.rv_rect, cvp.GetBBox(), cvp.ref_scale,
4878 GetOCPNCanvasWindow()->GetContentScaleFactor());
4879 ps52plib->PrepareForRender();
4880
4881 ps52plib->RenderAreaToDC(pdc, &rzRules, &pb_spec);
4882
4883 // Update the PLIB context after the render operation
4884 UpdatePIObjectPlibContext(pObj, &cobj, &rzRules);
4885 }
4886
4887 return 1;
4888}
4889
4890int PI_PLIBRenderAreaToGL(const wxGLContext& glcc, PI_S57Obj* pObj,
4891 PlugIn_ViewPort* vp, wxRect& render_rect) {
4892#ifdef ocpnUSE_GL
4893 // Create and populate a compatible s57 Object
4894 S57Obj cobj;
4895 chart_context ctx;
4896 CreateCompatibleS57Object(pObj, &cobj, &ctx);
4897
4898 // chart_context *pct = (chart_context *)pObj->m_chart_context;
4899
4900 // If the PlugIn does not support it nativiely, build a fully described
4901 // Geomoetry
4902
4903 if (!(gs_plib_flags & PLIB_CAPS_SINGLEGEO_BUFFER)) {
4904 if (!pObj->geoPtMulti) { // only do this once
4905 PolyTessGeo* tess = (PolyTessGeo*)pObj->pPolyTessGeo;
4906
4907 if (!tess) return 1; // bail on empty data
4908
4909 PolyTriGroup* ptg =
4910 new PolyTriGroup; // this will leak a little, but is POD
4911 ptg->tri_prim_head = tess->Get_PolyTriGroup_head()->tri_prim_head;
4912 ptg->bsingle_alloc = false;
4913 ptg->data_type = DATA_TYPE_DOUBLE;
4914 tess->Set_PolyTriGroup_head(ptg);
4915
4916 // Mark this object using geoPtMulti
4917 // The malloc will get free'ed when the object is deleted.
4918 double* pd = (double*)malloc(sizeof(double));
4919 pObj->geoPtMulti = pd; // Hack hack
4920 }
4921 cobj.auxParm0 = -6; // signal that this object render cannot use VBO
4922 cobj.auxParm1 = -1; // signal that this object render cannot have single
4923 // buffer conversion done
4924 } else { // it is a newer PLugIn, so can do single buffer conversion and VBOs
4925 if (pObj->auxParm0 < 1)
4926 cobj.auxParm0 = -7; // signal that this object render can use a
4927 // persistent VBO for area triangle vertices
4928 }
4929
4930 S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context;
4931
4932 // Set up object SM rendering constants
4933 sm_parms transform;
4934 toSM(vp->clat, vp->clon, pObj->chart_ref_lat, pObj->chart_ref_lon,
4935 &transform.easting_vp_center, &transform.northing_vp_center);
4936
4937 // Create and populate a minimally compatible object container
4938 ObjRazRules rzRules;
4939 rzRules.obj = &cobj;
4940 rzRules.LUP = pContext->LUP;
4941 rzRules.sm_transform_parms = &transform;
4942 rzRules.child = pContext->ChildRazRules;
4943 rzRules.next = NULL;
4944 rzRules.mps = pContext->MPSRulesList;
4945
4946 if (pContext->LUP) {
4947 ViewPort cvp = CreateCompatibleViewport(*vp);
4948
4949 // Do the render
4950 // FIXME (plib)
4951 ps52plib->SetVPointCompat(cvp.pix_width, cvp.pix_height, cvp.view_scale_ppm,
4952 cvp.rotation, cvp.clat, cvp.clon, cvp.chart_scale,
4953 cvp.rv_rect, cvp.GetBBox(), cvp.ref_scale,
4954 GetOCPNCanvasWindow()->GetContentScaleFactor());
4955 ps52plib->PrepareForRender();
4956
4957 ps52plib->RenderAreaToGL(glcc, &rzRules);
4958
4959 // Update the PLIB context after the render operation
4960 UpdatePIObjectPlibContext(pObj, &cobj, &rzRules);
4961 }
4962
4963#endif
4964 return 1;
4965}
4966
4967int PI_PLIBRenderObjectToGL(const wxGLContext& glcc, PI_S57Obj* pObj,
4968 PlugIn_ViewPort* vp, wxRect& render_rect) {
4969 // Create and populate a compatible s57 Object
4970 S57Obj cobj;
4971 chart_context ctx;
4972 CreateCompatibleS57Object(pObj, &cobj, &ctx);
4973
4974 S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context;
4975
4976 // Set up object SM rendering constants
4977 sm_parms transform;
4978 toSM(vp->clat, vp->clon, pObj->chart_ref_lat, pObj->chart_ref_lon,
4979 &transform.easting_vp_center, &transform.northing_vp_center);
4980
4981 // Create and populate a minimally compatible object container
4982 ObjRazRules rzRules;
4983 rzRules.obj = &cobj;
4984 rzRules.LUP = pContext->LUP;
4985 rzRules.sm_transform_parms = &transform;
4986 rzRules.child = pContext->ChildRazRules;
4987 rzRules.next = NULL;
4988 rzRules.mps = pContext->MPSRulesList;
4989
4990 if (pContext->LUP) {
4991 ViewPort cvp = CreateCompatibleViewport(*vp);
4992
4993 // Do the render
4994 // FIXME (plib)
4995 ps52plib->SetVPointCompat(cvp.pix_width, cvp.pix_height, cvp.view_scale_ppm,
4996 cvp.rotation, cvp.clat, cvp.clon, cvp.chart_scale,
4997 cvp.rv_rect, cvp.GetBBox(), cvp.ref_scale,
4998 GetOCPNCanvasWindow()->GetContentScaleFactor());
4999 ps52plib->PrepareForRender();
5000
5001 ps52plib->RenderObjectToGL(glcc, &rzRules);
5002
5003 // Update the PLIB context after the render operation
5004 UpdatePIObjectPlibContext(pObj, &cobj, &rzRules);
5005 }
5006
5007 return 1;
5008}
5009
5010// http File Download Support
5011
5012// OCPN_downloadEvent Implementation
5013
5014OCPN_downloadEvent::OCPN_downloadEvent(wxEventType commandType, int id)
5015 : wxEvent(id, commandType) {
5016 m_stat = OCPN_DL_UNKNOWN;
5017 m_condition = OCPN_DL_EVENT_TYPE_UNKNOWN;
5018 m_b_complete = false;
5019 m_sofarBytes = 0;
5020}
5021
5022OCPN_downloadEvent::~OCPN_downloadEvent() {}
5023
5024wxEvent* OCPN_downloadEvent::Clone() const {
5025 OCPN_downloadEvent* newevent = new OCPN_downloadEvent(*this);
5026 newevent->m_stat = this->m_stat;
5027 newevent->m_condition = this->m_condition;
5028
5029 newevent->m_totalBytes = this->m_totalBytes;
5030 newevent->m_sofarBytes = this->m_sofarBytes;
5031 newevent->m_b_complete = this->m_b_complete;
5032
5033 return newevent;
5034}
5035
5036// const wxEventType wxEVT_DOWNLOAD_EVENT = wxNewEventType();
5037DECL_EXP wxEventType wxEVT_DOWNLOAD_EVENT = wxNewEventType();
5038
5039_OCPN_DLStatus g_download_status;
5040_OCPN_DLCondition g_download_condition;
5041
5042#define DL_EVENT_TIMER 4388
5043
5044class PI_DLEvtHandler : public wxEvtHandler {
5045public:
5048
5049 void onDLEvent(OCPN_downloadEvent& event);
5050 void setBackgroundMode(long ID, wxEvtHandler* handler);
5051 void clearBackgroundMode();
5052 void onTimerEvent(wxTimerEvent& event);
5053
5054 long m_id;
5055 wxTimer m_eventTimer;
5056 wxEvtHandler* m_download_evHandler;
5057
5058 long m_sofarBytes;
5059 long m_totalBytes;
5060};
5061
5062PI_DLEvtHandler::PI_DLEvtHandler() {
5063 g_download_status = OCPN_DL_UNKNOWN;
5064 g_download_condition = OCPN_DL_EVENT_TYPE_UNKNOWN;
5065
5066 m_download_evHandler = NULL;
5067 m_id = -1;
5068 m_sofarBytes = 0;
5069 m_totalBytes = 0;
5070}
5071
5072PI_DLEvtHandler::~PI_DLEvtHandler() {
5073 m_eventTimer.Stop();
5074 Disconnect(
5075 wxEVT_TIMER,
5076 (wxObjectEventFunction)(wxEventFunction)&PI_DLEvtHandler::onTimerEvent);
5077}
5078
5079void PI_DLEvtHandler::onDLEvent(OCPN_downloadEvent& event) {
5080 // qDebug() << "Got Event " << (int)event.getDLEventStatus() <<
5081 // (int)event.getDLEventCondition();
5082
5083 g_download_status = event.getDLEventStatus();
5084 g_download_condition = event.getDLEventCondition();
5085
5086 // This is an END event, happening at the end of BACKGROUND file download
5087 if (m_download_evHandler &&
5088 (OCPN_DL_EVENT_TYPE_END == event.getDLEventCondition())) {
5089 OCPN_downloadEvent ev(wxEVT_DOWNLOAD_EVENT, 0);
5090 ev.setComplete(true);
5091 ev.setTransferred(m_sofarBytes);
5092 ev.setTotal(m_totalBytes);
5093
5094 ev.setDLEventStatus(event.getDLEventStatus());
5095 ev.setDLEventCondition(OCPN_DL_EVENT_TYPE_END);
5096
5097 m_download_evHandler->AddPendingEvent(ev);
5098 m_eventTimer.Stop();
5099#ifdef __ANDROID__
5100 finishAndroidFileDownload();
5101#endif
5102 }
5103
5104 event.Skip();
5105}
5106
5107void PI_DLEvtHandler::setBackgroundMode(long ID, wxEvtHandler* handler) {
5108 m_id = ID;
5109 m_download_evHandler = handler;
5110
5111 m_eventTimer.SetOwner(this, DL_EVENT_TIMER);
5112
5113 Connect(
5114 wxEVT_TIMER,
5115 (wxObjectEventFunction)(wxEventFunction)&PI_DLEvtHandler::onTimerEvent);
5116 m_eventTimer.Start(1000, wxTIMER_CONTINUOUS);
5117}
5118
5119void PI_DLEvtHandler::clearBackgroundMode() {
5120 m_download_evHandler = NULL;
5121 m_eventTimer.Stop();
5122}
5123
5124void PI_DLEvtHandler::onTimerEvent(wxTimerEvent& event) {
5125#ifdef __ANDROID__
5126 // Query the download status, and post to the original requestor
5127 // This method only happens on Background file downloads
5128
5129 wxString sstat;
5130 int stat = queryAndroidFileDownload(m_id, &sstat);
5131
5132 OCPN_downloadEvent ev(wxEVT_DOWNLOAD_EVENT, 0);
5133 long sofarBytes = 0;
5134 long totalBytes = -1;
5135 long state = -1;
5136
5137 if (stat) { // some error
5138 qDebug() << "Error on queryAndroidFileDownload, ending download";
5139 ev.setComplete(true);
5140 ev.setTransferred(sofarBytes);
5141 ev.setTotal(totalBytes);
5142
5143 ev.setDLEventStatus(OCPN_DL_FAILED);
5144 ev.setDLEventCondition(OCPN_DL_EVENT_TYPE_END);
5145 } else {
5146 wxStringTokenizer tk(sstat, ";");
5147 if (tk.HasMoreTokens()) {
5148 wxString token = tk.GetNextToken();
5149 token.ToLong(&state);
5150 token = tk.GetNextToken();
5151 token.ToLong(&sofarBytes);
5152 token = tk.GetNextToken();
5153 token.ToLong(&totalBytes);
5154 }
5155
5156 qDebug() << state << sofarBytes << totalBytes;
5157
5158 m_sofarBytes = sofarBytes;
5159 m_totalBytes = totalBytes;
5160
5161 ev.setTransferred(sofarBytes);
5162 ev.setTotal(totalBytes);
5163
5164 if (state == 16) { // error
5165 qDebug() << "Event OCPN_DL_FAILED/OCPN_DL_EVENT_TYPE_END";
5166 ev.setComplete(true);
5167 ev.setDLEventStatus(OCPN_DL_FAILED);
5168 ev.setDLEventCondition(OCPN_DL_EVENT_TYPE_END);
5169 } else if (state == 8) { // Completed OK
5170 qDebug() << "Event OCPN_DL_NO_ERROR/OCPN_DL_EVENT_TYPE_END";
5171 ev.setComplete(true);
5172 ev.setDLEventStatus(OCPN_DL_NO_ERROR);
5173 ev.setDLEventCondition(OCPN_DL_EVENT_TYPE_END);
5174 } else {
5175 ev.setComplete(false);
5176 ev.setDLEventStatus(OCPN_DL_UNKNOWN);
5177 ev.setDLEventCondition(OCPN_DL_EVENT_TYPE_PROGRESS);
5178 }
5179
5180 // 2;0;148686
5181 }
5182
5183 if (m_download_evHandler) {
5184 // qDebug() << "Sending event on timer...";
5185 m_download_evHandler->AddPendingEvent(ev);
5186 }
5187
5188 // Background download is all done.
5189 if (OCPN_DL_EVENT_TYPE_END == ev.getDLEventCondition()) {
5190 m_eventTimer.Stop();
5191 finishAndroidFileDownload();
5192 }
5193
5194#endif
5195}
5196
5197PI_DLEvtHandler* g_piEventHandler;
5198
5199// Blocking download of single file
5201 const wxString& outputFile,
5202 const wxString& title, const wxString& message,
5203 const wxBitmap& bitmap, wxWindow* parent,
5204 long style, int timeout_secs) {
5205#ifdef __ANDROID__
5206
5207 wxString msg = "Downloading file synchronously: ";
5208 msg += url;
5209 msg += " to: ";
5210 msg += outputFile;
5211 wxLogMessage(msg);
5212
5213 // Validate the write location
5214 int vres = validateAndroidWriteLocation(outputFile);
5215 if (vres == 0) // Pending permission dialog
5216 return OCPN_DL_ABORTED;
5217
5218 // Create a single event handler to receive status events
5219 if (!g_piEventHandler) g_piEventHandler = new PI_DLEvtHandler;
5220
5221 // Reset global status indicators
5222 g_download_status = OCPN_DL_UNKNOWN;
5223 g_download_condition = OCPN_DL_EVENT_TYPE_UNKNOWN;
5224
5225 // Create a connection for the expected events from Android Activity
5226 g_piEventHandler->Connect(
5227 wxEVT_DOWNLOAD_EVENT,
5228 (wxObjectEventFunction)(wxEventFunction)&PI_DLEvtHandler::onDLEvent);
5229
5230 long dl_ID = -1;
5231
5232 // Make sure the outputfile is a file URI
5233 wxString fURI = outputFile;
5234 if (!fURI.StartsWith("file://")) {
5235 fURI.Prepend("file://");
5236 }
5237
5238 int res = startAndroidFileDownload(url, fURI, g_piEventHandler, &dl_ID);
5239 // Started OK?
5240 if (res) {
5241 finishAndroidFileDownload();
5242 g_piEventHandler->Disconnect(
5243 wxEVT_DOWNLOAD_EVENT,
5244 (wxObjectEventFunction)(wxEventFunction)&PI_DLEvtHandler::onDLEvent);
5245 // delete g_piEventHandler;
5246 return OCPN_DL_FAILED;
5247 }
5248
5249 wxDateTime dl_start_time = wxDateTime::Now();
5250
5251 // Spin, waiting for timeout or event from downstream, and checking status
5252 while (1) {
5253 wxTimeSpan dt = wxDateTime::Now() - dl_start_time;
5254 qDebug() << "Spin.." << dt.GetSeconds().GetLo();
5255
5256 if (dt.GetSeconds() > timeout_secs) {
5257 qDebug() << "USER_TIMOUT";
5258 finishAndroidFileDownload();
5259 g_piEventHandler->Disconnect(
5260 wxEVT_DOWNLOAD_EVENT,
5261 (wxObjectEventFunction)(wxEventFunction)&PI_DLEvtHandler::onDLEvent);
5262 // delete g_piEventHandler;
5263 return (OCPN_DL_USER_TIMEOUT);
5264 }
5265
5266 if (g_download_condition != OCPN_DL_EVENT_TYPE_UNKNOWN) {
5267 if (OCPN_DL_EVENT_TYPE_END == g_download_condition) {
5268 _OCPN_DLStatus ss = g_download_status;
5269 finishAndroidFileDownload();
5270 g_piEventHandler->Disconnect(
5271 wxEVT_DOWNLOAD_EVENT,
5272 (wxObjectEventFunction)(wxEventFunction)&PI_DLEvtHandler::
5273 onDLEvent);
5274 // delete g_piEventHandler;
5275 qDebug() << "RETURN DL_END" << (int)ss;
5276 return ss; // The actual return code
5277 }
5278 }
5279
5280 wxString sstat;
5281 int stat = queryAndroidFileDownload(dl_ID, &sstat);
5282 if (stat) { // some error
5283 qDebug() << "Error on queryAndroidFileDownload";
5284 finishAndroidFileDownload();
5285 g_piEventHandler->Disconnect(
5286 wxEVT_DOWNLOAD_EVENT,
5287 (wxObjectEventFunction)(wxEventFunction)&PI_DLEvtHandler::onDLEvent);
5288 // delete g_piEventHandler;
5289
5290 return OCPN_DL_FAILED; // so abort
5291 }
5292
5293 wxSleep(1);
5294 wxSafeYield();
5295 }
5296
5297#elif defined(OCPN_USE_CURL)
5298 wxFileName tfn = wxFileName::CreateTempFileName(outputFile);
5299 wxFileOutputStream output(tfn.GetFullPath());
5300
5301 wxCurlDownloadDialog ddlg(url, &output, title, message, bitmap, parent,
5302 style);
5303 wxCurlDialogReturnFlag ret = ddlg.RunModal();
5304 output.Close();
5305
5307
5308 switch (ret) {
5309 case wxCDRF_SUCCESS: {
5310 if (wxCopyFile(tfn.GetFullPath(), outputFile))
5311 result = OCPN_DL_NO_ERROR;
5312 else
5313 result = OCPN_DL_FAILED;
5314 break;
5315 }
5316 case wxCDRF_FAILED: {
5317 result = OCPN_DL_FAILED;
5318 break;
5319 }
5320 case wxCDRF_USER_ABORTED: {
5321 result = OCPN_DL_ABORTED;
5322 break;
5323 }
5324 default:
5325 wxASSERT(false); // This should never happen because we handle all
5326 // possible cases of ret
5327 }
5328 if (wxFileExists(tfn.GetFullPath())) wxRemoveFile(tfn.GetFullPath());
5329 return result;
5330
5331#else
5332 return OCPN_DL_FAILED;
5333#endif
5334}
5335
5336wxString toUsrDateTimeFormat_Plugin(const wxDateTime date_time,
5338 return ocpn::toUsrDateTimeFormat(date_time, options);
5339}
5340
5341// Non-Blocking download of single file
5343 const wxString& outputFile,
5344 wxEvtHandler* handler,
5345 long* handle) {
5346#ifdef __ANDROID__
5347 wxString msg = "Downloading file asynchronously: ";
5348 msg += url;
5349 msg += " to: ";
5350 msg += outputFile;
5351 wxLogMessage(msg);
5352
5353 // Create a single event handler to receive status events
5354
5355 if (!g_piEventHandler) g_piEventHandler = new PI_DLEvtHandler;
5356
5357 long dl_ID = -1;
5358
5359 int res = startAndroidFileDownload(url, outputFile, NULL /*g_piEventHandler*/,
5360 &dl_ID);
5361 // Started OK?
5362 if (res) {
5363 finishAndroidFileDownload();
5364 return OCPN_DL_FAILED;
5365 }
5366
5367 // configure the local event handler for background transfer
5368 g_piEventHandler->setBackgroundMode(dl_ID, handler);
5369
5370 if (handle) *handle = dl_ID;
5371
5372 return OCPN_DL_STARTED;
5373
5374#elif defined(OCPN_USE_CURL)
5375 if (g_pi_manager->m_pCurlThread) // We allow just one download at a time. Do
5376 // we want more? Or at least return some
5377 // other status in this case?
5378 return OCPN_DL_FAILED;
5379 g_pi_manager->m_pCurlThread =
5380 new wxCurlDownloadThread(g_pi_manager, CurlThreadId);
5381 bool http = (url.StartsWith(wxS("http:")) || url.StartsWith(wxS("https:")));
5382 bool keep = false;
5383 if (http && g_pi_manager->m_pCurl &&
5384 dynamic_cast<wxCurlHTTP*>(g_pi_manager->m_pCurl.get())) {
5385 keep = true;
5386 }
5387 if (!keep) {
5388 g_pi_manager->m_pCurl = 0;
5389 }
5390
5391 bool failed = false;
5392 if (!g_pi_manager->HandleCurlThreadError(
5393 g_pi_manager->m_pCurlThread->SetURL(url, g_pi_manager->m_pCurl),
5394 g_pi_manager->m_pCurlThread, url))
5395 failed = true;
5396 if (!failed) {
5397 g_pi_manager->m_pCurl = g_pi_manager->m_pCurlThread->GetCurlSharedPtr();
5398 if (!g_pi_manager->HandleCurlThreadError(
5399 g_pi_manager->m_pCurlThread->SetOutputStream(
5400 new wxFileOutputStream(outputFile)),
5401 g_pi_manager->m_pCurlThread))
5402 failed = true;
5403 }
5404 if (!failed) {
5405 g_pi_manager->m_download_evHandler = handler;
5406 g_pi_manager->m_downloadHandle = handle;
5407
5408 wxCurlThreadError err = g_pi_manager->m_pCurlThread->Download();
5409 if (err != wxCTE_NO_ERROR) {
5410 g_pi_manager->HandleCurlThreadError(
5411 err, g_pi_manager->m_pCurlThread); // shows a message to the user
5412 g_pi_manager->m_pCurlThread->Abort();
5413 failed = true;
5414 }
5415 }
5416
5417 if (!failed) return OCPN_DL_STARTED;
5418
5419 if (g_pi_manager->m_pCurlThread) {
5420 if (g_pi_manager->m_pCurlThread->IsAlive())
5421 g_pi_manager->m_pCurlThread->Abort();
5422 if (g_pi_manager->m_pCurlThread->GetOutputStream())
5423 delete (g_pi_manager->m_pCurlThread->GetOutputStream());
5424 wxDELETE(g_pi_manager->m_pCurlThread);
5425 g_pi_manager->m_download_evHandler = NULL;
5426 g_pi_manager->m_downloadHandle = NULL;
5427 return OCPN_DL_STARTED;
5428 }
5429 g_pi_manager->m_pCurl = 0;
5430 return OCPN_DL_FAILED;
5431
5432#else
5433 return OCPN_DL_FAILED;
5434#endif
5435}
5436
5438#ifdef OCPN_USE_CURL
5439
5440#ifdef __ANDROID__
5441 cancelAndroidFileDownload(handle);
5442 finishAndroidFileDownload();
5443 if (g_piEventHandler) g_piEventHandler->clearBackgroundMode();
5444#else
5445 if (g_pi_manager->m_pCurlThread) {
5446 g_pi_manager->m_pCurlThread->Abort();
5447 delete (g_pi_manager->m_pCurlThread->GetOutputStream());
5448 wxDELETE(g_pi_manager->m_pCurlThread);
5449 g_pi_manager->m_download_evHandler = NULL;
5450 g_pi_manager->m_downloadHandle = NULL;
5451 }
5452 g_pi_manager->m_pCurl = 0;
5453#endif
5454#endif
5455}
5456
5458 const wxString& parameters, wxString& result,
5459 int timeout_secs) {
5460#ifdef __ANDROID__
5461 wxString lparms = parameters;
5462 wxString postResult = doAndroidPOST(url, lparms, timeout_secs * 1000);
5463 if (postResult.IsSameAs("NOK")) return OCPN_DL_FAILED;
5464
5465 result = postResult;
5466 return OCPN_DL_NO_ERROR;
5467
5468#elif defined(OCPN_USE_CURL)
5469 wxCurlHTTP post;
5470 post.SetOpt(CURLOPT_TIMEOUT, timeout_secs);
5471 size_t res = post.Post(parameters.ToAscii(), parameters.Len(), url);
5472
5473 if (res) {
5474 result = wxString(post.GetResponseBody().c_str(), wxConvUTF8);
5475 return OCPN_DL_NO_ERROR;
5476 } else
5477 result = wxEmptyString;
5478
5479 return OCPN_DL_FAILED;
5480
5481#else
5482 return OCPN_DL_FAILED;
5483#endif
5484}
5485
5487#ifdef __ANDROID__
5488 return androidCheckOnline();
5489#endif
5490
5491#if !defined(__ANDROID__) && defined(OCPN_USE_CURL)
5492 if (wxDateTime::GetTimeNow() >
5493 g_pi_manager->m_last_online_chk + ONLINE_CHECK_RETRY) {
5494 wxCurlHTTP get;
5495 get.Head("http://yahoo.com/");
5496 g_pi_manager->m_last_online = get.GetResponseCode() > 0;
5497
5498 g_pi_manager->m_last_online_chk = wxDateTime::GetTimeNow();
5499 }
5500 return g_pi_manager->m_last_online;
5501#else
5502 return false;
5503#endif
5504}
5505
5506#if !defined(__ANDROID__) && defined(OCPN_USE_CURL)
5507void PlugInManager::OnEndPerformCurlDownload(wxCurlEndPerformEvent& ev) {
5508 OCPN_downloadEvent event(wxEVT_DOWNLOAD_EVENT, 0);
5509 if (ev.IsSuccessful()) {
5510 event.setDLEventStatus(OCPN_DL_NO_ERROR);
5511 } else {
5512 g_pi_manager->m_pCurl = 0;
5513 event.setDLEventStatus(OCPN_DL_FAILED);
5514 }
5515 event.setDLEventCondition(OCPN_DL_EVENT_TYPE_END);
5516 event.setComplete(true);
5517
5518 if (m_download_evHandler) {
5519 m_download_evHandler->AddPendingEvent(event);
5520 m_download_evHandler = NULL;
5521 m_downloadHandle = NULL;
5522 }
5523
5524 if (m_pCurlThread) {
5525 m_pCurlThread->Wait();
5526 if (!m_pCurlThread->IsAborting()) {
5527 delete (m_pCurlThread->GetOutputStream());
5528 wxDELETE(m_pCurlThread);
5529 }
5530 }
5531}
5532
5533void PlugInManager::OnCurlDownload(wxCurlDownloadEvent& ev) {
5534 OCPN_downloadEvent event(wxEVT_DOWNLOAD_EVENT, 0);
5535 event.setDLEventStatus(OCPN_DL_UNKNOWN);
5536 event.setDLEventCondition(OCPN_DL_EVENT_TYPE_PROGRESS);
5537 event.setTotal(ev.GetTotalBytes());
5538 event.setTransferred(ev.GetDownloadedBytes());
5539 event.setComplete(false);
5540
5541 if (m_download_evHandler) {
5542 m_download_evHandler->AddPendingEvent(event);
5543 }
5544}
5545
5546bool PlugInManager::HandleCurlThreadError(wxCurlThreadError err,
5547 wxCurlBaseThread* p,
5548 const wxString& url) {
5549 switch (err) {
5550 case wxCTE_NO_ERROR:
5551 return true; // ignore this
5552
5553 case wxCTE_NO_RESOURCE:
5554 wxLogError(
5555 wxS("Insufficient resources for correct execution of the program."));
5556 break;
5557
5558 case wxCTE_ALREADY_RUNNING:
5559 wxFAIL; // should never happen!
5560 break;
5561
5562 case wxCTE_INVALID_PROTOCOL:
5563 wxLogError(wxS("The URL '%s' uses an unsupported protocol."),
5564 url.c_str());
5565 break;
5566
5567 case wxCTE_NO_VALID_STREAM:
5568 wxFAIL; // should never happen - the user streams should always be valid!
5569 break;
5570
5571 case wxCTE_ABORTED:
5572 return true; // ignore this
5573
5574 case wxCTE_CURL_ERROR: {
5575 wxString ws = wxS("unknown");
5576 if (p->GetCurlSession())
5577 ws =
5578 wxString(p->GetCurlSession()->GetErrorString().c_str(), wxConvUTF8);
5579 wxLogError(wxS("Network error: %s"), ws.c_str());
5580 } break;
5581 }
5582
5583 // stop the thread
5584 if (p->IsAlive()) p->Abort();
5585
5586 // this is an unrecoverable error:
5587 return false;
5588}
5589#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
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
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.
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.
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.
Date and time utilities.
Generic GUI downloads tool.
Handle downloading of files from remote urls.
Font list manager.
OpenCPN Georef utility.
OpenGL chart rendering canvas.
Platform independent GL includes.
GSHHS Chart Object (Global Self-consistent, Hierarchical, High-resolution Shoreline) Derived from htt...
Miscellaneous globals primarely used by gui layer, not persisted in configuration file.
JSON event definition used in internal communications to/from plugins.
Enhanced logging interface on top of wx/log.h.
Multiplexer class and helpers.
bool replace(std::string &str, const std::string &from, const std::string &to)
Perform in place substitution in str, replacing "from" with "to".
std::string lookup_tarball(const char *uri)
Get path to tarball in cache for given filename.
std::string tolower(const std::string &input)
Return copy of s with all characters converted to lower case.
std::vector< std::string > split(const char *token_string, const std::string &delimiter)
Return vector of items in s separated by delimiter.
bool store_metadata(const char *path)
Store metadata in metadata cache, return success/fail.
bool store_tarball(const char *path, const char *basename)
Store a tarball in tarball cache, return success/fail.
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:91
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.