30#include <wx/filename.h>
34#include <wx/progdlg.h>
36#include <wx/statline.h>
40#include "model/downloader.h"
42#include "OCPNPlatform.h"
44#include "download_mgr.h"
56static const std::string SEP(
"\\");
58static const std::string SEP(
"/");
61namespace catalog_mgr {
65 Helpers(wxWindow* _parent) : parent(_parent) {}
67 wxStaticText* staticText(
const char* text) {
68 return new wxStaticText(parent, wxID_ANY, text);
71 wxButton* makeButton(
const char* label,
int id = wxID_ANY) {
72 return new wxButton(parent, wxID_ANY, label);
87 : wxDialog(parent, wxID_ANY, _(
"Manage Plugin Catalog"),
88 wxDefaultPosition, wxDefaultSize,
89 wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxSTAY_ON_TOP),
92 HIDE =
"<span foreground=\'blue\'>";
94 HIDE +=
" <<<</span>";
95 ADVANCED =
"<span foreground=\'blue\'>";
96 ADVANCED += _(
"Ultra advanced");
97 ADVANCED +=
" >>></span>";
99 auto sizer =
new wxBoxSizer(wxVERTICAL);
100 auto flags = wxSizerFlags().Expand().Border();
104 sizer->Add(
new UrlChannel(
this, m_catalog_grid), flags);
105 sizer->Add(
new wxStaticLine(
this), flags);
107 sizer->Add(m_catalog_grid, flags);
108 sizer->Add(
new wxStaticLine(
this), flags);
110 m_advanced =
new wxStaticText(
this, wxID_ANY,
"");
111 m_advanced->Bind(wxEVT_LEFT_DOWN,
112 [=](wxMouseEvent& e) { toggleUrlEdit(); });
113 sizer->Add(m_advanced, flags);
115 m_url_box =
new wxBoxSizer(wxVERTICAL);
116 m_url_edit =
new UrlEdit(
this);
117 m_url_box->Add(m_url_edit, flags);
118 m_url_box->Add(
new Buttons(
this, m_catalog_grid),
119 wxSizerFlags().Border().Right());
120 sizer->Add(m_url_box, flags);
121 sizer->Add(
new wxStaticLine(
this), flags);
123 auto done = makeButton(_(
"Done"));
124 sizer->Add(done, wxSizerFlags().Border().Right());
125 done->Bind(wxEVT_COMMAND_BUTTON_CLICKED, [=](wxCommandEvent e) {
127 wxCommandEvent evt(CATALOG_DLG_CLOSE);
128 wxPostEvent(GetParent(), evt);
133 Bind(wxEVT_CLOSE_WINDOW, [=](wxCloseEvent& e) {
135 wxCommandEvent cmd_evt(CATALOG_DLG_CLOSE);
136 wxPostEvent(GetParent(), cmd_evt);
143 SetMinClientSize(size);
149 wxString HIDE, ADVANCED;
151 wxBoxSizer* m_url_box;
154 wxStaticText* m_advanced;
164 auto size = GetTextExtent(uri);
165 size.SetWidth(size.GetWidth() * 120 / 100);
166 size.SetHeight(size.GetHeight() * 130 / 100);
170 void toggleUrlEdit() {
171 m_show_edit = !m_show_edit;
172 m_url_box->GetItem((
size_t)0)->GetWindow()->Show(m_show_edit);
173 m_url_box->GetItem((
size_t)1)->GetWindow()->Show(m_show_edit);
174 m_advanced->SetLabelMarkup(m_show_edit ? HIDE : ADVANCED);
181 auto sizer =
new wxBoxSizer(wxHORIZONTAL);
183 wxSizerFlags().Expand().Border().Align(wxALIGN_CENTER_VERTICAL);
184 sizer->Add(staticText(_(
"Catalog URL status: ")), flags);
185 auto catalog = CatalogHandler::GetInstance();
186 int channels = catalog->GetChannels().size();
187 auto text = staticText(channels > 0 ? _(
"OK") : _(
"Error"));
188 sizer->Add(text, flags);
202 using CmdEvt = wxCommandEvent;
204 auto grid =
new wxFlexGridSizer(4, 0, 0);
206 grid->AddGrowableCol(0);
207 grid->AddGrowableCol(1);
208 grid->AddGrowableCol(2);
209 grid->AddGrowableCol(3);
210 auto flags = wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL);
211 flags = flags.DoubleBorder();
216 grid->Add(staticText(_(
"Current active plugin catalog")), flags);
217 grid->Add(staticText(
""), flags);
218 grid->Add(staticText(
""), flags);
219 grid->Add(staticText(
""), flags);
223 grid->Add(staticText(_(
"Default catalog")), flags);
224 grid->Add(staticText(
""), flags);
225 grid->Add(staticText(
""), flags);
226 auto use_default = makeButton(_(
"Use as active catalog"));
227 grid->Add(use_default, wxSizerFlags().Border());
228 use_default->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
229 [=](CmdEvt& e) { useDefaultCatalog(); });
233 grid->Add(staticText(_(
"Latest available catalog:")), flags);
234 grid->Add(staticText(
""), flags);
235 grid->Add(staticText(
""), flags);
236 auto use_latest = makeButton(_(
"Use as active catalog"));
237 use_latest->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
238 [=](CmdEvt& e) { UseLatestCatalog(); });
240 grid->Add(use_latest, wxSizerFlags().Border());
248 void UpdateVersion(wxSizer* grid,
CatalogData data,
size_t ix) {
250 dynamic_cast<wxStaticText*
>(grid->GetItem(ix)->GetWindow());
251 version->SetLabel(data.version);
253 dynamic_cast<wxStaticText*
>(grid->GetItem(ix + 1)->GetWindow());
254 date->SetLabel(data.date);
263 auto grid =
dynamic_cast<wxSizer*
>(GetSizer());
264 UpdateVersion(grid, data, 1);
266 UpdateVersion(grid, data, 5);
268 UpdateVersion(grid, data, 9);
273 std::string GetDefaultCatalogPath() {
274 std::string path = g_Platform->GetSharedDataDir().ToStdString();
276 path +=
"ocpn-plugins.xml";
280 void ReloadAvailableVersion() {
281 auto handler = CatalogHandler::GetInstance();
282 std::ostringstream xml;
283 auto status = handler->DownloadCatalog(&xml);
285 if (status != CatalogHandler::ServerStatus::OK) {
286 message = _(
"Cannot download data from url");
288 status = handler->ParseCatalog(xml.str(),
true);
289 if (status != CatalogHandler::ServerStatus::OK) {
290 message = _(
"Cannot parse downloaded data");
293 wxMessageBox(message, _(
"Catalog update problem"), wxICON_ERROR);
299 std::string GetPrivateCatalogPath() {
302 path += SEP +
"ocpn-plugins.xml";
306 void useDefaultCatalog() {
307 auto src = GetDefaultCatalogPath();
308 auto dest = GetPrivateCatalogPath();
314 void UseLatestCatalog() {
315 auto catalog = CatalogHandler::GetInstance();
316 std::ofstream dest(GetPrivateCatalogPath());
317 catalog->DownloadCatalog(&dest);
319 catalog->ClearCatalogData();
329 m_catalog_grid(catalog_grid),
331 auto sizer =
new wxBoxSizer(wxHORIZONTAL);
332 auto flags = wxSizerFlags().Right().Bottom().Border();
334 auto clear = makeButton(_(
"Clear"));
335 clear->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
336 [=](wxCommandEvent& ev) { clearUrl(); });
337 sizer->Add(clear, flags);
339 auto use_default = makeButton(_(
"Use default location"));
340 use_default->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
341 [=](wxCommandEvent& ev) { useDefaultUrl(); });
342 sizer->Add(use_default, flags);
344 auto update = makeButton(_(
"Save"));
345 sizer->Add(update, flags);
346 update->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
347 [=](wxCommandEvent& ev) { updateUrl(); });
354 void useDefaultUrl() {
356 m_parent->m_url_edit->setText(url);
359 void clearUrl() { m_parent->m_url_edit->clear(); }
362 auto text = m_parent->m_url_edit->getText();
363 CatalogHandler::GetInstance()->
SetCustomUrl(text.c_str());
364 m_catalog_grid->ReloadAvailableVersion();
374 : wxPanel(parent),
Helpers(
this), m_catalog_grid(catalog_grid) {
375 auto sizer =
new wxBoxSizer(wxHORIZONTAL);
377 wxSizerFlags().Expand().Border().Align(wxALIGN_CENTER_VERTICAL);
378 sizer->Add(staticText(_(
"Catalog channel: ")), flags);
379 auto catalog = CatalogHandler::GetInstance();
380 wxArrayString channel_list;
381 for (
auto channel : catalog->GetChannels()) {
382 channel_list.Add(channel.c_str());
384 auto channels =
new wxChoice(
this, wxID_ANY, wxDefaultPosition,
385 wxDefaultSize, channel_list);
387 int ix = channels->FindString(current.c_str());
388 channels->SetSelection(ix != wxNOT_FOUND ? ix : 0);
389 channels->Bind(wxEVT_CHOICE,
390 [=](wxCommandEvent& e) { onChannelChange(e); });
391 sizer->Add(channels, flags);
397 void onChannelChange(wxCommandEvent& ev) {
399 m_catalog_grid->ReloadAvailableVersion();
402 std::string m_active_channel;
409 auto sizer =
new wxBoxSizer(wxVERTICAL);
410 auto url_location =
new wxBoxSizer(wxHORIZONTAL);
411 auto flags = wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL);
412 url_location->Add(staticText(_(
"Custom catalog URL: ")), flags);
413 flags = wxSizerFlags().Expand().Border();
414 sizer->Add(url_location, flags);
416 auto url_edit =
new wxBoxSizer(wxHORIZONTAL);
417 auto uri = CatalogHandler::GetInstance()->
GetCustomUrl();
418 m_url_ctrl =
new wxTextCtrl(
this, wxID_ANY, uri);
420 m_url_ctrl->SetMinClientSize(the_parent->getWindowSize());
421 url_edit->Add(m_url_ctrl, wxSizerFlags().Expand());
423 sizer->Add(url_edit, flags);
429 void setText(std::string text) {
432 m_url_ctrl->Refresh();
435 void clear() { m_url_ctrl->Clear(); }
437 std::string getText() {
438 return std::string(m_url_ctrl->GetLineText(0).ToStdString());
441 wxTextCtrl* m_url_ctrl;
450 CatalogLoad(wxWindow* parent,
bool use_latest =
false)
451 : wxPanel(parent),
Helpers(
this), m_simple(use_latest) {
452 auto sizer =
new wxBoxSizer(wxVERTICAL);
453 auto flags = wxSizerFlags().Expand().Border();
455 sizer->Add(m_grid, flags);
456 sizer->Add(1, 1, 1, wxEXPAND);
459 sizer->Add(m_buttons, flags.Bottom().Right());
461 auto size = GetTextExtent(_(
"Check latest release..."));
462 size.SetHeight(size.GetHeight() * 10);
463 size.SetWidth(size.GetWidth() * 5 / 2);
464 SetMinClientSize(size);
465 std::thread worker([=]() {
Worker(); });
472 Bind(CHANNELS_DL_DONE,
473 [=](wxCommandEvent& ev) { m_grid->CellDone(ev, 1); });
474 Bind(CHANNELS_PARSE_DONE,
475 [=](wxCommandEvent& ev) { m_grid->CellDone(ev, 3); });
476 Bind(CATALOG_DL_DONE, [=](wxCommandEvent& ev) { m_grid->CellDone(ev, 5); });
477 Bind(CATALOG_PARSE_DONE, [=](wxCommandEvent& ev) {
workerDone(ev); });
480 void PostEvent(
int evt_id, catalog_status status, std::string message) {
481 wxCommandEvent evt(evt_id);
482 evt.SetInt(
static_cast<int>(status));
483 evt.SetString(message);
484 wxPostEvent(
this, evt);
489 m_grid->CellDone(ev, 5);
491 std::string message = _(
"Catalog updated").ToStdString();
494 path +=
"ocpn-plugins.xml";
495 std::ofstream stream;
496 stream.open(path, std::ios::out);
497 if (stream.is_open()) {
498 stream << m_xml.str();
500 ev.SetString(
"Catalog updated");
501 ev.SetInt(
static_cast<int>(catalog_status::OK_MSG));
503 ev.SetString(
"Update error (cannot writé to file)");
504 ev.SetInt(
static_cast<int>(catalog_status::OS_ERROR));
505 wxLogMessage(
"Update error: Cannot write to %s", path.c_str());
507 m_grid->CellDone(ev, 6);
508 m_buttons->ActivateOk();
511 auto handler = CatalogHandler::GetInstance();
512 catalog_data = handler->LatestCatalogData();
522 auto catalog = CatalogHandler::GetInstance();
523 std::ostringstream json;
525 auto status = catalog->LoadChannels(&json);
526 PostEvent(CHANNELS_DL_DONE, status, catalog->LastErrorMsg());
528 status = catalog->LoadChannels(json.str());
529 PostEvent(CHANNELS_PARSE_DONE, status, catalog->LastErrorMsg());
531 if (status == catalog_status::OK) {
532 auto channel = catalog->GetActiveChannel();
533 status = catalog_status::OK_MSG;
534 PostEvent(CHANNELS_PARSE_DONE, status, channel);
537 status = catalog->DownloadCatalog(&m_xml);
538 PostEvent(CATALOG_DL_DONE, status, catalog->LastErrorMsg());
540 status = catalog->ParseCatalog(m_xml.str(),
true);
541 if (status == catalog_status::OK) {
542 PostEvent(CATALOG_PARSE_DONE, catalog_status::OK_MSG,
543 catalog->LatestCatalogData().version);
545 PostEvent(CATALOG_PARSE_DONE, status, catalog->LastErrorMsg());
552 auto grid =
new wxFlexGridSizer(2, 0, 0);
554 wxSizerFlags().Expand().Border().Align(wxALIGN_CENTER_VERTICAL);
555 grid->Add(staticText(_(
"Server is reachable...")), flags);
556 grid->Add(staticText(
""), flags);
557 grid->Add(staticText(_(
"Check channel...")), flags);
558 grid->Add(staticText(
""), flags);
559 grid->Add(staticText(_(
"Check latest release...")), flags);
560 grid->Add(staticText(
""), flags);
561 grid->Add(staticText(
""), flags);
562 grid->Add(staticText(
""), flags);
563 auto url = CatalogHandler::GetInstance()->
GetCustomUrl();
565 grid->Add(staticText(_(
"Custom URL")), flags);
566 grid->Add(staticText(url.c_str()), flags);
574 void CellDone(
const wxCommandEvent& event,
size_t index) {
576 auto cell = GetSizer()->GetItem(index)->GetWindow();
577 auto code =
static_cast<catalog_status
>(
event.GetInt());
578 if (code == catalog_status::OK) {
579 cell->SetLabel(_(
"OK"));
580 }
else if (code == catalog_status::OK_MSG) {
581 cell->SetLabel(event.GetString().ToStdString());
583 auto msg = std::string(_(
"Fail: ")) +
event.GetString().ToStdString();
584 cell->SetLabel(msg.c_str());
592 Buttons(wxWindow* parent) : wxPanel(parent) {
593 using CmdEvt = wxCommandEvent;
595 auto sizer =
new wxBoxSizer(wxHORIZONTAL);
596 auto flags = wxSizerFlags().Right().Bottom().Border();
597 sizer->Add(1, 1, 100, wxEXPAND);
598 auto cancel =
new wxButton(
this, wxID_CANCEL, _(
"Cancel"));
599 sizer->Add(cancel, flags);
600 m_ok =
new wxButton(
this, wxID_OK, _(
"OK"));
602 sizer->Add(m_ok, flags);
609 void ActivateOk() { m_ok->Enable(
true); }
614 std::ostringstream m_xml;
624 : wxFrame(parent, wxID_ANY, _(
"Catalog Manager"), wxDefaultPosition,
625 wxDefaultSize, wxDEFAULT_FRAME_STYLE | wxRESIZE_BORDER) {
626 auto vbox =
new wxBoxSizer(wxHORIZONTAL);
628 wxSizerFlags(1).Expand());
629 Bind(wxEVT_CLOSE_WINDOW, [
this](wxCloseEvent&) {
630 wxCommandEvent evt(EVT_PLUGINS_RELOAD);
631 wxPostEvent(GetParent(), evt);
634 Bind(CATALOG_DLG_CLOSE, [
this](wxCommandEvent&) {
635 wxCommandEvent evt(EVT_PLUGINS_RELOAD);
636 wxPostEvent(GetParent(), evt);
649 : wxDialog(parent, wxID_ANY, _(
"Catalog Manager"), wxDefaultPosition,
650 wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) {
651 auto vbox =
new wxBoxSizer(wxHORIZONTAL);
653 Bind(wxEVT_CLOSE_WINDOW, [&](wxCloseEvent& e) { EndModal(wxID_OK); });
654 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.
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.