32#include <wx/filename.h>
36#include <wx/progdlg.h>
38#include <wx/statline.h>
58static const std::string SEP(
"\\");
60static const std::string SEP(
"/");
63namespace catalog_mgr {
67 Helpers(wxWindow* _parent) : parent(_parent) {}
69 wxStaticText* staticText(
const char* text) {
70 return new wxStaticText(parent, wxID_ANY, text);
73 wxButton* makeButton(
const char* label,
int id = wxID_ANY) {
74 return new wxButton(parent, wxID_ANY, label);
89 : wxDialog(parent, wxID_ANY, _(
"Manage Plugin Catalog"),
90 wxDefaultPosition, wxDefaultSize,
91 wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxSTAY_ON_TOP),
94 HIDE =
"<span foreground=\'blue\'>";
96 HIDE +=
" <<<</span>";
97 ADVANCED =
"<span foreground=\'blue\'>";
98 ADVANCED += _(
"Ultra advanced");
99 ADVANCED +=
" >>></span>";
101 auto sizer =
new wxBoxSizer(wxVERTICAL);
102 auto flags = wxSizerFlags().Expand().Border();
106 sizer->Add(
new UrlChannel(
this, m_catalog_grid), flags);
107 sizer->Add(
new wxStaticLine(
this), flags);
109 sizer->Add(m_catalog_grid, flags);
110 sizer->Add(
new wxStaticLine(
this), flags);
112 m_advanced =
new wxStaticText(
this, wxID_ANY,
"");
113 m_advanced->Bind(wxEVT_LEFT_DOWN,
114 [=](wxMouseEvent& e) { toggleUrlEdit(); });
115 sizer->Add(m_advanced, flags);
117 m_url_box =
new wxBoxSizer(wxVERTICAL);
118 m_url_edit =
new UrlEdit(
this);
119 m_url_box->Add(m_url_edit, flags);
120 m_url_box->Add(
new Buttons(
this, m_catalog_grid),
121 wxSizerFlags().Border().Right());
122 sizer->Add(m_url_box, flags);
123 sizer->Add(
new wxStaticLine(
this), flags);
125 auto done = makeButton(_(
"Done"));
126 sizer->Add(done, wxSizerFlags().Border().Right());
127 done->Bind(wxEVT_COMMAND_BUTTON_CLICKED, [=](wxCommandEvent e) {
129 wxCommandEvent evt(CATALOG_DLG_CLOSE);
130 wxPostEvent(GetParent(), evt);
135 Bind(wxEVT_CLOSE_WINDOW, [=](wxCloseEvent& e) {
137 wxCommandEvent cmd_evt(CATALOG_DLG_CLOSE);
138 wxPostEvent(GetParent(), cmd_evt);
145 SetMinClientSize(size);
151 wxString HIDE, ADVANCED;
153 wxBoxSizer* m_url_box;
156 wxStaticText* m_advanced;
166 auto size = GetTextExtent(uri);
167 size.SetWidth(size.GetWidth() * 120 / 100);
168 size.SetHeight(size.GetHeight() * 130 / 100);
172 void toggleUrlEdit() {
173 m_show_edit = !m_show_edit;
174 m_url_box->GetItem((
size_t)0)->GetWindow()->Show(m_show_edit);
175 m_url_box->GetItem((
size_t)1)->GetWindow()->Show(m_show_edit);
176 m_advanced->SetLabelMarkup(m_show_edit ? HIDE : ADVANCED);
183 auto sizer =
new wxBoxSizer(wxHORIZONTAL);
185 wxSizerFlags().Expand().Border().Align(wxALIGN_CENTER_VERTICAL);
186 sizer->Add(staticText(_(
"Catalog URL status: ")), flags);
187 auto catalog = CatalogHandler::GetInstance();
188 int channels = catalog->GetChannels().size();
189 auto text = staticText(channels > 0 ? _(
"OK") : _(
"Error"));
190 sizer->Add(text, flags);
204 using CmdEvt = wxCommandEvent;
206 auto grid =
new wxFlexGridSizer(4, 0, 0);
208 grid->AddGrowableCol(0);
209 grid->AddGrowableCol(1);
210 grid->AddGrowableCol(2);
211 grid->AddGrowableCol(3);
212 auto flags = wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL);
213 flags = flags.DoubleBorder();
218 grid->Add(staticText(_(
"Current active plugin catalog")), flags);
219 grid->Add(staticText(
""), flags);
220 grid->Add(staticText(
""), flags);
221 grid->Add(staticText(
""), flags);
225 grid->Add(staticText(_(
"Default catalog")), flags);
226 grid->Add(staticText(
""), flags);
227 grid->Add(staticText(
""), flags);
228 auto use_default = makeButton(_(
"Use as active catalog"));
229 grid->Add(use_default, wxSizerFlags().Border());
230 use_default->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
231 [=](CmdEvt& e) { useDefaultCatalog(); });
235 grid->Add(staticText(_(
"Latest available catalog:")), flags);
236 grid->Add(staticText(
""), flags);
237 grid->Add(staticText(
""), flags);
238 auto use_latest = makeButton(_(
"Use as active catalog"));
239 use_latest->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
240 [=](CmdEvt& e) { UseLatestCatalog(); });
242 grid->Add(use_latest, wxSizerFlags().Border());
250 void UpdateVersion(wxSizer* grid,
CatalogData data,
size_t ix) {
252 dynamic_cast<wxStaticText*
>(grid->GetItem(ix)->GetWindow());
253 version->SetLabel(data.version);
255 dynamic_cast<wxStaticText*
>(grid->GetItem(ix + 1)->GetWindow());
256 date->SetLabel(data.date);
265 auto grid =
dynamic_cast<wxSizer*
>(GetSizer());
266 UpdateVersion(grid, data, 1);
268 UpdateVersion(grid, data, 5);
270 UpdateVersion(grid, data, 9);
275 std::string GetDefaultCatalogPath() {
276 std::string path = g_Platform->GetSharedDataDir().ToStdString();
278 path +=
"ocpn-plugins.xml";
282 void ReloadAvailableVersion() {
283 auto handler = CatalogHandler::GetInstance();
284 std::ostringstream xml;
285 auto status = handler->DownloadCatalog(&xml);
287 if (status != CatalogHandler::ServerStatus::OK) {
288 message = _(
"Cannot download data from url");
290 status = handler->ParseCatalog(xml.str(),
true);
291 if (status != CatalogHandler::ServerStatus::OK) {
292 message = _(
"Cannot parse downloaded data");
295 wxMessageBox(message, _(
"Catalog update problem"), wxICON_ERROR);
301 std::string GetPrivateCatalogPath() {
304 path += SEP +
"ocpn-plugins.xml";
308 void useDefaultCatalog() {
309 auto src = GetDefaultCatalogPath();
310 auto dest = GetPrivateCatalogPath();
316 void UseLatestCatalog() {
317 auto catalog = CatalogHandler::GetInstance();
318 std::ofstream dest(GetPrivateCatalogPath());
319 catalog->DownloadCatalog(&dest);
321 catalog->ClearCatalogData();
331 m_catalog_grid(catalog_grid),
333 auto sizer =
new wxBoxSizer(wxHORIZONTAL);
334 auto flags = wxSizerFlags().Right().Bottom().Border();
336 auto clear = makeButton(_(
"Clear"));
337 clear->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
338 [=](wxCommandEvent& ev) { clearUrl(); });
339 sizer->Add(clear, flags);
341 auto use_default = makeButton(_(
"Use default location"));
342 use_default->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
343 [=](wxCommandEvent& ev) { useDefaultUrl(); });
344 sizer->Add(use_default, flags);
346 auto update = makeButton(_(
"Save"));
347 sizer->Add(update, flags);
348 update->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
349 [=](wxCommandEvent& ev) { updateUrl(); });
356 void useDefaultUrl() {
358 m_parent->m_url_edit->setText(url);
361 void clearUrl() { m_parent->m_url_edit->clear(); }
364 auto text = m_parent->m_url_edit->getText();
365 CatalogHandler::GetInstance()->
SetCustomUrl(text.c_str());
366 m_catalog_grid->ReloadAvailableVersion();
376 : wxPanel(parent),
Helpers(
this), m_catalog_grid(catalog_grid) {
377 auto sizer =
new wxBoxSizer(wxHORIZONTAL);
379 wxSizerFlags().Expand().Border().Align(wxALIGN_CENTER_VERTICAL);
380 sizer->Add(staticText(_(
"Catalog channel: ")), flags);
381 auto catalog = CatalogHandler::GetInstance();
382 wxArrayString channel_list;
383 for (
auto channel : catalog->GetChannels()) {
384 channel_list.Add(channel.c_str());
386 auto channels =
new wxChoice(
this, wxID_ANY, wxDefaultPosition,
387 wxDefaultSize, channel_list);
389 int ix = channels->FindString(current.c_str());
390 channels->SetSelection(ix != wxNOT_FOUND ? ix : 0);
391 channels->Bind(wxEVT_CHOICE,
392 [=](wxCommandEvent& e) { onChannelChange(e); });
393 sizer->Add(channels, flags);
399 void onChannelChange(wxCommandEvent& ev) {
401 m_catalog_grid->ReloadAvailableVersion();
404 std::string m_active_channel;
411 auto sizer =
new wxBoxSizer(wxVERTICAL);
412 auto url_location =
new wxBoxSizer(wxHORIZONTAL);
413 auto flags = wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL);
414 url_location->Add(staticText(_(
"Custom catalog URL: ")), flags);
415 flags = wxSizerFlags().Expand().Border();
416 sizer->Add(url_location, flags);
418 auto url_edit =
new wxBoxSizer(wxHORIZONTAL);
419 auto uri = CatalogHandler::GetInstance()->
GetCustomUrl();
420 m_url_ctrl =
new wxTextCtrl(
this, wxID_ANY, uri);
422 m_url_ctrl->SetMinClientSize(the_parent->getWindowSize());
423 url_edit->Add(m_url_ctrl, wxSizerFlags().Expand());
425 sizer->Add(url_edit, flags);
431 void setText(std::string text) {
434 m_url_ctrl->Refresh();
437 void clear() { m_url_ctrl->Clear(); }
439 std::string getText() {
440 return std::string(m_url_ctrl->GetLineText(0).ToStdString());
443 wxTextCtrl* m_url_ctrl;
452 CatalogLoad(wxWindow* parent,
bool use_latest =
false)
453 : wxPanel(parent),
Helpers(
this), m_simple(use_latest) {
454 auto sizer =
new wxBoxSizer(wxVERTICAL);
455 auto flags = wxSizerFlags().Expand().Border();
457 sizer->Add(m_grid, flags);
458 sizer->Add(1, 1, 1, wxEXPAND);
461 sizer->Add(m_buttons, flags.Bottom().Right());
463 auto size = GetTextExtent(_(
"Check latest release..."));
464 size.SetHeight(size.GetHeight() * 10);
465 size.SetWidth(size.GetWidth() * 5 / 2);
466 SetMinClientSize(size);
467 std::thread worker([=]() {
Worker(); });
474 Bind(CHANNELS_DL_DONE,
475 [=](wxCommandEvent& ev) { m_grid->CellDone(ev, 1); });
476 Bind(CHANNELS_PARSE_DONE,
477 [=](wxCommandEvent& ev) { m_grid->CellDone(ev, 3); });
478 Bind(CATALOG_DL_DONE, [=](wxCommandEvent& ev) { m_grid->CellDone(ev, 5); });
479 Bind(CATALOG_PARSE_DONE, [=](wxCommandEvent& ev) {
workerDone(ev); });
482 void PostEvent(
int evt_id, catalog_status status, std::string message) {
483 wxCommandEvent evt(evt_id);
484 evt.SetInt(
static_cast<int>(status));
485 evt.SetString(message);
486 wxPostEvent(
this, evt);
491 m_grid->CellDone(ev, 5);
493 std::string message = _(
"Catalog updated").ToStdString();
496 path +=
"ocpn-plugins.xml";
497 std::ofstream stream;
498 stream.open(path, std::ios::out);
499 if (stream.is_open()) {
500 stream << m_xml.str();
502 ev.SetString(
"Catalog updated");
503 ev.SetInt(
static_cast<int>(catalog_status::OK_MSG));
505 ev.SetString(
"Update error (cannot writé to file)");
506 ev.SetInt(
static_cast<int>(catalog_status::OS_ERROR));
507 wxLogMessage(
"Update error: Cannot write to %s", path.c_str());
509 m_grid->CellDone(ev, 6);
510 m_buttons->ActivateOk();
513 auto handler = CatalogHandler::GetInstance();
514 catalog_data = handler->LatestCatalogData();
524 auto catalog = CatalogHandler::GetInstance();
525 std::ostringstream json;
527 auto status = catalog->LoadChannels(&json);
528 PostEvent(CHANNELS_DL_DONE, status, catalog->LastErrorMsg());
530 status = catalog->LoadChannels(json.str());
531 PostEvent(CHANNELS_PARSE_DONE, status, catalog->LastErrorMsg());
533 if (status == catalog_status::OK) {
534 auto channel = catalog->GetActiveChannel();
535 status = catalog_status::OK_MSG;
536 PostEvent(CHANNELS_PARSE_DONE, status, channel);
539 status = catalog->DownloadCatalog(&m_xml);
540 PostEvent(CATALOG_DL_DONE, status, catalog->LastErrorMsg());
542 status = catalog->ParseCatalog(m_xml.str(),
true);
543 if (status == catalog_status::OK) {
544 PostEvent(CATALOG_PARSE_DONE, catalog_status::OK_MSG,
545 catalog->LatestCatalogData().version);
547 PostEvent(CATALOG_PARSE_DONE, status, catalog->LastErrorMsg());
554 auto grid =
new wxFlexGridSizer(2, 0, 0);
556 wxSizerFlags().Expand().Border().Align(wxALIGN_CENTER_VERTICAL);
557 grid->Add(staticText(_(
"Server is reachable...")), flags);
558 grid->Add(staticText(
""), flags);
559 grid->Add(staticText(_(
"Check channel...")), flags);
560 grid->Add(staticText(
""), flags);
561 grid->Add(staticText(_(
"Check latest release...")), flags);
562 grid->Add(staticText(
""), flags);
563 grid->Add(staticText(
""), flags);
564 grid->Add(staticText(
""), flags);
565 auto url = CatalogHandler::GetInstance()->
GetCustomUrl();
567 grid->Add(staticText(_(
"Custom URL")), flags);
568 grid->Add(staticText(url.c_str()), flags);
576 void CellDone(
const wxCommandEvent& event,
size_t index) {
578 auto cell = GetSizer()->GetItem(index)->GetWindow();
579 auto code =
static_cast<catalog_status
>(
event.GetInt());
580 if (code == catalog_status::OK) {
581 cell->SetLabel(_(
"OK"));
582 }
else if (code == catalog_status::OK_MSG) {
583 cell->SetLabel(event.GetString().ToStdString());
585 auto msg = std::string(_(
"Fail: ")) +
event.GetString().ToStdString();
586 cell->SetLabel(msg.c_str());
594 Buttons(wxWindow* parent) : wxPanel(parent) {
595 using CmdEvt = wxCommandEvent;
597 auto sizer =
new wxBoxSizer(wxHORIZONTAL);
598 auto flags = wxSizerFlags().Right().Bottom().Border();
599 sizer->Add(1, 1, 100, wxEXPAND);
600 auto cancel =
new wxButton(
this, wxID_CANCEL, _(
"Cancel"));
601 sizer->Add(cancel, flags);
602 m_ok =
new wxButton(
this, wxID_OK, _(
"OK"));
604 sizer->Add(m_ok, flags);
611 void ActivateOk() { m_ok->Enable(
true); }
616 std::ostringstream m_xml;
626 : wxFrame(parent, wxID_ANY, _(
"Catalog Manager"), wxDefaultPosition,
627 wxDefaultSize, wxDEFAULT_FRAME_STYLE | wxRESIZE_BORDER) {
628 auto vbox =
new wxBoxSizer(wxHORIZONTAL);
630 wxSizerFlags(1).Expand());
631 Bind(wxEVT_CLOSE_WINDOW, [
this](wxCloseEvent&) {
632 wxCommandEvent evt(EVT_PLUGINS_RELOAD);
633 wxPostEvent(GetParent(), evt);
636 Bind(CATALOG_DLG_CLOSE, [
this](wxCommandEvent&) {
637 wxCommandEvent evt(EVT_PLUGINS_RELOAD);
638 wxPostEvent(GetParent(), evt);
651 : wxDialog(parent, wxID_ANY, _(
"Catalog Manager"), wxDefaultPosition,
652 wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) {
653 auto vbox =
new wxBoxSizer(wxHORIZONTAL);
655 Bind(wxEVT_CLOSE_WINDOW, [&](wxCloseEvent& e) { EndModal(wxID_OK); });
656 Bind(CATALOG_DLG_CLOSE, [&](wxCommandEvent& ev) { EndModal(wxID_OK); });
Plugin catalog management: Build the runtime catalog, handling downloads as required.
wxDEFINE_EVENT(CHANNELS_DL_DONE, wxCommandEvent)
Posted by Worker() in CatalogLoad, consumed by CatalogLoad::DialogGrid.
Catalog options dialog, by default disabled.
AdvancedCatalogDialog(wxWindow *parent)
Invoke the advanced catalog dialog after a status check.
CatalogData DefaultCatalogData()
Data for default version, installed with main opencpn.
std::string GetDefaultUrl()
Get the default URL, with actual channel included.
CatalogData LatestCatalogData()
Data for latest parsed data marked as latest.
CatalogData UserCatalogData()
Data for user catalog which overrides the default one.
std::string GetActiveChannel()
Get the branch (a.
void SetCustomUrl(const char *url)
Set a custom url, overrides also channel settings.
void ClearCatalogData()
Invalidate *CatalogData caches.
std::string GetCustomUrl()
Get the custom url set by SetCustomUrl.
bool SetActiveChannel(const char *channel)
Set the active channel used when downloading catalog.
static PluginHandler * GetInstance()
Singleton factory.
SimpleCatalogDialog(wxWindow *parent)
Perform a simple catalog update without options.
Download status/progress window, always shown at start-up.
void Worker()
Runs in separate, detached thread, started from ctor.
void workerDone(wxCommandEvent &ev)
Last part of initial Worker() call, in main thread.
The advanced dialog showing channels, possible updates, rollback etc.
wxSize getWindowSize()
The window width is determined by the normally hidden custom url text control.
Generic GUI downloads tool.
Handle downloading of files from remote urls.
void copy_file(const std::string &src_path, const std::string &dest_path)
Copy file contents in path src_path to dest_path.
Miscellaneous utilities, many of which string related.
Plugin remote repositories installation and Uninstall/list operations.
Overall metadata for the set of plugins used.
Grid with Server is Reachable..., Check channel... etc.
Active catalog: The current active, the default and latest downloaded + buttons to use default or lat...
void UpdateVersions()
Update version and date for default, latest and active catalog.
Combobox where user selects active catalog channel.
Custom url edit control, a text line.
The Url Status line at top.