OpenCPN Partial API docs
pluginmanager.cpp
1 /***************************************************************************
2  *
3  * Project: OpenCPN
4  * Purpose: PlugIn Manager Object
5  * Author: David Register
6  *
7  ***************************************************************************
8  * Copyright (C) 2010 by David S. Register *
9  * *
10  * This program is free software; you can redistribute it and/or modify *
11  * it under the terms of the GNU General Public License as published by *
12  * the Free Software Foundation; either version 2 of the License, or *
13  * (at your option) any later version. *
14  * *
15  * This program is distributed in the hope that it will be useful, *
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
18  * GNU General Public License for more details. *
19  * *
20  * You should have received a copy of the GNU General Public License *
21  * along with this program; if not, write to the *
22  * Free Software Foundation, Inc., *
23  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
24  **************************************************************************/
25 #include <algorithm>
26 #include <archive.h>
27 #include <cstdio>
28 #include <cstdio>
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <fstream>
32 #include <iostream>
33 #include <iostream>
34 #include <memory>
35 #include <set>
36 #include <sstream>
37 #include <stdint.h>
38 #include <string>
39 #include <unordered_map>
40 
41 
42 
43 #ifdef __MINGW32__
44 #undef IPV6STRICT // mingw FTBS fix: missing struct ip_mreq
45 #include <windows.h>
46 #endif
47 
48 #include <typeinfo>
49 #if defined(__linux__) && !defined(__ANDROID__)
50 #include <wordexp.h>
51 #endif
52 #include <wx/wx.h>
53 #include <wx/dir.h>
54 #include <wx/event.h>
55 #include <wx/filename.h>
56 #include <wx/aui/aui.h>
57 #include <wx/platinfo.h>
58 #include <wx/popupwin.h>
59 #include <wx/progdlg.h>
60 #include <wx/statline.h>
61 #include <wx/tokenzr.h>
62 #include <wx/tooltip.h>
63 #include <wx/app.h>
64 #include <wx/hashset.h>
65 #include <wx/hashmap.h>
66 #include <wx/jsonval.h>
67 #include <wx/uri.h>
68 #include <wx/zipstrm.h>
69 #include <wx/zstream.h>
70 #include <wx/tarstrm.h>
71 #include <wx/textwrapper.h>
72 #include <wx/app.h>
73 
74 #ifndef __WXMSW__
75 #include <cxxabi.h>
76 #endif // __WXMSW__
77 
78 #include <archive_entry.h>
79 typedef __LA_INT64_T la_int64_t; // "older" libarchive versions support
80 
81 #ifdef USE_LIBELF
82 #include <elf.h>
83 #include <libelf.h>
84 #include <gelf.h>
85 #endif
86 
87 #include "config.h"
88 
89 #include "model/ais_target_data.h"
90 #include "model/catalog_handler.h"
91 #include "model/comm_drv_n0183_net.h"
93 #include "model/comm_drv_n2k.h"
94 #include "model/comm_drv_registry.h"
95 #include "model/comm_navmsg_bus.h"
96 #include "model/comm_vars.h"
97 #include "model/config_vars.h"
98 #include "model/downloader.h"
99 #include "model/georef.h"
100 #include "model/json_event.h"
101 #include "model/logger.h"
102 #include "model/multiplexer.h"
103 #include "model/nav_object_database.h"
104 #include "model/navutil_base.h"
105 #include "model/ocpn_utils.h"
106 #include "model/plugin_cache.h"
107 #include "model/plugin_handler.h"
108 #include "model/plugin_loader.h"
109 #include "model/plugin_paths.h"
110 #include "model/route.h"
111 #include "model/routeman.h"
112 #include "model/safe_mode.h"
113 #include "model/select.h"
114 #include "model/semantic_vers.h"
115 #include "model/track.h"
116 
117 #include "ais.h"
118 #include "canvasMenu.h"
119 #include "cat_settings.h"
120 #include "chartbase.h" // for ChartPlugInWrapper
121 #include "chartdb.h"
122 #include "chartdbs.h"
123 #include "chcanv.h"
124 #include "config.h"
125 #include "download_mgr.h"
126 #include "dychart.h"
127 #include "FontMgr.h"
128 #include "gshhs.h"
129 #include "model/ais_decoder.h"
130 #include "mygeom.h"
131 #include "navutil.h"
132 #include "observable_confvar.h"
133 #include "observable_globvar.h"
134 #include "ocpn_app.h"
135 #include "OCPN_AUIManager.h"
136 #include "ocpndc.h"
137 #include "ocpn_frame.h"
138 #include "ocpn_pixel.h"
139 #include "OCPNPlatform.h"
140 #include "OCPNRegion.h"
141 #include "options.h"
142 #include "piano.h"
143 #include "pluginmanager.h"
144 #include "routemanagerdialog.h"
145 #include "routeman_gui.h"
146 #include "s52plib.h"
147 #include "s52utils.h"
148 #include "SoundFactory.h"
149 #include "styles.h"
150 #include "svg_utils.h"
151 #include "SystemCmdSound.h"
152 #include "toolbar.h"
153 #include "update_mgr.h"
154 #include "waypointman_gui.h"
155 
156 #ifdef __ANDROID__
157 #include <dlfcn.h>
158 #include "androidUTIL.h"
159 #endif
160 
161 #ifdef ocpnUSE_GL
162 #include "glChartCanvas.h"
163 #endif
164 
165 #ifndef __WXMSW__
166 #include <signal.h>
167 #include <setjmp.h>
168 
169 struct sigaction sa_all_PIM;
170 struct sigaction sa_all_PIM_previous;
171 
172 sigjmp_buf env_PIM; // the context saved by sigsetjmp();
173 
174 void catch_signals_PIM(int signo) {
175  switch (signo) {
176  case SIGSEGV:
177  siglongjmp(env_PIM, 1); // jump back to the setjmp() point
178  break;
179 
180  default:
181  break;
182  }
183 }
184 
185 #endif
186 
187 extern MyConfig* pConfig;
188 extern OCPN_AUIManager* g_pauimgr;
189 
190 #if wxUSE_XLOCALE || !wxCHECK_VERSION(3, 0, 0)
191 extern wxLocale* plocale_def_lang;
192 #endif
193 
194 extern OCPNPlatform* g_Platform;
195 extern ChartDB* ChartData;
196 extern MyFrame* gFrame;
197 extern ocpnStyle::StyleManager* g_StyleManager;
198 extern options* g_pOptions;
199 extern Multiplexer* g_pMUX;
200 extern bool g_bShowChartBar;
201 extern Routeman* g_pRouteMan;
202 extern Select* pSelect;
203 extern RouteManagerDialog* pRouteManagerDialog;
204 extern RouteList* pRouteList;
205 extern std::vector<Track*> g_TrackList;
206 extern PlugInManager* g_pi_manager;
207 extern s52plib* ps52plib;
208 extern wxString ChartListFileName;
209 extern bool g_boptionsactive;
210 extern options* g_options;
211 extern ColorScheme global_color_scheme;
212 extern wxArrayString g_locale_catalog_array;
213 extern int g_GUIScaleFactor;
214 extern int g_ChartScaleFactor;
215 extern wxString g_locale;
216 extern ocpnFloatingToolbarDialog* g_MainToolbar;
217 
218 extern int g_chart_zoom_modifier_raster;
219 extern int g_chart_zoom_modifier_vector;
220 extern double g_display_size_mm;
221 extern bool g_bopengl;
222 
223 extern ChartGroupArray* g_pGroupArray;
224 extern unsigned int g_canvasConfig;
225 
226 extern wxString g_CmdSoundString;
227 
228 unsigned int gs_plib_flags;
229 wxString g_lastPluginMessage;
230 extern ChartCanvas* g_focusCanvas;
231 extern ChartCanvas* g_overlayCanvas;
232 extern bool g_bquiting;
233 
234 WX_DEFINE_ARRAY_PTR(ChartCanvas*, arrayofCanvasPtr);
235 extern arrayofCanvasPtr g_canvasArray;
236 
237 void NotifySetupOptionsPlugin(const PlugInData* pic);
238 
239 enum { CurlThreadId = wxID_HIGHEST + 1 };
240 
241 #include <wx/listimpl.cpp>
242 WX_DEFINE_LIST(Plugin_WaypointList);
243 WX_DEFINE_LIST(Plugin_HyperlinkList);
244 
245 wxDEFINE_EVENT(EVT_N0183_PLUGIN, ObservedEvt);
246 wxDEFINE_EVENT(EVT_SIGNALK, ObservedEvt);
247 
248 static void SendAisJsonMessage(std::shared_ptr<const AisTargetData> pTarget) {
249  // Only send messages if someone is listening...
250  if (!g_pi_manager->GetJSONMessageTargetCount()) return;
251 
252  // Do JSON message to all Plugin to inform of target
253  wxJSONValue jMsg;
254 
255  wxLongLong t = ::wxGetLocalTimeMillis();
256 
257  jMsg[wxS("Source")] = wxS("AisDecoder");
258  jMsg[wxT("Type")] = wxT("Information");
259  jMsg[wxT("Msg")] = wxS("AIS Target");
260  jMsg[wxT("MsgId")] = t.GetValue();
261  jMsg[wxS("lat")] = pTarget->Lat;
262  jMsg[wxS("lon")] = pTarget->Lon;
263  jMsg[wxS("sog")] = pTarget->SOG;
264  jMsg[wxS("cog")] = pTarget->COG;
265  jMsg[wxS("hdg")] = pTarget->HDG;
266  jMsg[wxS("mmsi")] = pTarget->MMSI;
267  jMsg[wxS("class")] = pTarget->Class;
268  jMsg[wxS("ownship")] = pTarget->b_OwnShip;
269  jMsg[wxS("active")] = pTarget->b_active;
270  jMsg[wxS("lost")] = pTarget->b_lost;
271  wxString l_ShipName = wxString::FromUTF8(pTarget->ShipName);
272  for (size_t i = 0; i < l_ShipName.Len(); i++) {
273  if (l_ShipName.GetChar(i) == '@') l_ShipName.SetChar(i, '\n');
274  }
275  jMsg[wxS("shipname")] = l_ShipName;
276  wxString l_CallSign = wxString::FromUTF8(pTarget->CallSign);
277  for (size_t i = 0; i < l_CallSign.Len(); i++) {
278  if (l_CallSign.GetChar(i) == '@') l_CallSign.SetChar(i, '\n');
279  }
280  jMsg[wxS("callsign")] = l_CallSign;
281  jMsg[wxS("removed")] = pTarget->b_removed;
282  g_pi_manager->SendJSONMessageToAllPlugins(wxT("AIS"), jMsg);
283 }
284 
285 static int ComparePlugins(PlugInContainer** p1, PlugInContainer** p2) {
286  return (*p1)->Key().compare((*p2)->Key());
287 }
288 
293 class BlacklistUI {
294 public:
295  void message(const std::string& message) {
296  if (m_defer)
297  m_deferred_messages.push_back(message);
298  else
299  show_msg(message);
300  }
301 
302  void show_deferred_messages() {
303  for (auto m : m_deferred_messages) show_msg(m);
304  m_defer = false;
305  }
306 
307  BlacklistUI() : m_defer(true){};
308 
309 private:
310  void show_msg(wxString msg) {
311  OCPNMessageBox(NULL, msg, wxString(_("OpenCPN Info")),
312  wxICON_INFORMATION | wxOK, 10); // 10 second timeout
313  }
314 
315  bool m_defer; // defer dialogs until setup completed
316  std::vector<wxString> m_deferred_messages;
317 };
318 
319 class PanelHardBreakWrapper : public wxTextWrapper {
320 public:
321  PanelHardBreakWrapper(wxWindow* win, const wxString& text, int widthMax) {
322  m_lineCount = 0;
323  Wrap(win, text, widthMax);
324  }
325  wxString const& GetWrapped() const { return m_wrapped; }
326  int const GetLineCount() const { return m_lineCount; }
327  wxArrayString GetLineArray() { return m_array; }
328 
329 protected:
330  virtual void OnOutputLine(const wxString& line) {
331  m_wrapped += line;
332  m_array.Add(line);
333  }
334  virtual void OnNewLine() {
335  m_wrapped += '\n';
336  m_lineCount++;
337  }
338 
339 private:
340  wxString m_wrapped;
341  int m_lineCount;
342  wxArrayString m_array;
343 };
344 
346  template <typename T>
347  std::size_t operator()(T t) const {
348  return static_cast<std::size_t>(t);
349  }
350 };
351 
352 wxString message_by_status(PluginStatus stat) {
353  switch (stat) {
354  case PluginStatus::System:
355  return _("Plugin is a standard system plugin");
356  case PluginStatus::Managed:
357  return _("Plugin is managed by OpenCPN");
358  case PluginStatus::Unmanaged:
359  return _("Plugin is not managed by OpenCPN");
360  case PluginStatus::Ghost:
361  return ("");
362  case PluginStatus::Unknown:
363  return _("Plugin status unknown");
364  case PluginStatus::LegacyUpdateAvailable:
365  return _("Update to managed Plugin is available");
366  case PluginStatus::ManagedInstallAvailable:
367  return _("New managed Plugin installation available");
368  case PluginStatus::ManagedInstalledUpdateAvailable:
369  return _("Update to installed Plugin is available");
370  case PluginStatus::ManagedInstalledCurrentVersion:
371  return _("Plugin is latest available");
372  case PluginStatus::ManagedInstalledDowngradeAvailable:
373  return ("");
374  case PluginStatus::PendingListRemoval:
375  return ("");
376  default:
377  return ("");
378  }
379 }
380 
381 static const std::unordered_map<PluginStatus, const char*, EnumClassHash>
382  icon_by_status(
383  {{PluginStatus::System, "emblem-default.svg"},
384  {PluginStatus::Managed, "emblem-default.svg"},
385  {PluginStatus::Unmanaged, "emblem-unmanaged.svg"},
386  {PluginStatus::Ghost, "ghost.svg"},
387  {PluginStatus::Unknown, "emblem-unmanaged.svg"},
388  {PluginStatus::LegacyUpdateAvailable, "emblem-legacy-update.svg"},
389  {PluginStatus::ManagedInstallAvailable, "emblem-download.svg"},
390  {PluginStatus::ManagedInstalledUpdateAvailable,
391  "emblem-legacy-update.svg"},
392  {PluginStatus::ManagedInstalledCurrentVersion, "emblem-default.svg"},
393  {PluginStatus::ManagedInstalledDowngradeAvailable,
394  "emblem-default.svg"},
395  {PluginStatus::PendingListRemoval, "emblem-default.svg"}
396 
397  });
398 
399 static const std::unordered_map<PluginStatus, const char*, EnumClassHash>
400  literalstatus_by_status(
401  {{PluginStatus::System, "System"},
402  {PluginStatus::Managed, "Managed"},
403  {PluginStatus::Unmanaged, "Unmanaged"},
404  {PluginStatus::Ghost, "Ghost"},
405  {PluginStatus::Unknown, "Unknown"},
406  {PluginStatus::LegacyUpdateAvailable, "LegacyUpdateAvailable"},
407  {PluginStatus::ManagedInstallAvailable, "ManagedInstallAvailable"},
408  {PluginStatus::ManagedInstalledUpdateAvailable,
409  "ManagedInstalledUpdateAvailable"},
410  {PluginStatus::ManagedInstalledCurrentVersion,
411  "ManagedInstalledCurrentVersion"},
412  {PluginStatus::ManagedInstalledDowngradeAvailable,
413  "ManagedInstalledDowngradeAvailable"},
414  {PluginStatus::PendingListRemoval, "PendingListRemoval"}
415 
416  });
417 
422 static std::vector<PluginMetadata> getCompatiblePlugins() {
424  struct metadata_compare {
425  bool operator()(const PluginMetadata& lhs,
426  const PluginMetadata& rhs) const {
427  return lhs.key() < rhs.key();
428  }
429  };
430 
431  std::vector<PluginMetadata> returnArray;
432 
433  std::set<PluginMetadata, metadata_compare> unique_plugins;
434  for (auto plugin : PluginHandler::getInstance()->getAvailable()) {
435  unique_plugins.insert(plugin);
436  }
437  for (auto plugin : unique_plugins) {
438  if (PluginHandler::isCompatible(plugin)) {
439  returnArray.push_back(plugin);
440  }
441  }
442  return returnArray;
443 }
444 
445 static SemanticVersion metadata_version(const PluginMetadata pm) {
446  return SemanticVersion::parse(pm.name);
447 }
448 
449 // Get installed version from manifest for given plugin. For
450 // older plugins this contains more detailed info then the
451 // plugin API. From API level 117 the API should contain the
452 // same info.
453 //
454 // TODO: Get version from API for api level 117+
455 SemanticVersion getInstalledVersion(const std::string name) {
456  std::string installed;
457  std::string path = PluginHandler::versionPath(name);
458  if (path == "" || !wxFileName::IsFileReadable(path)) {
459  return SemanticVersion(-1, -1);
460  }
461  std::ifstream stream;
462  stream.open(path, std::ifstream::in);
463  stream >> installed;
464  return SemanticVersion::parse(installed);
465 }
466 
471 static std::vector<PluginMetadata> getUpdates(const char* name) {
472  auto updates = getCompatiblePlugins();
473  updates.erase(
474  std::remove_if(updates.begin(), updates.end(),
475  [&](const PluginMetadata m) { return m.name != name; }),
476  updates.end());
477 
478  auto inst_vers = getInstalledVersion(name);
479  if (inst_vers.major == -1) {
480  return updates;
481  }
482 
483  // Drop already installed plugin, it has its own update options.
484  updates.erase(std::remove_if(updates.begin(), updates.end(),
485  [&](const PluginMetadata m) {
486  return metadata_version(m) == inst_vers;
487  }),
488  updates.end());
489  return updates;
490 }
491 
493 static void gui_uninstall(const PlugInData* pic, const char* plugin) {
494  g_Platform->ShowBusySpinner();
495  PluginLoader::getInstance()->DeactivatePlugIn(*pic);
496  PluginLoader::getInstance()->SetEnabled(pic->m_common_name, false);
497  PluginLoader::getInstance()->UpdatePlugIns();
498 
499  wxLogMessage("Uninstalling %s", plugin);
500  PluginHandler::getInstance()->uninstall(plugin);
501  PluginLoader::getInstance()->UpdatePlugIns();
502  g_Platform->HideBusySpinner();
503 }
504 
505 static bool LoadAllPlugIns(bool load_enabled, bool keep_orphans = false) {
506  g_Platform->ShowBusySpinner();
507  bool b = PluginLoader::getInstance()->LoadAllPlugIns(load_enabled,
508  keep_orphans);
509  g_Platform->HideBusySpinner();
510  return b;
511 }
512 
514 static void UninstallPlugin(const std::string& name) {
515  auto handler = PluginHandler::getInstance();
516  auto loader = PluginLoader::getInstance();
517  auto finder = [name](const PluginMetadata pm) { return pm.name == name; };
518  const auto& installed = handler->getInstalled();
519  auto found = std::find_if(installed.begin(), installed.end(), finder);
520  if (found != installed.end()) {
521  for (size_t i = 0; i < loader->GetPlugInArray()->GetCount(); i++) {
522  auto const& item = loader->GetPlugInArray()->Item(i);
523  if (item->m_common_name.ToStdString() == name) {
524  DEBUG_LOG << "Unloading plugin: " << name;
525  loader->UnLoadPlugIn(i);
526  break;
527  }
528  }
529  handler->uninstall(found->name);
530  DEBUG_LOG << "Uninstalling: " << found->name;
531  }
532 }
533 
534 static void run_update_dialog(PluginListPanel* parent, const PlugInData* pic,
535  bool uninstall, const char* name = 0,
536  bool b_forceEnable = false) {
537  wxString pluginName = pic->m_common_name;
538  const char* plugin = name == 0 ? pic->m_common_name.mb_str().data() : name;
539  auto updates = getUpdates(plugin);
540  auto parent_dlg = dynamic_cast<wxScrolledWindow*>(parent);
541  wxASSERT(parent_dlg != 0);
542  UpdateDialog dialog(parent_dlg, updates);
543  auto status = dialog.ShowModal();
544  status = dialog.GetReturnCode();
545  if (status != wxID_OK) {
546  return;
547  }
548 
549  auto update = dialog.GetUpdate();
550  if (!g_pi_manager->CheckBlacklistedPlugin(update)) {
551  return;
552  }
553 
554  wxLogMessage("Installing %s", update.name.c_str());
555 
556  auto pluginHandler = PluginHandler::getInstance();
557  auto path = ocpn::lookup_tarball(update.tarball_url.c_str());
558  if (uninstall && path != "") {
559  gui_uninstall(pic, update.name.c_str());
560  }
561  bool cacheResult = pluginHandler->installPluginFromCache(update);
562 
563  if (!cacheResult) {
564  g_Platform->ShowBusySpinner(); // Will be cancelled in downloader->run()
565  wxYield();
566 
567  auto downloader = new GuiDownloader(parent_dlg, update);
568  std::string tempTarballPath = downloader->run(parent_dlg, uninstall);
569 
570  if (!tempTarballPath.size()) // Error, dialog already presented
571  return;
572 
573  // Provisional error check
574  bool bOK = true;
575  std::string manifestPath = PluginHandler::fileListPath(update.name);
576  if (!isRegularFile(manifestPath.c_str())) {
577  wxLogMessage("Installation of %s failed", update.name.c_str());
578  PluginHandler::cleanupFiles(manifestPath, update.name);
579  bOK = false;
580  }
581 
582  // On successful installation, copy the temp tarball to the local cache
583  if (bOK) {
584  wxLogMessage("Installation of %s successful", update.name.c_str());
585  wxURI uri(wxString(update.tarball_url.c_str()));
586  wxFileName fn(uri.GetPath());
587  std::string basename = fn.GetFullName().ToStdString();
588 
589  if (ocpn::store_tarball(tempTarballPath.c_str(), basename.c_str())) {
590  wxLogDebug("Copied %s to local cache at %s", tempTarballPath.c_str(),
591  basename);
592  remove(tempTarballPath.c_str());
593  }
594  }
595  }
596 
597  // Check the library compatibility of the subject plugin
598  // Find the plugin library file, looking for "_pi.{dll/so/dylib file}
599 #ifdef __WXMSW__
600  wxString pispec = _T("_pi.dll");
601 #elif defined(__WXOSX__)
602  wxString pispec = _T("_pi.dylib");
603 #else
604  wxString pispec = _T("_pi.so");
605 #endif
606 
607  std::string manifestPath = PluginHandler::fileListPath(update.name);
608  wxTextFile manifest_file(manifestPath);
609  wxString pluginFile;
610  if (manifest_file.Open()) {
611  wxString val;
612  for (wxString str = manifest_file.GetFirstLine(); !manifest_file.Eof();
613  str = manifest_file.GetNextLine()) {
614  if (str.Contains(pispec)) {
615  if (getenv("OCPN_KEEP_PLUGINS")) {
616  // Undocumented debug hook
617  continue;
618  }
619  auto loader = PluginLoader::getInstance();
620  if (!loader->CheckPluginCompatibility(str)) {
621  wxString msg =
622  _("The plugin is not compatible with this version of OpenCPN, "
623  "and will be uninstalled.");
624  OCPNMessageBox(NULL, msg, wxString(_("OpenCPN Info")),
625  wxICON_INFORMATION | wxOK, 10);
626 
627  PluginHandler::cleanupFiles(manifestPath, update.name);
628  } else {
629  pluginFile = str;
630  }
631  break;
632  }
633  }
634  }
635 
636  if (b_forceEnable && pluginFile.Length()) {
637  wxString config_section = (_T ( "/PlugIns/" ));
638  wxFileName fn(pluginFile);
639  config_section += fn.GetFullName();
640  pConfig->SetPath(config_section);
641  pConfig->Write(_T ( "bEnabled" ), true);
642  }
643 
644  // This is installed from catalog, remove possible imported
645  // metadata leftovers
646  auto handler = PluginHandler::getInstance();
647  std::remove(handler->ImportedMetadataPath(update.name).c_str());
648 
649  // Reload all plugins, which will bring in the action results.
650  LoadAllPlugIns(false);
651 
652  parent->ReloadPluginPanels();
653 }
654 
655 static PlugIn_ViewPort CreatePlugInViewport(const ViewPort& vp) {
656  // Create a PlugIn Viewport
657  ViewPort tvp = vp;
658  PlugIn_ViewPort pivp;
659 
660  pivp.clat = tvp.clat; // center point
661  pivp.clon = tvp.clon;
662  pivp.view_scale_ppm = tvp.view_scale_ppm;
663  pivp.skew = tvp.skew;
664  pivp.rotation = tvp.rotation;
665  pivp.chart_scale = tvp.chart_scale;
666  pivp.pix_width = tvp.pix_width;
667  pivp.pix_height = tvp.pix_height;
668  pivp.rv_rect = tvp.rv_rect;
669  pivp.b_quilt = tvp.b_quilt;
670  pivp.m_projection_type = tvp.m_projection_type;
671 
672  pivp.lat_min = tvp.GetBBox().GetMinLat();
673  pivp.lat_max = tvp.GetBBox().GetMaxLat();
674  pivp.lon_min = tvp.GetBBox().GetMinLon();
675  pivp.lon_max = tvp.GetBBox().GetMaxLon();
676 
677  pivp.bValid = tvp.IsValid(); // This VP is valid
678 
679  return pivp;
680 }
681 
682 static ViewPort CreateCompatibleViewport(const PlugIn_ViewPort& pivp) {
683  // Create a system ViewPort
684  ViewPort vp;
685 
686  vp.clat = pivp.clat; // center point
687  vp.clon = pivp.clon;
688  vp.view_scale_ppm = pivp.view_scale_ppm;
689  vp.skew = pivp.skew;
690  vp.rotation = pivp.rotation;
691  vp.chart_scale = pivp.chart_scale;
692  vp.pix_width = pivp.pix_width;
693  vp.pix_height = pivp.pix_height;
694  vp.rv_rect = pivp.rv_rect;
695  vp.b_quilt = pivp.b_quilt;
696  vp.m_projection_type = pivp.m_projection_type;
697 
698  if (gFrame->GetPrimaryCanvas())
699  vp.ref_scale = gFrame->GetPrimaryCanvas()->GetVP().ref_scale;
700  else
701  vp.ref_scale = vp.chart_scale;
702 
703  vp.SetBoxes();
704  vp.Validate(); // This VP is valid
705 
706  return vp;
707 }
708 
709 class pluginUtilHandler : public wxEvtHandler {
710 public:
712  ~pluginUtilHandler() {}
713 
714  void OnPluginUtilAction(wxCommandEvent& event);
715 
716  DECLARE_EVENT_TABLE()
717 };
718 
719 BEGIN_EVENT_TABLE(pluginUtilHandler, wxEvtHandler)
720 EVT_BUTTON(ID_CMD_BUTTON_PERFORM_ACTION, pluginUtilHandler::OnPluginUtilAction)
721 END_EVENT_TABLE()
722 
724 
725 void pluginUtilHandler::OnPluginUtilAction(wxCommandEvent& event) {
726  auto panel = static_cast<PluginPanel*>(event.GetClientData());
727  PluginListPanel* plugin_list_panel =
728  dynamic_cast<PluginListPanel*>(panel->GetParent());
729  wxASSERT(plugin_list_panel != 0);
730 
731  auto actionPIC = panel->GetPlugin();
732  wxString name = actionPIC->m_common_name;
733 
734  // Perform the indicated action according to the verb...
735  switch (panel->GetAction()) {
736  case ActionVerb::UPGRADE_TO_MANAGED_VERSION: {
737  auto loader = PluginLoader::getInstance();
738 
739  // capture the plugin name
740  std::string pluginName = actionPIC->m_managed_metadata.name;
741 
742  wxLogMessage("Installing managed plugin: %s", pluginName.c_str());
743  auto downloader =
744  new GuiDownloader(plugin_list_panel, actionPIC->m_managed_metadata);
745  downloader->run(plugin_list_panel, false);
746 
747  // Provisional error check
748  std::string manifestPath = PluginHandler::fileListPath(pluginName);
749  if (isRegularFile(manifestPath.c_str())) {
750  // dynamically deactivate the legacy plugin, making way for the upgrade.
751  for (unsigned i = 0; i < loader->GetPlugInArray()->GetCount(); i += 1) {
752  if (actionPIC->m_managed_metadata.name ==
753  loader->GetPlugInArray()->Item(i)->m_common_name.ToStdString()) {
754  loader->UnLoadPlugIn(i);
755  break;
756  }
757  }
758 
759  // Reload all plugins, which will bring in the new, managed version.
760  LoadAllPlugIns(false);
761  } else {
762  PluginHandler::cleanupFiles(manifestPath,
763  actionPIC->m_managed_metadata.name);
764  }
765  plugin_list_panel->ReloadPluginPanels();
766  plugin_list_panel->SelectByName(name);
767 
768  break;
769  }
770 
771  case ActionVerb::UPGRADE_INSTALLED_MANAGED_VERSION:
772  case ActionVerb::REINSTALL_MANAGED_VERSION:
773  case ActionVerb::DOWNGRADE_INSTALLED_MANAGED_VERSION: {
774  // Grab a copy of the managed metadata
775  auto metaSave = actionPIC->m_managed_metadata;
776  run_update_dialog(plugin_list_panel, actionPIC, true,
777  metaSave.name.c_str());
778  break;
779  }
780 
781  case ActionVerb::INSTALL_MANAGED_VERSION: {
782  wxLogMessage("Installing new managed plugin.");
783  run_update_dialog(plugin_list_panel, actionPIC, false);
784  break;
785  }
786 
787  case ActionVerb::UNINSTALL_MANAGED_VERSION: {
788  PluginLoader::getInstance()->DeactivatePlugIn(*actionPIC);
789 
790  // Capture the confirmation dialog contents before the plugin goes away
791  wxString message;
792  message.Printf("%s %s\n", actionPIC->m_managed_metadata.name.c_str(),
793  actionPIC->m_managed_metadata.version.c_str());
794  message += _("successfully un-installed");
795 
796  wxLogMessage("Uninstalling %s",
797  actionPIC->m_managed_metadata.name.c_str());
798 
799  PluginHandler::getInstance()->uninstall(
800  actionPIC->m_managed_metadata.name);
801 
802  // Reload all plugins, which will bring in the action results.
803  auto loader = PluginLoader::getInstance();
804  LoadAllPlugIns(false);
805  plugin_list_panel->ReloadPluginPanels();
806 
807  OCPNMessageBox(gFrame, message, _("Un-Installation complete"),
808  wxICON_INFORMATION | wxOK);
809 
810  break;
811  }
812 
813  case ActionVerb::NOP:
814  default:
815  break;
816  }
817 }
818 
819 //------------------------------------------------------------------------------
820 // NMEA Event Implementation
821 // PlugIn Messaging scheme Event
822 //------------------------------------------------------------------------------
823 
824 const wxEventType wxEVT_OCPN_MSG = wxNewEventType();
825 
826 OCPN_MsgEvent::OCPN_MsgEvent(wxEventType commandType, int id)
827  : wxEvent(id, commandType) {}
828 
829 OCPN_MsgEvent::~OCPN_MsgEvent() {}
830 
831 wxEvent* OCPN_MsgEvent::Clone() const {
832  OCPN_MsgEvent* newevent = new OCPN_MsgEvent(*this);
833  newevent->m_MessageID =
834  this->m_MessageID
835  .c_str(); // this enforces a deep copy of the string data
836  newevent->m_MessageText = this->m_MessageText.c_str();
837  return newevent;
838 }
839 
840 //------------------------------------------------------------------------------------------------
841 //
842 // The PlugInToolbarToolContainer Implementation
843 //
844 //------------------------------------------------------------------------------------------------
845 PlugInToolbarToolContainer::PlugInToolbarToolContainer() {
846  bitmap_dusk = NULL;
847  bitmap_night = NULL;
848  bitmap_day = NULL;
849  bitmap_Rollover_day = NULL;
850  bitmap_Rollover_dusk = NULL;
851  bitmap_Rollover_night = NULL;
852 }
853 
854 PlugInToolbarToolContainer::~PlugInToolbarToolContainer() {
855  delete bitmap_dusk;
856  delete bitmap_night;
857  delete bitmap_day;
858  delete bitmap_Rollover_day;
859  delete bitmap_Rollover_dusk;
860  delete bitmap_Rollover_night;
861 }
862 
863 //-----------------------------------------------------------------------------------------------------
864 //
865 // The PlugIn Manager Implementation
866 //
867 //-----------------------------------------------------------------------------------------------------
868 PlugInManager* s_ppim;
869 
870 BEGIN_EVENT_TABLE(PlugInManager, wxEvtHandler)
871 #if !defined(__ANDROID__) && defined(OCPN_USE_CURL)
872 EVT_CURL_END_PERFORM(CurlThreadId, PlugInManager::OnEndPerformCurlDownload)
873 EVT_CURL_DOWNLOAD(CurlThreadId, PlugInManager::OnCurlDownload)
874 #endif
875 END_EVENT_TABLE()
876 
877 static void event_message_box(const wxString& msg) {
878  OCPNMessageBox(NULL, msg, wxString(_("OpenCPN Info")),
879  wxICON_INFORMATION | wxOK, 0); // no timeout
880 }
881 
882 static void OnLoadPlugin(const PlugInContainer* pic) {
883  if (g_options) {
884  if ((pic->m_cap_flag & INSTALLS_TOOLBOX_PAGE)) {
885  if (!pic->m_toolbox_panel) NotifySetupOptionsPlugin(pic);
886  }
887  }
888 }
889 
890 PlugInManager::PlugInManager(MyFrame* parent) {
891 #if !defined(__ANDROID__) && defined(OCPN_USE_CURL)
892  m_pCurlThread = NULL;
893  m_pCurl = 0;
894 #endif
895  pParent = parent;
896  s_ppim = this;
897 
898  MyFrame* pFrame = GetParentFrame();
899  if (pFrame) {
900  m_plugin_menu_item_id_next = CanvasMenuHandler::GetNextContextMenuId();
901  m_plugin_tool_id_next = pFrame->GetNextToolbarToolId();
902  }
903 
904 #ifdef __ANDROID__
905  // Due to the oddball mixed static/dynamic linking model used in the Android
906  // architecture, all classes used in PlugIns must be present in the core,
907  // even if stubs.
908  //
909  // Here is where we do that....
910  if (pFrame) {
911  wxArrayString as;
912  as.Add(_T("Item0"));
913  wxRadioBox* box =
914  new wxRadioBox(pFrame, -1, _T(""), wxPoint(0, 0), wxSize(-1, -1), as);
915  delete box;
916  }
917 
918 #endif
919 
920 #if !defined(__ANDROID__) && defined(OCPN_USE_CURL)
921  wxCurlBase::Init();
922  m_last_online = false;
923  m_last_online_chk = -1;
924 #endif
925 
926  m_utilHandler = new pluginUtilHandler();
927  m_listPanel = NULL;
928  m_blacklist = blacklist_factory();
929  m_blacklist_ui = std::unique_ptr<BlacklistUI>(new BlacklistUI());
930 
931  wxDEFINE_EVENT(EVT_JSON_TO_ALL_PLUGINS, ObservedEvt);
932  evt_json_to_all_plugins_listener.Listen(g_pRouteMan->json_msg, this,
933  EVT_JSON_TO_ALL_PLUGINS);
934  Bind(EVT_JSON_TO_ALL_PLUGINS, [&](ObservedEvt& ev) {
935  auto json = std::static_pointer_cast<const wxJSONValue>(ev.GetSharedPtr());
936  SendJSONMessageToAllPlugins(ev.GetString(), *json);
937  });
938 
939  wxDEFINE_EVENT(EVT_LEGINFO_TO_ALL_PLUGINS, ObservedEvt);
940  evt_routeman_leginfo_listener.Listen(g_pRouteMan->json_leg_info, this,
941  EVT_LEGINFO_TO_ALL_PLUGINS);
942  Bind(EVT_LEGINFO_TO_ALL_PLUGINS, [&](ObservedEvt& ev) {
943  auto ptr = UnpackEvtPointer<ActiveLegDat>(ev);
944  SendActiveLegInfoToAllPlugIns(ptr.get());
945  });
946 
947  HandlePluginLoaderEvents();
948  InitCommListeners();
949  auto msg_sent_action = [](ObservedEvt ev) {
950  SendNMEASentenceToAllPlugIns(ev.GetString()); };
951  m_on_msg_sent_listener.Init(g_pRouteMan->on_message_sent, msg_sent_action);
952 }
953 PlugInManager::~PlugInManager() {
954 #if !defined(__ANDROID__) && defined(OCPN_USE_CURL)
955  wxCurlBase::Shutdown();
956 #endif
957  delete m_utilHandler;
958 }
959 
960 void PlugInManager::InitCommListeners(void) {
961  // Initialize the comm listener to support
962  // void SetNMEASentence(wxString &sentence);
963 
964  auto& msgbus = NavMsgBus::GetInstance();
965 
966  m_listener_N0183_all.Listen(Nmea0183Msg::MessageKey("ALL"), this,
967  EVT_N0183_PLUGIN);
968  Bind(EVT_N0183_PLUGIN, [&](ObservedEvt ev) {
969  auto ptr = ev.GetSharedPtr();
970  auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
971  HandleN0183(n0183_msg);
972  });
973 
974  SignalkMsg sk_msg;
975  m_listener_SignalK.Listen(sk_msg, this, EVT_SIGNALK);
976 
977  Bind(EVT_SIGNALK, [&](ObservedEvt ev) {
978  HandleSignalK(UnpackEvtPointer<SignalkMsg>(ev));
979  });
980 }
981 
982 void PlugInManager::HandleN0183(std::shared_ptr<const Nmea0183Msg> n0183_msg) {
983  std::string s = n0183_msg->payload;
984  wxString sentence(s.c_str());
985 
986  if (s[0] == '$') {
987  const auto& drivers = CommDriverRegistry::GetInstance().GetDrivers();
988  auto target_driver = FindDriver(drivers, n0183_msg->source->iface);
989 
990  bool bpass_input_filter = true;
991 
992  // Get the params for the driver sending this message
993  ConnectionParams params;
994  auto drv_serial =
995  std::dynamic_pointer_cast<CommDriverN0183Serial>(target_driver);
996  if (drv_serial) {
997  params = drv_serial->GetParams();
998  } else {
999  auto drv_net =
1000  std::dynamic_pointer_cast<CommDriverN0183Net>(target_driver);
1001  if (drv_net) {
1002  params = drv_net->GetParams();
1003  }
1004  }
1005 
1006  // Check to see if the message passes the source's input filter
1007  bpass_input_filter = params.SentencePassesFilter(sentence, FILTER_INPUT);
1008 
1009  if (bpass_input_filter) SendNMEASentenceToAllPlugIns(sentence);
1010  } else if (s[0] == '!') {
1011  // printf("AIS to all: %s", s.c_str());
1012  SendAISSentenceToAllPlugIns(sentence);
1013  }
1014 }
1015 
1016 void PlugInManager::HandleSignalK(std::shared_ptr<const SignalkMsg> sK_msg) {
1017  g_ownshipMMSI_SK = sK_msg->context_self;
1018 
1019  wxJSONReader jsonReader;
1020  wxJSONValue root;
1021 
1022  std::string msgTerminated = sK_msg->raw_message;
1023  ;
1024 
1025  int errors = jsonReader.Parse(msgTerminated, &root);
1026  if (errors == 0) SendJSONMessageToAllPlugins(wxT("OCPN_CORE_SIGNALK"), root);
1027 }
1028 
1034 wxDEFINE_EVENT(EVT_PLUGMGR_AIS_MSG, ObservedEvt);
1035 wxDEFINE_EVENT(EVT_PLUGMGR_ROUTEMAN_MSG, ObservedEvt);
1036 wxDEFINE_EVENT(EVT_BLACKLISTED_PLUGIN, wxCommandEvent);
1037 wxDEFINE_EVENT(EVT_LOAD_DIRECTORY, wxCommandEvent);
1038 wxDEFINE_EVENT(EVT_LOAD_PLUGIN, wxCommandEvent);
1039 wxDEFINE_EVENT(EVT_PLUGIN_UNLOAD, wxCommandEvent);
1040 wxDEFINE_EVENT(EVT_PLUGLIST_CHANGE, wxCommandEvent);
1041 wxDEFINE_EVENT(EVT_UPDATE_CHART_TYPES, wxCommandEvent);
1042 wxDEFINE_EVENT(EVT_PLUGIN_LOADALL_FINALIZE, wxCommandEvent);
1043 
1044 void PlugInManager::HandlePluginLoaderEvents() {
1045  auto loader = PluginLoader::getInstance();
1046 
1047  evt_blacklisted_plugin_listener.Listen(loader->evt_blacklisted_plugin, this,
1048  EVT_BLACKLISTED_PLUGIN);
1049  Bind(EVT_BLACKLISTED_PLUGIN, [&](wxCommandEvent& ev) {
1050  m_blacklist_ui->message(ev.GetString().ToStdString());
1051  });
1052 
1053  loader->SetOnDeactivateCb(
1054  [&](const PlugInContainer* pic) { OnPluginDeactivate(pic); });
1055  evt_pluglist_change_listener.Listen(loader->evt_pluglist_change, this,
1056  EVT_PLUGLIST_CHANGE);
1057  Bind(EVT_PLUGLIST_CHANGE, [&](wxCommandEvent&) {
1058  if (m_listPanel) m_listPanel->ReloadPluginPanels();
1059  if (g_options) g_options->itemBoxSizerPanelPlugins->Layout();
1060  });
1061 
1062  evt_load_directory_listener.Listen(loader->evt_load_directory, this,
1063  EVT_LOAD_DIRECTORY);
1064  Bind(EVT_LOAD_DIRECTORY, [&](wxCommandEvent&) {
1065  pConfig->SetPath("/PlugIns/");
1066  SetPluginOrder(pConfig->Read("PluginOrder", wxEmptyString));
1067  });
1068 
1069  evt_load_plugin_listener.Listen(loader->evt_load_plugin, this,
1070  EVT_LOAD_PLUGIN);
1071  Bind(EVT_LOAD_PLUGIN, [&](wxCommandEvent& ev) {
1072  auto pic = static_cast<const PlugInContainer*>(ev.GetClientData());
1073  OnLoadPlugin(pic);
1074  });
1075 
1076  evt_update_chart_types_listener.Listen(loader->evt_update_chart_types, this,
1077  EVT_UPDATE_CHART_TYPES);
1078  Bind(EVT_UPDATE_CHART_TYPES,
1079  [&](wxCommandEvent& ev) { UpDateChartDataTypes(); });
1080 
1081  evt_plugin_loadall_finalize_listener.Listen(
1082  loader->evt_plugin_loadall_finalize, this, EVT_PLUGIN_LOADALL_FINALIZE);
1083  Bind(EVT_PLUGIN_LOADALL_FINALIZE,
1084  [&](wxCommandEvent& ev) { FinalizePluginLoadall(); });
1085 
1086  evt_ais_json_listener.Listen(g_pAIS->plugin_msg, this, EVT_PLUGMGR_AIS_MSG);
1087  evt_routeman_json_listener.Listen(g_pRouteMan->json_msg, this,
1088  EVT_PLUGMGR_ROUTEMAN_MSG);
1089  Bind(EVT_PLUGMGR_AIS_MSG, [&](ObservedEvt& ev) {
1090  auto pTarget = UnpackEvtPointer<AisTargetData>(ev);
1091  SendAisJsonMessage(pTarget);
1092  });
1093  Bind(EVT_PLUGMGR_ROUTEMAN_MSG, [&](ObservedEvt& ev) {
1094  auto msg = UnpackEvtPointer<wxJSONValue>(ev);
1095  SendJSONMessageToAllPlugins(ev.GetString(), *msg);
1096  });
1097 }
1098 
1103 wxDEFINE_EVENT(EVT_DOWNLOAD_FAILED, wxCommandEvent);
1104 wxDEFINE_EVENT(EVT_DOWNLOAD_OK, wxCommandEvent);
1105 
1106 void PlugInManager::HandlePluginHandlerEvents() {
1107  auto loader = PluginLoader::getInstance();
1108 
1109  evt_download_failed_listener.Listen(loader->evt_update_chart_types, this,
1110  EVT_DOWNLOAD_FAILED);
1111  Bind(EVT_DOWNLOAD_FAILED, [&](wxCommandEvent& ev) {
1112  wxString message = _("Please check system log for more info.");
1113  OCPNMessageBox(gFrame, message, _("Installation error"),
1114  wxICON_ERROR | wxOK | wxCENTRE);
1115  });
1116 
1117  evt_download_ok_listener.Listen(loader->evt_update_chart_types, this,
1118  EVT_DOWNLOAD_OK);
1119  Bind(EVT_DOWNLOAD_OK, [&](wxCommandEvent& ev) {
1120  wxString message(ev.GetString());
1121  message += _(" successfully installed from cache");
1122  OCPNMessageBox(gFrame, message, _("Installation complete"),
1123  wxICON_INFORMATION | wxOK | wxCENTRE);
1124  });
1125 }
1126 
1127 bool PlugInManager::CallLateInit(void) {
1128  bool bret = true;
1129 
1130  auto plugin_array = PluginLoader::getInstance()->GetPlugInArray();
1131  for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
1132  PlugInContainer* pic = (*plugin_array)[i];
1133 
1134  switch (pic->m_api_version) {
1135  case 110:
1136  case 111:
1137  case 112:
1138  case 113:
1139  case 114:
1140  case 115:
1141  case 116:
1142  case 117:
1143  case 118:
1144  ProcessLateInit(pic);
1145  break;
1146  }
1147  }
1148 
1149  return bret;
1150 }
1151 
1152 void PlugInManager::ProcessLateInit(const PlugInContainer* pic) {
1153  if (pic->m_cap_flag & WANTS_LATE_INIT) {
1154  wxString msg("PlugInManager: Calling LateInit PlugIn: ");
1155  msg += pic->m_plugin_file;
1156  wxLogMessage(msg);
1157 
1158  opencpn_plugin_110* ppi = dynamic_cast<opencpn_plugin_110*>(pic->m_pplugin);
1159  if (ppi) ppi->LateInit();
1160  }
1161 }
1162 
1163 void PlugInManager::OnPluginDeactivate(const PlugInContainer* pic) {
1164  // Unload chart cache if this plugin is responsible for any charts
1165  if ((pic->m_cap_flag & INSTALLS_PLUGIN_CHART) ||
1166  (pic->m_cap_flag & INSTALLS_PLUGIN_CHART_GL)) {
1167  ChartData->PurgeCachePlugins();
1168  gFrame->InvalidateAllQuilts();
1169  }
1170  // Deactivate (Remove) any ToolbarTools added by this PlugIn
1171  for (unsigned int i = 0; i < m_PlugInToolbarTools.GetCount(); i++) {
1172  PlugInToolbarToolContainer* pttc = m_PlugInToolbarTools[i];
1173 
1174  if (pttc->m_pplugin == pic->m_pplugin) {
1175  m_PlugInToolbarTools.Remove(pttc);
1176  delete pttc;
1177  }
1178  }
1179 
1180  // Deactivate (Remove) any ContextMenu items addded by this PlugIn
1181  for (unsigned int i = 0; i < m_PlugInMenuItems.GetCount(); i++) {
1182  PlugInMenuItemContainer* pimis = m_PlugInMenuItems[i];
1183  if (pimis->m_pplugin == pic->m_pplugin) {
1184  m_PlugInMenuItems.Remove(pimis);
1185  delete pimis;
1186  }
1187  }
1188 }
1189 
1190 void PlugInManager::SendVectorChartObjectInfo(const wxString& chart,
1191  const wxString& feature,
1192  const wxString& objname,
1193  double& lat, double& lon,
1194  double& scale, int& nativescale) {
1195  wxString decouple_chart(chart);
1196  wxString decouple_feature(feature);
1197  wxString decouple_objname(objname);
1198  auto plugin_array = PluginLoader::getInstance()->GetPlugInArray();
1199  for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
1200  PlugInContainer* pic = (*plugin_array)[i];
1201  if (pic->m_enabled && pic->m_init_state) {
1202  if (pic->m_cap_flag & WANTS_VECTOR_CHART_OBJECT_INFO) {
1203  switch (pic->m_api_version) {
1204  case 112:
1205  case 113:
1206  case 114:
1207  case 115:
1208  case 116:
1209  case 117:
1210  case 118: {
1211  opencpn_plugin_112* ppi =
1212  dynamic_cast<opencpn_plugin_112*>(pic->m_pplugin);
1213  if (ppi)
1214  ppi->SendVectorChartObjectInfo(decouple_chart, decouple_feature,
1215  decouple_objname, lat, lon, scale,
1216  nativescale);
1217  break;
1218  }
1219  default:
1220  break;
1221  }
1222  }
1223  }
1224  }
1225 }
1226 
1227 bool PlugInManager::IsAnyPlugInChartEnabled() {
1228  // Is there a PlugIn installed and active that implements PlugIn Chart
1229  // type(s)?
1230  auto plugin_array = PluginLoader::getInstance()->GetPlugInArray();
1231  for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
1232  PlugInContainer* pic = (*plugin_array)[i];
1233  if (pic->m_enabled && pic->m_init_state) {
1234  if ((pic->m_cap_flag & INSTALLS_PLUGIN_CHART) ||
1235  (pic->m_cap_flag & INSTALLS_PLUGIN_CHART_GL))
1236  return true;
1237  }
1238  }
1239  return false;
1240 }
1241 
1242 void PlugInManager::UpdateManagedPlugins() {
1243  PluginLoader::getInstance()->UpdateManagedPlugins(false);
1244  PluginLoader::getInstance()->SortPlugins(ComparePlugins);
1245 
1246  if (m_listPanel) m_listPanel->ReloadPluginPanels();
1247  g_options->itemBoxSizerPanelPlugins->Layout();
1248 }
1249 
1250 bool PlugInManager::UpDateChartDataTypes() {
1251  bool bret = false;
1252  if (NULL == ChartData) return bret;
1253 
1254  auto plugin_array = PluginLoader::getInstance()->GetPlugInArray();
1255  for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
1256  PlugInContainer* pic = plugin_array->Item(i);
1257 
1258  if (pic->m_init_state) {
1259  if ((pic->m_cap_flag & INSTALLS_PLUGIN_CHART) ||
1260  (pic->m_cap_flag & INSTALLS_PLUGIN_CHART_GL))
1261  bret = true;
1262  }
1263  }
1264 
1265  if (bret) ChartData->UpdateChartClassDescriptorArray();
1266 
1267  return bret;
1268 }
1269 
1270 void PlugInManager::FinalizePluginLoadall() {
1271  // FIXME
1272  // Maybe this does not need to be done for CLI instance?
1273  // Inform plugins of the current color scheme
1274  SetColorSchemeForAllPlugIns(global_color_scheme);
1275 
1276  // Tell all the PlugIns about the current OCPN configuration
1277  SendBaseConfigToAllPlugIns();
1278  SendS52ConfigToAllPlugIns(true);
1279  SendSKConfigToAllPlugIns();
1280 
1281  // Inform Plugins of OpenGL configuration, if enabled
1282 #ifdef ocpnUSE_GL
1283  if (g_bopengl) {
1284  if (gFrame->GetPrimaryCanvas()->GetglCanvas())
1285  gFrame->GetPrimaryCanvas()->GetglCanvas()->SendJSONConfigMessage();
1286  }
1287 #endif
1288 
1289  // And then reload all catalogs.
1290  ReloadLocale();
1291 }
1292 
1293 void PlugInManager::SetPluginOrder(wxString serialized_names) {
1294  m_plugin_order.Empty();
1295  wxStringTokenizer tokenizer(serialized_names, ";");
1296  while (tokenizer.HasMoreTokens()) {
1297  m_plugin_order.Add(tokenizer.GetNextToken());
1298  }
1299 }
1300 
1301 wxString PlugInManager::GetPluginOrder() {
1302  wxString plugins = wxEmptyString;
1303  auto plugin_array = PluginLoader::getInstance()->GetPlugInArray();
1304  for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
1305  plugins.Append(plugin_array->Item(i)->m_common_name);
1306  if (i < plugin_array->GetCount() - 1) plugins.Append(';');
1307  }
1308  return plugins;
1309 }
1310 
1311 bool PlugInManager::UpdateConfig() {
1312  // pConfig->SetPath( _T("/PlugIns/") );
1313  // pConfig->Write( _T("PluginOrder"), GetPluginOrder() );
1314 
1315  auto plugin_array = PluginLoader::getInstance()->GetPlugInArray();
1316  for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
1317  PlugInContainer* pic = plugin_array->Item(i);
1318 
1319  if (pic) {
1320  wxString config_section = (_T ( "/PlugIns/" ));
1321  config_section += pic->m_plugin_filename;
1322  pConfig->SetPath(config_section);
1323  pConfig->Write(_T ( "bEnabled" ), pic->m_enabled);
1324  }
1325  }
1326 
1327  return true;
1328 }
1329 
1330 void PlugInManager::ShowDeferredBlacklistMessages() {
1331  m_blacklist_ui->show_deferred_messages();
1332 }
1333 
1334 bool PlugInManager::CheckBlacklistedPlugin(const PluginMetadata plugin) {
1335  auto v = SemanticVersion::parse(plugin.version);
1336  return CheckBlacklistedPlugin(wxString(plugin.name), v.major, v.minor);
1337 }
1338 
1339 bool PlugInManager::CheckBlacklistedPlugin(opencpn_plugin* plugin) {
1340  int major = plugin->GetPlugInVersionMajor();
1341  int minor = plugin->GetPlugInVersionMinor();
1342 
1343 #ifdef __WXMSW__
1344  wxString name = wxString::FromAscii(typeid(*plugin).name());
1345  name.Replace("class ", wxEmptyString);
1346 #else
1347  const std::type_info& ti = typeid(*plugin);
1348  int status;
1349  char* realname = abi::__cxa_demangle(ti.name(), 0, 0, &status);
1350  wxString name = wxString::FromAscii(realname);
1351  free(realname);
1352 #endif // __WXMSW__
1353  return CheckBlacklistedPlugin(name, major, minor);
1354 }
1355 
1356 bool PlugInManager::CheckBlacklistedPlugin(wxString name, int major,
1357  int minor) {
1358  auto block_status = m_blacklist->get_status(name.ToStdString(), major, minor);
1359  if (block_status == plug_status::unblocked) return true;
1360  plug_data data(name.ToStdString(), major, minor);
1361  auto msg = m_blacklist->get_message(block_status, data);
1362  m_blacklist_ui->message(msg);
1363  return false;
1364 }
1365 
1366 bool PlugInManager::RenderAllCanvasOverlayPlugIns(ocpnDC& dc,
1367  const ViewPort& vp,
1368  int canvasIndex,
1369  int priority) {
1370  auto plugin_array = PluginLoader::getInstance()->GetPlugInArray();
1371  for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
1372  PlugInContainer* pic = plugin_array->Item(i);
1373  if (pic->m_enabled && pic->m_init_state) {
1374  if (pic->m_cap_flag & WANTS_OVERLAY_CALLBACK) {
1375  PlugIn_ViewPort pivp = CreatePlugInViewport(vp);
1376 
1377  wxDC* pdc = dc.GetDC();
1378  if (pdc) // not in OpenGL mode
1379  {
1380  switch (pic->m_api_version) {
1381  case 106: {
1382  if (priority > 0) break;
1383  opencpn_plugin_16* ppi =
1384  dynamic_cast<opencpn_plugin_16*>(pic->m_pplugin);
1385  if (ppi) ppi->RenderOverlay(*pdc, &pivp);
1386  break;
1387  }
1388  case 107: {
1389  if (priority > 0) break;
1390  opencpn_plugin_17* ppi =
1391  dynamic_cast<opencpn_plugin_17*>(pic->m_pplugin);
1392  if (ppi) ppi->RenderOverlay(*pdc, &pivp);
1393  break;
1394  }
1395  case 108:
1396  case 109:
1397  case 110:
1398  case 111:
1399  case 112:
1400  case 113:
1401  case 114:
1402  case 115: {
1403  if (priority > 0) break;
1404  opencpn_plugin_18* ppi =
1405  dynamic_cast<opencpn_plugin_18*>(pic->m_pplugin);
1406  if (ppi) ppi->RenderOverlay(*pdc, &pivp);
1407  break;
1408  }
1409  case 116:
1410  case 117: {
1411  if (priority > 0) break;
1412  opencpn_plugin_18* ppi =
1413  dynamic_cast<opencpn_plugin_18*>(pic->m_pplugin);
1414  if (ppi) {
1415  ppi->RenderOverlay(*pdc, &pivp);
1416  }
1417  opencpn_plugin_116* ppi116 =
1418  dynamic_cast<opencpn_plugin_116*>(pic->m_pplugin);
1419  if (ppi116)
1420  ppi116->RenderOverlayMultiCanvas(*pdc, &pivp, canvasIndex);
1421  break;
1422  }
1423  case 118: {
1424  if (priority <= 0) {
1425  opencpn_plugin_18* ppi =
1426  dynamic_cast<opencpn_plugin_18*>(pic->m_pplugin);
1427  if (ppi) {
1428  ppi->RenderOverlay(*pdc, &pivp);
1429  }
1430  }
1431  opencpn_plugin_118* ppi118 =
1432  dynamic_cast<opencpn_plugin_118*>(pic->m_pplugin);
1433  if (ppi118)
1434  ppi118->RenderOverlayMultiCanvas(*pdc, &pivp, canvasIndex,
1435  priority);
1436  break;
1437  }
1438  default:
1439  break;
1440  }
1441  } else {
1442  // If in OpenGL mode, and the PlugIn has requested OpenGL render
1443  // callbacks, then there is no need to render by wxDC here.
1444  if (pic->m_cap_flag & WANTS_OPENGL_OVERLAY_CALLBACK) continue;
1445 
1446  if ((m_cached_overlay_bm.GetWidth() != vp.pix_width) ||
1447  (m_cached_overlay_bm.GetHeight() != vp.pix_height))
1448  m_cached_overlay_bm.Create(vp.pix_width, vp.pix_height, -1);
1449 
1450  wxMemoryDC mdc;
1451  mdc.SelectObject(m_cached_overlay_bm);
1452  mdc.SetBackground(*wxBLACK_BRUSH);
1453  mdc.Clear();
1454 
1455  bool b_rendered = false;
1456 
1457  switch (pic->m_api_version) {
1458  case 106: {
1459  if (priority > 0) break;
1460  opencpn_plugin_16* ppi =
1461  dynamic_cast<opencpn_plugin_16*>(pic->m_pplugin);
1462  if (ppi) b_rendered = ppi->RenderOverlay(mdc, &pivp);
1463  break;
1464  }
1465  case 107: {
1466  if (priority > 0) break;
1467  opencpn_plugin_17* ppi =
1468  dynamic_cast<opencpn_plugin_17*>(pic->m_pplugin);
1469  if (ppi) b_rendered = ppi->RenderOverlay(mdc, &pivp);
1470  break;
1471  }
1472  case 108:
1473  case 109:
1474  case 110:
1475  case 111:
1476  case 112:
1477  case 113:
1478  case 114:
1479  case 115: {
1480  if (priority > 0) break;
1481  opencpn_plugin_18* ppi =
1482  dynamic_cast<opencpn_plugin_18*>(pic->m_pplugin);
1483  if (ppi) b_rendered = ppi->RenderOverlay(mdc, &pivp);
1484  break;
1485  }
1486  case 116:
1487  case 117: {
1488  if (priority > 0) break;
1489  opencpn_plugin_18* ppi =
1490  dynamic_cast<opencpn_plugin_18*>(pic->m_pplugin);
1491  if (ppi) {
1492  b_rendered = ppi->RenderOverlay(mdc, &pivp);
1493  }
1494  opencpn_plugin_116* ppi116 =
1495  dynamic_cast<opencpn_plugin_116*>(pic->m_pplugin);
1496  if (ppi116)
1497  b_rendered = ppi116->RenderOverlayMultiCanvas(mdc, &pivp,
1498  g_canvasConfig);
1499  break;
1500  }
1501  case 118: {
1502  if (priority <= 0) {
1503  opencpn_plugin_18* ppi =
1504  dynamic_cast<opencpn_plugin_18*>(pic->m_pplugin);
1505  if (ppi) {
1506  b_rendered = ppi->RenderOverlay(mdc, &pivp);
1507  }
1508  }
1509  opencpn_plugin_118* ppi118 =
1510  dynamic_cast<opencpn_plugin_118*>(pic->m_pplugin);
1511  if (ppi118)
1512  b_rendered = ppi118->RenderOverlayMultiCanvas(
1513  mdc, &pivp, g_canvasConfig, priority);
1514  break;
1515  }
1516  default: {
1517  b_rendered = pic->m_pplugin->RenderOverlay(&mdc, &pivp);
1518  break;
1519  }
1520  }
1521 
1522  mdc.SelectObject(wxNullBitmap);
1523 
1524  if (b_rendered) {
1525  wxMask* p_msk = new wxMask(m_cached_overlay_bm, wxColour(0, 0, 0));
1526  m_cached_overlay_bm.SetMask(p_msk);
1527 
1528  dc.DrawBitmap(m_cached_overlay_bm, 0, 0, true);
1529  }
1530  }
1531  } else if (pic->m_cap_flag & WANTS_OPENGL_OVERLAY_CALLBACK) {
1532  }
1533  }
1534  }
1535 
1536  return true;
1537 }
1538 
1539 bool PlugInManager::RenderAllGLCanvasOverlayPlugIns(wxGLContext* pcontext,
1540  const ViewPort& vp,
1541  int canvasIndex,
1542  int priority) {
1543  auto plugin_array = PluginLoader::getInstance()->GetPlugInArray();
1544  for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
1545  PlugInContainer* pic = plugin_array->Item(i);
1546  if (pic->m_enabled && pic->m_init_state) {
1547  if (pic->m_cap_flag & WANTS_OPENGL_OVERLAY_CALLBACK) {
1548  PlugIn_ViewPort pivp = CreatePlugInViewport(vp);
1549 
1550  switch (pic->m_api_version) {
1551  case 107: {
1552  if (priority > 0) break;
1553  opencpn_plugin_17* ppi =
1554  dynamic_cast<opencpn_plugin_17*>(pic->m_pplugin);
1555  if (ppi) ppi->RenderGLOverlay(pcontext, &pivp);
1556  break;
1557  }
1558 
1559  case 108:
1560  case 109:
1561  case 110:
1562  case 111:
1563  case 112:
1564  case 113:
1565  case 114:
1566  case 115: {
1567  if (priority > 0) break;
1568  opencpn_plugin_18* ppi =
1569  dynamic_cast<opencpn_plugin_18*>(pic->m_pplugin);
1570  if (ppi) ppi->RenderGLOverlay(pcontext, &pivp);
1571  break;
1572  }
1573  case 116:
1574  case 117: {
1575  if (priority > 0) break;
1576  opencpn_plugin_18* ppi =
1577  dynamic_cast<opencpn_plugin_18*>(pic->m_pplugin);
1578  if (ppi) {
1579  ppi->RenderGLOverlay(pcontext, &pivp);
1580  }
1581  opencpn_plugin_116* ppi116 =
1582  dynamic_cast<opencpn_plugin_116*>(pic->m_pplugin);
1583  if (ppi116) {
1584  ppi116->RenderGLOverlayMultiCanvas(pcontext, &pivp, canvasIndex);
1585  }
1586  break;
1587  }
1588  case 118: {
1589  if (priority <= 0) {
1590  opencpn_plugin_18* ppi =
1591  dynamic_cast<opencpn_plugin_18*>(pic->m_pplugin);
1592  if (ppi) {
1593  ppi->RenderGLOverlay(pcontext, &pivp);
1594  }
1595  }
1596  opencpn_plugin_118* ppi118 =
1597  dynamic_cast<opencpn_plugin_118*>(pic->m_pplugin);
1598  if (ppi118) {
1599  ppi118->RenderGLOverlayMultiCanvas(pcontext, &pivp, canvasIndex,
1600  priority);
1601  }
1602  break;
1603  }
1604  default:
1605  break;
1606  }
1607  }
1608  }
1609  }
1610 
1611  return true;
1612 }
1613 
1614 bool PlugInManager::SendMouseEventToPlugins(wxMouseEvent& event) {
1615  bool bret = false;
1616  auto plugin_array = PluginLoader::getInstance()->GetPlugInArray();
1617  for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
1618  PlugInContainer* pic = plugin_array->Item(i);
1619  if (pic->m_enabled && pic->m_init_state) {
1620  if (pic->m_cap_flag & WANTS_MOUSE_EVENTS) {
1621  switch (pic->m_api_version) {
1622  case 112:
1623  case 113:
1624  case 114:
1625  case 115:
1626  case 116:
1627  case 117:
1628  case 118: {
1629  opencpn_plugin_112* ppi =
1630  dynamic_cast<opencpn_plugin_112*>(pic->m_pplugin);
1631  if (ppi)
1632  if (ppi->MouseEventHook(event)) bret = true;
1633  break;
1634  }
1635  default:
1636  break;
1637  }
1638  }
1639  }
1640  }
1641 
1642  return bret;
1643 }
1644 
1645 bool PlugInManager::SendKeyEventToPlugins(wxKeyEvent& event) {
1646  bool bret = false;
1647  auto plugin_array = PluginLoader::getInstance()->GetPlugInArray();
1648  for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
1649  PlugInContainer* pic = plugin_array->Item(i);
1650  if (pic->m_enabled && pic->m_init_state) {
1651  if (pic->m_cap_flag & WANTS_KEYBOARD_EVENTS) {
1652  {
1653  switch (pic->m_api_version) {
1654  case 113:
1655  case 114:
1656  case 115:
1657  case 116:
1658  case 117:
1659  case 118: {
1660  opencpn_plugin_113* ppi =
1661  dynamic_cast<opencpn_plugin_113*>(pic->m_pplugin);
1662  if (ppi && ppi->KeyboardEventHook(event)) bret = true;
1663  break;
1664  }
1665  default:
1666  break;
1667  }
1668  }
1669  }
1670  }
1671  }
1672 
1673  return bret;
1674 }
1675 
1676 void PlugInManager::SendViewPortToRequestingPlugIns(ViewPort& vp) {
1677  auto plugin_array = PluginLoader::getInstance()->GetPlugInArray();
1678  for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
1679  PlugInContainer* pic = plugin_array->Item(i);
1680  if (pic->m_enabled && pic->m_init_state) {
1681  if (pic->m_cap_flag & WANTS_ONPAINT_VIEWPORT) {
1682  PlugIn_ViewPort pivp = CreatePlugInViewport(vp);
1683  if (pic->m_pplugin) pic->m_pplugin->SetCurrentViewPort(pivp);
1684  }
1685  }
1686  }
1687 }
1688 
1689 void PlugInManager::SendCursorLatLonToAllPlugIns(double lat, double lon) {
1690  auto plugin_array = PluginLoader::getInstance()->GetPlugInArray();
1691  for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
1692  PlugInContainer* pic = plugin_array->Item(i);
1693  if (pic->m_enabled && pic->m_init_state) {
1694  if (pic->m_cap_flag & WANTS_CURSOR_LATLON)
1695  if (pic->m_pplugin) pic->m_pplugin->SetCursorLatLon(lat, lon);
1696  }
1697  }
1698 }
1699 
1700 void NotifySetupOptionsPlugin(const PlugInData* pd) {
1701  PluginLoader::getInstance()->NotifySetupOptionsPlugin(pd);
1702 }
1703 
1704 void PlugInManager::NotifySetupOptions() {
1705  auto plugin_array = PluginLoader::getInstance()->GetPlugInArray();
1706  for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
1707  PlugInContainer* pic = plugin_array->Item(i);
1708  NotifySetupOptionsPlugin(pic);
1709  }
1710 }
1711 
1712 void PlugInManager::ClosePlugInPanel(const PlugInContainer* pic,
1713  int ok_apply_cancel) {
1714  if (pic->m_enabled && pic->m_init_state) {
1715  if ((pic->m_cap_flag & INSTALLS_TOOLBOX_PAGE) && pic->m_toolbox_panel) {
1716  pic->m_pplugin->OnCloseToolboxPanel(0, ok_apply_cancel);
1717  auto loader = PluginLoader::getInstance();
1718  loader->SetToolboxPanel(pic->m_common_name, false);
1719  }
1720  }
1721 }
1722 
1723 void PlugInManager::CloseAllPlugInPanels(int ok_apply_cancel) {
1724  auto plugin_array = PluginLoader::getInstance()->GetPlugInArray();
1725  for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
1726  PlugInContainer* pic = plugin_array->Item(i);
1727  if (pic) {
1728  ClosePlugInPanel(pic, ok_apply_cancel);
1729  }
1730  }
1731 }
1732 
1733 int PlugInManager::AddCanvasContextMenuItem(wxMenuItem* pitem,
1734  opencpn_plugin* pplugin,
1735  const char* name) {
1737  pmic->pmenu_item = pitem;
1738  pmic->m_pplugin = pplugin;
1739  pmic->id = pitem->GetId() == wxID_SEPARATOR ? wxID_SEPARATOR
1740  : m_plugin_menu_item_id_next;
1741  pmic->b_viz = true;
1742  pmic->b_grey = false;
1743  pmic->m_in_menu = name;
1744 
1745  m_PlugInMenuItems.Add(pmic);
1746 
1747  m_plugin_menu_item_id_next++;
1748 
1749  return pmic->id;
1750 }
1751 
1752 void PlugInManager::RemoveCanvasContextMenuItem(int item, const char* name) {
1753  for (unsigned int i = 0; i < m_PlugInMenuItems.GetCount(); i++) {
1754  PlugInMenuItemContainer* pimis = m_PlugInMenuItems[i];
1755  {
1756  if (pimis->id == item && !strcmp(name, pimis->m_in_menu)) {
1757  m_PlugInMenuItems.Remove(pimis);
1758  delete pimis;
1759  break;
1760  }
1761  }
1762  }
1763 }
1764 
1765 void PlugInManager::SetCanvasContextMenuItemViz(int item, bool viz,
1766  const char* name) {
1767  for (unsigned int i = 0; i < m_PlugInMenuItems.GetCount(); i++) {
1768  PlugInMenuItemContainer* pimis = m_PlugInMenuItems[i];
1769  {
1770  if (pimis->id == item && !strcmp(name, pimis->m_in_menu)) {
1771  pimis->b_viz = viz;
1772  break;
1773  }
1774  }
1775  }
1776 }
1777 
1778 void PlugInManager::SetCanvasContextMenuItemGrey(int item, bool grey,
1779  const char* name) {
1780  for (unsigned int i = 0; i < m_PlugInMenuItems.GetCount(); i++) {
1781  PlugInMenuItemContainer* pimis = m_PlugInMenuItems[i];
1782  {
1783  if (pimis->id == item && !strcmp(name, pimis->m_in_menu)) {
1784  pimis->b_grey = grey;
1785  break;
1786  }
1787  }
1788  }
1789 }
1790 
1791 void PlugInManager::SendNMEASentenceToAllPlugIns(const wxString& sentence) {
1792  wxString decouple_sentence(
1793  sentence); // decouples 'const wxString &' and 'wxString &' to keep bin
1794  // compat for plugins
1795 #ifndef __WXMSW__
1796  // Set up a framework to catch (some) sigsegv faults from plugins.
1797  sigaction(SIGSEGV, NULL, &sa_all_PIM_previous); // save existing
1798  // action for this signal
1799  struct sigaction temp;
1800  sigaction(SIGSEGV, NULL, &temp); // inspect existing action for this signal
1801 
1802  temp.sa_handler = catch_signals_PIM; // point to my handler
1803  sigemptyset(&temp.sa_mask); // make the blocking set
1804  // empty, so that all
1805  // other signals will be
1806  // unblocked during my handler
1807  temp.sa_flags = 0;
1808  sigaction(SIGSEGV, &temp, NULL);
1809 #endif
1810  auto plugin_array = PluginLoader::getInstance()->GetPlugInArray();
1811  for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
1812  PlugInContainer* pic = plugin_array->Item(i);
1813  if (pic->m_enabled && pic->m_init_state) {
1814  if (pic->m_cap_flag & WANTS_NMEA_SENTENCES) {
1815 #ifndef __WXMSW__
1816  if (sigsetjmp(env_PIM, 1)) {
1817  // Something in the "else" code block faulted.
1818  // Probably safest to assume that all variables in this method are
1819  // trash... So, simply clean up and return.
1820  sigaction(SIGSEGV, &sa_all_PIM_previous, NULL);
1821  // reset signal handler
1822  return;
1823  } else
1824 #endif
1825  {
1826  // volatile int *x = 0;
1827  //*x = 0;
1828  if (pic->m_pplugin)
1829  pic->m_pplugin->SetNMEASentence(decouple_sentence);
1830  }
1831  }
1832  }
1833  }
1834 
1835 #ifndef __WXMSW__
1836  sigaction(SIGSEGV, &sa_all_PIM_previous, NULL); // reset signal handler
1837 #endif
1838 }
1839 
1840 int PlugInManager::GetJSONMessageTargetCount() {
1841  int rv = 0;
1842  auto plugin_array = PluginLoader::getInstance()->GetPlugInArray();
1843  for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
1844  PlugInContainer* pic = plugin_array->Item(i);
1845  if (pic->m_enabled && pic->m_init_state &&
1846  (pic->m_cap_flag & WANTS_PLUGIN_MESSAGING))
1847  rv++;
1848  }
1849  return rv;
1850 }
1851 
1852 void PlugInManager::SendJSONMessageToAllPlugins(const wxString& message_id,
1853  wxJSONValue v) {
1854  wxJSONWriter w;
1855  wxString out;
1856  w.Write(v, out);
1857  SendMessageToAllPlugins(message_id, out);
1858  wxLogDebug(message_id);
1859  wxLogDebug(out);
1860 }
1861 
1862 void PlugInManager::SendMessageToAllPlugins(const wxString& message_id,
1863  const wxString& message_body) {
1864  g_lastPluginMessage = message_body;
1865 
1866  wxString decouple_message_id(
1867  message_id); // decouples 'const wxString &' and 'wxString &' to keep bin
1868  // compat for plugins
1869  wxString decouple_message_body(
1870  message_body); // decouples 'const wxString &' and 'wxString &' to keep
1871  // bin compat for plugins
1872  auto plugin_array = PluginLoader::getInstance()->GetPlugInArray();
1873  for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
1874  PlugInContainer* pic = plugin_array->Item(i);
1875  if (pic->m_enabled && pic->m_init_state) {
1876  if (pic->m_cap_flag & WANTS_PLUGIN_MESSAGING) {
1877  switch (pic->m_api_version) {
1878  case 106: {
1879  opencpn_plugin_16* ppi =
1880  dynamic_cast<opencpn_plugin_16*>(pic->m_pplugin);
1881  if (ppi)
1882  ppi->SetPluginMessage(decouple_message_id, decouple_message_body);
1883  break;
1884  }
1885  case 107: {
1886  opencpn_plugin_17* ppi =
1887  dynamic_cast<opencpn_plugin_17*>(pic->m_pplugin);
1888  if (ppi)
1889  ppi->SetPluginMessage(decouple_message_id, decouple_message_body);
1890  break;
1891  }
1892  case 108:
1893  case 109:
1894  case 110:
1895  case 111:
1896  case 112:
1897  case 113:
1898  case 114:
1899  case 115:
1900  case 116:
1901  case 117:
1902  case 118: {
1903  opencpn_plugin_18* ppi =
1904  dynamic_cast<opencpn_plugin_18*>(pic->m_pplugin);
1905  if (ppi)
1906  ppi->SetPluginMessage(decouple_message_id, decouple_message_body);
1907  break;
1908  }
1909  default:
1910  break;
1911  }
1912  }
1913  }
1914  }
1915 }
1916 
1917 void PlugInManager::SendAISSentenceToAllPlugIns(const wxString& sentence) {
1918  wxString decouple_sentence(
1919  sentence); // decouples 'const wxString &' and 'wxString &' to keep bin
1920  // compat for plugins
1921  auto plugin_array = PluginLoader::getInstance()->GetPlugInArray();
1922  for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
1923  PlugInContainer* pic = plugin_array->Item(i);
1924  if (pic->m_enabled && pic->m_init_state) {
1925  if (pic->m_cap_flag & WANTS_AIS_SENTENCES)
1926  pic->m_pplugin->SetAISSentence(decouple_sentence);
1927  }
1928  }
1929 }
1930 
1931 void PlugInManager::SendPositionFixToAllPlugIns(GenericPosDatEx* ppos) {
1932  // Send basic position fix
1933  PlugIn_Position_Fix pfix;
1934  pfix.Lat = ppos->kLat;
1935  pfix.Lon = ppos->kLon;
1936  pfix.Cog = ppos->kCog;
1937  pfix.Sog = ppos->kSog;
1938  pfix.Var = ppos->kVar;
1939  pfix.FixTime = ppos->FixTime;
1940  pfix.nSats = ppos->nSats;
1941 
1942  auto plugin_array = PluginLoader::getInstance()->GetPlugInArray();
1943  for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
1944  PlugInContainer* pic = plugin_array->Item(i);
1945  if (pic->m_enabled && pic->m_init_state) {
1946  if (pic->m_cap_flag & WANTS_NMEA_EVENTS)
1947  if (pic->m_pplugin) pic->m_pplugin->SetPositionFix(pfix);
1948  }
1949  }
1950 
1951  // Send extended position fix to PlugIns at API 108 and later
1952  PlugIn_Position_Fix_Ex pfix_ex;
1953  pfix_ex.Lat = ppos->kLat;
1954  pfix_ex.Lon = ppos->kLon;
1955  pfix_ex.Cog = ppos->kCog;
1956  pfix_ex.Sog = ppos->kSog;
1957  pfix_ex.Var = ppos->kVar;
1958  pfix_ex.FixTime = ppos->FixTime;
1959  pfix_ex.nSats = ppos->nSats;
1960  pfix_ex.Hdt = ppos->kHdt;
1961  pfix_ex.Hdm = ppos->kHdm;
1962 
1963  for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
1964  PlugInContainer* pic = plugin_array->Item(i);
1965  if (pic->m_enabled && pic->m_init_state) {
1966  if (pic->m_cap_flag & WANTS_NMEA_EVENTS) {
1967  switch (pic->m_api_version) {
1968  case 108:
1969  case 109:
1970  case 110:
1971  case 111:
1972  case 112:
1973  case 113:
1974  case 114:
1975  case 115:
1976  case 116:
1977  case 117:
1978  case 118: {
1979  opencpn_plugin_18* ppi =
1980  dynamic_cast<opencpn_plugin_18*>(pic->m_pplugin);
1981  if (ppi) ppi->SetPositionFixEx(pfix_ex);
1982  break;
1983  }
1984  default:
1985  break;
1986  }
1987  }
1988  }
1989  }
1990 }
1991 
1992 void PlugInManager::SendActiveLegInfoToAllPlugIns(
1993  const ActiveLegDat* leg_info) {
1995  leg.Btw = leg_info->Btw;
1996  leg.Dtw = leg_info->Dtw;
1997  leg.wp_name = leg_info->wp_name;
1998  leg.Xte = leg_info->Xte;
1999  leg.arrival = leg_info->arrival;
2000  auto plugin_array = PluginLoader::getInstance()->GetPlugInArray();
2001  for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
2002  PlugInContainer* pic = plugin_array->Item(i);
2003  if (pic->m_enabled && pic->m_init_state) {
2004  if (pic->m_cap_flag & WANTS_NMEA_EVENTS) {
2005  switch (pic->m_api_version) {
2006  case 108:
2007  case 109:
2008  case 110:
2009  case 111:
2010  case 112:
2011  case 113:
2012  case 114:
2013  case 115:
2014  case 116:
2015  break;
2016  case 117:
2017  case 118: {
2018  opencpn_plugin_117* ppi =
2019  dynamic_cast<opencpn_plugin_117*>(pic->m_pplugin);
2020  if (ppi) ppi->SetActiveLegInfo(leg);
2021  break;
2022  }
2023  default:
2024  break;
2025  }
2026  }
2027  }
2028  }
2029 }
2030 
2031 void PlugInManager::SendResizeEventToAllPlugIns(int x, int y) {
2032  auto plugin_array = PluginLoader::getInstance()->GetPlugInArray();
2033  for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
2034  PlugInContainer* pic = plugin_array->Item(i);
2035  if (pic->m_enabled && pic->m_init_state)
2036  pic->m_pplugin->ProcessParentResize(x, y);
2037  }
2038 }
2039 
2040 void PlugInManager::SetColorSchemeForAllPlugIns(ColorScheme cs) {
2041  auto plugin_array = PluginLoader::getInstance()->GetPlugInArray();
2042  for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
2043  PlugInContainer* pic = plugin_array->Item(i);
2044  if (pic->m_enabled && pic->m_init_state)
2045  pic->m_pplugin->SetColorScheme((PI_ColorScheme)cs);
2046  }
2047 }
2048 
2049 void PlugInManager::PrepareAllPluginContextMenus() {
2050  int canvasIndex = gFrame->GetCanvasIndexUnderMouse();
2051  if (canvasIndex < 0) return;
2052 
2053  auto plugin_array = PluginLoader::getInstance()->GetPlugInArray();
2054  for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
2055  PlugInContainer* pic = plugin_array->Item(i);
2056  if (pic->m_enabled && pic->m_init_state) {
2057  if (pic->m_cap_flag & INSTALLS_CONTEXTMENU_ITEMS) {
2058  switch (pic->m_api_version) {
2059  case 116:
2060  case 117:
2061  case 118: {
2062  opencpn_plugin_116* ppi =
2063  dynamic_cast<opencpn_plugin_116*>(pic->m_pplugin);
2064  if (ppi) ppi->PrepareContextMenu(canvasIndex);
2065  break;
2066  }
2067  default:
2068  break;
2069  }
2070  }
2071  }
2072  }
2073 }
2074 
2075 void PlugInManager::SendSKConfigToAllPlugIns() {
2076  // Send the current ownship MMSI, encoded as sK, to all PlugIns
2077  wxJSONValue v;
2078  v[_T("self")] = g_ownshipMMSI_SK;
2079  wxJSONWriter w;
2080  wxString out;
2081  w.Write(v, out);
2082  SendMessageToAllPlugins(wxString(_T("OCPN_CORE_SIGNALK")), out);
2083 }
2084 
2085 void PlugInManager::SendBaseConfigToAllPlugIns() {
2086  // Send the current run-time configuration to all PlugIns
2087  wxJSONValue v;
2088  v[_T("OpenCPN Version Major")] = VERSION_MAJOR;
2089  v[_T("OpenCPN Version Minor")] = VERSION_MINOR;
2090  v[_T("OpenCPN Version Patch")] = VERSION_PATCH;
2091  v[_T("OpenCPN Version Date")] = VERSION_DATE;
2092  v[_T("OpenCPN Version Full")] = VERSION_FULL;
2093 
2094  // Some useful display metrics
2095  if (g_MainToolbar) {
2096  v[_T("OpenCPN Toolbar Width")] = g_MainToolbar->GetToolbarRect().width;
2097  v[_T("OpenCPN Toolbar Height")] = g_MainToolbar->GetToolbarRect().height;
2098  v[_T("OpenCPN Toolbar PosnX")] = g_MainToolbar->GetToolbarRect().x;
2099  v[_T("OpenCPN Toolbar PosnY")] = g_MainToolbar->GetToolbarRect().y;
2100  }
2101 
2102  // Some rendering parameters
2103  v[_T("OpenCPN Zoom Mod Vector")] = g_chart_zoom_modifier_vector;
2104  v[_T("OpenCPN Zoom Mod Other")] = g_chart_zoom_modifier_raster;
2105  v[_T("OpenCPN Scale Factor Exp")] =
2106  g_Platform->GetChartScaleFactorExp(g_ChartScaleFactor);
2107  v[_T("OpenCPN Display Width")] = (int)g_display_size_mm;
2108  v[_T("OpenCPN Content Scale Factor")] = OCPN_GetDisplayContentScaleFactor();
2109  v[_T("OpenCPN Display DIP Scale Factor")] = OCPN_GetWinDIPScaleFactor();
2110 
2111  wxJSONWriter w;
2112  wxString out;
2113  w.Write(v, out);
2114  SendMessageToAllPlugins(wxString(_T("OpenCPN Config")), out);
2115 }
2116 
2117 void PlugInManager::SendS52ConfigToAllPlugIns(bool bReconfig) {
2118  // Send the current run-time configuration to all PlugIns
2119  wxJSONValue v;
2120  v[_T("OpenCPN Version Major")] = VERSION_MAJOR;
2121  v[_T("OpenCPN Version Minor")] = VERSION_MINOR;
2122  v[_T("OpenCPN Version Patch")] = VERSION_PATCH;
2123  v[_T("OpenCPN Version Date")] = VERSION_DATE;
2124  v[_T("OpenCPN Version Full")] = VERSION_FULL;
2125 
2126  // S52PLIB state
2127  if (ps52plib) {
2128  // v[_T("OpenCPN S52PLIB ShowText")] = ps52plib->GetShowS57Text();
2129  // v[_T("OpenCPN S52PLIB ShowSoundings")] =
2130  // ps52plib->GetShowSoundings(); v[_T("OpenCPN S52PLIB ShowLights")]
2131  // = !ps52plib->GetLightsOff();
2132  v[_T("OpenCPN S52PLIB ShowAnchorConditions")] = ps52plib->GetAnchorOn();
2133  v[_T("OpenCPN S52PLIB ShowQualityOfData")] = ps52plib->GetQualityOfData();
2134  // v[_T("OpenCPN S52PLIB DisplayCategory")] =
2135  // ps52plib->GetDisplayCategory();
2136 
2137  // Global parameters
2138  v[_T("OpenCPN S52PLIB MetaDisplay")] = ps52plib->m_bShowMeta;
2139  v[_T("OpenCPN S52PLIB DeclutterText")] = ps52plib->m_bDeClutterText;
2140  v[_T("OpenCPN S52PLIB ShowNationalText")] = ps52plib->m_bShowNationalTexts;
2141  v[_T("OpenCPN S52PLIB ShowImportantTextOnly")] =
2142  ps52plib->m_bShowS57ImportantTextOnly;
2143  v[_T("OpenCPN S52PLIB UseSCAMIN")] = ps52plib->m_bUseSCAMIN;
2144  v[_T("OpenCPN S52PLIB UseSUPER_SCAMIN")] = ps52plib->m_bUseSUPER_SCAMIN;
2145  v[_T("OpenCPN S52PLIB SymbolStyle")] = ps52plib->m_nSymbolStyle;
2146  v[_T("OpenCPN S52PLIB BoundaryStyle")] = ps52plib->m_nBoundaryStyle;
2147  v[_T("OpenCPN S52PLIB ColorShades")] =
2148  S52_getMarinerParam(S52_MAR_TWO_SHADES);
2149  v[_T("OpenCPN S52PLIB Safety Depth")] =
2150  (double)S52_getMarinerParam(S52_MAR_SAFETY_DEPTH);
2151  v[_T("OpenCPN S52PLIB Shallow Contour")] =
2152  (double)S52_getMarinerParam(S52_MAR_SHALLOW_CONTOUR);
2153  v[_T("OpenCPN S52PLIB Deep Contour")] =
2154  (double)S52_getMarinerParam(S52_MAR_DEEP_CONTOUR);
2155  }
2156 
2157  // Notify plugins that S52PLIB may have reconfigured global options
2158  v[_T("OpenCPN S52PLIB GlobalReconfig")] = bReconfig;
2159 
2160  wxJSONWriter w;
2161  wxString out;
2162  w.Write(v, out);
2163  SendMessageToAllPlugins(wxString(_T("OpenCPN Config")), out);
2164 }
2165 
2166 void PlugInManager::NotifyAuiPlugIns(void) {
2167  auto plugin_array = PluginLoader::getInstance()->GetPlugInArray();
2168  for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
2169  PlugInContainer* pic = plugin_array->Item(i);
2170  if (pic->m_enabled && pic->m_init_state &&
2171  (pic->m_cap_flag & USES_AUI_MANAGER))
2172  pic->m_pplugin->UpdateAuiStatus();
2173  }
2174 }
2175 
2176 int PlugInManager::AddToolbarTool(wxString label, wxBitmap* bitmap,
2177  wxBitmap* bmpRollover, wxItemKind kind,
2178  wxString shortHelp, wxString longHelp,
2179  wxObject* clientData, int position,
2180  int tool_sel, opencpn_plugin* pplugin) {
2182  pttc->label = label;
2183 
2184  if (!bitmap->IsOk()) {
2185  ocpnStyle::Style* style = g_StyleManager->GetCurrentStyle();
2186  pttc->bitmap_day = new wxBitmap(style->GetIcon(_T("default_pi")));
2187  } else {
2188  // Force a non-reference copy of the bitmap from the PlugIn
2189  pttc->bitmap_day = new wxBitmap(*bitmap);
2190  pttc->bitmap_day->UnShare();
2191  }
2192 
2193  if (!bmpRollover->IsOk()) {
2194  ocpnStyle::Style* style = g_StyleManager->GetCurrentStyle();
2195  pttc->bitmap_Rollover_day = new wxBitmap(style->GetIcon(_T("default_pi")));
2196  } else {
2197  // Force a non-reference copy of the bitmap from the PlugIn
2198  pttc->bitmap_Rollover_day = new wxBitmap(*bmpRollover);
2199  pttc->bitmap_Rollover_day->UnShare();
2200  }
2201 
2202  pttc->bitmap_dusk = BuildDimmedToolBitmap(pttc->bitmap_day, 128);
2203  pttc->bitmap_night = BuildDimmedToolBitmap(pttc->bitmap_day, 32);
2204  pttc->bitmap_Rollover_dusk =
2205  BuildDimmedToolBitmap(pttc->bitmap_Rollover_day, 128);
2206  pttc->bitmap_Rollover_night =
2207  BuildDimmedToolBitmap(pttc->bitmap_Rollover_day, 32);
2208 
2209  pttc->kind = kind;
2210  pttc->shortHelp = shortHelp;
2211  pttc->longHelp = longHelp;
2212  pttc->clientData = clientData;
2213  pttc->position = position;
2214  pttc->m_pplugin = pplugin;
2215  pttc->tool_sel = tool_sel;
2216  pttc->b_viz = true;
2217  pttc->b_toggle = false;
2218  pttc->id = m_plugin_tool_id_next;
2219 
2220  m_PlugInToolbarTools.Add(pttc);
2221 
2222  m_plugin_tool_id_next++;
2223 
2224  return pttc->id;
2225 }
2226 
2227 int PlugInManager::AddToolbarTool(wxString label, wxString SVGfile,
2228  wxString SVGRolloverfile,
2229  wxString SVGToggledfile, wxItemKind kind,
2230  wxString shortHelp, wxString longHelp,
2231  wxObject* clientData, int position,
2232  int tool_sel, opencpn_plugin* pplugin) {
2234  pttc->label = label;
2235 
2236  pttc->pluginNormalIconSVG = SVGfile;
2237  pttc->pluginRolloverIconSVG = SVGRolloverfile;
2238  pttc->pluginToggledIconSVG = SVGToggledfile;
2239 
2240  // Build a set of bitmaps based on the generic "puzzle piece" icon,
2241  // In case there is some problem with the SVG file(s) specified.
2242  ocpnStyle::Style* style = g_StyleManager->GetCurrentStyle();
2243  pttc->bitmap_day = new wxBitmap(style->GetIcon(_T("default_pi")));
2244  pttc->bitmap_dusk = BuildDimmedToolBitmap(pttc->bitmap_day, 128);
2245  pttc->bitmap_night = BuildDimmedToolBitmap(pttc->bitmap_day, 32);
2246  pttc->bitmap_Rollover_day = new wxBitmap(*pttc->bitmap_day);
2247  pttc->bitmap_Rollover_dusk =
2248  BuildDimmedToolBitmap(pttc->bitmap_Rollover_day, 128);
2249  pttc->bitmap_Rollover_night =
2250  BuildDimmedToolBitmap(pttc->bitmap_Rollover_day, 32);
2251 
2252  pttc->kind = kind;
2253  pttc->shortHelp = shortHelp;
2254  pttc->longHelp = longHelp;
2255  pttc->clientData = clientData;
2256  pttc->position = position;
2257  pttc->m_pplugin = pplugin;
2258  pttc->tool_sel = tool_sel;
2259  pttc->b_viz = true;
2260  pttc->b_toggle = false;
2261  pttc->id = m_plugin_tool_id_next;
2262 
2263  m_PlugInToolbarTools.Add(pttc);
2264 
2265  m_plugin_tool_id_next++;
2266 
2267  return pttc->id;
2268 }
2269 
2270 void PlugInManager::RemoveToolbarTool(int tool_id) {
2271  for (unsigned int i = 0; i < m_PlugInToolbarTools.GetCount(); i++) {
2272  PlugInToolbarToolContainer* pttc = m_PlugInToolbarTools[i];
2273  {
2274  if (pttc->id == tool_id) {
2275  m_PlugInToolbarTools.Remove(pttc);
2276  delete pttc;
2277  break;
2278  }
2279  }
2280  }
2281  pParent->RequestNewToolbars();
2282 }
2283 
2284 void PlugInManager::SetToolbarToolViz(int item, bool viz) {
2285  for (unsigned int i = 0; i < m_PlugInToolbarTools.GetCount(); i++) {
2286  PlugInToolbarToolContainer* pttc = m_PlugInToolbarTools[i];
2287  {
2288  if (pttc->id == item) {
2289  pttc->b_viz = viz;
2290  pParent->RequestNewToolbars(); // Apply the change
2291  break;
2292  }
2293  }
2294  }
2295 }
2296 
2297 void PlugInManager::SetToolbarItemState(int item, bool toggle) {
2298  for (unsigned int i = 0; i < m_PlugInToolbarTools.GetCount(); i++) {
2299  PlugInToolbarToolContainer* pttc = m_PlugInToolbarTools[i];
2300  {
2301  if (pttc->id == item) {
2302  pttc->b_toggle = toggle;
2303  pParent->SetMasterToolbarItemState(item, toggle);
2304  break;
2305  }
2306  }
2307  }
2308 }
2309 
2310 void PlugInManager::SetToolbarItemBitmaps(int item, wxBitmap* bitmap,
2311  wxBitmap* bmpRollover) {
2312  for (unsigned int i = 0; i < m_PlugInToolbarTools.GetCount(); i++) {
2313  PlugInToolbarToolContainer* pttc = m_PlugInToolbarTools[i];
2314  {
2315  if (pttc->id == item) {
2316  delete pttc->bitmap_day;
2317  delete pttc->bitmap_dusk;
2318  delete pttc->bitmap_night;
2319  delete pttc->bitmap_Rollover_day;
2320 
2321  if (!bitmap->IsOk()) {
2322  ocpnStyle::Style* style = g_StyleManager->GetCurrentStyle();
2323  pttc->bitmap_day = new wxBitmap(style->GetIcon(_T("default_pi")));
2324  } else {
2325  // Force a non-reference copy of the bitmap from the PlugIn
2326  pttc->bitmap_day = new wxBitmap(*bitmap);
2327  pttc->bitmap_day->UnShare();
2328  }
2329 
2330  if (!bmpRollover->IsOk()) {
2331  ocpnStyle::Style* style = g_StyleManager->GetCurrentStyle();
2332  pttc->bitmap_Rollover_day =
2333  new wxBitmap(style->GetIcon(_T("default_pi")));
2334  } else {
2335  // Force a non-reference copy of the bitmap from the PlugIn
2336  pttc->bitmap_Rollover_day = new wxBitmap(*bmpRollover);
2337  pttc->bitmap_Rollover_day->UnShare();
2338  }
2339 
2340  pttc->bitmap_dusk = BuildDimmedToolBitmap(pttc->bitmap_day, 128);
2341  pttc->bitmap_night = BuildDimmedToolBitmap(pttc->bitmap_day, 32);
2342 
2343  pParent->SetToolbarItemBitmaps(item, pttc->bitmap_day,
2344  pttc->bitmap_Rollover_day);
2345  break;
2346  }
2347  }
2348  }
2349 }
2350 
2351 void PlugInManager::SetToolbarItemBitmaps(int item, wxString SVGfile,
2352  wxString SVGfileRollover,
2353  wxString SVGfileToggled) {
2354  for (unsigned int i = 0; i < m_PlugInToolbarTools.GetCount(); i++) {
2355  PlugInToolbarToolContainer* pttc = m_PlugInToolbarTools[i];
2356  {
2357  if (pttc->id == item) {
2358  pttc->pluginNormalIconSVG = SVGfile;
2359  pttc->pluginRolloverIconSVG = SVGfileRollover;
2360  pttc->pluginToggledIconSVG = SVGfileToggled;
2361  pParent->SetToolbarItemSVG(item, pttc->pluginNormalIconSVG,
2362  pttc->pluginRolloverIconSVG,
2363  pttc->pluginToggledIconSVG);
2364  break;
2365  }
2366  }
2367  }
2368 }
2369 
2370 opencpn_plugin* PlugInManager::FindToolOwner(const int id) {
2371  for (unsigned int i = 0; i < m_PlugInToolbarTools.GetCount(); i++) {
2372  PlugInToolbarToolContainer* pc = m_PlugInToolbarTools[i];
2373  if (id == pc->id) return pc->m_pplugin;
2374  }
2375 
2376  return NULL;
2377 }
2378 
2379 wxString PlugInManager::GetToolOwnerCommonName(const int id) {
2380  opencpn_plugin* ppi = FindToolOwner(id);
2381  if (ppi) {
2382  auto plugin_array = PluginLoader::getInstance()->GetPlugInArray();
2383  for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
2384  PlugInContainer* pic = plugin_array->Item(i);
2385  if (pic && (pic->m_pplugin == ppi)) return pic->m_common_name;
2386  }
2387  }
2388 
2389  return wxEmptyString;
2390 }
2391 
2392 wxString PlugInManager::GetLastError() { return m_last_error_string; }
2393 
2394 wxBitmap* PlugInManager::BuildDimmedToolBitmap(wxBitmap* pbmp_normal,
2395  unsigned char dim_ratio) {
2396  wxImage img_dup = pbmp_normal->ConvertToImage();
2397 
2398  if (!img_dup.IsOk()) return NULL;
2399 
2400  if (dim_ratio < 200) {
2401  // Create a dimmed version of the image/bitmap
2402  int gimg_width = img_dup.GetWidth();
2403  int gimg_height = img_dup.GetHeight();
2404 
2405  double factor = (double)(dim_ratio) / 256.0;
2406 
2407  for (int iy = 0; iy < gimg_height; iy++) {
2408  for (int ix = 0; ix < gimg_width; ix++) {
2409  if (!img_dup.IsTransparent(ix, iy)) {
2410  wxImage::RGBValue rgb(img_dup.GetRed(ix, iy),
2411  img_dup.GetGreen(ix, iy),
2412  img_dup.GetBlue(ix, iy));
2413  wxImage::HSVValue hsv = wxImage::RGBtoHSV(rgb);
2414  hsv.value = hsv.value * factor;
2415  wxImage::RGBValue nrgb = wxImage::HSVtoRGB(hsv);
2416  img_dup.SetRGB(ix, iy, nrgb.red, nrgb.green, nrgb.blue);
2417  }
2418  }
2419  }
2420  }
2421  // Make a bitmap
2422  wxBitmap* ptoolBarBitmap;
2423 
2424 #ifdef __WXMSW__
2425  wxBitmap tbmp(img_dup.GetWidth(), img_dup.GetHeight(), -1);
2426  wxMemoryDC dwxdc;
2427  dwxdc.SelectObject(tbmp);
2428 
2429  ptoolBarBitmap = new wxBitmap(img_dup, (wxDC&)dwxdc);
2430 #else
2431  ptoolBarBitmap = new wxBitmap(img_dup);
2432 #endif
2433 
2434  // store it
2435  return ptoolBarBitmap;
2436 }
2437 
2438 wxArrayString PlugInManager::GetPlugInChartClassNameArray(void) {
2439  wxArrayString array;
2440  auto plugin_array = PluginLoader::getInstance()->GetPlugInArray();
2441  for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
2442  PlugInContainer* pic = plugin_array->Item(i);
2443  if (pic && pic->m_enabled && pic->m_init_state &&
2444  ((pic->m_cap_flag & INSTALLS_PLUGIN_CHART) ||
2445  (pic->m_cap_flag & INSTALLS_PLUGIN_CHART_GL))) {
2446  wxArrayString carray = pic->m_pplugin->GetDynamicChartClassNameArray();
2447 
2448  for (unsigned int j = 0; j < carray.GetCount(); j++) {
2449  array.Add(carray[j]);
2450  }
2451  }
2452  }
2453 
2454  // Scrub the list for duplicates
2455  // Corrects a flaw in BSB4 and NVC PlugIns
2456  unsigned int j = 0;
2457  while (j < array.GetCount()) {
2458  wxString test = array[j];
2459  unsigned int k = j + 1;
2460  while (k < array.GetCount()) {
2461  if (test == array[k]) {
2462  array.RemoveAt(k);
2463  j = -1;
2464  break;
2465  } else
2466  k++;
2467  }
2468 
2469  j++;
2470  }
2471 
2472  return array;
2473 }
2474 
2475 //----------------------------------------------------------------------------------------------------------
2476 // The PlugIn CallBack API Implementation
2477 // The definitions of this API are found in ocpn_plugin.h
2478 //----------------------------------------------------------------------------------------------------------
2479 
2480 int InsertPlugInTool(wxString label, wxBitmap* bitmap, wxBitmap* bmpRollover,
2481  wxItemKind kind, wxString shortHelp, wxString longHelp,
2482  wxObject* clientData, int position, int tool_sel,
2483  opencpn_plugin* pplugin) {
2484  if (s_ppim)
2485  return s_ppim->AddToolbarTool(label, bitmap, bmpRollover, kind, shortHelp,
2486  longHelp, clientData, position, tool_sel,
2487  pplugin);
2488  else
2489  return -1;
2490 }
2491 
2492 void RemovePlugInTool(int tool_id) {
2493  if (s_ppim) s_ppim->RemoveToolbarTool(tool_id);
2494 }
2495 
2496 void SetToolbarToolViz(int item, bool viz) {
2497  if (s_ppim) s_ppim->SetToolbarToolViz(item, viz);
2498 }
2499 
2500 void SetToolbarItemState(int item, bool toggle) {
2501  if (s_ppim) s_ppim->SetToolbarItemState(item, toggle);
2502 }
2503 
2504 void SetToolbarToolBitmaps(int item, wxBitmap* bitmap, wxBitmap* bmpRollover) {
2505  if (s_ppim) s_ppim->SetToolbarItemBitmaps(item, bitmap, bmpRollover);
2506 }
2507 
2508 int InsertPlugInToolSVG(wxString label, wxString SVGfile,
2509  wxString SVGfileRollover, wxString SVGfileToggled,
2510  wxItemKind kind, wxString shortHelp, wxString longHelp,
2511  wxObject* clientData, int position, int tool_sel,
2512  opencpn_plugin* pplugin) {
2513  if (s_ppim)
2514  return s_ppim->AddToolbarTool(label, SVGfile, SVGfileRollover,
2515  SVGfileToggled, kind, shortHelp, longHelp,
2516  clientData, position, tool_sel, pplugin);
2517  else
2518  return -1;
2519 }
2520 
2521 void SetToolbarToolBitmapsSVG(int item, wxString SVGfile,
2522  wxString SVGfileRollover,
2523  wxString SVGfileToggled) {
2524  if (s_ppim)
2525  s_ppim->SetToolbarItemBitmaps(item, SVGfile, SVGfileRollover,
2526  SVGfileToggled);
2527 }
2528 
2529 int AddCanvasMenuItem(wxMenuItem* pitem, opencpn_plugin* pplugin,
2530  const char* name) {
2531  if (s_ppim)
2532  return s_ppim->AddCanvasContextMenuItem(pitem, pplugin, name);
2533  else
2534  return -1;
2535 }
2536 
2537 void SetCanvasMenuItemViz(int item, bool viz, const char* name) {
2538  if (s_ppim) s_ppim->SetCanvasContextMenuItemViz(item, viz, name);
2539 }
2540 
2541 void SetCanvasMenuItemGrey(int item, bool grey, const char* name) {
2542  if (s_ppim) s_ppim->SetCanvasContextMenuItemGrey(item, grey, name);
2543 }
2544 
2545 void RemoveCanvasMenuItem(int item, const char* name) {
2546  if (s_ppim) s_ppim->RemoveCanvasContextMenuItem(item, name);
2547 }
2548 
2549 int AddCanvasContextMenuItem(wxMenuItem* pitem, opencpn_plugin* pplugin) {
2550  /* main context popup menu */
2551  return AddCanvasMenuItem(pitem, pplugin, "");
2552 }
2553 
2554 void SetCanvasContextMenuItemViz(int item, bool viz) {
2555  SetCanvasMenuItemViz(item, viz);
2556 }
2557 
2558 void SetCanvasContextMenuItemGrey(int item, bool grey) {
2559  SetCanvasMenuItemGrey(item, grey);
2560 }
2561 
2562 void RemoveCanvasContextMenuItem(int item) { RemoveCanvasMenuItem(item); }
2563 
2564 wxFileConfig* GetOCPNConfigObject(void) {
2565  if (s_ppim)
2566  return pConfig; // return the global application config object
2567  else
2568  return NULL;
2569 }
2570 
2571 wxWindow* GetOCPNCanvasWindow() {
2572  wxWindow* pret = NULL;
2573  if (s_ppim) {
2574  MyFrame* pFrame = s_ppim->GetParentFrame();
2575  pret = (wxWindow*)pFrame->GetPrimaryCanvas();
2576  }
2577  return pret;
2578 }
2579 
2580 void RequestRefresh(wxWindow* win) {
2581  if (win) win->Refresh();
2582 }
2583 
2584 void GetCanvasPixLL(PlugIn_ViewPort* vp, wxPoint* pp, double lat, double lon) {
2585  // Make enough of an application viewport to run its method....
2586  ViewPort ocpn_vp;
2587  ocpn_vp.clat = vp->clat;
2588  ocpn_vp.clon = vp->clon;
2589  ocpn_vp.m_projection_type = vp->m_projection_type;
2590  ocpn_vp.view_scale_ppm = vp->view_scale_ppm;
2591  ocpn_vp.skew = vp->skew;
2592  ocpn_vp.rotation = vp->rotation;
2593  ocpn_vp.pix_width = vp->pix_width;
2594  ocpn_vp.pix_height = vp->pix_height;
2595 
2596  wxPoint ret = ocpn_vp.GetPixFromLL(lat, lon);
2597  pp->x = ret.x;
2598  pp->y = ret.y;
2599 }
2600 
2601 void GetDoubleCanvasPixLL(PlugIn_ViewPort* vp, wxPoint2DDouble* pp, double lat,
2602  double lon) {
2603  // Make enough of an application viewport to run its method....
2604  ViewPort ocpn_vp;
2605  ocpn_vp.clat = vp->clat;
2606  ocpn_vp.clon = vp->clon;
2607  ocpn_vp.m_projection_type = vp->m_projection_type;
2608  ocpn_vp.view_scale_ppm = vp->view_scale_ppm;
2609  ocpn_vp.skew = vp->skew;
2610  ocpn_vp.rotation = vp->rotation;
2611  ocpn_vp.pix_width = vp->pix_width;
2612  ocpn_vp.pix_height = vp->pix_height;
2613 
2614  *pp = ocpn_vp.GetDoublePixFromLL(lat, lon);
2615 }
2616 
2617 void GetCanvasLLPix(PlugIn_ViewPort* vp, wxPoint p, double* plat,
2618  double* plon) {
2619  // Make enough of an application viewport to run its method....
2620  ViewPort ocpn_vp;
2621  ocpn_vp.clat = vp->clat;
2622  ocpn_vp.clon = vp->clon;
2623  ocpn_vp.m_projection_type = vp->m_projection_type;
2624  ocpn_vp.view_scale_ppm = vp->view_scale_ppm;
2625  ocpn_vp.skew = vp->skew;
2626  ocpn_vp.rotation = vp->rotation;
2627  ocpn_vp.pix_width = vp->pix_width;
2628  ocpn_vp.pix_height = vp->pix_height;
2629 
2630  return ocpn_vp.GetLLFromPix(p, plat, plon);
2631 }
2632 
2633 bool GetGlobalColor(wxString colorName, wxColour* pcolour) {
2634  wxColour c = GetGlobalColor(colorName);
2635  *pcolour = c;
2636 
2637  return true;
2638 }
2639 
2640 wxFont* OCPNGetFont(wxString TextElement, int default_size) {
2641  return FontMgr::Get().GetFont(TextElement, default_size);
2642 }
2643 
2644 wxFont* GetOCPNScaledFont_PlugIn(wxString TextElement, int default_size) {
2645  return GetOCPNScaledFont(TextElement, default_size);
2646 }
2647 
2648 double GetOCPNGUIToolScaleFactor_PlugIn(int GUIScaleFactor) {
2649  return g_Platform->GetToolbarScaleFactor(GUIScaleFactor);
2650 }
2651 
2652 double GetOCPNGUIToolScaleFactor_PlugIn() {
2653  return g_Platform->GetToolbarScaleFactor(g_GUIScaleFactor);
2654 }
2655 
2656 float GetOCPNChartScaleFactor_Plugin() {
2657  return g_Platform->GetChartScaleFactorExp(g_ChartScaleFactor);
2658 }
2659 
2660 wxFont GetOCPNGUIScaledFont_PlugIn(wxString item) {
2661  return GetOCPNGUIScaledFont(item);
2662 }
2663 
2664 bool AddPersistentFontKey(wxString TextElement) {
2665  return FontMgr::Get().AddAuxKey(TextElement);
2666 }
2667 
2668 wxString GetActiveStyleName() {
2669  if (g_StyleManager)
2670  return g_StyleManager->GetCurrentStyle()->name;
2671  else
2672  return _T("");
2673 }
2674 
2675 wxBitmap GetBitmapFromSVGFile(wxString filename, unsigned int width,
2676  unsigned int height) {
2677  wxBitmap bmp = LoadSVG(filename, width, height);
2678 
2679  if (bmp.IsOk())
2680  return bmp;
2681  else {
2682  // On error in requested width/height parameters,
2683  // try to find and use dimensions embedded in the SVG file
2684  unsigned int w, h;
2685  SVGDocumentPixelSize(filename, w, h);
2686  if (w == 0 || h == 0) {
2687  // We did not succeed in deducing the size from SVG (svg element
2688  // x misses width, height or both attributes), let's use some "safe"
2689  // default
2690  w = 32;
2691  h = 32;
2692  }
2693  return LoadSVG(filename, w, h);
2694  }
2695 }
2696 
2697 bool IsTouchInterface_PlugIn(void) { return g_btouch; }
2698 
2699 wxColour GetFontColour_PlugIn(wxString TextElement) {
2700  return FontMgr::Get().GetFontColor(TextElement);
2701 }
2702 
2703 wxString* GetpSharedDataLocation(void) {
2704  return g_Platform->GetSharedDataDirPtr();
2705 }
2706 
2707 ArrayOfPlugIn_AIS_Targets* GetAISTargetArray(void) {
2708  if (!g_pAIS) return NULL;
2709 
2710  ArrayOfPlugIn_AIS_Targets* pret = new ArrayOfPlugIn_AIS_Targets;
2711 
2712  // Iterate over the AIS Target Hashmap
2713  for (const auto& it : g_pAIS->GetTargetList()) {
2714  auto td = it.second;
2715  PlugIn_AIS_Target* ptarget = Create_PI_AIS_Target(td.get());
2716  pret->Add(ptarget);
2717  }
2718 
2719 // Test one alarm target
2720 #if 0
2721  AisTargetData td;
2722  td.n_alarm_state = AIS_ALARM_SET;
2723  PlugIn_AIS_Target *ptarget = Create_PI_AIS_Target(&td);
2724  pret->Add(ptarget);
2725 #endif
2726  return pret;
2727 }
2728 
2729 wxAuiManager* GetFrameAuiManager(void) { return g_pauimgr; }
2730 
2731 bool AddLocaleCatalog(wxString catalog) {
2732 #if wxUSE_XLOCALE || !wxCHECK_VERSION(3, 0, 0)
2733 
2734  if (plocale_def_lang) {
2735  // Add this catalog to the persistent catalog array
2736  g_locale_catalog_array.Add(catalog);
2737 
2738  return plocale_def_lang->AddCatalog(catalog);
2739  } else
2740 #endif
2741  return false;
2742 }
2743 
2744 void PushNMEABuffer(wxString buf) {
2745  std::string full_sentence = buf.ToStdString();
2746 
2747  if ((full_sentence[0] == '$') || (full_sentence[0] == '!')) { // Sanity check
2748  std::string identifier;
2749  // We notify based on full message, including the Talker ID
2750  identifier = full_sentence.substr(1, 5);
2751 
2752  // notify message listener and also "ALL" N0183 messages, to support plugin
2753  // API using original talker id
2754  auto address = std::make_shared<NavAddr0183>("virtual");
2755  auto msg =
2756  std::make_shared<const Nmea0183Msg>(identifier, full_sentence, address);
2757  auto msg_all = std::make_shared<const Nmea0183Msg>(*msg, "ALL");
2758 
2759  auto& msgbus = NavMsgBus::GetInstance();
2760 
2761  msgbus.Notify(std::move(msg));
2762  msgbus.Notify(std::move(msg_all));
2763  }
2764 }
2765 
2766 wxXmlDocument GetChartDatabaseEntryXML(int dbIndex, bool b_getGeom) {
2767  wxXmlDocument doc = ChartData->GetXMLDescription(dbIndex, b_getGeom);
2768 
2769  return doc;
2770 }
2771 
2772 bool UpdateChartDBInplace(wxArrayString dir_array, bool b_force_update,
2773  bool b_ProgressDialog) {
2774  // Make an array of CDI
2775  ArrayOfCDI ChartDirArray;
2776  for (unsigned int i = 0; i < dir_array.GetCount(); i++) {
2777  wxString dirname = dir_array[i];
2778  ChartDirInfo cdi;
2779  cdi.fullpath = dirname;
2780  cdi.magic_number = _T("");
2781  ChartDirArray.Add(cdi);
2782  }
2783  bool b_ret = gFrame->UpdateChartDatabaseInplace(ChartDirArray, b_force_update,
2784  b_ProgressDialog,
2785  ChartData->GetDBFileName());
2786  gFrame->ChartsRefresh();
2787  return b_ret;
2788 }
2789 
2790 wxArrayString GetChartDBDirArrayString() {
2791  return ChartData->GetChartDirArrayString();
2792 }
2793 
2794 int AddChartToDBInPlace(wxString& full_path, bool b_RefreshCanvas) {
2795  // extract the path from the chart name
2796  wxFileName fn(full_path);
2797  wxString fdir = fn.GetPath();
2798 
2799  bool bret = false;
2800  if (ChartData) {
2801  bret = ChartData->AddSingleChart(full_path);
2802 
2803  if (bret) {
2804  // Save to disk
2805  pConfig->UpdateChartDirs(ChartData->GetChartDirArray());
2806  ChartData->SaveBinary(ChartListFileName);
2807 
2808  // Completely reload the chart database, for a fresh start
2809  ArrayOfCDI XnewChartDirArray;
2810  pConfig->LoadChartDirArray(XnewChartDirArray);
2811  delete ChartData;
2812  ChartData = new ChartDB();
2813  ChartData->LoadBinary(ChartListFileName, XnewChartDirArray);
2814 
2815  // Update group contents
2816  if (g_pGroupArray) ChartData->ApplyGroupArray(g_pGroupArray);
2817 
2818  if (g_boptionsactive) {
2819  g_options->UpdateDisplayedChartDirList(ChartData->GetChartDirArray());
2820  }
2821 
2822  if (b_RefreshCanvas || !gFrame->GetPrimaryCanvas()->GetQuiltMode()) {
2823  gFrame->ChartsRefresh();
2824  }
2825  }
2826  }
2827  return bret;
2828 }
2829 
2830 int RemoveChartFromDBInPlace(wxString& full_path) {
2831  bool bret = false;
2832  if (ChartData) {
2833  bret = ChartData->RemoveSingleChart(full_path);
2834 
2835  // Save to disk
2836  pConfig->UpdateChartDirs(ChartData->GetChartDirArray());
2837  ChartData->SaveBinary(ChartListFileName);
2838 
2839  // Completely reload the chart database, for a fresh start
2840  ArrayOfCDI XnewChartDirArray;
2841  pConfig->LoadChartDirArray(XnewChartDirArray);
2842  delete ChartData;
2843  ChartData = new ChartDB();
2844  ChartData->LoadBinary(ChartListFileName, XnewChartDirArray);
2845 
2846  // Update group contents
2847  if (g_pGroupArray) ChartData->ApplyGroupArray(g_pGroupArray);
2848 
2849  if (g_boptionsactive) {
2850  g_options->UpdateDisplayedChartDirList(ChartData->GetChartDirArray());
2851  }
2852 
2853  gFrame->ChartsRefresh();
2854  }
2855 
2856  return bret;
2857 }
2858 
2859 wxString GetLocaleCanonicalName() { return g_locale; }
2860 
2861 void SendPluginMessage(wxString message_id, wxString message_body) {
2862  s_ppim->SendMessageToAllPlugins(message_id, message_body);
2863 
2864  // We will send an event to the main application frame (gFrame)
2865  // for informational purposes.
2866  // Of course, gFrame is encouraged to use any or all the
2867  // data flying by if judged useful and dependable....
2868 
2869  OCPN_MsgEvent Nevent(wxEVT_OCPN_MSG, 0);
2870  Nevent.SetID(message_id);
2871  Nevent.SetJSONText(message_body);
2872  gFrame->GetEventHandler()->AddPendingEvent(Nevent);
2873 }
2874 
2875 void DimeWindow(wxWindow* win) { DimeControl(win); }
2876 
2877 void JumpToPosition(double lat, double lon, double scale) {
2878  gFrame->JumpToPosition(gFrame->GetFocusCanvas(), lat, lon, scale);
2879 }
2880 
2881 /* API 1.9 */
2882 wxScrolledWindow* AddOptionsPage(OptionsParentPI parent, wxString title) {
2883  if (!g_pOptions) return NULL;
2884 
2885  size_t parentid;
2886  switch (parent) {
2887  case PI_OPTIONS_PARENT_DISPLAY:
2888  parentid = g_pOptions->m_pageDisplay;
2889  break;
2890  case PI_OPTIONS_PARENT_CONNECTIONS:
2891  parentid = g_pOptions->m_pageConnections;
2892  break;
2893  case PI_OPTIONS_PARENT_CHARTS:
2894  parentid = g_pOptions->m_pageCharts;
2895  break;
2896  case PI_OPTIONS_PARENT_SHIPS:
2897  parentid = g_pOptions->m_pageShips;
2898  break;
2899  case PI_OPTIONS_PARENT_UI:
2900  parentid = g_pOptions->m_pageUI;
2901  break;
2902  case PI_OPTIONS_PARENT_PLUGINS:
2903  parentid = g_pOptions->m_pagePlugins;
2904  break;
2905  default:
2906  wxLogMessage(
2907  _T("Error in PluginManager::AddOptionsPage: Unknown parent"));
2908  return NULL;
2909  break;
2910  }
2911 
2912  return g_pOptions->AddPage(parentid, title);
2913 }
2914 
2915 bool DeleteOptionsPage(wxScrolledWindow* page) {
2916  if (!g_pOptions) return false;
2917  return g_pOptions->DeletePluginPage(page);
2918 }
2919 
2920 bool DecodeSingleVDOMessage(const wxString& str, PlugIn_Position_Fix_Ex* pos,
2921  wxString* accumulator) {
2922  if (!pos) return false;
2923 
2924  GenericPosDatEx gpd;
2925  AisError nerr = AIS_GENERIC_ERROR;
2926  if (g_pAIS) nerr = g_pAIS->DecodeSingleVDO(str, &gpd, accumulator);
2927  if (nerr == AIS_NoError) {
2928  pos->Lat = gpd.kLat;
2929  pos->Lon = gpd.kLon;
2930  pos->Cog = gpd.kCog;
2931  pos->Sog = gpd.kSog;
2932  pos->Hdt = gpd.kHdt;
2933 
2934  // Fill in the dummy values
2935  pos->FixTime = 0;
2936  pos->Hdm = 1000;
2937  pos->Var = 1000;
2938  pos->nSats = 0;
2939 
2940  return true;
2941  }
2942 
2943  return false;
2944 }
2945 
2946 int GetChartbarHeight(void) {
2947  int val = 0;
2948  if (g_bShowChartBar) {
2949  ChartCanvas* cc = gFrame->GetPrimaryCanvas();
2950  if (cc && cc->GetPiano()) {
2951  val = cc->GetPiano()->GetHeight();
2952  }
2953  }
2954  return val;
2955 }
2956 
2957 bool GetRoutepointGPX(RoutePoint* pRoutePoint, char* buffer,
2958  unsigned int buffer_length) {
2959  bool ret = false;
2960 
2962  pgpx->AddGPXWaypoint(pRoutePoint);
2963  wxString gpxfilename = wxFileName::CreateTempFileName(wxT("gpx"));
2964  pgpx->SaveFile(gpxfilename);
2965  delete pgpx;
2966 
2967  wxFFile gpxfile(gpxfilename);
2968  wxString s;
2969  if (gpxfile.ReadAll(&s)) {
2970  if (s.Length() < buffer_length) {
2971  strncpy(buffer, (const char*)s.mb_str(wxConvUTF8), buffer_length - 1);
2972  ret = true;
2973  }
2974  }
2975 
2976  gpxfile.Close();
2977  ::wxRemoveFile(gpxfilename);
2978 
2979  return ret;
2980 }
2981 
2982 bool GetActiveRoutepointGPX(char* buffer, unsigned int buffer_length) {
2983  if (g_pRouteMan->IsAnyRouteActive())
2984  return GetRoutepointGPX(g_pRouteMan->GetpActivePoint(), buffer,
2985  buffer_length);
2986  else
2987  return false;
2988 }
2989 
2990 void PositionBearingDistanceMercator_Plugin(double lat, double lon, double brg,
2991  double dist, double* dlat,
2992  double* dlon) {
2993  PositionBearingDistanceMercator(lat, lon, brg, dist, dlat, dlon);
2994 }
2995 
2996 void DistanceBearingMercator_Plugin(double lat0, double lon0, double lat1,
2997  double lon1, double* brg, double* dist) {
2998  DistanceBearingMercator(lat0, lon0, lat1, lon1, brg, dist);
2999 }
3000 
3001 double DistGreatCircle_Plugin(double slat, double slon, double dlat,
3002  double dlon) {
3003  return DistGreatCircle(slat, slon, dlat, dlon);
3004 }
3005 
3006 void toTM_Plugin(float lat, float lon, float lat0, float lon0, double* x,
3007  double* y) {
3008  toTM(lat, lon, lat0, lon0, x, y);
3009 }
3010 
3011 void fromTM_Plugin(double x, double y, double lat0, double lon0, double* lat,
3012  double* lon) {
3013  fromTM(x, y, lat0, lon0, lat, lon);
3014 }
3015 
3016 void toSM_Plugin(double lat, double lon, double lat0, double lon0, double* x,
3017  double* y) {
3018  toSM(lat, lon, lat0, lon0, x, y);
3019 }
3020 
3021 void fromSM_Plugin(double x, double y, double lat0, double lon0, double* lat,
3022  double* lon) {
3023  fromSM(x, y, lat0, lon0, lat, lon);
3024 }
3025 
3026 void toSM_ECC_Plugin(double lat, double lon, double lat0, double lon0,
3027  double* x, double* y) {
3028  toSM_ECC(lat, lon, lat0, lon0, x, y);
3029 }
3030 
3031 void fromSM_ECC_Plugin(double x, double y, double lat0, double lon0,
3032  double* lat, double* lon) {
3033  fromSM_ECC(x, y, lat0, lon0, lat, lon);
3034 }
3035 
3036 double toUsrDistance_Plugin(double nm_distance, int unit) {
3037  return toUsrDistance(nm_distance, unit);
3038 }
3039 
3040 double fromUsrDistance_Plugin(double usr_distance, int unit) {
3041  return fromUsrDistance(usr_distance, unit);
3042 }
3043 
3044 double toUsrSpeed_Plugin(double kts_speed, int unit) {
3045  return toUsrSpeed(kts_speed, unit);
3046 }
3047 
3048 double toUsrWindSpeed_Plugin(double kts_speed, int unit) {
3049  return toUsrWindSpeed(kts_speed, unit);
3050 }
3051 
3052 double fromUsrSpeed_Plugin(double usr_speed, int unit) {
3053  return fromUsrSpeed(usr_speed, unit);
3054 }
3055 
3056 double fromUsrWindSpeed_Plugin(double usr_wspeed, int unit) {
3057  return fromUsrWindSpeed(usr_wspeed, unit);
3058 }
3059 
3060 double toUsrTemp_Plugin(double cel_temp, int unit) {
3061  return toUsrTemp(cel_temp, unit);
3062 }
3063 
3064 double fromUsrTemp_Plugin(double usr_temp, int unit) {
3065  return fromUsrTemp(usr_temp, unit);
3066 }
3067 
3068 wxString getUsrDistanceUnit_Plugin(int unit) {
3069  return getUsrDistanceUnit(unit);
3070 }
3071 
3072 wxString getUsrSpeedUnit_Plugin(int unit) { return getUsrSpeedUnit(unit); }
3073 
3074 wxString getUsrWindSpeedUnit_Plugin(int unit) { return getUsrWindSpeedUnit(unit); }
3075 
3076 wxString getUsrTempUnit_Plugin(int unit) { return getUsrTempUnit(unit); }
3077 
3078 bool PlugIn_GSHHS_CrossesLand(double lat1, double lon1, double lat2,
3079  double lon2) {
3080  static bool loaded = false;
3081  if (!loaded) {
3082  gshhsCrossesLandInit();
3083  loaded = true;
3084  }
3085 
3086  return gshhsCrossesLand(lat1, lon1, lat2, lon2);
3087 }
3088 
3089 void PlugInPlaySound(wxString& sound_file) {
3090  PlugInPlaySoundEx(sound_file, -1);
3091 }
3092 
3093 // API 1.10 Route and Waypoint Support
3094 // wxBitmap *FindSystemWaypointIcon( wxString& icon_name );
3095 
3096 // PlugInWaypoint implementation
3097 PlugIn_Waypoint::PlugIn_Waypoint() { m_HyperlinkList = NULL; }
3098 
3099 PlugIn_Waypoint::PlugIn_Waypoint(double lat, double lon,
3100  const wxString& icon_ident,
3101  const wxString& wp_name,
3102  const wxString& GUID) {
3103  wxDateTime now = wxDateTime::Now();
3104  m_CreateTime = now.ToUTC();
3105  m_HyperlinkList = NULL;
3106 
3107  m_lat = lat;
3108  m_lon = lon;
3109  m_IconName = icon_ident;
3110  m_MarkName = wp_name;
3111  m_GUID = GUID;
3112 }
3113 
3114 PlugIn_Waypoint::~PlugIn_Waypoint() {}
3115 
3116 // PlugInRoute implementation
3117 PlugIn_Route::PlugIn_Route(void) { pWaypointList = new Plugin_WaypointList; }
3118 
3119 PlugIn_Route::~PlugIn_Route(void) {
3120  pWaypointList->DeleteContents(false); // do not delete Waypoints
3121  pWaypointList->Clear();
3122 
3123  delete pWaypointList;
3124 }
3125 
3126 // PlugInTrack implementation
3127 PlugIn_Track::PlugIn_Track(void) { pWaypointList = new Plugin_WaypointList; }
3128 
3129 PlugIn_Track::~PlugIn_Track(void) {
3130  pWaypointList->DeleteContents(false); // do not delete Waypoints
3131  pWaypointList->Clear();
3132 
3133  delete pWaypointList;
3134 }
3135 
3136 wxString GetNewGUID(void) { return GpxDocument::GetUUID(); }
3137 
3138 bool AddCustomWaypointIcon(wxBitmap* pimage, wxString key,
3139  wxString description) {
3140  wxImage image = pimage->ConvertToImage();
3141  WayPointmanGui(*pWayPointMan).ProcessIcon(image, key, description);
3142  return true;
3143 }
3144 
3145 static void cloneHyperlinkList(RoutePoint* dst, const PlugIn_Waypoint* src) {
3146  // Transcribe (clone) the html HyperLink List, if present
3147  if (src->m_HyperlinkList == nullptr) return;
3148 
3149  if (src->m_HyperlinkList->GetCount() > 0) {
3150  wxPlugin_HyperlinkListNode* linknode = src->m_HyperlinkList->GetFirst();
3151  while (linknode) {
3152  Plugin_Hyperlink* link = linknode->GetData();
3153 
3154  Hyperlink* h = new Hyperlink();
3155  h->DescrText = link->DescrText;
3156  h->Link = link->Link;
3157  h->LType = link->Type;
3158 
3159  dst->m_HyperlinkList->Append(h);
3160 
3161  linknode = linknode->GetNext();
3162  }
3163  }
3164 }
3165 
3166 bool AddSingleWaypoint(PlugIn_Waypoint* pwaypoint, bool b_permanent) {
3167  // Validate the waypoint parameters a little bit
3168 
3169  // GUID
3170  // Make sure that this GUID is indeed unique in the Routepoint list
3171  bool b_unique = true;
3172  wxRoutePointListNode* prpnode = pWayPointMan->GetWaypointList()->GetFirst();
3173  while (prpnode) {
3174  RoutePoint* prp = prpnode->GetData();
3175 
3176  if (prp->m_GUID == pwaypoint->m_GUID) {
3177  b_unique = false;
3178  break;
3179  }
3180  prpnode = prpnode->GetNext(); // RoutePoint
3181  }
3182 
3183  if (!b_unique) return false;
3184 
3185  RoutePoint* pWP =
3186  new RoutePoint(pwaypoint->m_lat, pwaypoint->m_lon, pwaypoint->m_IconName,
3187  pwaypoint->m_MarkName, pwaypoint->m_GUID);
3188 
3189  pWP->m_bIsolatedMark = true; // This is an isolated mark
3190 
3191  cloneHyperlinkList(pWP, pwaypoint);
3192 
3193  pWP->m_MarkDescription = pwaypoint->m_MarkDescription;
3194 
3195  if (pwaypoint->m_CreateTime.IsValid())
3196  pWP->SetCreateTime(pwaypoint->m_CreateTime);
3197  else {
3198  wxDateTime dtnow(wxDateTime::Now());
3199  pWP->SetCreateTime(dtnow);
3200  }
3201 
3202  pWP->m_btemp = (b_permanent == false);
3203 
3204  pSelect->AddSelectableRoutePoint(pwaypoint->m_lat, pwaypoint->m_lon, pWP);
3205  if (b_permanent) pConfig->AddNewWayPoint(pWP, -1);
3206 
3207  if (pRouteManagerDialog && pRouteManagerDialog->IsShown())
3208  pRouteManagerDialog->UpdateWptListCtrl();
3209 
3210  return true;
3211 }
3212 
3213 bool DeleteSingleWaypoint(wxString& GUID) {
3214  // Find the RoutePoint
3215  bool b_found = false;
3216  RoutePoint* prp = pWayPointMan->FindRoutePointByGUID(GUID);
3217 
3218  if (prp) b_found = true;
3219 
3220  if (b_found) {
3221  pWayPointMan->DestroyWaypoint(prp);
3222  if (pRouteManagerDialog && pRouteManagerDialog->IsShown())
3223  pRouteManagerDialog->UpdateWptListCtrl();
3224  }
3225 
3226  return b_found;
3227 }
3228 
3229 bool UpdateSingleWaypoint(PlugIn_Waypoint* pwaypoint) {
3230  // Find the RoutePoint
3231  bool b_found = false;
3232  RoutePoint* prp = pWayPointMan->FindRoutePointByGUID(pwaypoint->m_GUID);
3233 
3234  if (prp) b_found = true;
3235 
3236  if (b_found) {
3237  double lat_save = prp->m_lat;
3238  double lon_save = prp->m_lon;
3239 
3240  prp->m_lat = pwaypoint->m_lat;
3241  prp->m_lon = pwaypoint->m_lon;
3242  prp->SetIconName(pwaypoint->m_IconName);
3243  prp->SetName(pwaypoint->m_MarkName);
3244  prp->m_MarkDescription = pwaypoint->m_MarkDescription;
3245  prp->SetVisible(pwaypoint->m_IsVisible);
3246  if (pwaypoint->m_CreateTime.IsValid())
3247  prp->SetCreateTime(pwaypoint->m_CreateTime);
3248 
3249  // Transcribe (clone) the html HyperLink List, if present
3250 
3251  if (pwaypoint->m_HyperlinkList) {
3252  prp->m_HyperlinkList->Clear();
3253  if (pwaypoint->m_HyperlinkList->GetCount() > 0) {
3254  wxPlugin_HyperlinkListNode* linknode =
3255  pwaypoint->m_HyperlinkList->GetFirst();
3256  while (linknode) {
3257  Plugin_Hyperlink* link = linknode->GetData();
3258 
3259  Hyperlink* h = new Hyperlink();
3260  h->DescrText = link->DescrText;
3261  h->Link = link->Link;
3262  h->LType = link->Type;
3263 
3264  prp->m_HyperlinkList->Append(h);
3265 
3266  linknode = linknode->GetNext();
3267  }
3268  }
3269  }
3270 
3271  if (prp) prp->ReLoadIcon();
3272 
3273  auto canvas = gFrame->GetPrimaryCanvas();
3274  SelectCtx ctx(canvas->m_bShowNavobjects, canvas->GetCanvasTrueScale(), canvas->GetScaleValue());
3275  SelectItem* pFind =
3276  pSelect->FindSelection(ctx, lat_save, lon_save, SELTYPE_ROUTEPOINT);
3277  if (pFind) {
3278  pFind->m_slat = pwaypoint->m_lat; // update the SelectList entry
3279  pFind->m_slon = pwaypoint->m_lon;
3280  }
3281 
3282  if (!prp->m_btemp) pConfig->UpdateWayPoint(prp);
3283 
3284  if (pRouteManagerDialog && pRouteManagerDialog->IsShown())
3285  pRouteManagerDialog->UpdateWptListCtrl();
3286  }
3287 
3288  return b_found;
3289 }
3290 
3291 // translate O route class to Plugin one
3292 static void PlugInFromRoutePoint(PlugIn_Waypoint* dst,
3293  /* const*/ RoutePoint* src) {
3294  dst->m_lat = src->m_lat;
3295  dst->m_lon = src->m_lon;
3296  dst->m_IconName = src->GetIconName();
3297  dst->m_MarkName = src->GetName();
3298  dst->m_MarkDescription = src->m_MarkDescription;
3299  dst->m_IsVisible = src->IsVisible();
3300  dst->m_CreateTime = src->GetCreateTime(); // not const
3301  dst->m_GUID = src->m_GUID;
3302 
3303  // Transcribe (clone) the html HyperLink List, if present
3304  if (src->m_HyperlinkList == nullptr) return;
3305 
3306  delete dst->m_HyperlinkList;
3307  dst->m_HyperlinkList = nullptr;
3308 
3309  if (src->m_HyperlinkList->GetCount() > 0) {
3310  dst->m_HyperlinkList = new Plugin_HyperlinkList;
3311 
3312  wxHyperlinkListNode* linknode = src->m_HyperlinkList->GetFirst();
3313  while (linknode) {
3314  Hyperlink* link = linknode->GetData();
3315 
3317  h->DescrText = link->DescrText;
3318  h->Link = link->Link;
3319  h->Type = link->LType;
3320 
3321  dst->m_HyperlinkList->Append(h);
3322 
3323  linknode = linknode->GetNext();
3324  }
3325  }
3326 }
3327 
3328 bool GetSingleWaypoint(wxString GUID, PlugIn_Waypoint* pwaypoint) {
3329  // Find the RoutePoint
3330  RoutePoint* prp = pWayPointMan->FindRoutePointByGUID(GUID);
3331 
3332  if (!prp) return false;
3333 
3334  PlugInFromRoutePoint(pwaypoint, prp);
3335 
3336  return true;
3337 }
3338 
3339 wxArrayString GetWaypointGUIDArray(void) {
3340  wxArrayString result;
3341  RoutePointList* list = pWayPointMan->GetWaypointList();
3342 
3343  wxRoutePointListNode* prpnode = list->GetFirst();
3344  while (prpnode) {
3345  RoutePoint* prp = prpnode->GetData();
3346  result.Add(prp->m_GUID);
3347 
3348  prpnode = prpnode->GetNext(); // RoutePoint
3349  }
3350 
3351  return result;
3352 }
3353 
3354 wxArrayString GetRouteGUIDArray(void) {
3355  wxArrayString result;
3356  RouteList* list = pRouteList;
3357 
3358  wxRouteListNode* prpnode = list->GetFirst();
3359  while (prpnode) {
3360  Route* proute = prpnode->GetData();
3361  result.Add(proute->m_GUID);
3362 
3363  prpnode = prpnode->GetNext(); // Route
3364  }
3365 
3366  return result;
3367 }
3368 
3369 wxArrayString GetTrackGUIDArray(void) {
3370  wxArrayString result;
3371  for (Track* ptrack : g_TrackList) {
3372  result.Add(ptrack->m_GUID);
3373  }
3374 
3375  return result;
3376 }
3377 
3378 wxArrayString GetWaypointGUIDArray(OBJECT_LAYER_REQ req) {
3379  wxArrayString result;
3380  RoutePointList* list = pWayPointMan->GetWaypointList();
3381 
3382  wxRoutePointListNode* prpnode = list->GetFirst();
3383  while (prpnode) {
3384  RoutePoint* prp = prpnode->GetData();
3385  switch (req) {
3386  case OBJECTS_ALL:
3387  result.Add(prp->m_GUID);
3388  break;
3389  case OBJECTS_NO_LAYERS:
3390  if (!prp->m_bIsInLayer) result.Add(prp->m_GUID);
3391  break;
3392  case OBJECTS_ONLY_LAYERS:
3393  if (prp->m_bIsInLayer) result.Add(prp->m_GUID);
3394  break;
3395  }
3396 
3397  prpnode = prpnode->GetNext(); // RoutePoint
3398  }
3399 
3400  return result;
3401 }
3402 
3403 wxArrayString GetRouteGUIDArray(OBJECT_LAYER_REQ req) {
3404  wxArrayString result;
3405  RouteList* list = pRouteList;
3406 
3407  wxRouteListNode* prpnode = list->GetFirst();
3408  while (prpnode) {
3409  Route* proute = prpnode->GetData();
3410  switch (req) {
3411  case OBJECTS_ALL:
3412  result.Add(proute->m_GUID);
3413  break;
3414  case OBJECTS_NO_LAYERS:
3415  if (!proute->m_bIsInLayer) result.Add(proute->m_GUID);
3416  break;
3417  case OBJECTS_ONLY_LAYERS:
3418  if (proute->m_bIsInLayer) result.Add(proute->m_GUID);
3419  break;
3420  }
3421 
3422  prpnode = prpnode->GetNext(); // Route
3423  }
3424 
3425  return result;
3426 }
3427 
3428 wxArrayString GetTrackGUIDArray(OBJECT_LAYER_REQ req) {
3429  wxArrayString result;
3430  for (Track* ptrack : g_TrackList) {
3431  switch (req) {
3432  case OBJECTS_ALL:
3433  result.Add(ptrack->m_GUID);
3434  break;
3435  case OBJECTS_NO_LAYERS:
3436  if (!ptrack->m_bIsInLayer) result.Add(ptrack->m_GUID);
3437  break;
3438  case OBJECTS_ONLY_LAYERS:
3439  if (ptrack->m_bIsInLayer) result.Add(ptrack->m_GUID);
3440  break;
3441  }
3442  }
3443 
3444  return result;
3445 }
3446 
3447 wxArrayString GetIconNameArray(void) {
3448  wxArrayString result;
3449 
3450  for (int i = 0; i < pWayPointMan->GetNumIcons(); i++) {
3451  wxString* ps = pWayPointMan->GetIconKey(i);
3452  result.Add(*ps);
3453  }
3454  return result;
3455 }
3456 
3457 bool AddPlugInRoute(PlugIn_Route* proute, bool b_permanent) {
3458  Route* route = new Route();
3459 
3460  PlugIn_Waypoint* pwp;
3461  RoutePoint* pWP_src;
3462  int ip = 0;
3463  wxDateTime plannedDeparture;
3464 
3465  wxPlugin_WaypointListNode* pwpnode = proute->pWaypointList->GetFirst();
3466  while (pwpnode) {
3467  pwp = pwpnode->GetData();
3468 
3469  RoutePoint* pWP = new RoutePoint(pwp->m_lat, pwp->m_lon, pwp->m_IconName,
3470  pwp->m_MarkName, pwp->m_GUID);
3471 
3472  // Transcribe (clone) the html HyperLink List, if present
3473  cloneHyperlinkList(pWP, pwp);
3474  pWP->m_MarkDescription = pwp->m_MarkDescription;
3475  pWP->m_bShowName = false;
3476  pWP->SetCreateTime(pwp->m_CreateTime);
3477 
3478  route->AddPoint(pWP);
3479 
3480  pSelect->AddSelectableRoutePoint(pWP->m_lat, pWP->m_lon, pWP);
3481 
3482  if (ip > 0)
3483  pSelect->AddSelectableRouteSegment(pWP_src->m_lat, pWP_src->m_lon,
3484  pWP->m_lat, pWP->m_lon, pWP_src, pWP,
3485  route);
3486  else
3487  plannedDeparture = pwp->m_CreateTime;
3488  ip++;
3489  pWP_src = pWP;
3490 
3491  pwpnode = pwpnode->GetNext(); // PlugInWaypoint
3492  }
3493 
3494  route->m_PlannedDeparture = plannedDeparture;
3495 
3496  route->m_RouteNameString = proute->m_NameString;
3497  route->m_RouteStartString = proute->m_StartString;
3498  route->m_RouteEndString = proute->m_EndString;
3499  if (!proute->m_GUID.IsEmpty()) {
3500  route->m_GUID = proute->m_GUID;
3501  }
3502  route->m_btemp = (b_permanent == false);
3503 
3504  pRouteList->Append(route);
3505 
3506  if (b_permanent) pConfig->AddNewRoute(route);
3507 
3508  if (pRouteManagerDialog && pRouteManagerDialog->IsShown())
3509  pRouteManagerDialog->UpdateRouteListCtrl();
3510 
3511  return true;
3512 }
3513 
3514 bool DeletePlugInRoute(wxString& GUID) {
3515  bool b_found = false;
3516 
3517  // Find the Route
3518  Route* pRoute = g_pRouteMan->FindRouteByGUID(GUID);
3519  if (pRoute) {
3520  g_pRouteMan->DeleteRoute(pRoute, NavObjectChanges::getInstance());
3521  b_found = true;
3522  }
3523  return b_found;
3524 }
3525 
3526 bool UpdatePlugInRoute(PlugIn_Route* proute) {
3527  bool b_found = false;
3528 
3529  // Find the Route
3530  Route* pRoute = g_pRouteMan->FindRouteByGUID(proute->m_GUID);
3531  if (pRoute) b_found = true;
3532 
3533  if (b_found) {
3534  bool b_permanent = (pRoute->m_btemp == false);
3535  g_pRouteMan->DeleteRoute(pRoute, NavObjectChanges::getInstance());
3536 
3537  b_found = AddPlugInRoute(proute, b_permanent);
3538  }
3539 
3540  return b_found;
3541 }
3542 
3543 bool AddPlugInTrack(PlugIn_Track* ptrack, bool b_permanent) {
3544  Track* track = new Track();
3545 
3546  PlugIn_Waypoint* pwp = 0;
3547  TrackPoint* pWP_src = 0;
3548  int ip = 0;
3549 
3550  wxPlugin_WaypointListNode* pwpnode = ptrack->pWaypointList->GetFirst();
3551  while (pwpnode) {
3552  pwp = pwpnode->GetData();
3553 
3554  TrackPoint* pWP = new TrackPoint(pwp->m_lat, pwp->m_lon);
3555  pWP->SetCreateTime(pwp->m_CreateTime);
3556 
3557  track->AddPoint(pWP);
3558 
3559  if (ip > 0)
3560  pSelect->AddSelectableTrackSegment(pWP_src->m_lat, pWP_src->m_lon,
3561  pWP->m_lat, pWP->m_lon, pWP_src, pWP,
3562  track);
3563  ip++;
3564  pWP_src = pWP;
3565 
3566  pwpnode = pwpnode->GetNext(); // PlugInWaypoint
3567  }
3568 
3569  track->SetName(ptrack->m_NameString);
3570  track->m_TrackStartString = ptrack->m_StartString;
3571  track->m_TrackEndString = ptrack->m_EndString;
3572  track->m_GUID = ptrack->m_GUID;
3573  track->m_btemp = (b_permanent == false);
3574 
3575  g_TrackList.push_back(track);
3576 
3577  if (b_permanent) pConfig->AddNewTrack(track);
3578 
3579  if (pRouteManagerDialog && pRouteManagerDialog->IsShown())
3580  pRouteManagerDialog->UpdateTrkListCtrl();
3581 
3582  return true;
3583 }
3584 
3585 bool DeletePlugInTrack(wxString& GUID) {
3586  bool b_found = false;
3587 
3588  // Find the Route
3589  Track* pTrack = g_pRouteMan->FindTrackByGUID(GUID);
3590  if (pTrack) {
3591  RoutemanGui(*g_pRouteMan).DeleteTrack(pTrack);
3592  b_found = true;
3593  }
3594 
3595  if (pRouteManagerDialog && pRouteManagerDialog->IsShown())
3596  pRouteManagerDialog->UpdateTrkListCtrl();
3597 
3598  return b_found;
3599 }
3600 
3601 bool UpdatePlugInTrack(PlugIn_Track* ptrack) {
3602  bool b_found = false;
3603 
3604  // Find the Track
3605  Track* pTrack = g_pRouteMan->FindTrackByGUID(ptrack->m_GUID);
3606  if (pTrack) b_found = true;
3607 
3608  if (b_found) {
3609  bool b_permanent = (pTrack->m_btemp == false);
3610  RoutemanGui(*g_pRouteMan).DeleteTrack(pTrack);
3611 
3612  b_found = AddPlugInTrack(ptrack, b_permanent);
3613  }
3614 
3615  return b_found;
3616 }
3617 
3618 bool PlugInHasNormalizedViewPort(PlugIn_ViewPort* vp) {
3619 #ifdef ocpnUSE_GL
3620  ViewPort ocpn_vp;
3621  ocpn_vp.m_projection_type = vp->m_projection_type;
3622 
3623  return glChartCanvas::HasNormalizedViewPort(ocpn_vp);
3624 #else
3625  return false;
3626 #endif
3627 }
3628 
3629 void PlugInMultMatrixViewport(PlugIn_ViewPort* vp, float lat, float lon) {
3630 #ifdef ocpnUSE_GL
3631  ViewPort ocpn_vp;
3632  ocpn_vp.clat = vp->clat;
3633  ocpn_vp.clon = vp->clon;
3634  ocpn_vp.m_projection_type = vp->m_projection_type;
3635  ocpn_vp.view_scale_ppm = vp->view_scale_ppm;
3636  ocpn_vp.skew = vp->skew;
3637  ocpn_vp.rotation = vp->rotation;
3638  ocpn_vp.pix_width = vp->pix_width;
3639  ocpn_vp.pix_height = vp->pix_height;
3640 
3641 // TODO fix for multicanvas glChartCanvas::MultMatrixViewPort(ocpn_vp, lat,
3642 // lon);
3643 #endif
3644 }
3645 
3646 void PlugInNormalizeViewport(PlugIn_ViewPort* vp, float lat, float lon) {
3647 #ifdef ocpnUSE_GL
3648  ViewPort ocpn_vp;
3649  glChartCanvas::NormalizedViewPort(ocpn_vp, lat, lon);
3650 
3651  vp->clat = ocpn_vp.clat;
3652  vp->clon = ocpn_vp.clon;
3653  vp->view_scale_ppm = ocpn_vp.view_scale_ppm;
3654  vp->rotation = ocpn_vp.rotation;
3655  vp->skew = ocpn_vp.skew;
3656 #endif
3657 }
3658 
3659 // Helper and interface classes
3660 
3661 //-------------------------------------------------------------------------------
3662 // PlugIn_AIS_Target Implementation
3663 //-------------------------------------------------------------------------------
3664 
3665 PlugIn_AIS_Target* Create_PI_AIS_Target(AisTargetData* ptarget) {
3667 
3668  pret->MMSI = ptarget->MMSI;
3669  pret->Class = ptarget->Class;
3670  pret->NavStatus = ptarget->NavStatus;
3671  pret->SOG = ptarget->SOG;
3672  pret->COG = ptarget->COG;
3673  pret->HDG = ptarget->HDG;
3674  pret->Lon = ptarget->Lon;
3675  pret->Lat = ptarget->Lat;
3676  pret->ROTAIS = ptarget->ROTAIS;
3677  pret->ShipType = ptarget->ShipType;
3678  pret->IMO = ptarget->IMO;
3679 
3680  pret->Range_NM = ptarget->Range_NM;
3681  pret->Brg = ptarget->Brg;
3682 
3683  // Per target collision parameters
3684  pret->bCPA_Valid = ptarget->bCPA_Valid;
3685  pret->TCPA = ptarget->TCPA; // Minutes
3686  pret->CPA = ptarget->CPA; // Nautical Miles
3687 
3688  pret->alarm_state = (plugin_ais_alarm_type)ptarget->n_alert_state;
3689 
3690  memcpy(pret->CallSign, ptarget->CallSign, sizeof(ptarget->CallSign) - 1);
3691  memcpy(pret->ShipName, ptarget->ShipName, sizeof(ptarget->ShipName) - 1);
3692 
3693  return pret;
3694 }
3695 
3696 //-------------------------------------------------------------------------------
3697 // PluginListPanel & PluginPanel Implementation
3698 //-------------------------------------------------------------------------------
3699 
3700 #define DISABLED_SETTINGS_MSG \
3701  _("These settings might destabilize OpenCPN and are by default disabled." \
3702  " To despite the dangers enable them manually add a CatalogExpert=1" \
3703  " line in the [PlugIns] section in the configuration file.")
3704 
3705 /*
3706  * Panel with buttons to control plugin catalog management.
3707  */
3708 CatalogMgrPanel::CatalogMgrPanel(wxWindow* parent)
3709  : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize),
3710  m_parent(parent) {
3711  wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL);
3712  SetSizer(topSizer);
3713 
3714  topSizer->Add(new wxStaticLine(this), 0, wxGROW | wxLEFT | wxRIGHT, 4);
3715 
3716  wxStaticBox* itemStaticBoxSizer4Static =
3717  new wxStaticBox(this, wxID_ANY, _("Plugin Catalog"));
3718  wxStaticBoxSizer* itemStaticBoxSizer4 =
3719  new wxStaticBoxSizer(itemStaticBoxSizer4Static, wxVERTICAL);
3720  topSizer->Add(itemStaticBoxSizer4, 1, wxEXPAND | wxALL, 2);
3721 
3722 #ifndef __ANDROID__
3723  // First line
3724  m_catalogText = new wxStaticText(this, wxID_STATIC, _T(""));
3725  itemStaticBoxSizer4->Add(m_catalogText,
3726  wxSizerFlags().Border().Proportion(1));
3727  m_catalogText->SetLabel(GetCatalogText(false));
3728 
3729  // Next line
3730  wxBoxSizer* rowSizer2 = new wxBoxSizer(wxHORIZONTAL);
3731  itemStaticBoxSizer4->Add(rowSizer2,
3732  wxSizerFlags().Expand().Border().Proportion(1));
3733 
3734  m_updateButton = new wxButton(this, wxID_ANY, _("Update Plugin Catalog"),
3735  wxDefaultPosition, wxDefaultSize, 0);
3736  rowSizer2->Add(m_updateButton, 0, wxALIGN_LEFT);
3737  m_updateButton->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
3738  &CatalogMgrPanel::OnUpdateButton, this);
3739  rowSizer2->AddSpacer(4 * GetCharWidth());
3740  m_tarballButton = new wxButton(this, wxID_ANY, _("Import plugin..."),
3741  wxDefaultPosition, wxDefaultSize, 0);
3742  rowSizer2->Add(m_tarballButton, 0, wxALIGN_LEFT | wxLEFT, 2 * GetCharWidth());
3743  m_tarballButton->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
3744  &CatalogMgrPanel::OnTarballButton, this);
3745 
3746  rowSizer2->AddSpacer(4 * GetCharWidth());
3747  m_adv_button = new wxButton(this, wxID_ANY, _("Settings..."),
3748  wxDefaultPosition, wxDefaultSize, 0);
3749  ConfigVar<bool> expert("/PlugIns", "CatalogExpert", pConfig);
3750  if (expert.Get(false)) {
3751  m_adv_button->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
3752  &CatalogMgrPanel::OnPluginSettingsButton, this);
3753  } else {
3754  m_adv_button->Bind(wxEVT_COMMAND_BUTTON_CLICKED, [&](wxCommandEvent&) {
3755  wxMessageBox(DISABLED_SETTINGS_MSG, _("Disabled"));
3756  });
3757  }
3758  rowSizer2->AddSpacer(4 * GetCharWidth());
3759  rowSizer2->Add(m_adv_button, 0, wxALIGN_LEFT);
3760 
3761  SetUpdateButtonLabel();
3762 
3763  // Next line
3764  wxBoxSizer* rowSizer3 = new wxBoxSizer(wxHORIZONTAL);
3765  itemStaticBoxSizer4->Add(rowSizer3, 0, wxEXPAND | wxALL, 4);
3766 
3767  SetMinSize(wxSize(m_parent->GetClientSize().x - (4 * GetCharWidth()), -1));
3768  Fit();
3769 
3770  GlobalVar<wxString> catalog(&g_catalog_channel);
3771  wxDEFINE_EVENT(EVT_CATALOG_CHANGE, wxCommandEvent);
3772  catalog_listener.Listen(catalog, this, EVT_CATALOG_CHANGE);
3773  Bind(EVT_CATALOG_CHANGE, [&](wxCommandEvent&) { SetUpdateButtonLabel(); });
3774 
3775 #else // __ANDROID__
3776  SetBackgroundColour(wxColour(0x7c, 0xb0, 0xe9)); // light blue
3777  ConfigVar<bool> expert("/PlugIns", "CatalogExpert", pConfig);
3778  if (!expert.Get(false)) {
3779  m_updateButton =
3780  new wxButton(this, wxID_ANY, _("Update Plugin Catalog: master"),
3781  wxDefaultPosition, wxDefaultSize, 0);
3782  itemStaticBoxSizer4->Add(m_updateButton, 0, wxALIGN_LEFT);
3783  m_updateButton->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
3784  &CatalogMgrPanel::OnUpdateButton, this);
3785  SetUpdateButtonLabel();
3786  m_tarballButton = NULL;
3787  m_adv_button = NULL;
3788  } else {
3789  // First line
3790  m_catalogText = new wxStaticText(this, wxID_STATIC, GetCatalogText(false));
3791  itemStaticBoxSizer4->Add(m_catalogText,
3792  wxSizerFlags().Border(wxALL, 5).Proportion(1));
3793  // m_catalogText->SetLabel(GetCatalogText(false));
3794 
3795  m_updateButton = new wxButton(
3796  this, wxID_ANY, "Update Plugin Catalog:master ",
3797  wxDefaultPosition, wxDefaultSize, 0);
3798  itemStaticBoxSizer4->Add(m_updateButton, 0, wxALIGN_LEFT | wxTOP, 5);
3799  m_updateButton->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
3800  &CatalogMgrPanel::OnUpdateButton, this);
3801  SetUpdateButtonLabel();
3802 
3803  // Next line
3804  m_adv_button = new wxButton(this, wxID_ANY, _("Settings..."),
3805  wxDefaultPosition, wxDefaultSize, 0);
3806  itemStaticBoxSizer4->Add(m_adv_button, 0, wxALIGN_LEFT | wxTOP,
3807  GetCharWidth());
3808  m_adv_button->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
3809  &CatalogMgrPanel::OnPluginSettingsButton, this);
3810 
3811  // Next line
3812  m_tarballButton = new wxButton(this, wxID_ANY, _("Import plugin..."),
3813  wxDefaultPosition, wxDefaultSize, 0);
3814  itemStaticBoxSizer4->Add(m_tarballButton, 0, wxALIGN_LEFT | wxALL,
3815  2 * GetCharWidth());
3816  m_tarballButton->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
3817  &CatalogMgrPanel::OnTarballButton, this);
3818  }
3819 
3820 #endif
3821 }
3822 
3823 CatalogMgrPanel::~CatalogMgrPanel() {
3824  m_updateButton->Unbind(wxEVT_COMMAND_BUTTON_CLICKED,
3825  &CatalogMgrPanel::OnUpdateButton, this);
3826  if (m_tarballButton)
3827  m_tarballButton->Unbind(wxEVT_COMMAND_BUTTON_CLICKED,
3828  &CatalogMgrPanel::OnTarballButton, this);
3829 }
3830 
3831 static const char* const DOWNLOAD_REPO_PROTO =
3832  "https://raw.githubusercontent.com/OpenCPN/plugins/@branch@/"
3833  "ocpn-plugins.xml";
3834 
3835 void CatalogMgrPanel::OnUpdateButton(wxCommandEvent& event) {
3836  // Craft the url
3837  std::string catalog(g_catalog_channel == "" ? "master" : g_catalog_channel);
3838  std::string url(g_catalog_custom_url);
3839  if (catalog != "custom") {
3840  url = std::string(DOWNLOAD_REPO_PROTO);
3841  ocpn::replace(url, "@branch@", catalog);
3842  }
3843  // Download to a temp file
3844  std::string filePath =
3845  wxFileName::CreateTempFileName("ocpn_dl").ToStdString();
3846 
3847  auto catalogHdlr = CatalogHandler::getInstance();
3848 
3849  g_Platform->ShowBusySpinner();
3850  auto status = catalogHdlr->DownloadCatalog(filePath, url);
3851  g_Platform->HideBusySpinner();
3852 
3853  std::string message;
3854  if (status != CatalogHandler::ServerStatus::OK) {
3855  message = _("Cannot download data from url");
3856  OCPNMessageBox(this, message, _("OpenCPN Catalog update"),
3857  wxICON_ERROR | wxOK);
3858  return;
3859  }
3860 
3861  // TODO Validate xml using xsd here....
3862 #ifdef __ANDROID__
3863  if (!AndroidSecureCopyFile(wxString(filePath.c_str()),
3864  g_Platform->GetPrivateDataDir() +
3865  wxFileName::GetPathSeparator() +
3866  _T("ocpn-plugins.xml"))) {
3867  OCPNMessageBox(this, _("Unable to copy catalog file"),
3868  _("OpenCPN Catalog update"), wxICON_ERROR | wxOK);
3869  return;
3870  }
3871 #else
3872  // Copy the downloaded file to proper local location
3873  if (!wxCopyFile(wxString(filePath.c_str()),
3874  g_Platform->GetPrivateDataDir() +
3875  wxFileName::GetPathSeparator() +
3876  _T("ocpn-plugins.xml"))) {
3877  OCPNMessageBox(this, _("Unable to copy catalog file"),
3878  _("OpenCPN Catalog update"), wxICON_ERROR | wxOK);
3879  return;
3880  }
3881 #endif
3882 
3883  // If this is the "master" catalog, also copy to plugin cache
3884  if (catalog == "master") {
3885  if (!ocpn::store_metadata(filePath.c_str())) {
3886  OCPNMessageBox(this, _("Unable to copy catalog file to cache"),
3887  _("OpenCPN Catalog update"), wxICON_ERROR | wxOK);
3888  return;
3889  }
3890  }
3891 
3892  // Record in the config file the name of the catalog downloaded
3893  pConfig->SetPath(_T("/PlugIns/"));
3894  pConfig->Write("LatestCatalogDownloaded", catalog.c_str());
3895  pConfig->Flush();
3896 
3897  // Reset the PluginHandler catalog file source.
3898  // This will case the Handler to find, load, and parse the just-downloaded
3899  // catalog as copied to g_Platform->GetPrivateDataDir()...
3900  auto pluginHandler = PluginHandler::getInstance();
3901  pluginHandler->setMetadata("");
3902 
3903  // Also clear the cached values in the CatalogHandler, forcing
3904  // a reload and parse of the catalog.
3905  auto cataloghdlr = CatalogHandler::getInstance();
3906  cataloghdlr->ClearCatalogData();
3907 
3908 
3909  // Reload all plugins, which will also update the status fields
3910  LoadAllPlugIns(false);
3911 
3912  // Update this Panel, and the entire list.
3913 #ifndef __ANDROID__
3914  m_catalogText->SetLabel(GetCatalogText(true));
3915 #endif
3916  if (m_PluginListPanel) m_PluginListPanel->ReloadPluginPanels();
3917  OCPNMessageBox(this, _("Catalog update successful"),
3918  _("OpenCPN Catalog update"), wxICON_INFORMATION | wxOK);
3919 }
3920 
3921 void CatalogMgrPanel::OnPluginSettingsButton(wxCommandEvent& event) {
3922  auto dialog = new CatalogSettingsDialog(this);
3923 
3924 #ifdef __ANDROID__
3925  androidDisableRotation();
3926 #endif
3927 
3928  dialog->ShowModal();
3929 
3930 #ifdef __ANDROID__
3931  androidEnableRotation();
3932 #endif
3933 }
3934 
3935 void CatalogMgrPanel::OnTarballButton(wxCommandEvent& event) {
3936  // Present a file selector dialog to get the file name..
3937  wxString path;
3938  int response = g_Platform->DoFileSelectorDialog(
3939  this, &path, _("Select tarball file"), GetImportInitDir(), "",
3940  "tar files (*.tar.gz)|*.tar.gz|All Files (*.*)|*.*");
3941 
3942  if (response != wxID_OK) {
3943  return;
3944  }
3945  auto handler = PluginHandler::getInstance();
3946  PluginMetadata metadata;
3947  bool ok = handler->ExtractMetadata(path.ToStdString(), metadata);
3948  if (!ok) {
3949  OCPNMessageBox(this, _("Error extracting metadata from tarball."),
3950  _("OpenCPN Plugin Import Error"));
3951  return;
3952  }
3953  if (!PluginHandler::isCompatible(metadata)) {
3954  OCPNMessageBox(this, _("Incompatible import plugin detected."),
3955  _("OpenCPN Plugin Import Error"));
3956  handler->uninstall(metadata.name);
3957  return;
3958  }
3959  UninstallPlugin(metadata.name);
3960  ok = handler->installPlugin(metadata, path.ToStdString());
3961  if (!ok) {
3962  OCPNMessageBox(this, _("Error extracting import plugin tarball."),
3963  _("OpenCPN Plugin Import Error"));
3964  return;
3965  }
3966  metadata.is_imported = true;
3967  auto metadata_path = PluginHandler::ImportedMetadataPath(metadata.name);
3968  std::ofstream file(metadata_path);
3969  file << metadata.to_string();
3970  if (!file.good()) {
3971  WARNING_LOG << "Error saving metadata file: " << metadata_path
3972  << " for imported plugin: " << metadata.name;
3973  }
3974  LoadAllPlugIns(false, true);
3975  PluginHandler::getInstance()->SetInstalledMetadata(metadata);
3976  m_PluginListPanel->ReloadPluginPanels();
3977  wxString ws(_("Plugin"));
3978  ws += metadata.name + _(" successfully imported");
3979  OCPNMessageBox(gFrame, ws, _("Installation complete"),
3980  wxICON_INFORMATION | wxOK | wxCENTRE);
3981 }
3982 
3983 wxString CatalogMgrPanel::GetCatalogText(bool updated) {
3984  wxString catalog;
3985  catalog = updated ? _("Active Catalog") : _("Last Catalog");
3986  catalog += _T(": ");
3987 
3988  // Check the config file to learn what was the last catalog downloaded.
3989  pConfig->SetPath(_T("/PlugIns/"));
3990  wxString latestCatalog =
3991  pConfig->Read(_T("LatestCatalogDownloaded"), _T("default"));
3992  catalog += latestCatalog;
3993 
3994 #ifndef __ANDROID__
3995  // Get the version from the currently active catalog, by which we mean
3996  // the latest catalog parsed.
3997  auto pluginHandler = PluginHandler::getInstance();
3998  std::string date = pluginHandler->GetCatalogData()->date;
3999 
4000  catalog += wxString(" ") + _("Last change: ") + " " + date;
4001  if (!updated) catalog += _T(" : ") + _("Please Update Plugin Catalog.");
4002 #endif
4003 
4004  return catalog;
4005 }
4006 
4007 void CatalogMgrPanel::SetUpdateButtonLabel() {
4008  wxString label = _("Update Plugin Catalog");
4009  label += _T(": ");
4010  label += g_catalog_channel == "" ? "master" : g_catalog_channel;
4011  m_updateButton->SetLabel(label);
4012  Layout();
4013 }
4014 
4015 wxString CatalogMgrPanel::GetImportInitDir() {
4016  // Check the config file for the last Import path.
4017  pConfig->SetPath(_T("/PlugIns/"));
4018  wxString lastImportDir;
4019  lastImportDir = pConfig->Read(_T("LatestImportDir"),
4020  g_Platform->GetWritableDocumentsDir());
4021  if (wxDirExists(lastImportDir)) {
4022  return lastImportDir;
4023  }
4024  return (g_Platform->GetWritableDocumentsDir());
4025 }
4026 
4027 BEGIN_EVENT_TABLE(PluginListPanel, wxScrolledWindow)
4028 // EVT_BUTTON( ID_CMD_BUTTON_PERFORM_ACTION,
4029 // PluginListPanel::OnPluginPanelAction )
4030 END_EVENT_TABLE()
4031 
4032 PluginListPanel::PluginListPanel(wxWindow* parent, wxWindowID id,
4033  const wxPoint& pos, const wxSize& size)
4034  : wxScrolledWindow(parent, id, pos, size, wxTAB_TRAVERSAL | wxVSCROLL),
4035  m_PluginSelected(0) {
4036  m_is_loading.clear();
4037  SetSizer(new wxBoxSizer(wxVERTICAL));
4038  ReloadPluginPanels();
4039 }
4040 
4041 void PluginListPanel::SelectByName(wxString& name) {
4042  for (auto it = GetChildren().GetFirst(); it; it = it->GetNext()) {
4043  auto pluginPanel = dynamic_cast<PluginPanel*>(it->GetData());
4044  if (pluginPanel) {
4045  if (pluginPanel->GetPluginPtr()->m_common_name.IsSameAs(name)) {
4046  pluginPanel->SetSelected(true);
4047  pluginPanel->Layout();
4048  SelectPlugin(pluginPanel);
4049  break;
4050  }
4051  }
4052  }
4053 }
4054 
4056 std::vector<const PlugInData*> GetInstalled() {
4057  std::vector<const PlugInData*> result;
4058  auto loader = PluginLoader::getInstance();
4059  for (size_t i = 0; i < loader->GetPlugInArray()->GetCount(); i++) {
4060  auto const item = loader->GetPlugInArray()->Item(i);
4061  if (item->m_managed_metadata.name.empty()) {
4062  const auto name = item->m_common_name.ToStdString();
4063  item->m_managed_metadata = PluginLoader::MetadataByName(name);
4064  }
4065  PluginLoader::UpdatePlugin(item, item->m_managed_metadata);
4066  result.push_back(item);
4067  }
4068  auto compare = [](const PlugInData* lhs, const PlugInData* rhs) {
4069  std::string slhs, srhs;
4070  for (auto &cl : lhs->Key()) slhs += toupper(cl);
4071  for (auto &cr : rhs->Key()) srhs += toupper(cr);
4072  return slhs.compare(srhs) < 0;
4073  };
4074  std::sort(result.begin(), result.end(), compare);
4075  return result;
4076 }
4077 
4078 /* Is plugin with given name present in loaded or safe list if installed? */
4079 static bool IsPluginLoaded(const std::string& name) {
4080  if (safe_mode::get_mode()) {
4081  auto installed = PluginHandler::getInstance()->GetInstalldataPlugins();
4082  auto found =
4083  std::find(installed.begin(), installed.end(), ocpn::tolower(name));
4084  return found != installed.end();
4085  } else {
4086  auto loaded = PluginLoader::getInstance()->GetPlugInArray();
4087  for (size_t i = 0; i < loaded->GetCount(); i++) {
4088  if (loaded->Item(i)->m_common_name.ToStdString() == name) return true;
4089  }
4090  return false;
4091  }
4092 }
4093 
4095  if (m_is_loading.test_and_set()) {
4096  // recursive call...
4097  DEBUG_LOG << "LoadAllPlugins: recursive invocation";
4098  return;
4099  }
4100 
4101  auto plugins = PluginLoader::getInstance()->GetPlugInArray();
4102  m_PluginItems.Clear();
4103 
4104  wxWindowList kids = GetChildren();
4105  for (unsigned int i = 0; i < kids.GetCount(); i++) {
4106  wxWindowListNode* node = kids.Item(i);
4107  wxWindow* win = node->GetData();
4108  PluginPanel* pp = dynamic_cast<PluginPanel*>(win);
4109  if (pp) win->Destroy();
4110  }
4111  GetSizer()->Clear();
4112 
4113  Hide();
4114  m_PluginSelected = 0;
4115 
4116  if (safe_mode::get_mode()) {
4118  auto installed = PluginHandler::getInstance()->GetInstalldataPlugins();
4119  for (const auto& name : installed) AddPlugin(name) ;
4120  } else {
4121  /* The catalog entries. */
4122  auto available = getCompatiblePlugins();
4123 
4124  /* Remove those which are loaded or in safe list. */
4125  auto predicate = [](const PluginMetadata& md) {
4126  return IsPluginLoaded(md.name);
4127  };
4128  auto end = std::remove_if(available.begin(), available.end(), predicate);
4129  available.erase(end, available.end());
4130 
4131  // Sort on case-insensitive name
4132  struct CompSort {
4133  bool operator()(const PluginMetadata& lhs, const PluginMetadata rhs) const {
4134  std::string slhs, srhs;
4135  for (auto &cl : lhs.name) slhs += toupper(cl);
4136  for (auto &cr : rhs.name) srhs += toupper(cr);
4137  return slhs.compare(srhs) < 0;
4138  }
4139  } comp_sort;
4140 
4141  std::set<PluginMetadata, CompSort> unique_sorted_entries(comp_sort);
4142  for (const auto& p : available) unique_sorted_entries.insert(p);
4143 
4144  // Build the list of panels.
4145 
4146  // Add Installed and active plugins
4147  for (const auto& p : GetInstalled())
4148  if (p->m_enabled) AddPlugin(*p);
4149 
4150  // Add Installed and inactive plugins
4151  for (const auto& p : GetInstalled())
4152  if (!p->m_enabled) AddPlugin(*p);
4153 
4154  // Add available plugins, sorted
4155  for (const auto& p : unique_sorted_entries) AddPlugin(PlugInData(p));
4156  }
4157 
4158  Show();
4159  Layout();
4160  Refresh(true);
4161  Scroll(0, 0);
4162 
4163  m_is_loading.clear();
4164 }
4165 
4166 void PluginListPanel::AddPlugin(const std::string& name) {
4167  auto panel = new PluginPanel(this, name);
4168  DimeControl(panel);
4169  panel->SetSelected(false);
4170  GetSizer()->Add(panel, 0, wxEXPAND);
4171  m_PluginItems.Add(panel);
4172  m_pluginSpacer = g_Platform->GetDisplayDPmm() * 1.0;
4173  GetSizer()->AddSpacer(m_pluginSpacer);
4174 }
4175 
4176 void PluginListPanel::AddPlugin(const PlugInData& pic) {
4177  auto pPluginPanel =
4178  new PluginPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, pic);
4179  pPluginPanel->SetSelected(false);
4180  GetSizer()->Add(pPluginPanel, 0, wxEXPAND);
4181  m_PluginItems.Add(pPluginPanel);
4182 
4183  m_pluginSpacer = g_Platform->GetDisplayDPmm() * 1.0;
4184  GetSizer()->AddSpacer(m_pluginSpacer);
4185 
4186  // wxStaticLine* itemStaticLine = new wxStaticLine( m_panel, wxID_ANY,
4187  // wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
4188  // m_pitemBoxSizer01->Add( itemStaticLine, wxSizerFlags().Expand());
4189 }
4190 
4191 // When a child Panel is selected, its size grows to include "Preferences"
4192 // and Enable" buttons. As a consequence, the vertical size of the
4193 // ListPanel grows as well.Calculate and add a spacer to bottom of
4194 // ListPanel so that initial ListPanel minimum size calculations account
4195 // for selected Panel size growth. Sadly, this does not work right on wxQt.
4196 // So, just punt for now...
4197 int PluginListPanel::ComputePluginSpace(ArrayOfPluginPanel plugins,
4198  wxBoxSizer* sizer) {
4199  int max_dy = 0;
4200  for (size_t i = 0; i < plugins.GetCount(); i++) {
4201  auto panel = plugins.Item(i);
4202  bool was_selected = panel->GetSelected();
4203  panel->SetSelected(false);
4204  sizer->Layout();
4205  wxSize unselected = panel->GetSize();
4206 
4207  panel->SetSelected(true); // switch to selected, a bit bigger
4208  sizer->Layout();
4209  wxSize selected = panel->GetSize();
4210 
4211  int dy = selected.GetHeight() - unselected.GetHeight();
4212  max_dy = wxMax(max_dy, dy);
4213  panel->SetSelected(was_selected);
4214  }
4215  return max_dy;
4216 }
4217 
4218 PluginListPanel::~PluginListPanel() {}
4219 
4220 void PluginListPanel::UpdateSelections() {
4221  for (unsigned int i = 0; i < m_PluginItems.GetCount(); i++) {
4222  PluginPanel* pPluginPanel = m_PluginItems[i];
4223  if (pPluginPanel) {
4224  pPluginPanel->SetSelected(pPluginPanel->GetSelected());
4225  }
4226  }
4227 }
4228 
4229 void PluginListPanel::SelectPlugin(PluginPanel* pi) {
4230  int xs, ys;
4231  GetViewStart(&xs, &ys);
4232  Scroll(0, 0);
4233 
4234  if (m_PluginSelected) {
4235  m_PluginSelected->SetSelected(false);
4236  m_PluginSelected->Layout();
4237  }
4238 
4239  if (pi == NULL) m_PluginSelected->SetSelected(false);
4240 
4241  m_PluginSelected = pi;
4242 
4243  GetSizer()->Layout();
4244  Refresh(false);
4245  wxSize size = GetBestVirtualSize();
4246  SetVirtualSize(size);
4247 
4248  // Measure, and ensure that the selected item is fully visible in the
4249  // vertical scroll box.
4250  int htop = 0;
4251  for (unsigned int i = 0; i < m_PluginItems.GetCount(); i++) {
4252  PluginPanel* pPluginPanel = m_PluginItems[i];
4253  int yd = pPluginPanel->GetSize().y;
4254  htop += yd;
4255  htop += m_pluginSpacer;
4256  if (pPluginPanel == pi) {
4257  int piBottom = htop - (ys * g_options->GetScrollRate());
4258  if (piBottom > GetClientSize().y) {
4259  ys += (piBottom - GetClientSize().y) / g_options->GetScrollRate();
4260  }
4261  break;
4262  }
4263  }
4264 
4265  Scroll(xs, ys);
4266 }
4267 
4268 void PluginListPanel::MoveUp(PluginPanel* pi) {
4269  int pos = m_PluginItems.Index(pi);
4270  if (pos == 0) // The first one can't be moved further up
4271  return;
4272  m_PluginItems.RemoveAt(pos);
4273  // m_pitemBoxSizer01->Remove( pos * 2 + 1 );
4274  // m_pitemBoxSizer01->Remove( pos * 2 );
4275  m_PluginItems.Insert(pi, pos - 1);
4276  wxStaticLine* itemStaticLine = new wxStaticLine(
4277  this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL);
4278  // m_pitemBoxSizer01->Insert( (pos - 1) * 2, itemStaticLine, 0,
4279  // wxEXPAND|wxALL, 0 ); m_pitemBoxSizer01->Insert( (pos - 1) * 2, pi, 0,
4280  // wxEXPAND|wxALL, 0 );
4281 
4282  m_PluginSelected = pi;
4283 
4284  GetSizer()->Layout();
4285  m_parent->Layout();
4286  Refresh(true);
4287 }
4288 
4289 void PluginListPanel::MoveDown(PluginPanel* pi) {
4290  int pos = m_PluginItems.Index(pi);
4291  if (pos == (int)m_PluginItems.Count() -
4292  1) // The last one can't be moved further down
4293  return;
4294  m_PluginItems.RemoveAt(pos);
4295  // m_pitemBoxSizer01->Remove( pos * 2 + 1 );
4296  // m_pitemBoxSizer01->Remove( pos * 2 );
4297  m_PluginItems.Insert(pi, pos + 1);
4298  wxStaticLine* itemStaticLine = new wxStaticLine(
4299  this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL);
4300  // m_pitemBoxSizer01->Insert( (pos + 1) * 2 - 1, itemStaticLine, 0,
4301  // wxEXPAND|wxALL, 0 ); m_pitemBoxSizer01->Insert( (pos + 1) * 2, pi, 0,
4302  // wxEXPAND|wxALL, 0 );
4303 
4304  m_PluginSelected = pi;
4305 
4306  GetSizer()->Layout();
4307  m_parent->Layout();
4308  Refresh(false);
4309 }
4310 
4311 static bool canUninstall(std::string name) {
4312  PluginHandler* pluginHandler = PluginHandler::getInstance();
4313  // std::transform(name.begin(), name.end(), name.begin(), ::tolower);
4314 
4315  for (auto plugin : pluginHandler->getInstalled()) {
4316  if (plugin.name == name) {
4317  if (safe_mode::get_mode())
4318  return true;
4319  else
4320  return !plugin.readonly;
4321  }
4322  }
4323  return false;
4324 }
4325 
4326 PluginPanel::PluginPanel(wxPanel* parent, const std::string& name)
4327  : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize,
4328  wxBORDER_NONE),
4329  m_is_safe_panel(true) {
4330 
4331  m_PluginListPanel = dynamic_cast<PluginListPanel*>(parent);
4332  wxASSERT(m_PluginListPanel != 0);
4333  wxBoxSizer* top_sizer = new wxBoxSizer(wxVERTICAL);
4334  SetSizer(top_sizer);
4335  wxBoxSizer* top_horizontal = new wxBoxSizer(wxHORIZONTAL);
4336  top_sizer->Add(top_horizontal, 0, wxEXPAND);
4337 
4338  double iconSize = GetCharWidth() * 4;
4339  double dpi_mult = g_Platform->GetDisplayDIPMult(this);
4340  int icon_scale = iconSize * dpi_mult;
4341  ocpnStyle::Style* style = g_StyleManager->GetCurrentStyle();
4342  wxBitmap bitmap(style->GetIcon("default_pi", icon_scale, icon_scale));
4343  m_itemStaticBitmap = new wxStaticBitmap(this, wxID_ANY, bitmap);
4344  top_horizontal->Add(m_itemStaticBitmap, 0, wxEXPAND | wxALL, 10);
4345 
4346  m_pName = new wxStaticText(this, wxID_ANY, name);
4347  top_horizontal->Add(m_pName, wxID_ANY, wxALIGN_CENTER_VERTICAL);
4348  m_pVersion = new wxStaticText(this, wxID_ANY, "");
4349  top_horizontal->Add(m_pVersion);
4350  m_pVersion->Hide();
4351 
4352  m_pButtons = new wxBoxSizer(wxHORIZONTAL);
4353  top_horizontal->Add(m_pButtons);
4354  m_info_btn = new WebsiteButton(this, "https:\\opencpn.org");
4355  top_horizontal->Add(m_info_btn);
4356  m_pButtonUninstall = new wxButton(this, wxID_ANY, _("Uninstall"),
4357  wxDefaultPosition, wxDefaultSize, 0);
4358  top_horizontal->Add(m_pButtonUninstall, 0,
4359  wxALIGN_CENTER_VERTICAL | wxALL, 2);
4360  auto uninstall = [&](wxCommandEvent ev) {
4361  auto n = m_pName->GetLabel().ToStdString();
4362  int result = OCPNMessageBox(gFrame,
4363  std::string(_("Uninstall plugin ")) + n + "?",
4364  _("Un-Installation"),
4365  wxICON_QUESTION | wxOK | wxCANCEL);
4366  if (result != wxID_OK) return;
4367  PluginHandler::getInstance()->ClearInstallData(n);
4368  m_PluginListPanel->ReloadPluginPanels();
4369  };
4370  m_pButtonUninstall->Bind(wxEVT_COMMAND_BUTTON_CLICKED, uninstall);
4371 }
4372 
4373 BEGIN_EVENT_TABLE(PluginPanel, wxPanel)
4374 EVT_PAINT(PluginPanel::OnPaint)
4375 END_EVENT_TABLE()
4376 
4377 PluginPanel::PluginPanel(wxPanel* parent, wxWindowID id, const wxPoint& pos,
4378  const wxSize& size, const PlugInData plugin)
4379  : wxPanel(parent, id, pos, size, wxBORDER_NONE),
4380  m_plugin(plugin),
4381  m_is_safe_panel(false) {
4382  m_PluginListPanel = (PluginListPanel*)parent; //->GetParent();
4383  m_PluginListPanel = dynamic_cast<PluginListPanel*>(parent /*->GetParent()*/);
4384  wxASSERT(m_PluginListPanel != 0);
4385 
4386  m_bSelected = false;
4387  m_penWidthUnselected = g_Platform->GetDisplayDPmm() * .25;
4388  m_penWidthSelected = g_Platform->GetDisplayDPmm() * .5;
4389 
4390  wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL);
4391  SetSizer(topSizer);
4392 
4393  wxBoxSizer* itemBoxSizer01 = new wxBoxSizer(wxHORIZONTAL);
4394  topSizer->Add(itemBoxSizer01, 0, wxEXPAND);
4395  Bind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this);
4396  Bind(wxEVT_LEFT_UP, &PluginPanel::OnPluginSelectedUp, this);
4397 
4398  double iconSize = GetCharWidth() * 4;
4399  double dpi_mult = g_Platform->GetDisplayDIPMult(this);
4400  int icon_scale = iconSize * dpi_mult;
4401 
4402  wxImage plugin_icon;
4403  ocpnStyle::Style* style = g_StyleManager->GetCurrentStyle();
4404  if (m_plugin.m_bitmap.IsOk()) {
4405  plugin_icon = m_plugin.m_bitmap.ConvertToImage();
4406  }
4407  wxBitmap bitmap;
4408  if (m_plugin.m_status == PluginStatus::ManagedInstallAvailable) {
4409  wxFileName path(g_Platform->GetSharedDataDir(), "packageBox.svg");
4410  path.AppendDir("uidata");
4411  path.AppendDir("traditional"); // FIXME(leamas) cache it.
4412  bitmap = LoadSVG(path.GetFullPath(), icon_scale, icon_scale);
4413  } else if (plugin_icon.IsOk()) {
4414  int nowSize = plugin_icon.GetWidth();
4415  plugin_icon.Rescale(icon_scale, icon_scale, wxIMAGE_QUALITY_HIGH);
4416  bitmap = wxBitmap(plugin_icon);
4417  } else {
4418  bitmap = wxBitmap(style->GetIcon("default_pi", icon_scale, icon_scale));
4419  }
4420  m_itemStaticBitmap = new wxStaticBitmap(this, wxID_ANY, bitmap);
4421 
4422  itemBoxSizer01->Add(m_itemStaticBitmap, 0, wxEXPAND | wxALL, 10);
4423  m_itemStaticBitmap->Bind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected,
4424  this);
4425  m_itemStaticBitmap->Bind(wxEVT_LEFT_UP, &PluginPanel::OnPluginSelectedUp,
4426  this);
4427 
4428  wxBoxSizer* itemBoxSizer02 = new wxBoxSizer(wxVERTICAL);
4429  itemBoxSizer01->Add(itemBoxSizer02, 1, wxEXPAND | wxALL, 0);
4430 
4431  // Calculate character width available
4432  int nChars = g_options->GetSize().x / GetCharWidth();
4433  bool bCompact = false;
4434  if (nChars < 60) // Arbitrary, detecting mobile devices in portrait mode.
4435  bCompact = true;
4436 
4437  if (bCompact) {
4438  // Might need to shorten the Plugin name string
4439  wxString nameString = m_plugin.m_common_name;
4440  int maxWidth = g_Platform->getDisplaySize().x * 3 / 10;
4441  wxScreenDC dc;
4442  int nameWidth;
4443  dc.GetTextExtent(m_plugin.m_common_name, &nameWidth, NULL);
4444  if (nameWidth > maxWidth) {
4445  nameString = wxControl::Ellipsize(m_plugin.m_common_name, dc,
4446  wxELLIPSIZE_END, maxWidth);
4447  }
4448  m_pName = new wxStaticText(this, wxID_ANY, nameString);
4449  m_pName->Bind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this);
4450  m_pName->Bind(wxEVT_LEFT_UP, &PluginPanel::OnPluginSelectedUp, this);
4451  itemBoxSizer02->Add(m_pName, 0, /*wxEXPAND|*/ wxALL, 5);
4452 
4453  wxFlexGridSizer* sl1 = new wxFlexGridSizer(2, 0, 0);
4454  sl1->AddGrowableCol(1);
4455  itemBoxSizer02->Add(sl1, 0, wxEXPAND);
4456 
4457  m_pVersion = new wxStaticText(this, wxID_ANY, _T("X.YY.ZZ.AA"));
4458  sl1->Add(m_pVersion, 0, /*wxEXPAND|*/ wxALL, 5);
4459  if (m_plugin.m_status == PluginStatus::ManagedInstallAvailable) {
4460  m_pVersion->Hide();
4461  }
4462  m_pVersion->Bind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this);
4463  m_pVersion->Bind(wxEVT_LEFT_UP, &PluginPanel::OnPluginSelectedUp, this);
4464 
4465  m_cbEnable = new wxCheckBox(this, wxID_ANY, _("Enabled"));
4466  sl1->Add(m_cbEnable, 1, wxALIGN_RIGHT | wxTOP, 5);
4467  m_cbEnable->Bind(wxEVT_CHECKBOX, &PluginPanel::OnPluginEnableToggle, this);
4468 
4469  // Might need to shorten the Plugin description string
4470  wxString descriptionString = m_plugin.m_short_description;
4471  int maxDescriptionWidth = g_Platform->getDisplaySize().x - (iconSize * 4);
4472  int descriptionWidth;
4473  dc.GetTextExtent(m_plugin.m_short_description, &descriptionWidth, NULL);
4474  if (descriptionWidth > maxDescriptionWidth)
4475  descriptionString =
4476  wxControl::Ellipsize(m_plugin.m_short_description, dc,
4477  wxELLIPSIZE_END, maxDescriptionWidth);
4478 
4479  // This invocation has the effect of setting the minimum width of the
4480  // descriptor field.
4481  m_pDescription =
4482  new wxStaticText(this, wxID_ANY, descriptionString, wxDefaultPosition,
4483  wxSize(maxDescriptionWidth, -1), wxST_NO_AUTORESIZE);
4484  itemBoxSizer02->Add(m_pDescription, 0, wxEXPAND | wxALL, 5);
4485  m_pDescription->Bind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this);
4486  m_pDescription->Bind(wxEVT_LEFT_UP, &PluginPanel::OnPluginSelectedUp, this);
4487 
4488  } else {
4489  wxFlexGridSizer* itemBoxSizer03 = new wxFlexGridSizer(4, 0, 0);
4490  itemBoxSizer03->AddGrowableCol(2);
4491  itemBoxSizer02->Add(itemBoxSizer03, 0, wxEXPAND);
4492 
4493  wxString nameString = m_plugin.m_common_name;
4494  m_pName = new wxStaticText(this, wxID_ANY, nameString);
4495  m_pName->Bind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this);
4496  m_pName->Bind(wxEVT_LEFT_UP, &PluginPanel::OnPluginSelectedUp, this);
4497 
4498  // Avoid known bug in wxGTK3
4499 #ifndef __WXGTK3__
4500  wxFont font = GetFont();
4501  font.SetWeight(wxFONTWEIGHT_BOLD);
4502  m_pName->SetFont(font);
4503 #endif
4504 
4505  itemBoxSizer03->Add(m_pName, 0, /*wxEXPAND|*/ wxALL, 10);
4506 
4507  m_pVersion = new wxStaticText(this, wxID_ANY, _T("X.YY.ZZ.AA"));
4508  itemBoxSizer03->Add(m_pVersion, 0, /*wxEXPAND|*/ wxALL, 10);
4509  if (m_plugin.m_status == PluginStatus::ManagedInstallAvailable ||
4510  m_plugin.m_status == PluginStatus::System ||
4511  (m_plugin.m_status == PluginStatus::Unmanaged &&
4512  !m_plugin.m_managed_metadata.is_orphan) ) {
4513  m_pVersion->Hide();
4514  }
4515  m_pVersion->Bind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this);
4516  m_pVersion->Bind(wxEVT_LEFT_UP, &PluginPanel::OnPluginSelectedUp, this);
4517 
4518  m_cbEnable = new wxCheckBox(this, wxID_ANY, _("Enabled"));
4519  itemBoxSizer03->Add(m_cbEnable, 1, wxALIGN_RIGHT | wxTOP, 10);
4520  m_cbEnable->Bind(wxEVT_CHECKBOX, &PluginPanel::OnPluginEnableToggle, this);
4521 
4522  itemBoxSizer03->Add(5 * GetCharWidth(), 1, 0, wxALIGN_RIGHT | wxTOP, 10);
4523 
4524  m_pDescription = new wxStaticText(
4525  this, wxID_ANY, m_plugin.m_short_description, wxDefaultPosition,
4526  wxSize(-1, -1) /*, wxST_NO_AUTORESIZE*/);
4527  itemBoxSizer02->Add(m_pDescription, 1, wxEXPAND | wxALL, 5);
4528  m_pDescription->Bind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this);
4529  m_pDescription->Bind(wxEVT_LEFT_UP, &PluginPanel::OnPluginSelectedUp, this);
4530  }
4531 
4532  if (!bCompact) {
4533  m_info_btn = new WebsiteButton(this, "https:\\opencpn.org");
4534  m_info_btn->Hide();
4535  itemBoxSizer02->Add(m_info_btn, 0);
4536 
4537  m_pButtons = new wxBoxSizer(wxHORIZONTAL);
4538  itemBoxSizer02->Add(m_pButtons, 0, /*wxEXPAND|*/ wxALL, 0);
4539  m_pButtonPreferences = new wxButton(this, wxID_ANY, _("Preferences"),
4540  wxDefaultPosition, wxDefaultSize, 0);
4541  m_pButtons->Add(m_pButtonPreferences, 0, wxALIGN_LEFT | wxALL, 2);
4542 
4543  m_pButtons->AddSpacer(3 * GetCharWidth());
4544 
4545  m_pButtonAction =
4546  new wxButton(this, wxID_ANY, "Upgrade to Version XX.XX.XX",
4547  wxDefaultPosition, wxDefaultSize, 0);
4548  m_pButtons->Add(m_pButtonAction, 0, wxALIGN_LEFT | wxALL, 2);
4549 
4550  m_pButtonUninstall = new wxButton(this, wxID_ANY, _("Uninstall"),
4551  wxDefaultPosition, wxDefaultSize, 0);
4552  m_pButtons->Add(m_pButtonUninstall, 0, wxALIGN_LEFT | wxALL, 2);
4553  } else {
4554  m_pButtons = new wxBoxSizer(wxVERTICAL);
4555  itemBoxSizer02->Add(m_pButtons, 0, /*wxEXPAND|*/ wxALL, 0);
4556 
4557  wxBoxSizer* tline = new wxBoxSizer(wxHORIZONTAL);
4558  m_pButtons->Add(tline, 0, wxALL, 2);
4559 
4560  m_pButtonPreferences = new wxButton(this, wxID_ANY, _("Preferences"),
4561  wxDefaultPosition, wxDefaultSize, 0);
4562  tline->Add(m_pButtonPreferences, 0, wxALIGN_LEFT | wxALL, 0);
4563 
4564  tline->AddSpacer(3 * GetCharWidth());
4565 
4566  m_info_btn = new WebsiteButton(this, "https:\\opencpn.org");
4567  m_info_btn->Hide();
4568  tline->Add(m_info_btn, 0);
4569 
4570  m_pButtonAction =
4571  new wxButton(this, wxID_ANY, "Upgrade to Version XX.XX.XX",
4572  wxDefaultPosition, wxDefaultSize);
4573  m_pButtons->Add(m_pButtonAction, 0, wxALIGN_LEFT | wxALL, 2);
4574 
4575  m_pButtonUninstall = new wxButton(this, wxID_ANY, _("Uninstall"),
4576  wxDefaultPosition, wxDefaultSize, 0);
4577  m_pButtons->Add(m_pButtonUninstall, 0, wxALIGN_LEFT | wxALL, 2);
4578  }
4579 
4580  wxBitmap statusBitmap;
4581  const auto stat = m_plugin.m_status;
4582  auto icon_name = icon_by_status.at(stat);
4583 
4584  wxFileName path(g_Platform->GetSharedDataDir(), icon_name);
4585  path.AppendDir("uidata");
4586  path.AppendDir("traditional");
4587  bool ok = false;
4588  int bmsize = GetCharWidth() * 3 * dpi_mult;
4589  if (path.IsFileReadable()) {
4590  statusBitmap = LoadSVG(path.GetFullPath(), bmsize, bmsize);
4591  ok = statusBitmap.IsOk();
4592  }
4593  if (!ok) {
4594  auto style = g_StyleManager->GetCurrentStyle();
4595  statusBitmap = wxBitmap(style->GetIcon(_T("default_pi"), bmsize, bmsize));
4596  wxLogMessage("Icon: %s not found.", path.GetFullPath());
4597  }
4598 
4599  m_itemStatusIconBitmap = new wxStaticBitmap(this, wxID_ANY, statusBitmap);
4600  m_itemStatusIconBitmap->SetToolTip(message_by_status(stat));
4601  m_itemStatusIconBitmap->Bind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this);
4602  m_itemStatusIconBitmap->Bind(wxEVT_LEFT_UP, &PluginPanel::OnPluginSelectedUp, this);
4603 
4604  itemBoxSizer01->Add(m_itemStatusIconBitmap, 0, wxEXPAND | wxALL, 20);
4605 
4606  itemBoxSizer02->AddSpacer(GetCharWidth());
4607 
4608  m_pButtonPreferences->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
4609  &PluginPanel::OnPluginPreferences, this);
4610  m_pButtonUninstall->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
4611  &PluginPanel::OnPluginUninstall, this);
4612  m_pButtonAction->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
4613  &PluginPanel::OnPluginAction, this);
4614 
4615  SetSelected(m_bSelected);
4616  SetAutoLayout(true);
4617  Fit();
4618 }
4619 
4620 PluginPanel::PluginPanel(wxPanel* parent, wxWindowID id, const wxPoint& pos,
4621  const wxSize& size, PluginMetadata md)
4622  : PluginPanel(parent, id, pos, size, PlugInData(md)){};
4623 
4624 PluginPanel::~PluginPanel() {
4625  Unbind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this);
4626  if (m_is_safe_panel) return;
4627  m_itemStaticBitmap->Unbind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected,
4628  this);
4629  m_pName->Unbind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this);
4630  m_pVersion->Unbind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this);
4631  m_pDescription->Unbind(wxEVT_LEFT_DOWN, &PluginPanel::OnPluginSelected, this);
4632  if (m_pButtonAction) {
4633  m_pButtonAction->Unbind(wxEVT_COMMAND_BUTTON_CLICKED,
4634  &PluginPanel::OnPluginAction, this);
4635  }
4636  m_pButtonPreferences->Unbind(wxEVT_COMMAND_BUTTON_CLICKED,
4637  &PluginPanel::OnPluginPreferences, this);
4638  m_cbEnable->Unbind(wxEVT_COMMAND_BUTTON_CLICKED,
4639  &PluginPanel::OnPluginEnableToggle, this);
4640 }
4641 
4642 void PluginPanel::SetActionLabel(wxString& label) {
4643  m_pButtonAction->SetLabel(label);
4644  Refresh();
4645 }
4646 
4647 static wxStopWatch swclick;
4648 static int downx, downy;
4649 
4650 void PluginPanel::OnPluginSelected(wxMouseEvent& event) {
4651 #ifdef __ANDROID__
4652  swclick.Start();
4653  event.GetPosition(&downx, &downy);
4654 #else
4655  DoPluginSelect();
4656 #endif
4657 }
4658 
4659 void PluginPanel::OnPluginSelectedUp(wxMouseEvent& event) {
4660 #ifdef __ANDROID__
4661  qDebug() << swclick.Time();
4662  if (swclick.Time() < 200) {
4663  int upx, upy;
4664  event.GetPosition(&upx, &upy);
4665  if ((fabs(upx - downx) < GetCharWidth()) &&
4666  (fabs(upy - downy) < GetCharWidth())) {
4667  DoPluginSelect();
4668  }
4669  }
4670  swclick.Start();
4671 #endif
4672 }
4673 
4674 void PluginPanel::DoPluginSelect() {
4675  if (m_plugin.m_status == PluginStatus::ManagedInstallAvailable) {
4676  // auto dialog = dynamic_cast<PluginListPanel*>(GetParent());
4677  // auto dialog = dynamic_cast<PluginListPanel*>(m_parent);
4678  // wxASSERT(dialog != 0);
4679 
4680  // Install the new plugin, auto-enabling as a convenience measure.
4681  run_update_dialog(m_PluginListPanel, &m_plugin, false, 0, true);
4682  } else if (m_bSelected) {
4683  SetSelected(false);
4684  m_PluginListPanel->SelectPlugin(NULL);
4685  } else {
4686  SetSelected(true);
4687  m_PluginListPanel->SelectPlugin(this);
4688  }
4689 }
4690 
4695 static PluginMetadata GetMetadataByName(const std::string& name) {
4696  auto plugins = PluginHandler::getInstance()->getInstalled();
4697  auto predicate = [name](const PluginMetadata& pm) { return pm.name == name; };
4698  auto found = std::find_if(plugins.begin(), plugins.end(), predicate);
4699  if (found == plugins.end()) {
4700  wxLogDebug("Cannot find metadata for %s", name.c_str());
4701  }
4702  return found != plugins.end() ? *found : PluginMetadata();
4703 }
4704 
4705 void PluginPanel::SetSelected(bool selected) {
4706  m_bSelected = selected;
4707 
4708  m_pVersion->SetLabel(
4709  PluginLoader::GetPluginVersion(m_plugin, GetMetadataByName));
4710  if (selected) {
4711  SetBackgroundColour(GetDialogColor(DLG_SELECTED_BACKGROUND));
4712  m_pButtons->Show(true);
4713  bool unInstallPossible = canUninstall(m_plugin.m_common_name.ToStdString());
4714 
4715  // Directly mark Legacy and system plugins as "not uninstallable"
4716  if (m_plugin.m_status == PluginStatus::LegacyUpdateAvailable ||
4717  m_plugin.m_status == PluginStatus::Unmanaged ||
4718  m_plugin.m_status == PluginStatus::System)
4719  unInstallPossible = false;
4720 
4721  // Orphan plugins can usually be uninstalled, at best effort.
4722  if (m_plugin.m_managed_metadata.is_orphan)
4723  unInstallPossible = true;
4724 
4725  m_pButtonUninstall->Show(unInstallPossible);
4726 
4727  if (m_plugin.m_managed_metadata.info_url.size()) {
4728  m_info_btn->SetURL(m_plugin.m_managed_metadata.info_url.c_str());
4729  m_info_btn->Show();
4730  }
4731 
4732  m_cbEnable->Show(true);
4733 
4734  // Configure the "Action" button
4735  wxString label;
4736  SemanticVersion newVersion;
4737  switch (m_plugin.m_status) {
4738  case PluginStatus::LegacyUpdateAvailable:
4739  label = _("Upgrade to Version ");
4740  label += wxString(m_plugin.m_managed_metadata.version.c_str());
4741  m_action = ActionVerb::UPGRADE_TO_MANAGED_VERSION;
4742  m_pButtonAction->Enable();
4743  break;
4744 
4745  case PluginStatus::ManagedInstallAvailable:
4746  label = _("Install...");
4747  m_action = ActionVerb::INSTALL_MANAGED_VERSION;
4748  m_pButtonAction->Enable();
4749  break;
4750 
4751  case PluginStatus::ManagedInstalledUpdateAvailable:
4752  label = _("Update to ");
4753  label += wxString(m_plugin.m_managed_metadata.version.c_str());
4754  m_action = ActionVerb::UPGRADE_INSTALLED_MANAGED_VERSION;
4755  m_pButtonAction->Enable();
4756  break;
4757 
4758  case PluginStatus::ManagedInstalledCurrentVersion:
4759  label = _("Reinstall");
4760  m_action = ActionVerb::REINSTALL_MANAGED_VERSION;
4761  m_pButtonAction->Enable();
4762  break;
4763 
4764  case PluginStatus::ManagedInstalledDowngradeAvailable:
4765  label = _("Downgrade");
4766  m_action = ActionVerb::DOWNGRADE_INSTALLED_MANAGED_VERSION;
4767  m_pButtonAction->Enable();
4768  break;
4769 
4770  case PluginStatus::Unmanaged:
4771  m_action = ActionVerb::NOP;
4772  m_pButtonAction->Hide();
4773  break;
4774 
4775  case PluginStatus::System:
4776  m_action = ActionVerb::NOP;
4777  m_pButtonAction->Hide();
4778  break;
4779 
4780  default:
4781  label = "TBD";
4782  m_action = ActionVerb::NOP;
4783  break;
4784  }
4785  SetActionLabel(label);
4786  const auto plugin_name = m_plugin.m_common_name.ToStdString();
4787  if (ocpn::exists(PluginHandler::ImportedMetadataPath(plugin_name))) {
4788  m_pButtonAction->Hide();
4789  }
4790 
4791  Layout();
4792  } else {
4793  SetBackgroundColour(GetDialogColor(DLG_UNSELECTED_BACKGROUND));
4794  // m_pDescription->SetLabel( m_pPlugin->m_short_description );
4795 #ifndef __WXQT__
4796  // m_pButtons->Show(false);
4797 #else
4798  // m_pButtons->Show(true);
4799 #endif
4800  //();
4801 
4802  m_pButtons->Show(false);
4803  m_info_btn->Hide();
4804 
4805  if (m_plugin.m_status == PluginStatus::ManagedInstallAvailable)
4806  m_cbEnable->Show(false);
4807 
4808  Layout();
4809  }
4810 
4811  // m_pButtons->Show(selected); // For most platforms, show buttons if
4812  // selected m_pButtonsUpDown->Show(selected);
4813 #ifdef __ANDROID__
4814  // Some Android devices (e.g. Kyocera) have trouble with wxBitmapButton...
4815  // m_pButtonsUpDown->Show(false);
4816  // m_pButtons->Show(true); // Always enable buttons for Android
4817 #endif
4818 
4819  Layout();
4820 
4821  if (selected) {
4822  SetBackgroundColour(GetDialogColor(DLG_SELECTED_BACKGROUND));
4823  } else {
4824  SetBackgroundColour(GetDialogColor(DLG_UNSELECTED_BACKGROUND));
4825  }
4826 
4827  SetEnabled(m_plugin.m_enabled);
4828 
4829 #ifdef __ANDROID__
4830  // Android (wxQT) sizers have troubles...
4831  // So we set some layout factors to avoid re-sizing on select/deselect.
4832  // m_rgSizer->Show(true);
4833  // m_pButtons->Show(true);
4834  // m_pButtonAction->Hide();
4835  // m_pButtonUninstall->Hide();
4836 
4837  Fit();
4838  // m_PluginListPanel->m_pitemBoxSizer01->Layout();
4839 #endif
4840 }
4841 
4842 void PluginPanel::OnPaint(wxPaintEvent& event) {
4843  wxPaintDC dc(this);
4844 
4845  int penWidth = m_penWidthUnselected;
4846  wxColour color = GetDialogColor(DLG_UNSELECTED_BACKGROUND);
4847  wxColour border = GetDialogColor(DLG_UNSELECTED_ACCENT);
4848 
4849  if (m_bSelected) {
4850  penWidth = m_penWidthSelected;
4851  color = GetDialogColor(DLG_SELECTED_BACKGROUND);
4852  border = GetDialogColor(DLG_SELECTED_ACCENT);
4853  }
4854 
4855  wxBrush b(color, wxBRUSHSTYLE_SOLID);
4856  dc.SetBrush(b);
4857  dc.SetPen(wxPen(border, penWidth));
4858 
4859  dc.DrawRoundedRectangle(5, 5, GetSize().x - 10, GetSize().y - 10, 5);
4860 }
4861 
4862 void PluginPanel::OnPluginPreferences(wxCommandEvent& event) {
4863  if (m_plugin.m_enabled && m_plugin.m_init_state &&
4864  (m_plugin.m_cap_flag & WANTS_PREFERENCES)) {
4865 #ifdef __ANDROID__
4866  androidDisableRotation();
4867  PluginLoader::getInstance()->ShowPreferencesDialog(m_plugin,
4868  GetGrandParent());
4869  // GrandParent will be the entire list panel, not the plugin panel.
4870  // Ensures better centering on small screens
4871 #else
4872  PluginLoader::getInstance()->ShowPreferencesDialog(m_plugin, this);
4873 #endif
4874  }
4875 }
4876 
4877 void PluginPanel::OnPluginEnableToggle(wxCommandEvent& event) {
4878  SetEnabled(event.IsChecked());
4879  m_pVersion->SetLabel(
4880  PluginLoader::GetPluginVersion(m_plugin, GetMetadataByName));
4881  if (m_plugin.m_status == PluginStatus::System) {
4882  // Force pluginmanager to reload all panels. Not kosher --
4883  // the EventVar should really only be notified from within PluginLoader.
4884  PluginLoader::getInstance()->evt_pluglist_change.Notify();
4885  }
4886 }
4887 
4888 void PluginPanel::OnPluginUninstall(wxCommandEvent& event) {
4889  m_action = ActionVerb::UNINSTALL_MANAGED_VERSION;
4890 
4891  // Chain up to the utility event handler
4892  wxCommandEvent actionEvent(wxEVT_COMMAND_BUTTON_CLICKED);
4893  actionEvent.SetId(ID_CMD_BUTTON_PERFORM_ACTION);
4894  actionEvent.SetClientData(this);
4895  g_pi_manager->GetUtilHandler()->AddPendingEvent(actionEvent);
4896 }
4897 
4898 void PluginPanel::OnPluginAction(wxCommandEvent& event) {
4899  // Chain up to the utility event handler
4900  wxCommandEvent actionEvent(wxEVT_COMMAND_BUTTON_CLICKED);
4901  actionEvent.SetId(ID_CMD_BUTTON_PERFORM_ACTION);
4902  actionEvent.SetClientData(this);
4903  g_pi_manager->GetUtilHandler()->AddPendingEvent(actionEvent);
4904 
4905  return;
4906 }
4907 
4908 static void SetWindowFontStyle(wxWindow* window, wxFontStyle style) {
4909  auto font = window->GetFont();
4910  font.SetStyle(style);
4911  window->SetFont(font);
4912 }
4913 
4914 
4915 void PluginPanel::SetEnabled(bool enabled) {
4916  if (m_is_safe_panel) return;
4917  PluginLoader::getInstance()->SetEnabled(m_plugin.m_common_name, enabled);
4918  PluginLoader::getInstance()->UpdatePlugIns();
4919  NotifySetupOptionsPlugin(&m_plugin);
4920  if (!enabled && !m_bSelected) {
4921  SetWindowFontStyle(m_pName, wxFONTSTYLE_ITALIC);
4922  SetWindowFontStyle(m_pVersion, wxFONTSTYLE_ITALIC);
4923  SetWindowFontStyle(m_pDescription, wxFONTSTYLE_ITALIC);
4924 #ifdef x__ANDROID__
4925  m_pName->Disable();
4926  m_pVersion->Disable();
4927  m_pDescription->Disable();
4928 #endif
4929  } else {
4930  SetWindowFontStyle(m_pName, wxFONTSTYLE_NORMAL);
4931  SetWindowFontStyle(m_pVersion, wxFONTSTYLE_NORMAL);
4932  SetWindowFontStyle(m_pDescription, wxFONTSTYLE_NORMAL);
4933 #ifdef x__ANDROID__
4934  m_pName->Enable();
4935  m_pVersion->Enable();
4936  m_pDescription->Enable();
4937 #endif
4938  }
4939 
4940 #ifdef __ANDROID__
4941  m_pName->Enable(enabled || m_bSelected);
4942  m_pVersion->Enable(enabled || m_bSelected);
4943  m_pDescription->Enable(enabled || m_bSelected);
4944 #endif
4945 
4946  if (m_bSelected) {
4947  wxString description = m_plugin.m_long_description;
4948  if (description.IsEmpty())
4949  description = wxString(m_plugin.m_managed_metadata.description.c_str());
4950 
4951  PanelHardBreakWrapper wrapper(this, description,
4952  g_options->GetSize().x * 7 / 10);
4953  m_pDescription->SetLabel(wrapper.GetWrapped());
4954  if (m_plugin.m_managed_metadata.info_url.size()) {
4955  m_info_btn->SetURL(m_plugin.m_managed_metadata.info_url.c_str());
4956  m_info_btn->Show();
4957  }
4958  } else {
4959  wxString description = m_plugin.m_short_description;
4960  if (description.IsEmpty())
4961  description = wxString(m_plugin.m_managed_metadata.summary.c_str());
4962  PanelHardBreakWrapper wrapper(this, description,
4963  g_options->GetSize().x * 7 / 10);
4964  m_pDescription->SetLabel(wrapper.GetWrapped());
4965  }
4966 
4967  m_pButtonPreferences->Enable(enabled &&
4968  (m_plugin.m_cap_flag & WANTS_PREFERENCES));
4969  m_cbEnable->SetValue(enabled);
4970 }
4971 
4972 void PluginPanel::OnPluginUp(wxCommandEvent& event) {
4973  m_PluginListPanel->MoveUp(this);
4974 }
4975 
4976 void PluginPanel::OnPluginDown(wxCommandEvent& event) {
4977  m_PluginListPanel->MoveDown(this);
4978 }
4979 
4981 WebsiteButton::WebsiteButton(wxWindow* parent, const char* url)
4982  : wxPanel(parent), m_url(url) {
4983  auto vbox = new wxBoxSizer(wxVERTICAL);
4984  auto button = new wxButton(this, wxID_ANY, _("Website"));
4985  button->Enable(strlen(url) > 0);
4986  vbox->Add(button);
4987  SetSizer(vbox);
4988  Bind(wxEVT_COMMAND_BUTTON_CLICKED,
4989  [=](wxCommandEvent&) { wxLaunchDefaultBrowser(m_url); });
4990 }
4991 
4992 // ----------------------------------------------------------------------------
4993 // PlugInChartBase Implmentation
4994 // This class is the base class for Plug-able chart types
4995 // ----------------------------------------------------------------------------
4996 
4997 PlugInChartBase::PlugInChartBase() { m_Chart_Error_Factor = 0.; }
4998 
4999 PlugInChartBase::~PlugInChartBase() {}
5000 
5001 wxString PlugInChartBase::GetFileSearchMask(void) { return _T(""); }
5002 
5003 int PlugInChartBase::Init(const wxString& name, int init_flags) { return 0; }
5004 
5005 // Accessors
5006 
5007 double PlugInChartBase::GetNormalScaleMin(double canvas_scale_factor,
5008  bool b_allow_overzoom) {
5009  return 1.0;
5010 }
5011 
5012 double PlugInChartBase::GetNormalScaleMax(double canvas_scale_factor,
5013  int canvas_width) {
5014  return 2.0e7;
5015 }
5016 
5017 bool PlugInChartBase::GetChartExtent(ExtentPI* pext) { return false; }
5018 
5019 wxBitmap& PlugInChartBase::RenderRegionView(const PlugIn_ViewPort& VPoint,
5020  const wxRegion& Region) {
5021  return wxNullBitmap;
5022 }
5023 
5024 bool PlugInChartBase::AdjustVP(PlugIn_ViewPort& vp_last,
5025  PlugIn_ViewPort& vp_proposed) {
5026  return false;
5027 }
5028 
5029 void PlugInChartBase::GetValidCanvasRegion(const PlugIn_ViewPort& VPoint,
5030  wxRegion* pValidRegion) {}
5031 
5032 void PlugInChartBase::SetColorScheme(int cs, bool bApplyImmediate) {}
5033 
5034 double PlugInChartBase::GetNearestPreferredScalePPM(double target_scale_ppm) {
5035  return 1.0;
5036 }
5037 
5038 wxBitmap* PlugInChartBase::GetThumbnail(int tnx, int tny, int cs) {
5039  return NULL;
5040 }
5041 
5042 void PlugInChartBase::ComputeSourceRectangle(const PlugIn_ViewPort& vp,
5043  wxRect* pSourceRect) {}
5044 
5045 double PlugInChartBase::GetRasterScaleFactor() { return 1.0; }
5046 
5047 bool PlugInChartBase::GetChartBits(wxRect& source, unsigned char* pPix,
5048  int sub_samp) {
5049  return false;
5050 }
5051 
5052 int PlugInChartBase::GetSize_X() { return 1; }
5053 
5054 int PlugInChartBase::GetSize_Y() { return 1; }
5055 
5056 void PlugInChartBase::latlong_to_chartpix(double lat, double lon, double& pixx,
5057  double& pixy) {}
5058 
5059 void PlugInChartBase::chartpix_to_latlong(double pixx, double pixy,
5060  double* plat, double* plon) {}
5061 
5062 // ----------------------------------------------------------------------------
5063 // PlugInChartBaseGL Implementation
5064 //
5065 // ----------------------------------------------------------------------------
5066 
5067 PlugInChartBaseGL::PlugInChartBaseGL() {}
5068 
5069 PlugInChartBaseGL::~PlugInChartBaseGL() {}
5070 
5071 int PlugInChartBaseGL::RenderRegionViewOnGL(const wxGLContext& glc,
5072  const PlugIn_ViewPort& VPoint,
5073  const wxRegion& Region,
5074  bool b_use_stencil) {
5075  return 0;
5076 }
5077 
5078 ListOfPI_S57Obj* PlugInChartBaseGL::GetObjRuleListAtLatLon(
5079  float lat, float lon, float select_radius, PlugIn_ViewPort* VPoint) {
5080  return NULL;
5081 }
5082 
5083 wxString PlugInChartBaseGL::CreateObjDescriptions(ListOfPI_S57Obj* obj_list) {
5084  return _T("");
5085 }
5086 
5087 int PlugInChartBaseGL::GetNoCOVREntries() { return 0; }
5088 
5089 int PlugInChartBaseGL::GetNoCOVRTablePoints(int iTable) { return 0; }
5090 
5091 int PlugInChartBaseGL::GetNoCOVRTablenPoints(int iTable) { return 0; }
5092 
5093 float* PlugInChartBaseGL::GetNoCOVRTableHead(int iTable) { return 0; }
5094 
5095 // ----------------------------------------------------------------------------
5096 // PlugInChartBaseExtended Implementation
5097 //
5098 // ----------------------------------------------------------------------------
5099 
5100 PlugInChartBaseExtended::PlugInChartBaseExtended() {}
5101 
5102 PlugInChartBaseExtended::~PlugInChartBaseExtended() {}
5103 
5104 int PlugInChartBaseExtended::RenderRegionViewOnGL(const wxGLContext& glc,
5105  const PlugIn_ViewPort& VPoint,
5106  const wxRegion& Region,
5107  bool b_use_stencil) {
5108  return 0;
5109 }
5110 
5111 int PlugInChartBaseExtended::RenderRegionViewOnGLNoText(
5112  const wxGLContext& glc, const PlugIn_ViewPort& VPoint,
5113  const wxRegion& Region, bool b_use_stencil) {
5114  return 0;
5115 }
5116 
5117 int PlugInChartBaseExtended::RenderRegionViewOnGLTextOnly(
5118  const wxGLContext& glc, const PlugIn_ViewPort& VPoint,
5119  const wxRegion& Region, bool b_use_stencil) {
5120  return 0;
5121 }
5122 
5123 wxBitmap& PlugInChartBaseExtended::RenderRegionViewOnDCNoText(
5124  const PlugIn_ViewPort& VPoint, const wxRegion& Region) {
5125  return wxNullBitmap;
5126 }
5127 
5128 bool PlugInChartBaseExtended::RenderRegionViewOnDCTextOnly(
5129  wxMemoryDC& dc, const PlugIn_ViewPort& VPoint, const wxRegion& Region) {
5130  return false;
5131 }
5132 
5133 ListOfPI_S57Obj* PlugInChartBaseExtended::GetObjRuleListAtLatLon(
5134  float lat, float lon, float select_radius, PlugIn_ViewPort* VPoint) {
5135  return NULL;
5136 }
5137 
5138 wxString PlugInChartBaseExtended::CreateObjDescriptions(
5139  ListOfPI_S57Obj* obj_list) {
5140  return _T("");
5141 }
5142 
5143 int PlugInChartBaseExtended::GetNoCOVREntries() { return 0; }
5144 
5145 int PlugInChartBaseExtended::GetNoCOVRTablePoints(int iTable) { return 0; }
5146 
5147 int PlugInChartBaseExtended::GetNoCOVRTablenPoints(int iTable) { return 0; }
5148 
5149 float* PlugInChartBaseExtended::GetNoCOVRTableHead(int iTable) { return 0; }
5150 
5151 void PlugInChartBaseExtended::ClearPLIBTextList() {}
5152 
5153 // ----------------------------------------------------------------------------
5154 // PlugInChartBaseExtendedPlus2 Implementation
5155 //
5156 // ----------------------------------------------------------------------------
5157 
5158 PlugInChartBaseExtendedPlus2::PlugInChartBaseExtendedPlus2() {}
5159 
5160 PlugInChartBaseExtendedPlus2::~PlugInChartBaseExtendedPlus2() {}
5161 
5162 ListOfPI_S57Obj*
5163 PlugInChartBaseExtendedPlus2::GetLightsObjRuleListVisibleAtLatLon(
5164  float lat, float lon, PlugIn_ViewPort* VPoint) {
5165  return NULL;
5166 }
5167 
5168 // ----------------------------------------------------------------------------
5169 // PlugInChartBaseGLPlus2 Implementation
5170 //
5171 // ----------------------------------------------------------------------------
5172 
5173 PlugInChartBaseGLPlus2::PlugInChartBaseGLPlus2() {}
5174 
5175 PlugInChartBaseGLPlus2::~PlugInChartBaseGLPlus2() {}
5176 
5177 ListOfPI_S57Obj* PlugInChartBaseGLPlus2::GetLightsObjRuleListVisibleAtLatLon(
5178  float lat, float lon, PlugIn_ViewPort* VPoint) {
5179  return NULL;
5180 }
5181 
5182 // ----------------------------------------------------------------------------
5183 // ChartPlugInWrapper Implementation
5184 // This class is a wrapper/interface to PlugIn charts(PlugInChartBase)
5185 // ----------------------------------------------------------------------------
5186 
5187 ChartPlugInWrapper::ChartPlugInWrapper() {}
5188 
5189 ChartPlugInWrapper::ChartPlugInWrapper(const wxString& chart_class) {
5190  m_ppo = ::wxCreateDynamicObject(chart_class);
5191  m_ppicb = wxDynamicCast(m_ppo, PlugInChartBase);
5192 }
5193 
5194 ChartPlugInWrapper::~ChartPlugInWrapper() {
5195  if (m_ppicb) delete m_ppicb;
5196 }
5197 
5198 wxString ChartPlugInWrapper::GetFileSearchMask(void) {
5199  if (m_ppicb)
5200  return m_ppicb->GetFileSearchMask();
5201  else
5202  return _T("");
5203 }
5204 
5205 InitReturn ChartPlugInWrapper::Init(const wxString& name,
5206  ChartInitFlag init_flags) {
5207  if (m_ppicb) {
5208  wxWindow* pa = wxWindow::FindFocus();
5209 
5210  InitReturn ret_val = (InitReturn)m_ppicb->Init(name, (int)init_flags);
5211 
5212  // Here we transcribe all the required wrapped member elements up into
5213  // the chartbase object which is the parent of this class
5214  if (ret_val == INIT_OK) {
5215  m_FullPath = m_ppicb->GetFullPath();
5216  m_ChartType = (ChartTypeEnum)m_ppicb->GetChartType();
5217  m_ChartFamily = (ChartFamilyEnum)m_ppicb->GetChartFamily();
5218  m_projection = (OcpnProjType)m_ppicb->GetChartProjection();
5219  m_EdDate = m_ppicb->GetEditionDate();
5220  m_Name = m_ppicb->GetName();
5221  m_ID = m_ppicb->GetID();
5222  m_DepthUnits = m_ppicb->GetDepthUnits();
5223  m_SoundingsDatum = m_ppicb->GetSoundingsDatum();
5224  m_datum_str = m_ppicb->GetDatumString();
5225  m_SE = m_ppicb->GetSE();
5226  m_EdDate = m_ppicb->GetEditionDate();
5227  m_ExtraInfo = m_ppicb->GetExtraInfo();
5228  Chart_Error_Factor = m_ppicb->GetChartErrorFactor();
5229  m_depth_unit_id = (ChartDepthUnitType)m_ppicb->GetDepthUnitId();
5230  m_Chart_Skew = m_ppicb->GetChartSkew();
5231  m_Chart_Scale = m_ppicb->GetNativeScale();
5232 
5233  // We estimate ppm_avg as needed by raster texture cache logic...
5234  // This number works for average BSB charts, scanned with average
5235  // resolution
5236  m_ppm_avg = 10000. / m_ppicb->GetNativeScale(); // fallback value
5237 
5238  // Calcuculate a "better" ppm from the chart geo extent and raster size.
5239  if ((fabs(m_Chart_Skew) < .01) &&
5240  (CHART_FAMILY_RASTER == m_ChartFamily)) {
5241  Extent extent;
5242  if (GetChartExtent(&extent)) {
5243  double lon_range = extent.ELON - extent.WLON;
5244  if ((lon_range > 0) &&
5245  (lon_range < 90.0)) // Be safe about IDL crossing and huge charts
5246  m_ppm_avg = GetSize_X() / (lon_range * 1852 * 60);
5247  }
5248  }
5249 
5250  m_overlayENC = false;
5251  if (m_ChartFamily == (ChartFamilyEnum)PI_CHART_FAMILY_VECTOR) {
5252  wxCharBuffer buf = m_FullPath.ToUTF8();
5253  m_overlayENC = s57chart::IsCellOverlayType(buf.data());
5254  }
5255 
5256  bReadyToRender = m_ppicb->IsReadyToRender();
5257  } else {
5258  // Mark the chart as unable to render
5259  m_ChartType = CHART_TYPE_UNKNOWN;
5260  m_ChartFamily = CHART_FAMILY_UNKNOWN;
5261  }
5262 
5263  // PlugIn may invoke wxExecute(), which steals the keyboard focus
5264  // So take it back
5265  ChartCanvas* pc = wxDynamicCast(pa, ChartCanvas);
5266  if (pc) pc->SetFocus();
5267 
5268  return ret_val;
5269  } else
5270  return INIT_FAIL_REMOVE;
5271 }
5272 
5273 // Accessors
5274 int ChartPlugInWrapper::GetCOVREntries() {
5275  if (m_ppicb)
5276  return m_ppicb->GetCOVREntries();
5277  else
5278  return 0;
5279 }
5280 
5281 int ChartPlugInWrapper::GetCOVRTablePoints(int iTable) {
5282  if (m_ppicb)
5283  return m_ppicb->GetCOVRTablePoints(iTable);
5284  else
5285  return 0;
5286 }
5287 
5288 int ChartPlugInWrapper::GetCOVRTablenPoints(int iTable) {
5289  if (m_ppicb)
5290  return m_ppicb->GetCOVRTablenPoints(iTable);
5291  else
5292  return 0;
5293 }
5294 
5295 float* ChartPlugInWrapper::GetCOVRTableHead(int iTable) {
5296  if (m_ppicb)
5297  return m_ppicb->GetCOVRTableHead(iTable);
5298  else
5299  return 0;
5300 }
5301 
5302 // TODO
5303 // PlugIn chart types do not properly support NoCovr Regions
5304 // Proper fix is to update PlugIn Chart Type API
5305 // Derive an extended PlugIn chart class from existing class,
5306 // and use some kind of RTTI to figure out which class to call.
5307 int ChartPlugInWrapper::GetNoCOVREntries() {
5308  if (m_ppicb) {
5309  PlugInChartBaseGL* ppicbgl = dynamic_cast<PlugInChartBaseGL*>(m_ppicb);
5310  if (ppicbgl) {
5311  return ppicbgl->GetNoCOVREntries();
5312  }
5313  }
5314  return 0;
5315 }
5316 
5317 int ChartPlugInWrapper::GetNoCOVRTablePoints(int iTable) {
5318  if (m_ppicb) {
5319  PlugInChartBaseGL* ppicbgl = dynamic_cast<PlugInChartBaseGL*>(m_ppicb);
5320  if (ppicbgl) {
5321  return ppicbgl->GetNoCOVRTablePoints(iTable);
5322  }
5323  }
5324  return 0;
5325 }
5326 
5327 int ChartPlugInWrapper::GetNoCOVRTablenPoints(int iTable) {
5328  if (m_ppicb) {
5329  PlugInChartBaseGL* ppicbgl = dynamic_cast<PlugInChartBaseGL*>(m_ppicb);
5330  if (ppicbgl) {
5331  return ppicbgl->GetNoCOVRTablenPoints(iTable);
5332  }
5333  }
5334  return 0;
5335 }
5336 
5337 float* ChartPlugInWrapper::GetNoCOVRTableHead(int iTable) {
5338  if (m_ppicb) {
5339  PlugInChartBaseGL* ppicbgl = dynamic_cast<PlugInChartBaseGL*>(m_ppicb);
5340  if (ppicbgl) {
5341  return ppicbgl->GetNoCOVRTableHead(iTable);
5342  }
5343  }
5344  return 0;
5345 }
5346 
5347 bool ChartPlugInWrapper::GetChartExtent(Extent* pext) {
5348  if (m_ppicb) {
5349  ExtentPI xpi;
5350  if (m_ppicb->GetChartExtent(&xpi)) {
5351  pext->NLAT = xpi.NLAT;
5352  pext->SLAT = xpi.SLAT;
5353  pext->ELON = xpi.ELON;
5354  pext->WLON = xpi.WLON;
5355 
5356  return true;
5357  } else
5358  return false;
5359  } else
5360  return false;
5361 }
5362 
5363 ThumbData* ChartPlugInWrapper::GetThumbData(int tnx, int tny, float lat,
5364  float lon) {
5365  if (m_ppicb) {
5366  // Create the bitmap if needed, doing a deep copy from the Bitmap owned
5367  // by the PlugIn Chart
5368  if (!pThumbData->pDIBThumb) {
5369  wxBitmap* pBMPOwnedByChart =
5370  m_ppicb->GetThumbnail(tnx, tny, m_global_color_scheme);
5371  if (pBMPOwnedByChart) {
5372  wxImage img = pBMPOwnedByChart->ConvertToImage();
5373  pThumbData->pDIBThumb = new wxBitmap(img);
5374  } else
5375  pThumbData->pDIBThumb = NULL;
5376  }
5377 
5378  pThumbData->Thumb_Size_X = tnx;
5379  pThumbData->Thumb_Size_Y = tny;
5380 
5381  /*
5382  // Plot the supplied Lat/Lon on the thumbnail
5383  int divx = m_ppicb->Size_X / tnx;
5384  int divy = m_ppicb->Size_Y / tny;
5385 
5386  int div_factor = __min(divx, divy);
5387 
5388  int pixx, pixy;
5389 
5390 
5391  // Using a temporary synthetic ViewPort and source rectangle,
5392  // calculate the ships position on the thumbnail
5393  ViewPort tvp;
5394  tvp.pix_width = tnx;
5395  tvp.pix_height = tny;
5396  tvp.view_scale_ppm = GetPPM() / div_factor;
5397  wxRect trex = Rsrc;
5398  Rsrc.x = 0;
5399  Rsrc.y = 0;
5400  latlong_to_pix_vp(lat, lon, pixx, pixy, tvp);
5401  Rsrc = trex;
5402 
5403  pThumbData->ShipX = pixx;// / div_factor;
5404  pThumbData->ShipY = pixy;// / div_factor;
5405  */
5406  pThumbData->ShipX = 0;
5407  pThumbData->ShipY = 0;
5408 
5409  return pThumbData;
5410  } else
5411  return NULL;
5412 }
5413 
5414 ThumbData* ChartPlugInWrapper::GetThumbData() { return pThumbData; }
5415 
5416 bool ChartPlugInWrapper::UpdateThumbData(double lat, double lon) {
5417  return true;
5418 }
5419 
5420 double ChartPlugInWrapper::GetNormalScaleMin(double canvas_scale_factor,
5421  bool b_allow_overzoom) {
5422  if (m_ppicb)
5423  return m_ppicb->GetNormalScaleMin(canvas_scale_factor, b_allow_overzoom);
5424  else
5425  return 1.0;
5426 }
5427 
5428 double ChartPlugInWrapper::GetNormalScaleMax(double canvas_scale_factor,
5429  int canvas_width) {
5430  if (m_ppicb)
5431  return m_ppicb->GetNormalScaleMax(canvas_scale_factor, canvas_width);
5432  else
5433  return 2.0e7;
5434 }
5435 
5436 /* RectRegion:
5437  * This is the Screen region desired to be updated. Will
5438  * be either 1 rectangle(full screen) or two rectangles (panning with FBO
5439  * accelerated pan logic)
5440  *
5441  * Region:
5442  * This is the LLRegion describing the quilt active region
5443  * for this chart.
5444  *
5445  * So, Actual rendering area onscreen should be clipped to the
5446  * intersection of the two regions.
5447  */
5448 
5449 // Render helpers
5450 void RenderRotateToViewPort(const ViewPort& VPoint) {
5451 #ifndef USE_ANDROID_GLES2
5452  float xt = VPoint.pix_width / 2.0, yt = VPoint.pix_height / 2.0;
5453  glTranslatef(xt, yt, 0);
5454  glRotatef(VPoint.rotation * 180. / PI, 0, 0, 1);
5455  glTranslatef(-xt, -yt, 0);
5456 #endif
5457 }
5458 
5459 void UndoRenderRotateToViewPort(const ViewPort& VPoint) {
5460 #ifndef USE_ANDROID_GLES2
5461  float xt = VPoint.pix_width / 2.0, yt = VPoint.pix_height / 2.0;
5462  glTranslatef(xt, yt, 0);
5463  glRotatef(-VPoint.rotation * 180. / PI, 0, 0, 1);
5464  glTranslatef(-xt, -yt, 0);
5465 #endif
5466 }
5467 
5468 bool ChartPlugInWrapper::RenderRegionViewOnGL(const wxGLContext& glc,
5469  const ViewPort& VPoint,
5470  const OCPNRegion& RectRegion,
5471  const LLRegion& Region) {
5472 #ifdef ocpnUSE_GL
5473  if (m_ppicb) {
5474  ViewPort vp = VPoint; // non-const copy
5475 
5476  gs_plib_flags = 0; // reset the CAPs flag
5477  PlugInChartBaseGL* ppicb_gl = dynamic_cast<PlugInChartBaseGL*>(m_ppicb);
5478  PlugInChartBaseExtended* ppicb_x =
5479  dynamic_cast<PlugInChartBaseExtended*>(m_ppicb);
5480  if (!Region.Empty() && (ppicb_gl || ppicb_x)) {
5481  wxRegion* r = RectRegion.GetNew_wxRegion();
5482  for (OCPNRegionIterator upd(RectRegion); upd.HaveRects();
5483  upd.NextRect()) {
5484  LLRegion chart_region = vp.GetLLRegion(upd.GetRect());
5485  chart_region.Intersect(Region);
5486 
5487  if (!chart_region.Empty()) {
5488  ViewPort cvp = glChartCanvas::ClippedViewport(VPoint, chart_region);
5489 
5490  glChartCanvas::SetClipRect(cvp, upd.GetRect(), false);
5491 
5492  // ps52plib->m_last_clip_rect = upd.GetRect();
5493 
5494 #ifndef USE_ANDROID_GLES2
5495 // glPushMatrix(); // Adjust for rotation
5496 #endif
5497  RenderRotateToViewPort(VPoint);
5498 
5499  PlugIn_ViewPort pivp = CreatePlugInViewport(cvp);
5500  if (ppicb_x)
5501  ppicb_x->RenderRegionViewOnGL(glc, pivp, *r,
5502  glChartCanvas::s_b_useStencil);
5503  else if (ppicb_gl)
5504  ppicb_gl->RenderRegionViewOnGL(glc, pivp, *r,
5505  glChartCanvas::s_b_useStencil);
5506  UndoRenderRotateToViewPort(VPoint);
5507 
5508 #ifndef USE_ANDROID_GLES2
5509 // glPopMatrix();
5510 #endif
5511  glChartCanvas::DisableClipRegion();
5512 
5513  }
5514  } // for
5515  delete r;
5516  }
5517  } else
5518  return false;
5519 #endif
5520  return true;
5521 }
5522 
5523 // int indexrr;
5524 
5525 bool ChartPlugInWrapper::RenderRegionViewOnGLNoText(
5526  const wxGLContext& glc, const ViewPort& VPoint,
5527  const OCPNRegion& RectRegion, const LLRegion& Region) {
5528 #ifdef ocpnUSE_GL
5529  if (m_ppicb) {
5530  // printf("\nCPIW::RRVOGLNT %d %d \n", indexrr++, m_Chart_Scale);
5531 
5532  gs_plib_flags = 0; // reset the CAPs flag
5533  PlugInChartBaseExtended* ppicb_x =
5534  dynamic_cast<PlugInChartBaseExtended*>(m_ppicb);
5535  PlugInChartBaseGL* ppicb = dynamic_cast<PlugInChartBaseGL*>(m_ppicb);
5536  if (!Region.Empty() && ppicb_x) {
5537  // Start with a clean slate
5538  glChartCanvas::SetClipRect(VPoint, VPoint.rv_rect, false);
5539  glChartCanvas::DisableClipRegion();
5540 
5541  // Apply rotation to this chart
5542  RenderRotateToViewPort(VPoint);
5543 
5544  PlugIn_ViewPort pivp = CreatePlugInViewport(VPoint);
5545  wxRegion* r = RectRegion.GetNew_wxRegion();
5546 
5547  ppicb_x->RenderRegionViewOnGLNoText(glc, pivp, *r,
5548  glChartCanvas::s_b_useStencil);
5549 
5550  // Undo rotation
5551  UndoRenderRotateToViewPort(VPoint);
5552 
5553  delete r;
5554  }
5555 
5556  else if (!Region.Empty() &&
5557  ppicb) // Legacy Vector GL Plugin chart (e.g.S63)
5558  {
5559  ViewPort vp = VPoint; // non-const copy
5560  wxRegion* r = RectRegion.GetNew_wxRegion();
5561  for (OCPNRegionIterator upd(RectRegion); upd.HaveRects();
5562  upd.NextRect()) {
5563  LLRegion chart_region = vp.GetLLRegion(upd.GetRect());
5564  chart_region.Intersect(Region);
5565 
5566  if (!chart_region.Empty()) {
5567  ViewPort cvp = glChartCanvas::ClippedViewport(VPoint, chart_region);
5568 
5569  glChartCanvas::SetClipRect(cvp, upd.GetRect(), false);
5570 
5571  RenderRotateToViewPort(VPoint);
5572 
5573  PlugIn_ViewPort pivp = CreatePlugInViewport(cvp);
5574  ppicb->RenderRegionViewOnGL(glc, pivp, *r,
5575  glChartCanvas::s_b_useStencil);
5576 
5577  // Undo rotation
5578  UndoRenderRotateToViewPort(VPoint);
5579 
5580  glChartCanvas::DisableClipRegion();
5581 
5582  }
5583  } // for
5584  delete r;
5585  }
5586 
5587  } else
5588  return false;
5589 #endif
5590  return true;
5591 }
5592 
5593 bool ChartPlugInWrapper::RenderRegionViewOnGLTextOnly(
5594  const wxGLContext& glc, const ViewPort& VPoint, const OCPNRegion& Region) {
5595 #ifdef ocpnUSE_GL
5596  if (m_ppicb) {
5597  gs_plib_flags = 0; // reset the CAPs flag
5598  PlugInChartBaseExtended* ppicb_x =
5599  dynamic_cast<PlugInChartBaseExtended*>(m_ppicb);
5600  if (!Region.Empty() && ppicb_x) {
5601  wxRegion* r = Region.GetNew_wxRegion();
5602  for (OCPNRegionIterator upd(Region); upd.HaveRects(); upd.NextRect()) {
5603 #ifndef USE_ANDROID_GLES2
5604 // glPushMatrix(); // Adjust for rotation
5605 #endif
5606  RenderRotateToViewPort(VPoint);
5607 
5608  PlugIn_ViewPort pivp = CreatePlugInViewport(VPoint);
5609  ppicb_x->RenderRegionViewOnGLTextOnly(glc, pivp, *r,
5610  glChartCanvas::s_b_useStencil);
5611  UndoRenderRotateToViewPort(VPoint);
5612 
5613 #ifndef USE_ANDROID_GLES2
5614 // glPopMatrix();
5615 #endif
5616 
5617  } // for
5618  delete r;
5619  }
5620  } else
5621  return false;
5622 #endif
5623  return true;
5624 }
5625 
5626 bool ChartPlugInWrapper::RenderRegionViewOnDC(wxMemoryDC& dc,
5627  const ViewPort& VPoint,
5628  const OCPNRegion& Region) {
5629  if (m_ppicb) {
5630  gs_plib_flags = 0; // reset the CAPs flag
5631  PlugIn_ViewPort pivp = CreatePlugInViewport(VPoint);
5632  if (Region.IsOk()) {
5633  wxRegion* r = Region.GetNew_wxRegion();
5634  if (!m_overlayENC)
5635  dc.SelectObject(m_ppicb->RenderRegionView(pivp, *r));
5636  else {
5637  wxBitmap& obmp = m_ppicb->RenderRegionView(pivp, *r);
5638 
5639  // Create a mask to remove the NODTA areas from overlay cells.
5640  wxColour nodat = GetGlobalColor(_T ( "NODTA" ));
5641  wxColour nodat_sub = nodat;
5642 
5643 #ifdef ocpnUSE_ocpnBitmap
5644  nodat_sub = wxColour(nodat.Blue(), nodat.Green(), nodat.Red());
5645 #endif
5646  m_pMask = new wxMask(obmp, nodat_sub);
5647  obmp.SetMask(m_pMask);
5648 
5649  dc.SelectObject(obmp);
5650  }
5651 
5652  delete r;
5653  return true;
5654  } else
5655  return false;
5656  } else
5657  return false;
5658 }
5659 
5660 bool ChartPlugInWrapper::RenderRegionViewOnDCNoText(wxMemoryDC& dc,
5661  const ViewPort& VPoint,
5662  const OCPNRegion& Region) {
5663  if (m_ppicb) {
5664  gs_plib_flags = 0; // reset the CAPs flag
5665  PlugIn_ViewPort pivp = CreatePlugInViewport(VPoint);
5666 
5667  PlugInChartBaseExtended* pCBx =
5668  dynamic_cast<PlugInChartBaseExtended*>(m_ppicb);
5669  PlugInChartBase* ppicb = dynamic_cast<PlugInChartBase*>(m_ppicb);
5670 
5671  if (Region.IsOk() && (pCBx || ppicb)) {
5672  wxRegion* r = Region.GetNew_wxRegion();
5673 
5674  if (pCBx)
5675  dc.SelectObject(pCBx->RenderRegionViewOnDCNoText(pivp, *r));
5676  else if (ppicb)
5677  dc.SelectObject(ppicb->RenderRegionView(pivp, *r));
5678 
5679  delete r;
5680  return true;
5681  } else
5682  return false;
5683  } else
5684  return false;
5685 }
5686 
5687 bool ChartPlugInWrapper::RenderRegionViewOnDCTextOnly(
5688  wxMemoryDC& dc, const ViewPort& VPoint, const OCPNRegion& Region) {
5689  if (m_ppicb) {
5690  bool ret_val = false;
5691  gs_plib_flags = 0; // reset the CAPs flag
5692  PlugIn_ViewPort pivp = CreatePlugInViewport(VPoint);
5693  if (Region.IsOk()) {
5694  wxRegion* r = Region.GetNew_wxRegion();
5695 
5696  PlugInChartBaseExtended* pCBx =
5697  dynamic_cast<PlugInChartBaseExtended*>(m_ppicb);
5698  if (pCBx) ret_val = pCBx->RenderRegionViewOnDCTextOnly(dc, pivp, *r);
5699 
5700  delete r;
5701  return ret_val;
5702  } else
5703  return false;
5704  } else
5705  return false;
5706 }
5707 
5708 void ChartPlugInWrapper::ClearPLIBTextList() {
5709  if (m_ppicb) {
5710  PlugInChartBaseExtended* pCBx =
5711  dynamic_cast<PlugInChartBaseExtended*>(m_ppicb);
5712  if (pCBx) pCBx->ClearPLIBTextList();
5713  }
5714 }
5715 
5716 bool ChartPlugInWrapper::AdjustVP(ViewPort& vp_last, ViewPort& vp_proposed) {
5717  if (m_ppicb) {
5718  PlugIn_ViewPort pivp_last = CreatePlugInViewport(vp_last);
5719  PlugIn_ViewPort pivp_proposed = CreatePlugInViewport(vp_proposed);
5720  return m_ppicb->AdjustVP(pivp_last, pivp_proposed);
5721  } else
5722  return false;
5723 }
5724 
5725 void ChartPlugInWrapper::GetValidCanvasRegion(const ViewPort& VPoint,
5726  OCPNRegion* pValidRegion) {
5727  if (m_ppicb) {
5728  PlugIn_ViewPort pivp = CreatePlugInViewport(VPoint);
5729  // currently convert using wxRegion,
5730  // this should be changed as wxRegion is proven unstable/buggy on various
5731  // platforms
5732  wxRegion region;
5733  m_ppicb->GetValidCanvasRegion(pivp, &region);
5734  *pValidRegion = OCPNRegion(region);
5735  }
5736 
5737  return;
5738 }
5739 
5740 void ChartPlugInWrapper::SetColorScheme(ColorScheme cs, bool bApplyImmediate) {
5741  if (m_ppicb) {
5742  m_ppicb->SetColorScheme(cs, bApplyImmediate);
5743  }
5744  m_global_color_scheme = cs;
5745  // Force a new thumbnail
5746  if (pThumbData) pThumbData->pDIBThumb = NULL;
5747 }
5748 
5749 double ChartPlugInWrapper::GetNearestPreferredScalePPM(
5750  double target_scale_ppm) {
5751  if (m_ppicb)
5752  return m_ppicb->GetNearestPreferredScalePPM(target_scale_ppm);
5753  else
5754  return 1.0;
5755 }
5756 
5757 void ChartPlugInWrapper::ComputeSourceRectangle(const ViewPort& VPoint,
5758  wxRect* pSourceRect) {
5759  if (m_ppicb) {
5760  PlugIn_ViewPort pivp = CreatePlugInViewport(VPoint);
5761  m_ppicb->ComputeSourceRectangle(pivp, pSourceRect);
5762  }
5763 }
5764 
5765 double ChartPlugInWrapper::GetRasterScaleFactor(const ViewPort& vp) {
5766  if (m_ppicb) {
5767  return (wxRound(100000 * GetPPM() / vp.view_scale_ppm)) / 100000.;
5768  } else
5769  return 1.0;
5770 }
5771 
5772 bool ChartPlugInWrapper::GetChartBits(wxRect& source, unsigned char* pPix,
5773  int sub_samp) {
5774  wxCriticalSectionLocker locker(m_critSect);
5775 
5776  if (m_ppicb)
5777 
5778  return m_ppicb->GetChartBits(source, pPix, sub_samp);
5779  else
5780  return false;
5781 }
5782 
5783 int ChartPlugInWrapper::GetSize_X() {
5784  if (m_ppicb)
5785  return m_ppicb->GetSize_X();
5786  else
5787  return 1;
5788 }
5789 
5790 int ChartPlugInWrapper::GetSize_Y() {
5791  if (m_ppicb)
5792  return m_ppicb->GetSize_Y();
5793  else
5794  return 1;
5795 }
5796 
5797 void ChartPlugInWrapper::latlong_to_chartpix(double lat, double lon,
5798  double& pixx, double& pixy) {
5799  if (m_ppicb) m_ppicb->latlong_to_chartpix(lat, lon, pixx, pixy);
5800 }
5801 
5802 void ChartPlugInWrapper::chartpix_to_latlong(double pixx, double pixy,
5803  double* plat, double* plon) {
5804  if (m_ppicb) m_ppicb->chartpix_to_latlong(pixx, pixy, plat, plon);
5805 }
5806 
5807 /* API 1.11 */
5808 
5809 /* API 1.11 adds some more common functions to avoid unnecessary code
5810  * duplication */
5811 
5812 wxString toSDMM_PlugIn(int NEflag, double a, bool hi_precision) {
5813  return toSDMM(NEflag, a, hi_precision);
5814 }
5815 
5816 wxColour GetBaseGlobalColor(wxString colorName) {
5817  return GetGlobalColor(colorName);
5818 }
5819 
5820 int OCPNMessageBox_PlugIn(wxWindow* parent, const wxString& message,
5821  const wxString& caption, int style, int x, int y) {
5822  return OCPNMessageBox(parent, message, caption, style, 100, x, y);
5823 }
5824 
5825 wxString GetOCPN_ExePath(void) { return g_Platform->GetExePath(); }
5826 
5827 wxString* GetpPlugInLocation() { return g_Platform->GetPluginDirPtr(); }
5828 
5829 wxString GetWritableDocumentsDir(void) {
5830  return g_Platform->GetWritableDocumentsDir();
5831 }
5832 
5833 wxString GetPlugInPath(opencpn_plugin* pplugin) {
5834  wxString ret_val;
5835  auto loader = PluginLoader::getInstance();
5836  for (unsigned int i = 0; i < loader->GetPlugInArray()->GetCount(); i++) {
5837  PlugInContainer* pic = loader->GetPlugInArray()->Item(i);
5838  if (pic->m_pplugin == pplugin) {
5839  ret_val = pic->m_plugin_file;
5840  break;
5841  }
5842  }
5843  return ret_val;
5844 }
5845 
5846 // API 1.11 Access to Vector PlugIn charts
5847 
5848 ListOfPI_S57Obj* PlugInManager::GetPlugInObjRuleListAtLatLon(
5849  ChartPlugInWrapper* target, float zlat, float zlon, float SelectRadius,
5850  const ViewPort& vp) {
5851  ListOfPI_S57Obj* list = NULL;
5852  if (target) {
5853  PlugInChartBaseGL* picbgl =
5854  dynamic_cast<PlugInChartBaseGL*>(target->GetPlugInChart());
5855  if (picbgl) {
5856  PlugIn_ViewPort pi_vp = CreatePlugInViewport(vp);
5857  list = picbgl->GetObjRuleListAtLatLon(zlat, zlon, SelectRadius, &pi_vp);
5858 
5859  return list;
5860  }
5861  PlugInChartBaseExtended* picbx =
5862  dynamic_cast<PlugInChartBaseExtended*>(target->GetPlugInChart());
5863  if (picbx) {
5864  PlugIn_ViewPort pi_vp = CreatePlugInViewport(vp);
5865  list = picbx->GetObjRuleListAtLatLon(zlat, zlon, SelectRadius, &pi_vp);
5866 
5867  return list;
5868  } else
5869  return list;
5870  } else
5871  return list;
5872 }
5873 
5874 wxString PlugInManager::CreateObjDescriptions(ChartPlugInWrapper* target,
5875  ListOfPI_S57Obj* rule_list) {
5876  wxString ret_str;
5877  if (target) {
5878  PlugInChartBaseGL* picbgl =
5879  dynamic_cast<PlugInChartBaseGL*>(target->GetPlugInChart());
5880  if (picbgl) {
5881  ret_str = picbgl->CreateObjDescriptions(rule_list);
5882  } else {
5883  PlugInChartBaseExtended* picbx =
5884  dynamic_cast<PlugInChartBaseExtended*>(target->GetPlugInChart());
5885  if (picbx) {
5886  ret_str = picbx->CreateObjDescriptions(rule_list);
5887  }
5888  }
5889  }
5890  return ret_str;
5891 }
5892 
5893 // API 1.11 Access to S52 PLIB
5894 wxString PI_GetPLIBColorScheme() {
5895  return _T(""); // ps52plib->GetPLIBColorScheme()
5896 }
5897 
5898 int PI_GetPLIBDepthUnitInt() {
5899  if (ps52plib)
5900  return ps52plib->m_nDepthUnitDisplay;
5901  else
5902  return 0;
5903 }
5904 
5905 int PI_GetPLIBSymbolStyle() {
5906  if (ps52plib)
5907  return ps52plib->m_nSymbolStyle;
5908  else
5909  return 0;
5910 }
5911 
5912 int PI_GetPLIBBoundaryStyle() {
5913  if (ps52plib)
5914  return ps52plib->m_nBoundaryStyle;
5915  else
5916  return 0;
5917 }
5918 
5919 bool PI_PLIBObjectRenderCheck(PI_S57Obj* pObj, PlugIn_ViewPort* vp) {
5920  if (ps52plib) {
5921  // Create and populate a compatible s57 Object
5922  S57Obj cobj;
5923  chart_context ctx;
5924  CreateCompatibleS57Object(pObj, &cobj, &ctx);
5925 
5926  ViewPort cvp = CreateCompatibleViewport(*vp);
5927 
5928  S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context;
5929 
5930  // Create and populate a minimally compatible object container
5931  ObjRazRules rzRules;
5932  rzRules.obj = &cobj;
5933  rzRules.LUP = pContext->LUP;
5934  rzRules.sm_transform_parms = 0;
5935  rzRules.child = NULL;
5936  rzRules.next = NULL;
5937 
5938  if (pContext->LUP) {
5939  ps52plib->SetVPointCompat(
5940  cvp.pix_width, cvp.pix_height, cvp.view_scale_ppm, cvp.rotation,
5941  cvp.clat, cvp.clon, cvp.chart_scale, cvp.rv_rect, cvp.GetBBox(),
5942  cvp.ref_scale, GetOCPNCanvasWindow()->GetContentScaleFactor());
5943  ps52plib->PrepareForRender();
5944 
5945  return ps52plib->ObjectRenderCheck(&rzRules);
5946  } else
5947  return false;
5948  } else
5949  return false;
5950 }
5951 
5952 int PI_GetPLIBStateHash() {
5953  if (ps52plib)
5954  return ps52plib->GetStateHash();
5955  else
5956  return 0;
5957 }
5958 
5959 void CreateCompatibleS57Object(PI_S57Obj* pObj, S57Obj* cobj,
5960  chart_context* pctx) {
5961  strncpy(cobj->FeatureName, pObj->FeatureName, 8);
5962  cobj->Primitive_type = (GeoPrim_t)pObj->Primitive_type;
5963  cobj->att_array = pObj->att_array;
5964  cobj->attVal = pObj->attVal;
5965  cobj->n_attr = pObj->n_attr;
5966 
5967  cobj->x = pObj->x;
5968  cobj->y = pObj->y;
5969  cobj->z = pObj->z;
5970  cobj->npt = pObj->npt;
5971 
5972  cobj->iOBJL = pObj->iOBJL;
5973  cobj->Index = pObj->Index;
5974 
5975  cobj->geoPt = (pt*)pObj->geoPt;
5976  cobj->geoPtz = pObj->geoPtz;
5977  cobj->geoPtMulti = pObj->geoPtMulti;
5978 
5979  cobj->m_lat = pObj->m_lat;
5980  cobj->m_lon = pObj->m_lon;
5981 
5982  cobj->m_DisplayCat = (DisCat)pObj->m_DisplayCat;
5983  cobj->x_rate = pObj->x_rate;
5984  cobj->y_rate = pObj->y_rate;
5985  cobj->x_origin = pObj->x_origin;
5986  cobj->y_origin = pObj->y_origin;
5987 
5988  cobj->Scamin = pObj->Scamin;
5989  cobj->nRef = pObj->nRef;
5990  cobj->bIsAton = pObj->bIsAton;
5991  cobj->bIsAssociable = pObj->bIsAssociable;
5992 
5993  cobj->m_n_lsindex = pObj->m_n_lsindex;
5994  cobj->m_lsindex_array = pObj->m_lsindex_array;
5995  cobj->m_n_edge_max_points = pObj->m_n_edge_max_points;
5996 
5997  if (gs_plib_flags & PLIB_CAPS_OBJSEGLIST) {
5998  cobj->m_ls_list_legacy =
6000  pObj->m_ls_list; // note the cast, assumes in-sync layout
6001  } else
6002  cobj->m_ls_list_legacy = 0;
6003  cobj->m_ls_list = 0;
6004 
6005  if (gs_plib_flags & PLIB_CAPS_OBJCATMUTATE)
6006  cobj->m_bcategory_mutable = pObj->m_bcategory_mutable;
6007  else
6008  cobj->m_bcategory_mutable = true; // assume all objects are mutable
6009 
6010  cobj->m_DPRI = -1; // default is unassigned, fixed at render time
6011  if (gs_plib_flags & PLIB_CAPS_OBJCATMUTATE) {
6012  if (pObj->m_DPRI == -1) {
6013  S52PLIB_Context* pCtx = (S52PLIB_Context*)pObj->S52_Context;
6014  if (pCtx->LUP) cobj->m_DPRI = pCtx->LUP->DPRI - '0';
6015  } else
6016  cobj->m_DPRI = pObj->m_DPRI;
6017  }
6018 
6019  cobj->pPolyTessGeo = (PolyTessGeo*)pObj->pPolyTessGeo;
6020  cobj->m_chart_context = (chart_context*)pObj->m_chart_context;
6021 
6022  if (pObj->auxParm3 != 1234) {
6023  pObj->auxParm3 = 1234;
6024  pObj->auxParm0 = -99;
6025  }
6026 
6027  cobj->auxParm0 = pObj->auxParm0;
6028  cobj->auxParm1 = 0;
6029  cobj->auxParm2 = 0;
6030  cobj->auxParm3 = 0;
6031 
6032  S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context;
6033 
6034  if (pContext->bBBObj_valid)
6035  // this is ugly because plugins still use BoundingBox
6036  cobj->BBObj.Set(pContext->BBObj.GetMinY(), pContext->BBObj.GetMinX(),
6037  pContext->BBObj.GetMaxY(), pContext->BBObj.GetMaxX());
6038 
6039  cobj->CSrules = pContext->CSrules;
6040  cobj->bCS_Added = pContext->bCS_Added;
6041 
6042  cobj->FText = pContext->FText;
6043  cobj->bFText_Added = pContext->bFText_Added;
6044  cobj->rText = pContext->rText;
6045 
6046  cobj->bIsClone = true; // Protect cloned object pointers in S57Obj dtor
6047 
6048  if (pctx) {
6049  cobj->m_chart_context = pctx;
6050  chart_context* ppctx = (chart_context*)pObj->m_chart_context;
6051 
6052  if (ppctx) {
6053  cobj->m_chart_context->m_pvc_hash = ppctx->m_pvc_hash;
6054  cobj->m_chart_context->m_pve_hash = ppctx->m_pve_hash;
6055  cobj->m_chart_context->ref_lat = ppctx->ref_lat;
6056  cobj->m_chart_context->ref_lon = ppctx->ref_lon;
6057  cobj->m_chart_context->pFloatingATONArray = ppctx->pFloatingATONArray;
6058  cobj->m_chart_context->pRigidATONArray = ppctx->pRigidATONArray;
6059  cobj->m_chart_context->safety_contour = ppctx->safety_contour;
6060  cobj->m_chart_context->vertex_buffer = ppctx->vertex_buffer;
6061  }
6062  cobj->m_chart_context->chart =
6063  0; // note bene, this is always NULL for a PlugIn chart
6064  cobj->m_chart_context->chart_type = S52_CHART_TYPE_PLUGIN;
6065  }
6066 }
6067 
6068 bool PI_PLIBSetContext(PI_S57Obj* pObj) {
6069  S52PLIB_Context* ctx;
6070  if (!pObj->S52_Context) {
6071  ctx = new S52PLIB_Context;
6072  pObj->S52_Context = ctx;
6073  }
6074 
6075  ctx = (S52PLIB_Context*)pObj->S52_Context;
6076 
6077  S57Obj cobj;
6078  CreateCompatibleS57Object(pObj, &cobj, NULL);
6079 
6080  LUPname LUP_Name = PAPER_CHART;
6081 
6082  // Force a re-evaluation of CS rules
6083  ctx->CSrules = NULL;
6084  ctx->bCS_Added = false;
6085 
6086  // Clear the rendered text cache
6087  if (ctx->bFText_Added) {
6088  ctx->bFText_Added = false;
6089  delete ctx->FText;
6090  ctx->FText = NULL;
6091  }
6092 
6093  // Reset object selection box
6094  ctx->bBBObj_valid = true;
6095  ctx->BBObj.SetMin(pObj->lon_min, pObj->lat_min);
6096  ctx->BBObj.SetMax(pObj->lon_max, pObj->lat_max);
6097 
6098  // This is where Simplified or Paper-Type point features are selected
6099  switch (cobj.Primitive_type) {
6100  case GEO_POINT:
6101  case GEO_META:
6102  case GEO_PRIM:
6103 
6104  if (PAPER_CHART == ps52plib->m_nSymbolStyle)
6105  LUP_Name = PAPER_CHART;
6106  else
6107  LUP_Name = SIMPLIFIED;
6108 
6109  break;
6110 
6111  case GEO_LINE:
6112  LUP_Name = LINES;
6113  break;
6114 
6115  case GEO_AREA:
6116  if (PLAIN_BOUNDARIES == ps52plib->m_nBoundaryStyle)
6117  LUP_Name = PLAIN_BOUNDARIES;
6118  else
6119  LUP_Name = SYMBOLIZED_BOUNDARIES;
6120 
6121  break;
6122  }
6123 
6124  LUPrec* lup = ps52plib->S52_LUPLookup(LUP_Name, cobj.FeatureName, &cobj);
6125  ctx->LUP = lup;
6126 
6127  // Convert LUP to rules set
6128  ps52plib->_LUP2rules(lup, &cobj);
6129 
6130  ctx->MPSRulesList = NULL;
6131 
6132  return true;
6133 }
6134 
6135 void PI_UpdateContext(PI_S57Obj* pObj) {
6136  S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context;
6137  if (pContext) {
6138  pContext->bBBObj_valid = true;
6139  pContext->BBObj.SetMin(pObj->lon_min, pObj->lat_min);
6140  pContext->BBObj.SetMax(pObj->lon_max, pObj->lat_max);
6141  }
6142 }
6143 
6144 void UpdatePIObjectPlibContext(PI_S57Obj* pObj, S57Obj* cobj,
6145  ObjRazRules* rzRules) {
6146  // Update the PLIB context after the render operation
6147  S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context;
6148 
6149  pContext->CSrules = cobj->CSrules;
6150  pContext->bCS_Added = cobj->bCS_Added;
6151 
6152  pContext->FText = cobj->FText;
6153  pContext->bFText_Added = cobj->bFText_Added;
6154  pContext->rText = cobj->rText;
6155 
6156  if (cobj->BBObj.GetValid()) {
6157  // ugly as plugins still use BoundingBox
6158  pContext->BBObj =
6159  BoundingBox(cobj->BBObj.GetMinLon(), cobj->BBObj.GetMinLat(),
6160  cobj->BBObj.GetMaxLon(), cobj->BBObj.GetMaxLat());
6161  pContext->bBBObj_valid = true;
6162  }
6163 
6164  // Render operation may have promoted the object's display category
6165  // (e.g.WRECKS)
6166  pObj->m_DisplayCat = (PI_DisCat)cobj->m_DisplayCat;
6167 
6168  if (gs_plib_flags & PLIB_CAPS_OBJCATMUTATE) pObj->m_DPRI = cobj->m_DPRI;
6169 
6170  pContext->ChildRazRules = rzRules->child;
6171  pContext->MPSRulesList = rzRules->mps;
6172 
6173  pObj->auxParm0 = cobj->auxParm0;
6174 }
6175 
6176 bool PI_GetObjectRenderBox(PI_S57Obj* pObj, double* lat_min, double* lat_max,
6177  double* lon_min, double* lon_max) {
6178  S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context;
6179  if (pContext) {
6180  if (lat_min) *lat_min = pContext->BBObj.GetMinY();
6181  if (lat_max) *lat_max = pContext->BBObj.GetMaxY();
6182  if (lon_min) *lon_min = pContext->BBObj.GetMinX();
6183  if (lon_max) *lon_max = pContext->BBObj.GetMaxX();
6184  return pContext->bBBObj_valid;
6185  } else
6186  return false;
6187 }
6188 
6189 PI_LUPname PI_GetObjectLUPName(PI_S57Obj* pObj) {
6190  S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context;
6191  if (pContext) {
6192  LUPrec* lup = pContext->LUP;
6193  if (lup) return (PI_LUPname)(lup->TNAM);
6194  }
6195  return (PI_LUPname)(-1);
6196 }
6197 
6198 PI_DisPrio PI_GetObjectDisplayPriority(PI_S57Obj* pObj) {
6199  S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context;
6200  if (pContext) {
6201  LUPrec* lup = pContext->LUP;
6202  if (lup) return (PI_DisPrio)(lup->DPRI);
6203  }
6204 
6205  return (PI_DisPrio)(-1);
6206 }
6207 
6208 PI_DisCat PI_GetObjectDisplayCategory(PI_S57Obj* pObj) {
6209  S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context;
6210  if (pContext) {
6211  LUPrec* lup = pContext->LUP;
6212  if (lup) return (PI_DisCat)(lup->DISC);
6213  }
6214  return (PI_DisCat)(-1);
6215 }
6216 double PI_GetPLIBMarinerSafetyContour() {
6217  return S52_getMarinerParam(S52_MAR_SAFETY_CONTOUR);
6218 }
6219 
6220 void PI_PLIBSetLineFeaturePriority(PI_S57Obj* pObj, int prio) {
6221  // Create and populate a compatible s57 Object
6222  S57Obj cobj;
6223  chart_context ctx;
6224  CreateCompatibleS57Object(pObj, &cobj, &ctx);
6225 
6226  S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context;
6227 
6228  // Create and populate a minimally compatible object container
6229  ObjRazRules rzRules;
6230  rzRules.obj = &cobj;
6231  rzRules.LUP = pContext->LUP;
6232  rzRules.sm_transform_parms = 0;
6233  rzRules.child = NULL;
6234  rzRules.next = NULL;
6235  rzRules.mps = pContext->MPSRulesList;
6236 
6237  if (pContext->LUP) {
6238  ps52plib->SetLineFeaturePriority(&rzRules, prio);
6239 
6240  // Update the PLIB context after the render operation
6241  UpdatePIObjectPlibContext(pObj, &cobj, &rzRules);
6242  }
6243 }
6244 
6245 void PI_PLIBPrepareForNewRender(void) {
6246  if (ps52plib) {
6247  ps52plib->PrepareForRender();
6248  ps52plib->ClearTextList();
6249 
6250  if (gs_plib_flags & PLIB_CAPS_LINE_BUFFER)
6251  ps52plib->EnableGLLS(true); // Newer PlugIns can use GLLS
6252  else
6253  ps52plib->EnableGLLS(false); // Older cannot
6254  }
6255 }
6256 
6257 void PI_PLIBSetRenderCaps(unsigned int flags) { gs_plib_flags = flags; }
6258 
6259 void PI_PLIBFreeContext(void* pContext) {
6260  S52PLIB_Context* pctx = (S52PLIB_Context*)pContext;
6261 
6262  if (pctx->ChildRazRules) {
6263  ObjRazRules* ctop = pctx->ChildRazRules;
6264  while (ctop) {
6265  delete ctop->obj;
6266 
6267  if (ps52plib) ps52plib->DestroyLUP(ctop->LUP);
6268 
6269  ObjRazRules* cnxx = ctop->next;
6270  delete ctop;
6271  ctop = cnxx;
6272  }
6273  }
6274 
6275  if (pctx->MPSRulesList) {
6276  if (ps52plib && pctx->MPSRulesList->cs_rules) {
6277  for (unsigned int i = 0; i < pctx->MPSRulesList->cs_rules->GetCount();
6278  i++) {
6279  Rules* top = pctx->MPSRulesList->cs_rules->Item(i);
6280  ps52plib->DestroyRulesChain(top);
6281  }
6282  delete pctx->MPSRulesList->cs_rules;
6283  }
6284  free(pctx->MPSRulesList);
6285  }
6286 
6287  delete pctx->FText;
6288 
6289  delete pctx;
6290 }
6291 
6292 int PI_PLIBRenderObjectToDC(wxDC* pdc, PI_S57Obj* pObj, PlugIn_ViewPort* vp) {
6293  // Create and populate a compatible s57 Object
6294  S57Obj cobj;
6295  chart_context ctx;
6296  CreateCompatibleS57Object(pObj, &cobj, &ctx);
6297 
6298  S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context;
6299 
6300  // Set up object SM rendering constants
6301  sm_parms transform;
6302  toSM(vp->clat, vp->clon, pObj->chart_ref_lat, pObj->chart_ref_lon,
6303  &transform.easting_vp_center, &transform.northing_vp_center);
6304 
6305  // Create and populate a minimally compatible object container
6306  ObjRazRules rzRules;
6307  rzRules.obj = &cobj;
6308  rzRules.LUP = pContext->LUP;
6309  rzRules.sm_transform_parms = &transform;
6310  rzRules.child = pContext->ChildRazRules;
6311  rzRules.next = NULL;
6312  rzRules.mps = pContext->MPSRulesList;
6313 
6314  if (pContext->LUP) {
6315  ViewPort cvp = CreateCompatibleViewport(*vp);
6316 
6317  // Do the render
6318  // FIXME (plib)
6319  ps52plib->SetVPointCompat(cvp.pix_width, cvp.pix_height, cvp.view_scale_ppm,
6320  cvp.rotation, cvp.clat, cvp.clon, cvp.chart_scale,
6321  cvp.rv_rect, cvp.GetBBox(), cvp.ref_scale,
6322  GetOCPNCanvasWindow()->GetContentScaleFactor());
6323  ps52plib->PrepareForRender();
6324 
6325  ps52plib->RenderObjectToDC(pdc, &rzRules);
6326 
6327  // Update the PLIB context after the render operation
6328  UpdatePIObjectPlibContext(pObj, &cobj, &rzRules);
6329  }
6330 
6331  return 1;
6332 }
6333 
6334 int PI_PLIBRenderAreaToDC(wxDC* pdc, PI_S57Obj* pObj, PlugIn_ViewPort* vp,
6335  wxRect rect, unsigned char* pixbuf) {
6336  // Create a compatible render canvas
6337  render_canvas_parms pb_spec;
6338 
6339  pb_spec.depth = BPP;
6340  pb_spec.pb_pitch = ((rect.width * pb_spec.depth / 8));
6341  pb_spec.lclip = rect.x;
6342  pb_spec.rclip = rect.x + rect.width - 1;
6343  pb_spec.pix_buff = pixbuf; // the passed buffer
6344  pb_spec.width = rect.width;
6345  pb_spec.height = rect.height;
6346  pb_spec.x = rect.x;
6347  pb_spec.y = rect.y;
6348 #ifdef ocpnUSE_ocpnBitmap
6349  pb_spec.b_revrgb = true;
6350 #else
6351  pb_spec.b_revrgb = false;
6352 #endif
6353 
6354  pb_spec.b_revrgb = false;
6355 
6356  // Create and populate a compatible s57 Object
6357  S57Obj cobj;
6358  chart_context ctx;
6359  CreateCompatibleS57Object(pObj, &cobj, &ctx);
6360 
6361  S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context;
6362 
6363  // Set up object SM rendering constants
6364  sm_parms transform;
6365  toSM(vp->clat, vp->clon, pObj->chart_ref_lat, pObj->chart_ref_lon,
6366  &transform.easting_vp_center, &transform.northing_vp_center);
6367 
6368  // Create and populate a minimally compatible object container
6369  ObjRazRules rzRules;
6370  rzRules.obj = &cobj;
6371  rzRules.LUP = pContext->LUP;
6372  rzRules.sm_transform_parms = &transform;
6373  rzRules.child = pContext->ChildRazRules;
6374  rzRules.next = NULL;
6375  rzRules.mps = pContext->MPSRulesList;
6376 
6377  ViewPort cvp = CreateCompatibleViewport(*vp);
6378 
6379  // If the PlugIn does not support it nativiely, build a fully described
6380  // Geomoetry
6381  if (!(gs_plib_flags & PLIB_CAPS_SINGLEGEO_BUFFER)) {
6382  if (!pObj->geoPtMulti) { // do this only once
6383  PolyTessGeo* tess = (PolyTessGeo*)pObj->pPolyTessGeo;
6384 
6385  if (!tess) return 1; // bail on empty data
6386 
6387  PolyTriGroup* ptg = new PolyTriGroup;
6388  ptg->tri_prim_head =
6389  tess->Get_PolyTriGroup_head()->tri_prim_head; // tph;
6390  ptg->bsingle_alloc = false;
6391  ptg->data_type = DATA_TYPE_DOUBLE;
6392  tess->Set_PolyTriGroup_head(ptg);
6393 
6394  double* pd = (double*)malloc(sizeof(double));
6395  pObj->geoPtMulti = pd; // Hack hack
6396  }
6397  }
6398 
6399  if (pContext->LUP) {
6400  // Do the render
6401  // FIXME (plib)
6402  ps52plib->SetVPointCompat(cvp.pix_width, cvp.pix_height, cvp.view_scale_ppm,
6403  cvp.rotation, cvp.clat, cvp.clon, cvp.chart_scale,
6404  cvp.rv_rect, cvp.GetBBox(), cvp.ref_scale,
6405  GetOCPNCanvasWindow()->GetContentScaleFactor());
6406  ps52plib->PrepareForRender();
6407 
6408  ps52plib->RenderAreaToDC(pdc, &rzRules, &pb_spec);
6409 
6410  // Update the PLIB context after the render operation
6411  UpdatePIObjectPlibContext(pObj, &cobj, &rzRules);
6412  }
6413 
6414  return 1;
6415 }
6416 
6417 int PI_PLIBRenderAreaToGL(const wxGLContext& glcc, PI_S57Obj* pObj,
6418  PlugIn_ViewPort* vp, wxRect& render_rect) {
6419 #ifdef ocpnUSE_GL
6420  // Create and populate a compatible s57 Object
6421  S57Obj cobj;
6422  chart_context ctx;
6423  CreateCompatibleS57Object(pObj, &cobj, &ctx);
6424 
6425  // chart_context *pct = (chart_context *)pObj->m_chart_context;
6426 
6427  // If the PlugIn does not support it nativiely, build a fully described
6428  // Geomoetry
6429 
6430  if (!(gs_plib_flags & PLIB_CAPS_SINGLEGEO_BUFFER)) {
6431  if (!pObj->geoPtMulti) { // only do this once
6432  PolyTessGeo* tess = (PolyTessGeo*)pObj->pPolyTessGeo;
6433 
6434  if (!tess) return 1; // bail on empty data
6435 
6436  PolyTriGroup* ptg =
6437  new PolyTriGroup; // this will leak a little, but is POD
6438  ptg->tri_prim_head = tess->Get_PolyTriGroup_head()->tri_prim_head;
6439  ptg->bsingle_alloc = false;
6440  ptg->data_type = DATA_TYPE_DOUBLE;
6441  tess->Set_PolyTriGroup_head(ptg);
6442 
6443  // Mark this object using geoPtMulti
6444  // The malloc will get free'ed when the object is deleted.
6445  double* pd = (double*)malloc(sizeof(double));
6446  pObj->geoPtMulti = pd; // Hack hack
6447  }
6448  cobj.auxParm0 = -6; // signal that this object render cannot use VBO
6449  cobj.auxParm1 = -1; // signal that this object render cannot have single
6450  // buffer conversion done
6451  } else { // it is a newer PLugIn, so can do single buffer conversion and VBOs
6452  if (pObj->auxParm0 < 1)
6453  cobj.auxParm0 = -7; // signal that this object render can use a
6454  // persistent VBO for area triangle vertices
6455  }
6456 
6457  S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context;
6458 
6459  // Set up object SM rendering constants
6460  sm_parms transform;
6461  toSM(vp->clat, vp->clon, pObj->chart_ref_lat, pObj->chart_ref_lon,
6462  &transform.easting_vp_center, &transform.northing_vp_center);
6463 
6464  // Create and populate a minimally compatible object container
6465  ObjRazRules rzRules;
6466  rzRules.obj = &cobj;
6467  rzRules.LUP = pContext->LUP;
6468  rzRules.sm_transform_parms = &transform;
6469  rzRules.child = pContext->ChildRazRules;
6470  rzRules.next = NULL;
6471  rzRules.mps = pContext->MPSRulesList;
6472 
6473  if (pContext->LUP) {
6474  ViewPort cvp = CreateCompatibleViewport(*vp);
6475 
6476  // Do the render
6477  // FIXME (plib)
6478  ps52plib->SetVPointCompat(cvp.pix_width, cvp.pix_height, cvp.view_scale_ppm,
6479  cvp.rotation, cvp.clat, cvp.clon, cvp.chart_scale,
6480  cvp.rv_rect, cvp.GetBBox(), cvp.ref_scale,
6481  GetOCPNCanvasWindow()->GetContentScaleFactor());
6482  ps52plib->PrepareForRender();
6483 
6484  ps52plib->RenderAreaToGL(glcc, &rzRules);
6485 
6486  // Update the PLIB context after the render operation
6487  UpdatePIObjectPlibContext(pObj, &cobj, &rzRules);
6488  }
6489 
6490 #endif
6491  return 1;
6492 }
6493 
6494 int PI_PLIBRenderObjectToGL(const wxGLContext& glcc, PI_S57Obj* pObj,
6495  PlugIn_ViewPort* vp, wxRect& render_rect) {
6496  // Create and populate a compatible s57 Object
6497  S57Obj cobj;
6498  chart_context ctx;
6499  CreateCompatibleS57Object(pObj, &cobj, &ctx);
6500 
6501  S52PLIB_Context* pContext = (S52PLIB_Context*)pObj->S52_Context;
6502 
6503  // Set up object SM rendering constants
6504  sm_parms transform;
6505  toSM(vp->clat, vp->clon, pObj->chart_ref_lat, pObj->chart_ref_lon,
6506  &transform.easting_vp_center, &transform.northing_vp_center);
6507 
6508  // Create and populate a minimally compatible object container
6509  ObjRazRules rzRules;
6510  rzRules.obj = &cobj;
6511  rzRules.LUP = pContext->LUP;
6512  rzRules.sm_transform_parms = &transform;
6513  rzRules.child = pContext->ChildRazRules;
6514  rzRules.next = NULL;
6515  rzRules.mps = pContext->MPSRulesList;
6516 
6517  if (pContext->LUP) {
6518  ViewPort cvp = CreateCompatibleViewport(*vp);
6519 
6520  // Do the render
6521  // FIXME (plib)
6522  ps52plib->SetVPointCompat(cvp.pix_width, cvp.pix_height, cvp.view_scale_ppm,
6523  cvp.rotation, cvp.clat, cvp.clon, cvp.chart_scale,
6524  cvp.rv_rect, cvp.GetBBox(), cvp.ref_scale,
6525  GetOCPNCanvasWindow()->GetContentScaleFactor());
6526  ps52plib->PrepareForRender();
6527 
6528  ps52plib->RenderObjectToGL(glcc, &rzRules);
6529 
6530  // Update the PLIB context after the render operation
6531  UpdatePIObjectPlibContext(pObj, &cobj, &rzRules);
6532  }
6533 
6534  return 1;
6535 }
6536 
6537 /* API 1.13 */
6538 
6539 /* API 1.13 adds some more common functions to avoid unnecessary code
6540  * duplication */
6541 
6542 double fromDMM_Plugin(wxString sdms) { return fromDMM(sdms); }
6543 
6544 void SetCanvasRotation(double rotation) {
6545  gFrame->GetPrimaryCanvas()->DoRotateCanvas(rotation);
6546 }
6547 
6548 double GetCanvasTilt() { return gFrame->GetPrimaryCanvas()->GetVPTilt(); }
6549 
6550 void SetCanvasTilt(double tilt) {
6551  gFrame->GetPrimaryCanvas()->DoTiltCanvas(tilt);
6552 }
6553 
6554 void SetCanvasProjection(int projection) {
6555  gFrame->GetPrimaryCanvas()->SetVPProjection(projection);
6556 }
6557 
6558 OcpnSound* g_PluginSound = SoundFactory();
6559 static void onPlugInPlaySoundExFinished(void* ptr) {}
6560 
6561 // Start playing a sound to a given device and return status to plugin
6562 bool PlugInPlaySoundEx(wxString& sound_file, int deviceIndex) {
6563  bool ok = g_PluginSound->Load(sound_file, deviceIndex);
6564  if (!ok) {
6565  wxLogWarning("Cannot load sound file: %s", sound_file);
6566  return false;
6567  }
6568  auto cmd_sound = dynamic_cast<SystemCmdSound*>(g_PluginSound);
6569  if (cmd_sound) cmd_sound->SetCmd(g_CmdSoundString.mb_str(wxConvUTF8));
6570 
6571  g_PluginSound->SetFinishedCallback(onPlugInPlaySoundExFinished, NULL);
6572  ok = g_PluginSound->Play();
6573  if (!ok) {
6574  wxLogWarning("Cannot play sound file: %s", sound_file);
6575  }
6576  return ok;
6577 }
6578 
6579 bool CheckEdgePan_PlugIn(int x, int y, bool dragging, int margin, int delta) {
6580  return gFrame->GetPrimaryCanvas()->CheckEdgePan(x, y, dragging, margin,
6581  delta);
6582 }
6583 
6584 wxBitmap GetIcon_PlugIn(const wxString& name) {
6585  ocpnStyle::Style* style = g_StyleManager->GetCurrentStyle();
6586  return style->GetIcon(name);
6587 }
6588 
6589 void SetCursor_PlugIn(wxCursor* pCursor) {
6590  gFrame->GetPrimaryCanvas()->pPlugIn_Cursor = pCursor;
6591 }
6592 
6593 void AddChartDirectory(wxString& path) {
6594  if (g_options) {
6595  g_options->AddChartDir(path);
6596  }
6597 }
6598 
6599 void ForceChartDBUpdate() {
6600  if (g_options) {
6601  g_options->pScanCheckBox->SetValue(true);
6602  g_options->pUpdateCheckBox->SetValue(true);
6603  }
6604 }
6605 
6606 void ForceChartDBRebuild() {
6607  if (g_options) {
6608  g_options->pUpdateCheckBox->SetValue(true);
6609  }
6610 }
6611 
6612 wxDialog* GetActiveOptionsDialog() { return g_options; }
6613 
6614 int PlatformDirSelectorDialog(wxWindow* parent, wxString* file_spec,
6615  wxString Title, wxString initDir) {
6616  return g_Platform->DoDirSelectorDialog(parent, file_spec, Title, initDir);
6617 }
6618 
6619 int PlatformFileSelectorDialog(wxWindow* parent, wxString* file_spec,
6620  wxString Title, wxString initDir,
6621  wxString suggestedName, wxString wildcard) {
6622  return g_Platform->DoFileSelectorDialog(parent, file_spec, Title, initDir,
6623  suggestedName, wildcard);
6624 }
6625 
6626 // http File Download Support
6627 
6628 // OCPN_downloadEvent Implementation
6629 
6630 OCPN_downloadEvent::OCPN_downloadEvent(wxEventType commandType, int id)
6631  : wxEvent(id, commandType) {
6632  m_stat = OCPN_DL_UNKNOWN;
6633  m_condition = OCPN_DL_EVENT_TYPE_UNKNOWN;
6634  m_b_complete = false;
6635  m_sofarBytes = 0;
6636 }
6637 
6638 OCPN_downloadEvent::~OCPN_downloadEvent() {}
6639 
6640 wxEvent* OCPN_downloadEvent::Clone() const {
6641  OCPN_downloadEvent* newevent = new OCPN_downloadEvent(*this);
6642  newevent->m_stat = this->m_stat;
6643  newevent->m_condition = this->m_condition;
6644 
6645  newevent->m_totalBytes = this->m_totalBytes;
6646  newevent->m_sofarBytes = this->m_sofarBytes;
6647  newevent->m_b_complete = this->m_b_complete;
6648 
6649  return newevent;
6650 }
6651 
6652 // const wxEventType wxEVT_DOWNLOAD_EVENT = wxNewEventType();
6653 DECL_EXP wxEventType wxEVT_DOWNLOAD_EVENT = wxNewEventType();
6654 
6655 _OCPN_DLStatus g_download_status;
6656 _OCPN_DLCondition g_download_condition;
6657 
6658 #define DL_EVENT_TIMER 4388
6659 
6660 class PI_DLEvtHandler : public wxEvtHandler {
6661 public:
6662  PI_DLEvtHandler();
6663  ~PI_DLEvtHandler();
6664 
6665  void onDLEvent(OCPN_downloadEvent& event);
6666  void setBackgroundMode(long ID, wxEvtHandler* handler);
6667  void clearBackgroundMode();
6668  void onTimerEvent(wxTimerEvent& event);
6669 
6670  long m_id;
6671  wxTimer m_eventTimer;
6672  wxEvtHandler* m_download_evHandler;
6673 
6674  long m_sofarBytes;
6675  long m_totalBytes;
6676 };
6677 
6678 PI_DLEvtHandler::PI_DLEvtHandler() {
6679  g_download_status = OCPN_DL_UNKNOWN;
6680  g_download_condition = OCPN_DL_EVENT_TYPE_UNKNOWN;
6681 
6682  m_download_evHandler = NULL;
6683  m_id = -1;
6684  m_sofarBytes = 0;
6685  m_totalBytes = 0;
6686 }
6687 
6688 PI_DLEvtHandler::~PI_DLEvtHandler() {
6689  m_eventTimer.Stop();
6690  Disconnect(
6691  wxEVT_TIMER,
6692  (wxObjectEventFunction)(wxEventFunction)&PI_DLEvtHandler::onTimerEvent);
6693 }
6694 
6695 void PI_DLEvtHandler::onDLEvent(OCPN_downloadEvent& event) {
6696  // qDebug() << "Got Event " << (int)event.getDLEventStatus() <<
6697  // (int)event.getDLEventCondition();
6698 
6699  g_download_status = event.getDLEventStatus();
6700  g_download_condition = event.getDLEventCondition();
6701 
6702  // This is an END event, happening at the end of BACKGROUND file download
6703  if (m_download_evHandler &&
6704  (OCPN_DL_EVENT_TYPE_END == event.getDLEventCondition())) {
6705  OCPN_downloadEvent ev(wxEVT_DOWNLOAD_EVENT, 0);
6706  ev.setComplete(true);
6707  ev.setTransferred(m_sofarBytes);
6708  ev.setTotal(m_totalBytes);
6709 
6710  ev.setDLEventStatus(event.getDLEventStatus());
6711  ev.setDLEventCondition(OCPN_DL_EVENT_TYPE_END);
6712 
6713  m_download_evHandler->AddPendingEvent(ev);
6714  m_eventTimer.Stop();
6715 #ifdef __ANDROID__
6716  finishAndroidFileDownload();
6717 #endif
6718  }
6719 
6720  event.Skip();
6721 }
6722 
6723 void PI_DLEvtHandler::setBackgroundMode(long ID, wxEvtHandler* handler) {
6724  m_id = ID;
6725  m_download_evHandler = handler;
6726 
6727  m_eventTimer.SetOwner(this, DL_EVENT_TIMER);
6728 
6729  Connect(
6730  wxEVT_TIMER,
6731  (wxObjectEventFunction)(wxEventFunction)&PI_DLEvtHandler::onTimerEvent);
6732  m_eventTimer.Start(1000, wxTIMER_CONTINUOUS);
6733 }
6734 
6735 void PI_DLEvtHandler::clearBackgroundMode() {
6736  m_download_evHandler = NULL;
6737  m_eventTimer.Stop();
6738 }
6739 
6740 void PI_DLEvtHandler::onTimerEvent(wxTimerEvent& event) {
6741 #ifdef __ANDROID__
6742  // Query the download status, and post to the original requestor
6743  // This method only happens on Background file downloads
6744 
6745  wxString sstat;
6746  int stat = queryAndroidFileDownload(m_id, &sstat);
6747 
6748  OCPN_downloadEvent ev(wxEVT_DOWNLOAD_EVENT, 0);
6749  long sofarBytes = 0;
6750  long totalBytes = -1;
6751  long state = -1;
6752 
6753  if (stat) { // some error
6754  qDebug() << "Error on queryAndroidFileDownload, ending download";
6755  ev.setComplete(true);
6756  ev.setTransferred(sofarBytes);
6757  ev.setTotal(totalBytes);
6758 
6759  ev.setDLEventStatus(OCPN_DL_FAILED);
6760  ev.setDLEventCondition(OCPN_DL_EVENT_TYPE_END);
6761  } else {
6762  wxStringTokenizer tk(sstat, _T(";"));
6763  if (tk.HasMoreTokens()) {
6764  wxString token = tk.GetNextToken();
6765  token.ToLong(&state);
6766  token = tk.GetNextToken();
6767  token.ToLong(&sofarBytes);
6768  token = tk.GetNextToken();
6769  token.ToLong(&totalBytes);
6770  }
6771 
6772  qDebug() << state << sofarBytes << totalBytes;
6773 
6774  m_sofarBytes = sofarBytes;
6775  m_totalBytes = totalBytes;
6776 
6777  ev.setTransferred(sofarBytes);
6778  ev.setTotal(totalBytes);
6779 
6780  if (state == 16) { // error
6781  qDebug() << "Event OCPN_DL_FAILED/OCPN_DL_EVENT_TYPE_END";
6782  ev.setComplete(true);
6783  ev.setDLEventStatus(OCPN_DL_FAILED);
6784  ev.setDLEventCondition(OCPN_DL_EVENT_TYPE_END);
6785  } else if (state == 8) { // Completed OK
6786  qDebug() << "Event OCPN_DL_NO_ERROR/OCPN_DL_EVENT_TYPE_END";
6787  ev.setComplete(true);
6788  ev.setDLEventStatus(OCPN_DL_NO_ERROR);
6789  ev.setDLEventCondition(OCPN_DL_EVENT_TYPE_END);
6790  } else {
6791  ev.setComplete(false);
6792  ev.setDLEventStatus(OCPN_DL_UNKNOWN);
6793  ev.setDLEventCondition(OCPN_DL_EVENT_TYPE_PROGRESS);
6794  }
6795 
6796  // 2;0;148686
6797  }
6798 
6799  if (m_download_evHandler) {
6800  // qDebug() << "Sending event on timer...";
6801  m_download_evHandler->AddPendingEvent(ev);
6802  }
6803 
6804  // Background download is all done.
6805  if (OCPN_DL_EVENT_TYPE_END == ev.getDLEventCondition()) {
6806  m_eventTimer.Stop();
6807  finishAndroidFileDownload();
6808  }
6809 
6810 #endif
6811 }
6812 
6813 PI_DLEvtHandler* g_piEventHandler;
6814 
6815 // Blocking download of single file
6816 _OCPN_DLStatus OCPN_downloadFile(const wxString& url,
6817  const wxString& outputFile,
6818  const wxString& title, const wxString& message,
6819  const wxBitmap& bitmap, wxWindow* parent,
6820  long style, int timeout_secs) {
6821 #ifdef __ANDROID__
6822 
6823  wxString msg = _T("Downloading file synchronously: ");
6824  msg += url;
6825  msg += _T(" to: ");
6826  msg += outputFile;
6827  wxLogMessage(msg);
6828 
6829  // Validate the write location
6830  int vres = validateAndroidWriteLocation(outputFile);
6831  if (vres == 0) // Pending permission dialog
6832  return OCPN_DL_ABORTED;
6833 
6834  // Create a single event handler to receive status events
6835  if (!g_piEventHandler) g_piEventHandler = new PI_DLEvtHandler;
6836 
6837  // Reset global status indicators
6838  g_download_status = OCPN_DL_UNKNOWN;
6839  g_download_condition = OCPN_DL_EVENT_TYPE_UNKNOWN;
6840 
6841  // Create a connection for the expected events from Android Activity
6842  g_piEventHandler->Connect(
6843  wxEVT_DOWNLOAD_EVENT,
6844  (wxObjectEventFunction)(wxEventFunction)&PI_DLEvtHandler::onDLEvent);
6845 
6846  long dl_ID = -1;
6847 
6848  // Make sure the outputfile is a file URI
6849  wxString fURI = outputFile;
6850  if (!fURI.StartsWith(_T("file://"))) {
6851  fURI.Prepend(_T("file://"));
6852  }
6853 
6854  int res = startAndroidFileDownload(url, fURI, g_piEventHandler, &dl_ID);
6855  // Started OK?
6856  if (res) {
6857  finishAndroidFileDownload();
6858  g_piEventHandler->Disconnect(
6859  wxEVT_DOWNLOAD_EVENT,
6860  (wxObjectEventFunction)(wxEventFunction)&PI_DLEvtHandler::onDLEvent);
6861  // delete g_piEventHandler;
6862  return OCPN_DL_FAILED;
6863  }
6864 
6865  wxDateTime dl_start_time = wxDateTime::Now();
6866 
6867  // Spin, waiting for timeout or event from downstream, and checking status
6868  while (1) {
6869  wxTimeSpan dt = wxDateTime::Now() - dl_start_time;
6870  qDebug() << "Spin.." << dt.GetSeconds().GetLo();
6871 
6872  if (dt.GetSeconds() > timeout_secs) {
6873  qDebug() << "USER_TIMOUT";
6874  finishAndroidFileDownload();
6875  g_piEventHandler->Disconnect(
6876  wxEVT_DOWNLOAD_EVENT,
6877  (wxObjectEventFunction)(wxEventFunction)&PI_DLEvtHandler::onDLEvent);
6878  // delete g_piEventHandler;
6879  return (OCPN_DL_USER_TIMEOUT);
6880  }
6881 
6882  if (g_download_condition != OCPN_DL_EVENT_TYPE_UNKNOWN) {
6883  if (OCPN_DL_EVENT_TYPE_END == g_download_condition) {
6884  _OCPN_DLStatus ss = g_download_status;
6885  finishAndroidFileDownload();
6886  g_piEventHandler->Disconnect(
6887  wxEVT_DOWNLOAD_EVENT,
6888  (wxObjectEventFunction)(wxEventFunction)&PI_DLEvtHandler::
6889  onDLEvent);
6890  // delete g_piEventHandler;
6891  qDebug() << "RETURN DL_END" << (int)ss;
6892  return ss; // The actual return code
6893  }
6894  }
6895 
6896  wxString sstat;
6897  int stat = queryAndroidFileDownload(dl_ID, &sstat);
6898  if (stat) { // some error
6899  qDebug() << "Error on queryAndroidFileDownload";
6900  finishAndroidFileDownload();
6901  g_piEventHandler->Disconnect(
6902  wxEVT_DOWNLOAD_EVENT,
6903  (wxObjectEventFunction)(wxEventFunction)&PI_DLEvtHandler::onDLEvent);
6904  // delete g_piEventHandler;
6905 
6906  return OCPN_DL_FAILED; // so abort
6907  }
6908 
6909  wxSleep(1);
6910  wxSafeYield();
6911  }
6912 
6913 #elif defined(OCPN_USE_CURL)
6914  wxFileName tfn = wxFileName::CreateTempFileName(outputFile);
6915  wxFileOutputStream output(tfn.GetFullPath());
6916 
6917  wxCurlDownloadDialog ddlg(url, &output, title, message + url, bitmap, parent,
6918  style);
6919  wxCurlDialogReturnFlag ret = ddlg.RunModal();
6920  output.Close();
6921 
6922  _OCPN_DLStatus result = OCPN_DL_UNKNOWN;
6923 
6924  switch (ret) {
6925  case wxCDRF_SUCCESS: {
6926  if (wxCopyFile(tfn.GetFullPath(), outputFile))
6927  result = OCPN_DL_NO_ERROR;
6928  else
6929  result = OCPN_DL_FAILED;
6930  break;
6931  }
6932  case wxCDRF_FAILED: {
6933  result = OCPN_DL_FAILED;
6934  break;
6935  }
6936  case wxCDRF_USER_ABORTED: {
6937  result = OCPN_DL_ABORTED;
6938  break;
6939  }
6940  default:
6941  wxASSERT(false); // This should never happen because we handle all
6942  // possible cases of ret
6943  }
6944  if (wxFileExists(tfn.GetFullPath())) wxRemoveFile(tfn.GetFullPath());
6945  return result;
6946 
6947 #else
6948  return OCPN_DL_FAILED;
6949 #endif
6950 }
6951 
6952 // Non-Blocking download of single file
6953 _OCPN_DLStatus OCPN_downloadFileBackground(const wxString& url,
6954  const wxString& outputFile,
6955  wxEvtHandler* handler,
6956  long* handle) {
6957 #ifdef __ANDROID__
6958  wxString msg = _T("Downloading file asynchronously: ");
6959  msg += url;
6960  msg += _T(" to: ");
6961  msg += outputFile;
6962  wxLogMessage(msg);
6963 
6964  // Create a single event handler to receive status events
6965 
6966  if (!g_piEventHandler) g_piEventHandler = new PI_DLEvtHandler;
6967 
6968  long dl_ID = -1;
6969 
6970  int res = startAndroidFileDownload(url, outputFile, NULL /*g_piEventHandler*/,
6971  &dl_ID);
6972  // Started OK?
6973  if (res) {
6974  finishAndroidFileDownload();
6975  return OCPN_DL_FAILED;
6976  }
6977 
6978  // configure the local event handler for background transfer
6979  g_piEventHandler->setBackgroundMode(dl_ID, handler);
6980 
6981  if (handle) *handle = dl_ID;
6982 
6983  return OCPN_DL_STARTED;
6984 
6985 #elif defined(OCPN_USE_CURL)
6986  if (g_pi_manager->m_pCurlThread) // We allow just one download at a time. Do
6987  // we want more? Or at least return some
6988  // other status in this case?
6989  return OCPN_DL_FAILED;
6990  g_pi_manager->m_pCurlThread =
6991  new wxCurlDownloadThread(g_pi_manager, CurlThreadId);
6992  bool http = (url.StartsWith(wxS("http:")) || url.StartsWith(wxS("https:")));
6993  bool keep = false;
6994  if (http && g_pi_manager->m_pCurl &&
6995  dynamic_cast<wxCurlHTTP*>(g_pi_manager->m_pCurl.get())) {
6996  keep = true;
6997  }
6998  if (!keep) {
6999  g_pi_manager->m_pCurl = 0;
7000  }
7001 
7002  bool failed = false;
7003  if (!g_pi_manager->HandleCurlThreadError(
7004  g_pi_manager->m_pCurlThread->SetURL(url, g_pi_manager->m_pCurl),
7005  g_pi_manager->m_pCurlThread, url))
7006  failed = true;
7007  if (!failed) {
7008  g_pi_manager->m_pCurl = g_pi_manager->m_pCurlThread->GetCurlSharedPtr();
7009  if (!g_pi_manager->HandleCurlThreadError(
7010  g_pi_manager->m_pCurlThread->SetOutputStream(
7011  new wxFileOutputStream(outputFile)),
7012  g_pi_manager->m_pCurlThread))
7013  failed = true;
7014  }
7015  if (!failed) {
7016  g_pi_manager->m_download_evHandler = handler;
7017  g_pi_manager->m_downloadHandle = handle;
7018 
7019  wxCurlThreadError err = g_pi_manager->m_pCurlThread->Download();
7020  if (err != wxCTE_NO_ERROR) {
7021  g_pi_manager->HandleCurlThreadError(
7022  err, g_pi_manager->m_pCurlThread); // shows a message to the user
7023  g_pi_manager->m_pCurlThread->Abort();
7024  failed = true;
7025  }
7026  }
7027 
7028  if (!failed) return OCPN_DL_STARTED;
7029 
7030  if (g_pi_manager->m_pCurlThread) {
7031  if (g_pi_manager->m_pCurlThread->IsAlive())
7032  g_pi_manager->m_pCurlThread->Abort();
7033  if (g_pi_manager->m_pCurlThread->GetOutputStream())
7034  delete (g_pi_manager->m_pCurlThread->GetOutputStream());
7035  wxDELETE(g_pi_manager->m_pCurlThread);
7036  g_pi_manager->m_download_evHandler = NULL;
7037  g_pi_manager->m_downloadHandle = NULL;
7038  return OCPN_DL_STARTED;
7039  }
7040  g_pi_manager->m_pCurl = 0;
7041  return OCPN_DL_FAILED;
7042 
7043 #else
7044  return OCPN_DL_FAILED;
7045 #endif
7046 }
7047 
7048 void OCPN_cancelDownloadFileBackground(long handle) {
7049 #ifdef OCPN_USE_CURL
7050 
7051 #ifdef __ANDROID__
7052  cancelAndroidFileDownload(handle);
7053  finishAndroidFileDownload();
7054  if (g_piEventHandler) g_piEventHandler->clearBackgroundMode();
7055 #else
7056  if (g_pi_manager->m_pCurlThread) {
7057  g_pi_manager->m_pCurlThread->Abort();
7058  delete (g_pi_manager->m_pCurlThread->GetOutputStream());
7059  wxDELETE(g_pi_manager->m_pCurlThread);
7060  g_pi_manager->m_download_evHandler = NULL;
7061  g_pi_manager->m_downloadHandle = NULL;
7062  }
7063  g_pi_manager->m_pCurl = 0;
7064 #endif
7065 #endif
7066 }
7067 
7068 _OCPN_DLStatus OCPN_postDataHttp(const wxString& url,
7069  const wxString& parameters, wxString& result,
7070  int timeout_secs) {
7071 #ifdef __ANDROID__
7072  wxString lparms = parameters;
7073  wxString postResult = doAndroidPOST(url, lparms, timeout_secs * 1000);
7074  if (postResult.IsSameAs(_T("NOK"))) return OCPN_DL_FAILED;
7075 
7076  result = postResult;
7077  return OCPN_DL_NO_ERROR;
7078 
7079 #elif defined(OCPN_USE_CURL)
7080  wxCurlHTTP post;
7081  post.SetOpt(CURLOPT_TIMEOUT, timeout_secs);
7082  size_t res = post.Post(parameters.ToAscii(), parameters.Len(), url);
7083 
7084  if (res) {
7085  result = wxString(post.GetResponseBody().c_str(), wxConvUTF8);
7086  return OCPN_DL_NO_ERROR;
7087  } else
7088  result = wxEmptyString;
7089 
7090  return OCPN_DL_FAILED;
7091 
7092 #else
7093  return OCPN_DL_FAILED;
7094 #endif
7095 }
7096 
7097 bool OCPN_isOnline() {
7098 #ifdef __ANDROID__
7099  return androidCheckOnline();
7100 #endif
7101 
7102 #if !defined(__ANDROID__) && defined(OCPN_USE_CURL)
7103  if (wxDateTime::GetTimeNow() >
7104  g_pi_manager->m_last_online_chk + ONLINE_CHECK_RETRY) {
7105  wxCurlHTTP get;
7106  get.Head(_T("http://yahoo.com/"));
7107  g_pi_manager->m_last_online = get.GetResponseCode() > 0;
7108 
7109  g_pi_manager->m_last_online_chk = wxDateTime::GetTimeNow();
7110  }
7111  return g_pi_manager->m_last_online;
7112 #else
7113  return false;
7114 #endif
7115 }
7116 
7117 #if !defined(__ANDROID__) && defined(OCPN_USE_CURL)
7118 void PlugInManager::OnEndPerformCurlDownload(wxCurlEndPerformEvent& ev) {
7119  OCPN_downloadEvent event(wxEVT_DOWNLOAD_EVENT, 0);
7120  if (ev.IsSuccessful()) {
7121  event.setDLEventStatus(OCPN_DL_NO_ERROR);
7122  } else {
7123  g_pi_manager->m_pCurl = 0;
7124  event.setDLEventStatus(OCPN_DL_FAILED);
7125  }
7126  event.setDLEventCondition(OCPN_DL_EVENT_TYPE_END);
7127  event.setComplete(true);
7128 
7129  if (m_download_evHandler) {
7130  m_download_evHandler->AddPendingEvent(event);
7131  m_download_evHandler = NULL;
7132  m_downloadHandle = NULL;
7133  }
7134 
7135  if (m_pCurlThread) {
7136  m_pCurlThread->Wait();
7137  if (!m_pCurlThread->IsAborting()) {
7138  delete (m_pCurlThread->GetOutputStream());
7139  wxDELETE(m_pCurlThread);
7140  }
7141  }
7142 }
7143 
7144 void PlugInManager::OnCurlDownload(wxCurlDownloadEvent& ev) {
7145  OCPN_downloadEvent event(wxEVT_DOWNLOAD_EVENT, 0);
7146  event.setDLEventStatus(OCPN_DL_UNKNOWN);
7147  event.setDLEventCondition(OCPN_DL_EVENT_TYPE_PROGRESS);
7148  event.setTotal(ev.GetTotalBytes());
7149  event.setTransferred(ev.GetDownloadedBytes());
7150  event.setComplete(false);
7151 
7152  if (m_download_evHandler) {
7153  m_download_evHandler->AddPendingEvent(event);
7154  }
7155 }
7156 
7157 bool PlugInManager::HandleCurlThreadError(wxCurlThreadError err,
7158  wxCurlBaseThread* p,
7159  const wxString& url) {
7160  switch (err) {
7161  case wxCTE_NO_ERROR:
7162  return true; // ignore this
7163 
7164  case wxCTE_NO_RESOURCE:
7165  wxLogError(
7166  wxS("Insufficient resources for correct execution of the program."));
7167  break;
7168 
7169  case wxCTE_ALREADY_RUNNING:
7170  wxFAIL; // should never happen!
7171  break;
7172 
7173  case wxCTE_INVALID_PROTOCOL:
7174  wxLogError(wxS("The URL '%s' uses an unsupported protocol."),
7175  url.c_str());
7176  break;
7177 
7178  case wxCTE_NO_VALID_STREAM:
7179  wxFAIL; // should never happen - the user streams should always be valid!
7180  break;
7181 
7182  case wxCTE_ABORTED:
7183  return true; // ignore this
7184 
7185  case wxCTE_CURL_ERROR: {
7186  wxString ws = wxS("unknown");
7187  if (p->GetCurlSession())
7188  ws =
7189  wxString(p->GetCurlSession()->GetErrorString().c_str(), wxConvUTF8);
7190  wxLogError(wxS("Network error: %s"), ws.c_str());
7191  } break;
7192  }
7193 
7194  // stop the thread
7195  if (p->IsAlive()) p->Abort();
7196 
7197  // this is an unrecoverable error:
7198  return false;
7199 }
7200 #endif
7201 
7202 bool LaunchDefaultBrowser_Plugin(wxString url) {
7203  if (g_Platform) g_Platform->platformLaunchDefaultBrowser(url);
7204 
7205  return true;
7206 }
7207 
7208 /* API 1.14 */
7209 
7210 void PlugInAISDrawGL(wxGLCanvas* glcanvas, const PlugIn_ViewPort& vp) {
7211  ViewPort ocpn_vp = CreateCompatibleViewport(vp);
7212 
7213  ocpnDC dc(*glcanvas);
7214  dc.SetVP(ocpn_vp);
7215 
7216  AISDraw(dc, ocpn_vp, NULL);
7217 }
7218 
7219 bool PlugInSetFontColor(const wxString TextElement, const wxColour color) {
7220  return FontMgr::Get().SetFontColor(TextElement, color);
7221 }
7222 
7223 /* API 1.15 */
7224 
7225 double PlugInGetDisplaySizeMM() { return g_Platform->GetDisplaySizeMM(); }
7226 
7227 wxFont* FindOrCreateFont_PlugIn(int point_size, wxFontFamily family,
7228  wxFontStyle style, wxFontWeight weight,
7229  bool underline, const wxString& facename,
7230  wxFontEncoding encoding) {
7231  return FontMgr::Get().FindOrCreateFont(point_size, family, style, weight,
7232  underline, facename, encoding);
7233 }
7234 
7235 int PluginGetMinAvailableGshhgQuality() {
7236  return gFrame->GetPrimaryCanvas()->GetMinAvailableGshhgQuality();
7237 }
7238 int PluginGetMaxAvailableGshhgQuality() {
7239  return gFrame->GetPrimaryCanvas()->GetMaxAvailableGshhgQuality();
7240 }
7241 
7242 // disable builtin console canvas, and autopilot nmea sentences
7243 void PlugInHandleAutopilotRoute(bool enable) {
7244  g_bPluginHandleAutopilotRoute = enable;
7245 }
7246 
7247 /* API 1.16 */
7248 wxString GetSelectedWaypointGUID_Plugin() {
7249  ChartCanvas* cc = gFrame->GetFocusCanvas();
7250  if (cc && cc->GetSelectedRoutePoint()) {
7251  return cc->GetSelectedRoutePoint()->m_GUID;
7252  }
7253  return wxEmptyString;
7254 }
7255 
7256 wxString GetSelectedRouteGUID_Plugin() {
7257  ChartCanvas* cc = gFrame->GetFocusCanvas();
7258  if (cc && cc->GetSelectedRoute()) {
7259  return cc->GetSelectedRoute()->m_GUID;
7260  }
7261  return wxEmptyString;
7262 }
7263 
7264 wxString GetSelectedTrackGUID_Plugin() {
7265  ChartCanvas* cc = gFrame->GetFocusCanvas();
7266  if (cc && cc->GetSelectedTrack()) {
7267  return cc->GetSelectedTrack()->m_GUID;
7268  }
7269  return wxEmptyString;
7270 }
7271 
7272 std::unique_ptr<PlugIn_Waypoint> GetWaypoint_Plugin(const wxString& GUID) {
7273  std::unique_ptr<PlugIn_Waypoint> w(new PlugIn_Waypoint);
7274  GetSingleWaypoint(GUID, w.get());
7275  return w;
7276 }
7277 
7278 std::unique_ptr<PlugIn_Route> GetRoute_Plugin(const wxString& GUID) {
7279  std::unique_ptr<PlugIn_Route> r;
7280  Route* route = g_pRouteMan->FindRouteByGUID(GUID);
7281  if (route == nullptr) return r;
7282 
7283  r = std::unique_ptr<PlugIn_Route>(new PlugIn_Route);
7284  PlugIn_Route* dst_route = r.get();
7285 
7286  // PlugIn_Waypoint *pwp;
7287  RoutePoint* src_wp;
7288  wxRoutePointListNode* node = route->pRoutePointList->GetFirst();
7289 
7290  while (node) {
7291  src_wp = node->GetData();
7292 
7293  PlugIn_Waypoint* dst_wp = new PlugIn_Waypoint();
7294  PlugInFromRoutePoint(dst_wp, src_wp);
7295 
7296  dst_route->pWaypointList->Append(dst_wp);
7297 
7298  node = node->GetNext();
7299  }
7300  dst_route->m_NameString = route->m_RouteNameString;
7301  dst_route->m_StartString = route->m_RouteStartString;
7302  dst_route->m_EndString = route->m_RouteEndString;
7303  dst_route->m_GUID = route->m_GUID;
7304 
7305  return r;
7306 }
7307 
7308 std::unique_ptr<PlugIn_Track> GetTrack_Plugin(const wxString& GUID) {
7309  std::unique_ptr<PlugIn_Track> t;
7310  // Find the Track
7311  Track* pTrack = g_pRouteMan->FindTrackByGUID(GUID);
7312  if (!pTrack) return t;
7313 
7314  std::unique_ptr<PlugIn_Track> tk =
7315  std::unique_ptr<PlugIn_Track>(new PlugIn_Track);
7316  PlugIn_Track* dst_track = tk.get();
7317  dst_track->m_NameString = pTrack->GetName();
7318  dst_track->m_StartString = pTrack->m_TrackStartString;
7319  dst_track->m_EndString = pTrack->m_TrackEndString;
7320  dst_track->m_GUID = pTrack->m_GUID;
7321 
7322  for (int i = 0; i < pTrack->GetnPoints(); i++) {
7323  TrackPoint* ptp = pTrack->GetPoint(i);
7324 
7325  PlugIn_Waypoint* dst_wp = new PlugIn_Waypoint();
7326 
7327  dst_wp->m_lat = ptp->m_lat;
7328  dst_wp->m_lon = ptp->m_lon;
7329  dst_wp->m_CreateTime = ptp->GetCreateTime(); // not const
7330 
7331  dst_track->pWaypointList->Append(dst_wp);
7332  }
7333 
7334  return tk;
7335 }
7336 
7337 wxWindow* PluginGetFocusCanvas() { return g_focusCanvas; }
7338 
7339 wxWindow* PluginGetOverlayRenderCanvas() {
7340  // if(g_overlayCanvas)
7341  return g_overlayCanvas;
7342  // else
7343 }
7344 
7345 void CanvasJumpToPosition(wxWindow* canvas, double lat, double lon,
7346  double scale) {
7347  ChartCanvas* oCanvas = wxDynamicCast(canvas, ChartCanvas);
7348  if (oCanvas) gFrame->JumpToPosition(oCanvas, lat, lon, scale);
7349 }
7350 
7351 bool ShuttingDown(void) { return g_bquiting; }
7352 
7353 wxWindow* GetCanvasUnderMouse(void) { return gFrame->GetCanvasUnderMouse(); }
7354 
7355 int GetCanvasIndexUnderMouse(void) {
7356  ChartCanvas* l_canvas = gFrame->GetCanvasUnderMouse();
7357  if (l_canvas) {
7358  for (unsigned int i = 0; i < g_canvasArray.GetCount(); ++i) {
7359  if (l_canvas == g_canvasArray[i]) return i;
7360  }
7361  }
7362  return 0;
7363 }
7364 
7365 // std::vector<wxWindow *> GetCanvasArray()
7366 // {
7367 // std::vector<wxWindow *> rv;
7368 // for(unsigned int i=0 ; i < g_canvasArray.GetCount() ; i++){
7369 // ChartCanvas *cc = g_canvasArray.Item(i);
7370 // rv.push_back(cc);
7371 // }
7372 //
7373 // return rv;
7374 // }
7375 
7376 wxWindow* GetCanvasByIndex(int canvasIndex) {
7377  if (g_canvasConfig == 0)
7378  return gFrame->GetPrimaryCanvas();
7379  else {
7380  if ((canvasIndex >= 0) && g_canvasArray[canvasIndex]) {
7381  return g_canvasArray[canvasIndex];
7382  }
7383  }
7384  return NULL;
7385 }
7386 
7387 bool CheckMUIEdgePan_PlugIn(int x, int y, bool dragging, int margin, int delta,
7388  int canvasIndex) {
7389  if (g_canvasConfig == 0)
7390  return gFrame->GetPrimaryCanvas()->CheckEdgePan(x, y, dragging, margin,
7391  delta);
7392  else {
7393  if ((canvasIndex >= 0) && g_canvasArray[canvasIndex]) {
7394  return g_canvasArray[canvasIndex]->CheckEdgePan(x, y, dragging, margin,
7395  delta);
7396  }
7397  }
7398 
7399  return false;
7400 }
7401 
7402 void SetMUICursor_PlugIn(wxCursor* pCursor, int canvasIndex) {
7403  if (g_canvasConfig == 0)
7404  gFrame->GetPrimaryCanvas()->pPlugIn_Cursor = pCursor;
7405  else {
7406  if ((canvasIndex >= 0) && g_canvasArray[canvasIndex]) {
7407  g_canvasArray[canvasIndex]->pPlugIn_Cursor = pCursor;
7408  }
7409  }
7410 }
7411 
7412 int GetCanvasCount() {
7413  if (g_canvasConfig == 1) return 2;
7414  // else
7415  return 1;
7416 }
7417 
7418 int GetLatLonFormat() { return g_iSDMMFormat; }
7419 
7420 wxRect GetMasterToolbarRect() {
7421  if (g_MainToolbar)
7422  return g_MainToolbar->GetToolbarRect();
7423  else
7424  return wxRect(0, 0, 1, 1);
7425 }
7426 
7427 /* API 1.17 */
7428 
7429 void ZeroXTE() {
7430  if (g_pRouteMan) {
7431  g_pRouteMan->ZeroCurrentXTEToActivePoint();
7432  }
7433 }
7434 
7435 ListOfPI_S57Obj* PlugInManager::GetLightsObjRuleListVisibleAtLatLon(
7436  ChartPlugInWrapper* target, float zlat, float zlon, const ViewPort& vp) {
7437  ListOfPI_S57Obj* list = NULL;
7438  if (target) {
7439  PlugInChartBaseGLPlus2* picbgl =
7440  dynamic_cast<PlugInChartBaseGLPlus2*>(target->GetPlugInChart());
7441  if (picbgl) {
7442  PlugIn_ViewPort pi_vp = CreatePlugInViewport(vp);
7443  list = picbgl->GetLightsObjRuleListVisibleAtLatLon(zlat, zlon, &pi_vp);
7444 
7445  return list;
7446  }
7448  dynamic_cast<PlugInChartBaseExtendedPlus2*>(target->GetPlugInChart());
7449  if (picbx) {
7450  PlugIn_ViewPort pi_vp = CreatePlugInViewport(vp);
7451  list = picbx->GetLightsObjRuleListVisibleAtLatLon(zlat, zlon, &pi_vp);
7452 
7453  return list;
7454  } else
7455  return list;
7456  } else
7457  return list;
7458 }
7459 
7460 // PlugInWaypointEx implementation
7461 WX_DEFINE_LIST(Plugin_WaypointExList)
7462 
7463 // The class implementations
7464 PlugIn_Waypoint_Ex::PlugIn_Waypoint_Ex() { InitDefaults(); }
7465 
7466 PlugIn_Waypoint_Ex::PlugIn_Waypoint_Ex(
7467  double lat, double lon, const wxString& icon_ident, const wxString& wp_name,
7468  const wxString& GUID, const double ScaMin, const bool bNameVisible,
7469  const int nRangeRings, const double RangeDistance,
7470  const wxColor RangeColor) {
7471  InitDefaults();
7472 
7473  wxDateTime now = wxDateTime::Now();
7474  m_CreateTime = now.ToUTC();
7475  m_HyperlinkList = NULL;
7476 
7477  m_lat = lat;
7478  m_lon = lon;
7479  IconName = icon_ident;
7480  m_MarkName = wp_name;
7481  m_GUID = GUID;
7482  scamin = ScaMin;
7483  IsNameVisible = bNameVisible;
7484  nrange_rings = nRangeRings;
7485  RangeRingSpace = RangeDistance;
7486  RangeRingColor = RangeColor;
7487 }
7488 
7489 void PlugIn_Waypoint_Ex::InitDefaults() {
7490  m_HyperlinkList = NULL;
7491  scamin = 1e9;
7492  b_useScamin = false;
7493  nrange_rings = 0;
7494  RangeRingSpace = 1;
7495  IsNameVisible = false;
7496  IsVisible = true;
7497  RangeRingColor = *wxBLACK;
7498  m_CreateTime = wxDateTime::Now();
7499  IsActive = false;
7500  m_lat = 0;
7501  m_lon = 0;
7502 }
7503 
7504 bool PlugIn_Waypoint_Ex::GetFSStatus() {
7505  RoutePoint* prp = pWayPointMan->FindRoutePointByGUID(m_GUID);
7506  if (!prp) return false;
7507 
7508  if (prp->m_bIsInRoute && !prp->IsShared()) return false;
7509 
7510  return true;
7511 }
7512 
7513 int PlugIn_Waypoint_Ex::GetRouteMembershipCount() {
7514  // Search all routes to count the membership of this point
7515  RoutePoint* pWP = pWayPointMan->FindRoutePointByGUID(m_GUID);
7516  if (!pWP) return 0;
7517 
7518  int nCount = 0;
7519  wxRouteListNode* node = pRouteList->GetFirst();
7520  while (node) {
7521  Route* proute = node->GetData();
7522  wxRoutePointListNode* pnode = (proute->pRoutePointList)->GetFirst();
7523  while (pnode) {
7524  RoutePoint* prp = pnode->GetData();
7525  if (prp == pWP) nCount++;
7526  pnode = pnode->GetNext();
7527  }
7528 
7529  node = node->GetNext();
7530  }
7531 
7532  return nCount;
7533 }
7534 
7535 PlugIn_Waypoint_Ex::~PlugIn_Waypoint_Ex() {}
7536 
7537 // PlugInRouteExtended implementation
7538 PlugIn_Route_Ex::PlugIn_Route_Ex(void) {
7539  pWaypointList = new Plugin_WaypointExList;
7540 }
7541 
7542 PlugIn_Route_Ex::~PlugIn_Route_Ex(void) {
7543  pWaypointList->DeleteContents(false); // do not delete Waypoints
7544  pWaypointList->Clear();
7545 
7546  delete pWaypointList;
7547 }
7548 
7549 // The utility methods implementations
7550 
7551 // translate O route class to PlugIn_Waypoint_Ex
7552 static void PlugInExFromRoutePoint(PlugIn_Waypoint_Ex* dst,
7553  /* const*/ RoutePoint* src) {
7554  dst->m_lat = src->m_lat;
7555  dst->m_lon = src->m_lon;
7556  dst->IconName = src->GetIconName();
7557  dst->m_MarkName = src->GetName();
7558  dst->m_MarkDescription = src->GetDescription();
7559  dst->IconDescription = pWayPointMan->GetIconDescription(src->GetIconName());
7560  dst->IsVisible = src->IsVisible();
7561  dst->m_CreateTime = src->GetCreateTime(); // not const
7562  dst->m_GUID = src->m_GUID;
7563 
7564  // Transcribe (clone) the html HyperLink List, if present
7565  if (src->m_HyperlinkList == nullptr) return;
7566 
7567  delete dst->m_HyperlinkList;
7568  dst->m_HyperlinkList = nullptr;
7569 
7570  if (src->m_HyperlinkList->GetCount() > 0) {
7571  dst->m_HyperlinkList = new Plugin_HyperlinkList;
7572 
7573  wxHyperlinkListNode* linknode = src->m_HyperlinkList->GetFirst();
7574  while (linknode) {
7575  Hyperlink* link = linknode->GetData();
7576 
7578  h->DescrText = link->DescrText;
7579  h->Link = link->Link;
7580  h->Type = link->LType;
7581 
7582  dst->m_HyperlinkList->Append(h);
7583 
7584  linknode = linknode->GetNext();
7585  }
7586  }
7587 
7588  // Get the range ring info
7589  dst->nrange_rings = src->m_iWaypointRangeRingsNumber;
7590  dst->RangeRingSpace = src->m_fWaypointRangeRingsStep;
7591  dst->RangeRingColor = src->m_wxcWaypointRangeRingsColour;
7592 
7593  // Get other extended info
7594  dst->IsNameVisible = src->m_bShowName;
7595  dst->scamin = src->GetScaMin();
7596  dst->b_useScamin = src->GetUseSca();
7597  dst->IsActive = src->m_bIsActive;
7598 }
7599 
7600 static void cloneHyperlinkListEx(RoutePoint* dst,
7601  const PlugIn_Waypoint_Ex* src) {
7602  // Transcribe (clone) the html HyperLink List, if present
7603  if (src->m_HyperlinkList == nullptr) return;
7604 
7605  if (src->m_HyperlinkList->GetCount() > 0) {
7606  wxPlugin_HyperlinkListNode* linknode = src->m_HyperlinkList->GetFirst();
7607  while (linknode) {
7608  Plugin_Hyperlink* link = linknode->GetData();
7609 
7610  Hyperlink* h = new Hyperlink();
7611  h->DescrText = link->DescrText;
7612  h->Link = link->Link;
7613  h->LType = link->Type;
7614 
7615  dst->m_HyperlinkList->Append(h);
7616 
7617  linknode = linknode->GetNext();
7618  }
7619  }
7620 }
7621 
7622 RoutePoint* CreateNewPoint(const PlugIn_Waypoint_Ex* src, bool b_permanent) {
7623  RoutePoint* pWP = new RoutePoint(src->m_lat, src->m_lon, src->IconName,
7624  src->m_MarkName, src->m_GUID);
7625 
7626  pWP->m_bIsolatedMark = true; // This is an isolated mark
7627 
7628  cloneHyperlinkListEx(pWP, src);
7629 
7630  pWP->m_MarkDescription = src->m_MarkDescription;
7631 
7632  if (src->m_CreateTime.IsValid())
7633  pWP->SetCreateTime(src->m_CreateTime);
7634  else {
7635  wxDateTime dtnow(wxDateTime::Now());
7636  pWP->SetCreateTime(dtnow);
7637  }
7638 
7639  pWP->m_btemp = (b_permanent == false);
7640 
7641  // Extended fields
7642  pWP->SetIconName(src->IconName);
7643  pWP->SetWaypointRangeRingsNumber(src->nrange_rings);
7644  pWP->SetWaypointRangeRingsStep(src->RangeRingSpace);
7645  pWP->SetWaypointRangeRingsColour(src->RangeRingColor);
7646  pWP->SetScaMin(src->scamin);
7647  pWP->SetUseSca(src->b_useScamin);
7648  pWP->SetNameShown(src->IsNameVisible);
7649  pWP->SetVisible(src->IsVisible);
7650 
7651  return pWP;
7652 }
7653 bool GetSingleWaypointEx(wxString GUID, PlugIn_Waypoint_Ex* pwaypoint) {
7654  // Find the RoutePoint
7655  RoutePoint* prp = pWayPointMan->FindRoutePointByGUID(GUID);
7656 
7657  if (!prp) return false;
7658 
7659  PlugInExFromRoutePoint(pwaypoint, prp);
7660 
7661  return true;
7662 }
7663 
7664 bool AddSingleWaypointEx(PlugIn_Waypoint_Ex* pwaypointex, bool b_permanent) {
7665  // Validate the waypoint parameters a little bit
7666 
7667  // GUID
7668  // Make sure that this GUID is indeed unique in the Routepoint list
7669  bool b_unique = true;
7670  wxRoutePointListNode* prpnode = pWayPointMan->GetWaypointList()->GetFirst();
7671  while (prpnode) {
7672  RoutePoint* prp = prpnode->GetData();
7673 
7674  if (prp->m_GUID == pwaypointex->m_GUID) {
7675  b_unique = false;
7676  break;
7677  }
7678  prpnode = prpnode->GetNext(); // RoutePoint
7679  }
7680 
7681  if (!b_unique) return false;
7682 
7683  RoutePoint* pWP = CreateNewPoint(pwaypointex, b_permanent);
7684 
7685  pWP->SetShowWaypointRangeRings(pwaypointex->nrange_rings > 0);
7686 
7687  pSelect->AddSelectableRoutePoint(pWP->m_lat, pWP->m_lon, pWP);
7688  if (b_permanent) pConfig->AddNewWayPoint(pWP, -1);
7689 
7690  if (pRouteManagerDialog && pRouteManagerDialog->IsShown())
7691  pRouteManagerDialog->UpdateWptListCtrl();
7692 
7693  return true;
7694 }
7695 
7696 bool UpdateSingleWaypointEx(PlugIn_Waypoint_Ex* pwaypoint) {
7697  // Find the RoutePoint
7698  bool b_found = false;
7699  RoutePoint* prp = pWayPointMan->FindRoutePointByGUID(pwaypoint->m_GUID);
7700 
7701  if (prp) b_found = true;
7702 
7703  if (b_found) {
7704  double lat_save = prp->m_lat;
7705  double lon_save = prp->m_lon;
7706 
7707  prp->m_lat = pwaypoint->m_lat;
7708  prp->m_lon = pwaypoint->m_lon;
7709  prp->SetIconName(pwaypoint->IconName);
7710  prp->SetName(pwaypoint->m_MarkName);
7711  prp->m_MarkDescription = pwaypoint->m_MarkDescription;
7712  prp->SetVisible(pwaypoint->IsVisible);
7713  if (pwaypoint->m_CreateTime.IsValid())
7714  prp->SetCreateTime(pwaypoint->m_CreateTime);
7715 
7716  // Transcribe (clone) the html HyperLink List, if present
7717 
7718  if (pwaypoint->m_HyperlinkList) {
7719  prp->m_HyperlinkList->Clear();
7720  if (pwaypoint->m_HyperlinkList->GetCount() > 0) {
7721  wxPlugin_HyperlinkListNode* linknode =
7722  pwaypoint->m_HyperlinkList->GetFirst();
7723  while (linknode) {
7724  Plugin_Hyperlink* link = linknode->GetData();
7725 
7726  Hyperlink* h = new Hyperlink();
7727  h->DescrText = link->DescrText;
7728  h->Link = link->Link;
7729  h->LType = link->Type;
7730 
7731  prp->m_HyperlinkList->Append(h);
7732 
7733  linknode = linknode->GetNext();
7734  }
7735  }
7736  }
7737 
7738  // Extended fields
7739  prp->SetWaypointRangeRingsNumber(pwaypoint->nrange_rings);
7740  prp->SetWaypointRangeRingsStep(pwaypoint->RangeRingSpace);
7741  prp->SetWaypointRangeRingsColour(pwaypoint->RangeRingColor);
7742  prp->SetScaMin(pwaypoint->scamin);
7743  prp->SetUseSca(pwaypoint->b_useScamin);
7744  prp->SetNameShown(pwaypoint->IsNameVisible);
7745 
7746  prp->SetShowWaypointRangeRings(pwaypoint->nrange_rings > 0);
7747 
7748  if (prp) prp->ReLoadIcon();
7749 
7750  auto canvas = gFrame->GetPrimaryCanvas();
7751  SelectCtx ctx(canvas->m_bShowNavobjects, canvas->GetCanvasTrueScale(), canvas->GetScaleValue());
7752  SelectItem* pFind =
7753  pSelect->FindSelection(ctx, lat_save, lon_save, SELTYPE_ROUTEPOINT);
7754  if (pFind) {
7755  pFind->m_slat = pwaypoint->m_lat; // update the SelectList entry
7756  pFind->m_slon = pwaypoint->m_lon;
7757  }
7758 
7759  if (!prp->m_btemp) pConfig->UpdateWayPoint(prp);
7760 
7761  if (pRouteManagerDialog && pRouteManagerDialog->IsShown())
7762  pRouteManagerDialog->UpdateWptListCtrl();
7763  }
7764 
7765  return b_found;
7766 }
7767 
7768 bool AddPlugInRouteEx(PlugIn_Route_Ex* proute, bool b_permanent) {
7769  Route* route = new Route();
7770 
7771  PlugIn_Waypoint_Ex* pwaypointex;
7772  RoutePoint *pWP, *pWP_src;
7773  int ip = 0;
7774  wxDateTime plannedDeparture;
7775 
7776  wxPlugin_WaypointExListNode* pwpnode = proute->pWaypointList->GetFirst();
7777  while (pwpnode) {
7778  pwaypointex = pwpnode->GetData();
7779 
7780  pWP = pWayPointMan->FindRoutePointByGUID(pwaypointex->m_GUID);
7781  if (!pWP) {
7782  pWP = CreateNewPoint(pwaypointex, b_permanent);
7783  pWP->m_bIsolatedMark = false;
7784  }
7785 
7786  route->AddPoint(pWP);
7787 
7788  pSelect->AddSelectableRoutePoint(pWP->m_lat, pWP->m_lon, pWP);
7789 
7790  if (ip > 0)
7791  pSelect->AddSelectableRouteSegment(pWP_src->m_lat, pWP_src->m_lon,
7792  pWP->m_lat, pWP->m_lon, pWP_src, pWP,
7793  route);
7794 
7795  plannedDeparture = pwaypointex->m_CreateTime;
7796  ip++;
7797  pWP_src = pWP;
7798 
7799  pwpnode = pwpnode->GetNext(); // PlugInWaypoint
7800  }
7801 
7802  route->m_PlannedDeparture = plannedDeparture;
7803 
7804  route->m_RouteNameString = proute->m_NameString;
7805  route->m_RouteStartString = proute->m_StartString;
7806  route->m_RouteEndString = proute->m_EndString;
7807  if (!proute->m_GUID.IsEmpty()) {
7808  route->m_GUID = proute->m_GUID;
7809  }
7810  route->m_btemp = (b_permanent == false);
7811  route->SetVisible(proute->m_isVisible);
7812  route->m_RouteDescription = proute->m_Description;
7813 
7814  pRouteList->Append(route);
7815 
7816  if (b_permanent) pConfig->AddNewRoute(route);
7817 
7818  if (pRouteManagerDialog && pRouteManagerDialog->IsShown())
7819  pRouteManagerDialog->UpdateRouteListCtrl();
7820 
7821  return true;
7822 }
7823 
7824 bool UpdatePlugInRouteEx(PlugIn_Route_Ex* proute) {
7825  bool b_found = false;
7826 
7827  // Find the Route
7828  Route* pRoute = g_pRouteMan->FindRouteByGUID(proute->m_GUID);
7829  if (pRoute) b_found = true;
7830 
7831  if (b_found) {
7832  bool b_permanent = !pRoute->m_btemp;
7833  g_pRouteMan->DeleteRoute(pRoute, NavObjectChanges::getInstance());
7834 
7835  b_found = AddPlugInRouteEx(proute, b_permanent);
7836  }
7837 
7838  return b_found;
7839 }
7840 
7841 // std::unique_ptr<PlugIn_Waypoint_Ex> GetWaypointEx_Plugin(const wxString &)
7842 // {
7843 // }
7844 
7845 // std::unique_ptr<PlugIn_Route_Ex> GetRouteEx_Plugin(const wxString &)
7846 // {
7847 // }
7848 
7849 std::unique_ptr<PlugIn_Waypoint_Ex> GetWaypointEx_Plugin(const wxString& GUID) {
7850  std::unique_ptr<PlugIn_Waypoint_Ex> w(new PlugIn_Waypoint_Ex);
7851  GetSingleWaypointEx(GUID, w.get());
7852  return w;
7853 }
7854 
7855 std::unique_ptr<PlugIn_Route_Ex> GetRouteEx_Plugin(const wxString& GUID) {
7856  std::unique_ptr<PlugIn_Route_Ex> r;
7857  Route* route = g_pRouteMan->FindRouteByGUID(GUID);
7858  if (route == nullptr) return r;
7859 
7860  r = std::unique_ptr<PlugIn_Route_Ex>(new PlugIn_Route_Ex);
7861  PlugIn_Route_Ex* dst_route = r.get();
7862 
7863  // PlugIn_Waypoint *pwp;
7864  RoutePoint* src_wp;
7865  wxRoutePointListNode* node = route->pRoutePointList->GetFirst();
7866 
7867  while (node) {
7868  src_wp = node->GetData();
7869 
7870  PlugIn_Waypoint_Ex* dst_wp = new PlugIn_Waypoint_Ex();
7871  PlugInExFromRoutePoint(dst_wp, src_wp);
7872 
7873  dst_route->pWaypointList->Append(dst_wp);
7874 
7875  node = node->GetNext();
7876  }
7877  dst_route->m_NameString = route->m_RouteNameString;
7878  dst_route->m_StartString = route->m_RouteStartString;
7879  dst_route->m_EndString = route->m_RouteEndString;
7880  dst_route->m_GUID = route->m_GUID;
7881  dst_route->m_isActive = g_pRouteMan->GetpActiveRoute() == route;
7882  dst_route->m_isVisible = route->IsVisible();
7883  dst_route->m_Description = route->m_RouteDescription;
7884 
7885  return r;
7886 }
7887 
7888 wxString GetActiveWaypointGUID(
7889  void) { // if no active waypoint, returns wxEmptyString
7890  RoutePoint* rp = g_pRouteMan->GetpActivePoint();
7891  if (!rp)
7892  return wxEmptyString;
7893  else
7894  return rp->m_GUID;
7895 }
7896 
7897 wxString GetActiveRouteGUID(
7898  void) { // if no active route, returns wxEmptyString
7899  Route* rt = g_pRouteMan->GetpActiveRoute();
7900  if (!rt)
7901  return wxEmptyString;
7902  else
7903  return rt->m_GUID;
7904 }
7905 
7907 int GetGlobalWatchdogTimoutSeconds() { return gps_watchdog_timeout_ticks; }
7908 
7910 std::vector<std::string> GetPriorityMaps() {
7911  MyApp& app = wxGetApp();
7912  return (app.m_comm_bridge.GetPriorityMaps());
7913 }
7914 
7915 std::vector<std::string> GetActivePriorityIdentifiers() {
7916  std::vector<std::string> result;
7917 
7918  MyApp& app = wxGetApp();
7919 
7920  std::string id =
7921  app.m_comm_bridge.GetPriorityContainer("position").active_source;
7922  result.push_back(id);
7923  id = app.m_comm_bridge.GetPriorityContainer("velocity").active_source;
7924  result.push_back(id);
7925  id = app.m_comm_bridge.GetPriorityContainer("heading").active_source;
7926  result.push_back(id);
7927  id = app.m_comm_bridge.GetPriorityContainer("variation").active_source;
7928  result.push_back(id);
7929  id = app.m_comm_bridge.GetPriorityContainer("satellites").active_source;
7930  result.push_back(id);
7931 
7932  return result;
7933 }
7934 
7935 double OCPN_GetDisplayContentScaleFactor() {
7936  double rv = 1.0;
7937 #if defined(__WXOSX__) || defined(__WXGTK3__)
7938  // Support scaled HDPI displays.
7939  if (gFrame) rv = gFrame->GetContentScaleFactor();
7940 #endif
7941  return rv;
7942 }
7943 double OCPN_GetWinDIPScaleFactor() {
7944  double scaler = 1.0;
7945 #ifdef __WXMSW__
7946  if (gFrame) scaler = (double)(gFrame->ToDIP(100)) / 100.;
7947 #endif
7948  return scaler;
7949 }
wxString & GetPrivateDataDir()
Return dir path for opencpn.log, etc., respecting -c cli option.
EventVar plugin_msg
A JSON message should be sent.
Definition: ais_decoder.h:142
Handle messages for blacklisted plugins.
Modal dialog, displays settings for plugin catalog.
Definition: cat_settings.h:34
const std::vector< DriverPtr > & GetDrivers()
Wrapper for configuration variables which lives in a wxBaseConfig object.
const void Notify()
Notify all listeners, no data supplied.
Wrapper for global variable, supports notification events when value changes.
Add progress and final message dialogs to the basic Downloader.
Definition: download_mgr.h:47
Definition: ocpn_app.h:45
static std::string MessageKey(const char *type="ALL")
Return key which should be used to listen to given message type.
Definition: comm_navmsg.h:281
void Init(const KeyProvider &kp, std::function< void(ObservedEvt &ev)> action)
Initiate an object yet not listening.
Definition: observable.h:227
void Listen(const std::string &key, wxEvtHandler *listener, wxEventType evt)
Set object to send wxEventType ev to listener on changes in key.
Definition: observable.cpp:98
Adds a std::shared<void> element to wxCommandEvent.
Definition: ocpn_plugin.h:1652
Data for a loaded plugin, including dl-loaded library.
Definition: plugin_loader.h:99
Basic data for a loaded plugin, trivially copyable.
Definition: plugin_loader.h:64
wxString m_plugin_filename
The short file path.
Definition: plugin_loader.h:77
wxString m_plugin_file
The full file path.
Definition: plugin_loader.h:76
int m_cap_flag
PlugIn Capabilities descriptor.
Definition: plugin_loader.h:75
wxString m_common_name
A common name string for the plugin.
Definition: plugin_loader.h:79
std::string Key() const
sort key.
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.
std::vector< std::string > GetInstalldataPlugins()
Return list of installed plugins lower case names, not necessarily loaded.
static std::string fileListPath(std::string name)
Return path to installation manifest for given plugin.
static std::string versionPath(std::string name)
Return path to file containing version for given plugin.
bool uninstall(const std::string plugin)
Uninstall an installed and loaded plugin.
bool ClearInstallData(const std::string plugin_name)
Remove installation data for not loaded plugin.
void SetInstalledMetadata(const PluginMetadata &pm)
Set metadata for an installed 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.
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.
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.
void ShowPreferencesDialog(const PlugInData &pd, wxWindow *parent)
Display the preferences dialog for a plugin.
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.
Definition: route.h:75
bool DeleteRoute(Route *pRoute, NavObjectChanges *nav_obj_changes)
Definition: routeman.cpp:751
EventVar json_msg
Notified with message targeting all plugins.
Definition: routeman.h:187
EventVar json_leg_info
Notified with a shared_ptr<ActiveLegDat>, leg info to all plugins.
Definition: routeman.h:190
EventVar on_message_sent
Notified when a message available as GetString() is sent to garmin.
Definition: routeman.h:193
Definition: select.h:54
A parsed SignalK message over ipv4.
Definition: comm_navmsg.h:319
Definition: track.h:78
Modal dialog, displays available updates (possibly just one) and lets user select and eventually conf...
Definition: update_mgr.h:41
Invokes client browser on plugin info_url when clicked.
WebsiteButton(wxWindow *parent, const char *url)
Invokes client browser on plugin info_url when clicked.
Definition: ocpndc.h:58
virtual bool RenderGLOverlayMultiCanvas(wxGLContext *pcontext, PlugIn_ViewPort *vp, int canvasIndex, int priority)
Render plugin overlay over chart canvas in OpenGL mode.
virtual bool RenderOverlayMultiCanvas(wxDC &dc, PlugIn_ViewPort *vp, int canvas_ix, int priority)
Render plugin overlay over chart canvas in non-OpenGL mode.
NMEA0183 serial driver.
std::string lookup_tarball(const char *uri)
Get path to tarball in cache for given filename.
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.
DECL_EXP std::vector< std::string > GetPriorityMaps()
Comm Priority query support methods
DECL_EXP int GetGlobalWatchdogTimoutSeconds()
Comm Global Watchdog Query
DECL_EXP bool PlugInPlaySoundEx(wxString &sound_file, int deviceIndex=-1)
Start playing a sound file asynchronously.
DECL_EXP void PlugInPlaySound(wxString &sound_file)
Start playing a sound file asynchronously.
wxDEFINE_EVENT(REST_IO_EVT, ObservedEvt)
Event from IO thread to main.
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.
Definition: semantic_vers.h:51
static SemanticVersion parse(std::string s)
Parse a version string, sets major == -1 on errors.
Definition: Quilt.cpp:867