32 #include <wx/bitmap.h>
33 #include <wx/button.h>
39 #include <wx/progdlg.h>
41 #include <wx/statline.h>
42 #include <wx/textwrapper.h>
44 #include "catalog_mgr.h"
45 #include "update_mgr.h"
46 #include "model/plugin_loader.h"
47 #include "model/downloader.h"
48 #include "OCPNPlatform.h"
49 #include "model/plugin_handler.h"
50 #include "pluginmanager.h"
51 #include "model/semantic_vers.h"
54 #include "svg_utils.h"
57 #include "androidUTIL.h"
72 Wrap(win, text, widthMax);
74 wxString
const& GetWrapped()
const {
return m_wrapped; }
75 int const GetLineCount()
const {
return m_lineCount; }
78 virtual void OnOutputLine(
const wxString& line) { m_wrapped += line; }
79 virtual void OnNewLine() {
98 static ssize_t PlugInIxByName(
const std::string name,
99 const ArrayOfPlugIns* plugins) {
100 for (
unsigned i = 0; i < plugins->GetCount(); i += 1) {
101 if (name == plugins->Item(i)->m_common_name.Lower().ToStdString()) {
110 const ArrayOfPlugIns* plugins) {
111 auto ix = PlugInIxByName(name, plugins);
112 return ix == -1 ? 0 : plugins->Item(ix);
116 static void LoadPNGIcon(
const char* path,
int size, wxBitmap& bitmap) {
117 wxPNGHandler handler;
118 if (!wxImage::FindHandler(handler.GetName())) {
119 wxImage::AddHandler(
new wxPNGHandler());
121 auto img =
new wxImage();
122 bool ok = img->LoadFile(path, wxBITMAP_TYPE_PNG);
127 img->Rescale(size, size);
128 bitmap = wxBitmap(*img);
140 : wxPanel(parent), m_plugin_name(plugin_name) {
141 auto size = GetClientSize();
142 auto minsize = GetTextExtent(
"OpenCPN");
143 SetMinClientSize(wxSize(minsize.GetWidth(), size.GetHeight()));
145 Bind(wxEVT_PAINT, &PluginIconPanel::OnPaint,
this);
148 void OnPaint(wxPaintEvent& event) {
149 auto size = GetClientSize();
150 int minsize = wxMin(size.GetHeight(), size.GetWidth());
151 auto offset = minsize / 10;
153 LoadIcon(
"packageBox.svg", m_bitmap, 2 * minsize / 3);
155 if (!m_bitmap.IsOk()) {
156 wxLogMessage(
"AddPluginPanel: bitmap is not OK!");
159 dc.DrawBitmap(m_bitmap, offset, offset,
true);
164 const std::string m_plugin_name;
166 void LoadIcon(
const char* plugin_name, wxBitmap& bitmap,
int size = 32) {
167 wxFileName path(g_Platform->GetSharedDataDir(), plugin_name);
168 path.AppendDir(
"uidata");
169 path.AppendDir(
"traditional");
172 if (path.IsFileReadable()) {
173 bitmap = LoadSVG(path.GetFullPath(), size, size);
178 auto style = g_StyleManager->GetCurrentStyle();
179 bitmap = wxBitmap(style->GetIcon(_T(
"default_pi"), size, size));
180 wxLogMessage(
"Icon: %s not found.", path.GetFullPath());
204 : wxPanel(parent), m_metadata(metadata), m_remove(
false) {
205 auto loader = PluginLoader::getInstance();
207 PlugInByName(metadata.name, loader->GetPlugInArray());
208 std::string label(_(
"Install"));
210 ((found->m_version_major > 0) || (found->m_version_minor > 0))) {
211 label = getUpdateLabel(found, metadata);
214 auto button =
new wxButton(
this, wxID_ANY, label);
215 auto pluginHandler = PluginHandler::getInstance();
216 auto box =
new wxBoxSizer(wxHORIZONTAL);
219 Bind(wxEVT_COMMAND_BUTTON_CLICKED, &InstallButton::OnClick,
this);
222 void OnClick(wxCommandEvent& event) {
223 wxLogMessage(
"Selected update: %s", m_metadata.name.c_str());
224 auto top_parent = GetParent()->GetParent()->GetParent();
226 wxASSERT(dialog != 0);
227 dialog->SetUpdate(m_metadata);
228 dialog->EndModal(wxID_OK);
236 SemanticVersion currentVersion(pic->m_version_major, pic->m_version_minor);
241 if (newVersion > currentVersion) {
243 }
else if (newVersion == currentVersion) {
244 return _(
"Reinstall");
246 return _(
"Downgrade");
255 : wxPanel(parent), m_url(url) {
256 auto vbox =
new wxBoxSizer(wxVERTICAL);
257 auto button =
new wxButton(
this, wxID_ANY, _(
"Website"));
258 button->Enable(strlen(url) > 0);
261 Bind(wxEVT_COMMAND_BUTTON_CLICKED,
262 [=](wxCommandEvent&) { wxLaunchDefaultBrowser(m_url); });
266 const std::string m_url;
274 auto flags = wxSizerFlags().Border();
276 auto vbox =
new wxBoxSizer(wxVERTICAL);
278 flags.DoubleBorder().Top().Right());
279 vbox->Add(1, 1, 1, wxEXPAND);
282 vbox->Add(m_info_btn, flags.DoubleBorder().Right());
287 void HideDetails(
bool hide) {
288 m_info_btn->Show(!hide);
289 GetParent()->Layout();
301 : wxPanel(parent), m_descr(0), m_buttons(buttons) {
302 auto flags = wxSizerFlags().Border();
305 MORE =
"<span foreground=\'blue\'>";
307 MORE +=
"...</span>";
308 LESS =
"<span foreground=\'blue\'>";
310 LESS +=
"...</span>";
313 if (g_Platform->getDisplaySize().x < 80 * GetCharWidth())
316 auto sum_hbox =
new wxBoxSizer(wxHORIZONTAL);
317 m_widthDescription = g_options->GetSize().x *4 / 10;
320 m_summary =
new wxStaticText(
321 this, wxID_ANY, _T(
""), wxDefaultPosition,
322 wxSize(m_widthDescription, -1) );
323 m_summaryText = wxString(plugin->summary.c_str());
324 m_summary->SetLabel(m_summaryText);
325 m_summary->Wrap(m_widthDescription);
328 m_summaryLineCount = wrapper.GetLineCount() + 1;
330 sum_hbox->Add(m_summary);
331 sum_hbox->AddSpacer(10);
332 m_more = staticText(
"4 Chars");
333 m_more->SetLabelMarkup(MORE);
334 sum_hbox->Add(m_more, wxSizerFlags());
336 auto vbox =
new wxBoxSizer(wxVERTICAL);
339 std::string name_reduced = plugin->name;
340 if(plugin->name.size() * GetCharWidth() > (
size_t)m_widthDescription * 7 / 10){
341 int nc = (m_widthDescription *7 / 10) / GetCharWidth();
343 name_reduced = plugin->name.substr(0, nc-3) +
"...";
347 wxString nameText(name_reduced +
" " + plugin->version);
348 if (bshowTuple) nameText +=
" " + plugin->target;
350 auto name = staticText(nameText);
352 m_descr =
new wxStaticText(
353 this, wxID_ANY, _T(
""), wxDefaultPosition,
354 wxSize(m_widthDescription, -1) );
355 m_descText = wxString(plugin->description.c_str());
356 m_descr->SetLabel(m_descText);
357 m_descr->Wrap(m_widthDescription);
359 vbox->Add(name, flags);
360 vbox->Add(sum_hbox, flags);
361 vbox->Add(m_descr, 0);
364 m_more->Bind(wxEVT_LEFT_DOWN, &PluginTextPanel::OnClick,
this);
365 m_descr->Bind(wxEVT_LEFT_DOWN, &PluginTextPanel::OnClick,
this);
368 void OnClick(wxMouseEvent& event) {
369 m_descr->Show(!m_descr->IsShown());
370 m_descr->SetLabel(_T(
""));
371 m_descr->SetLabel(m_descText);
372 m_descr->Wrap(m_widthDescription);
374 wxSize asize = GetEffectiveMinSize();
376 m_more->SetLabelMarkup(m_descr->IsShown() ? LESS : MORE);
377 m_buttons->HideDetails(!m_descr->IsShown());
381 swin->RecalculateSize();
385 int m_summaryLineCount;
391 wxStaticText* staticText(
const wxString& text) {
392 return new wxStaticText(
this, wxID_ANY, text, wxDefaultPosition,
393 wxDefaultSize, wxALIGN_LEFT);
396 wxStaticText* m_descr;
397 wxStaticText* m_more;
398 wxStaticText* m_summary;
400 int m_widthDescription;
402 wxString m_summaryText;
412 const std::vector<PluginMetadata>& updates)
413 : wxScrolledWindow(parent),
415 m_grid(
new wxFlexGridSizer(3, 0, 0)) {
416 auto box =
new wxBoxSizer(wxVERTICAL);
418 box->Add(m_grid, wxSizerFlags().Proportion(0).Expand());
419 auto butt_box =
new wxBoxSizer(wxHORIZONTAL);
420 auto cancel_btn =
new wxButton(
this, wxID_CANCEL, _(
"Dismiss"));
421 butt_box->Add(1, 1, 1, wxEXPAND);
422 butt_box->Add(cancel_btn, wxSizerFlags().Border());
423 box->Add(butt_box, wxSizerFlags().Proportion(0).Expand());
426 SetMinSize(GetEffectiveMinSize());
432 struct metadata_compare {
435 return lhs.key() < rhs.key();
439 auto flags = wxSizerFlags();
441 grid->AddGrowableCol(2);
442 for (
auto plugin : m_updates) {
445 bool b_show_tuple =
false;
446 if (g_Platform->getDisplaySize().x > 80 * GetCharWidth())
447 b_show_tuple = m_updates.size() > 1;
450 tpanel->m_isDesc =
true;
451 grid->Add(tpanel, flags.Proportion(1).Right());
452 grid->Add(buttons, flags.DoubleBorder());
453 grid->Add(
new wxStaticLine(
this), wxSizerFlags(0).Expand());
454 grid->Add(
new wxStaticLine(
this), wxSizerFlags(0).Expand());
455 grid->Add(
new wxStaticLine(
this), wxSizerFlags(0).Expand());
460 const std::vector<PluginMetadata> m_updates;
461 wxFlexGridSizer* m_grid;
468 const std::vector<PluginMetadata>& updates)
469 : wxDialog(parent, wxID_ANY, _(
"Plugin Manager"), wxDefaultPosition,
470 wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) {
471 auto vbox =
new wxBoxSizer(wxVERTICAL);
475 vbox->Add(m_scrwin, wxSizerFlags(1).Expand());
481 androidDisableRotation();
485 UpdateDialog::~UpdateDialog() {
487 androidEnableRotation();
491 void UpdateDialog::RecalculateSize() {
494 wxWindowList& kids = m_scrwin->GetChildren();
495 for (
unsigned int i = 0; i < kids.GetCount(); i++) {
496 wxWindowListNode* node = kids.Item(i);
497 wxWindow* win = node->GetData();
501 if (panel->m_isDesc) {
502 wxSize tsize = win->GetEffectiveMinSize();
503 calcHeight += tsize.y + GetCharHeight();
504 calcWidth = tsize.x * 2;
509 calcHeight += 3 * GetCharHeight();
510 calcWidth = wxMin(calcWidth, g_Platform->getDisplaySize().x);
512 m_scrwin->SetMinSize(wxSize(calcWidth, calcHeight));
514 #ifdef __OCPN__ANDROID__
515 SetMinSize(g_Platform->getDisplaySize());
520 SetMaxSize(g_Platform->getDisplaySize());
Data for a loaded plugin, including dl-loaded library.
wxString m_version_str
Complete version as of semantic_vers.
A plugin icon, scaled to about 2/3 of available space.
Plugin name, version, summary + an optionally shown description.
Modal dialog, displays available updates (possibly just one) and lets user select and eventually conf...
UpdateDialog(wxWindow *parent, const std::vector< PluginMetadata > &updates)
Top-level install plugins dialog.
Versions uses a modified semantic versioning scheme: major.minor.revision.post-tag+build.
static SemanticVersion parse(std::string s)
Parse a version string, sets major == -1 on errors.