33#include <wx/filename.h>
37#include <wx/progdlg.h>
39#include <wx/statline.h>
42#include "catalog_mgr.h"
43#include "model/downloader.h"
44#include "model/ocpn_utils.h"
45#include "OCPNPlatform.h"
46#include "model/plugin_handler.h"
47#include "download_mgr.h"
52wxDEFINE_EVENT(CHANNELS_DL_DONE, wxCommandEvent);
53wxDEFINE_EVENT(CHANNELS_PARSE_DONE, wxCommandEvent);
54wxDEFINE_EVENT(CATALOG_DL_DONE, wxCommandEvent);
55wxDEFINE_EVENT(CATALOG_PARSE_DONE, wxCommandEvent);
58wxDEFINE_EVENT(CATALOG_DLG_CLOSE, wxCommandEvent);
61static const std::string SEP(
"\\");
63static const std::string SEP(
"/");
66namespace catalog_mgr {
70 Helpers(wxWindow* _parent) : parent(_parent) {}
72 wxStaticText* staticText(
const char* text) {
73 return new wxStaticText(parent, wxID_ANY, text);
76 wxButton* makeButton(
const char* label,
int id = wxID_ANY) {
77 return new wxButton(parent, wxID_ANY, label);
92 : wxDialog(parent, wxID_ANY, _(
"Manage Plugin Catalog"),
93 wxDefaultPosition, wxDefaultSize,
94 wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxSTAY_ON_TOP),
97 HIDE =
"<span foreground=\'blue\'>";
99 HIDE +=
" <<<</span>";
100 ADVANCED =
"<span foreground=\'blue\'>";
101 ADVANCED += _(
"Ultra advanced");
102 ADVANCED +=
" >>></span>";
104 auto sizer =
new wxBoxSizer(wxVERTICAL);
105 auto flags = wxSizerFlags().Expand().Border();
109 sizer->Add(
new UrlChannel(
this, m_catalog_grid), flags);
110 sizer->Add(
new wxStaticLine(
this), flags);
112 sizer->Add(m_catalog_grid, flags);
113 sizer->Add(
new wxStaticLine(
this), flags);
115 m_advanced =
new wxStaticText(
this, wxID_ANY,
"");
116 m_advanced->Bind(wxEVT_LEFT_DOWN,
117 [=](wxMouseEvent& e) { toggleUrlEdit(); });
118 sizer->Add(m_advanced, flags);
120 m_url_box =
new wxBoxSizer(wxVERTICAL);
121 m_url_edit =
new UrlEdit(
this);
122 m_url_box->Add(m_url_edit, flags);
123 m_url_box->Add(
new Buttons(
this, m_catalog_grid),
124 wxSizerFlags().Border().Right());
125 sizer->Add(m_url_box, flags);
126 sizer->Add(
new wxStaticLine(
this), flags);
128 auto done = makeButton(_(
"Done"));
129 sizer->Add(done, wxSizerFlags().Border().Right());
130 done->Bind(wxEVT_COMMAND_BUTTON_CLICKED, [=](wxCommandEvent e) {
132 wxCommandEvent evt(CATALOG_DLG_CLOSE);
133 wxPostEvent(GetParent(), evt);
138 Bind(wxEVT_CLOSE_WINDOW, [=](wxCloseEvent& e) {
140 wxCommandEvent cmd_evt(CATALOG_DLG_CLOSE);
141 wxPostEvent(GetParent(), cmd_evt);
148 SetMinClientSize(size);
154 wxString HIDE, ADVANCED;
156 wxBoxSizer* m_url_box;
159 wxStaticText* m_advanced;
169 auto size = GetTextExtent(uri);
170 size.SetWidth(size.GetWidth() * 120 / 100);
171 size.SetHeight(size.GetHeight() * 130 / 100);
175 void toggleUrlEdit() {
176 m_show_edit = !m_show_edit;
177 m_url_box->GetItem((
size_t)0)->GetWindow()->Show(m_show_edit);
178 m_url_box->GetItem((
size_t)1)->GetWindow()->Show(m_show_edit);
179 m_advanced->SetLabelMarkup(m_show_edit ? HIDE : ADVANCED);
186 auto sizer =
new wxBoxSizer(wxHORIZONTAL);
188 wxSizerFlags().Expand().Border().Align(wxALIGN_CENTER_VERTICAL);
189 sizer->Add(staticText(_(
"Catalog URL status: ")), flags);
190 auto catalog = CatalogHandler::getInstance();
191 int channels = catalog->GetChannels().size();
192 auto text = staticText(channels > 0 ? _(
"OK") : _(
"Error"));
193 sizer->Add(text, flags);
207 using CmdEvt = wxCommandEvent;
209 auto grid =
new wxFlexGridSizer(4, 0, 0);
210 auto plugin_handler = PluginHandler::getInstance();
211 grid->AddGrowableCol(0);
212 grid->AddGrowableCol(1);
213 grid->AddGrowableCol(2);
214 grid->AddGrowableCol(3);
215 auto flags = wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL);
216 flags = flags.DoubleBorder();
221 grid->Add(staticText(_(
"Current active plugin catalog")), flags);
222 grid->Add(staticText(
""), flags);
223 grid->Add(staticText(
""), flags);
224 grid->Add(staticText(
""), flags);
228 grid->Add(staticText(_(
"Default catalog")), flags);
229 grid->Add(staticText(
""), flags);
230 grid->Add(staticText(
""), flags);
231 auto use_default = makeButton(_(
"Use as active catalog"));
232 grid->Add(use_default, wxSizerFlags().Border());
233 use_default->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
234 [=](CmdEvt& e) { useDefaultCatalog(); });
238 grid->Add(staticText(_(
"Latest available catalog:")), flags);
239 grid->Add(staticText(
""), flags);
240 grid->Add(staticText(
""), flags);
241 auto use_latest = makeButton(_(
"Use as active catalog"));
242 use_latest->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
243 [=](CmdEvt& e) { UseLatestCatalog(); });
245 grid->Add(use_latest, wxSizerFlags().Border());
253 void UpdateVersion(wxSizer* grid,
CatalogData data,
size_t ix) {
255 dynamic_cast<wxStaticText*
>(grid->GetItem(ix)->GetWindow());
256 version->SetLabel(data.version);
258 dynamic_cast<wxStaticText*
>(grid->GetItem(ix + 1)->GetWindow());
259 date->SetLabel(data.date);
268 auto grid =
dynamic_cast<wxSizer*
>(GetSizer());
269 UpdateVersion(grid, data, 1);
271 UpdateVersion(grid, data, 5);
273 UpdateVersion(grid, data, 9);
278 std::string GetDefaultCatalogPath() {
279 std::string path = g_Platform->GetSharedDataDir().ToStdString();
281 path +=
"ocpn-plugins.xml";
285 void ReloadAvailableVersion() {
286 auto handler = CatalogHandler::getInstance();
287 std::ostringstream xml;
288 auto status = handler->DownloadCatalog(&xml);
290 if (status != CatalogHandler::ServerStatus::OK) {
291 message = _(
"Cannot download data from url");
293 status = handler->ParseCatalog(xml.str(),
true);
294 if (status != CatalogHandler::ServerStatus::OK) {
295 message = _(
"Cannot parse downloaded data");
298 wxMessageBox(message, _(
"Catalog update problem"), wxICON_ERROR);
304 std::string GetPrivateCatalogPath() {
305 auto plugin_handler = PluginHandler::getInstance();
307 path += SEP +
"ocpn-plugins.xml";
311 void useDefaultCatalog() {
312 auto src = GetDefaultCatalogPath();
313 auto dest = GetPrivateCatalogPath();
314 ocpn::copy_file(src, dest);
319 void UseLatestCatalog() {
320 auto catalog = CatalogHandler::getInstance();
321 std::ofstream dest(GetPrivateCatalogPath());
322 catalog->DownloadCatalog(&dest);
324 catalog->ClearCatalogData();
334 m_catalog_grid(catalog_grid),
336 auto sizer =
new wxBoxSizer(wxHORIZONTAL);
337 auto flags = wxSizerFlags().Right().Bottom().Border();
339 auto clear = makeButton(_(
"Clear"));
340 clear->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
341 [=](wxCommandEvent& ev) { clearUrl(); });
342 sizer->Add(clear, flags);
344 auto use_default = makeButton(_(
"Use default location"));
345 use_default->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
346 [=](wxCommandEvent& ev) { useDefaultUrl(); });
347 sizer->Add(use_default, flags);
349 auto update = makeButton(_(
"Save"));
350 sizer->Add(update, flags);
351 update->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
352 [=](wxCommandEvent& ev) { updateUrl(); });
359 void useDefaultUrl() {
361 m_parent->m_url_edit->setText(url);
364 void clearUrl() { m_parent->m_url_edit->clear(); }
367 auto text = m_parent->m_url_edit->getText();
368 CatalogHandler::getInstance()->
SetCustomUrl(text.c_str());
369 m_catalog_grid->ReloadAvailableVersion();
379 : wxPanel(parent),
Helpers(
this), m_catalog_grid(catalog_grid) {
380 auto sizer =
new wxBoxSizer(wxHORIZONTAL);
382 wxSizerFlags().Expand().Border().Align(wxALIGN_CENTER_VERTICAL);
383 sizer->Add(staticText(_(
"Catalog channel: ")), flags);
384 auto catalog = CatalogHandler::getInstance();
385 wxArrayString channel_list;
386 for (
auto channel : catalog->GetChannels()) {
387 channel_list.Add(channel.c_str());
389 auto channels =
new wxChoice(
this, wxID_ANY, wxDefaultPosition,
390 wxDefaultSize, channel_list);
392 int ix = channels->FindString(current.c_str());
393 channels->SetSelection(ix != wxNOT_FOUND ? ix : 0);
394 channels->Bind(wxEVT_CHOICE,
395 [=](wxCommandEvent& e) { onChannelChange(e); });
396 sizer->Add(channels, flags);
402 void onChannelChange(wxCommandEvent& ev) {
404 m_catalog_grid->ReloadAvailableVersion();
407 std::string m_active_channel;
414 auto sizer =
new wxBoxSizer(wxVERTICAL);
415 auto url_location =
new wxBoxSizer(wxHORIZONTAL);
416 auto flags = wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL);
417 url_location->Add(staticText(_(
"Custom catalog URL: ")), flags);
418 flags = wxSizerFlags().Expand().Border();
419 sizer->Add(url_location, flags);
421 auto url_edit =
new wxBoxSizer(wxHORIZONTAL);
422 auto uri = CatalogHandler::getInstance()->
GetCustomUrl();
423 m_url_ctrl =
new wxTextCtrl(
this, wxID_ANY, uri);
425 m_url_ctrl->SetMinClientSize(the_parent->getWindowSize());
426 url_edit->Add(m_url_ctrl, wxSizerFlags().Expand());
428 sizer->Add(url_edit, flags);
434 void setText(std::string text) {
437 m_url_ctrl->Refresh();
440 void clear() { m_url_ctrl->Clear(); }
442 std::string getText() {
443 return std::string(m_url_ctrl->GetLineText(0).ToStdString());
446 wxTextCtrl* m_url_ctrl;
455 CatalogLoad(wxWindow* parent,
bool use_latest =
false)
456 : wxPanel(parent),
Helpers(
this), m_simple(use_latest) {
457 auto sizer =
new wxBoxSizer(wxVERTICAL);
458 auto flags = wxSizerFlags().Expand().Border();
460 sizer->Add(m_grid, flags);
461 sizer->Add(1, 1, 1, wxEXPAND);
464 sizer->Add(m_buttons, flags.Bottom().Right());
466 auto size = GetTextExtent(_(
"Check latest release..."));
467 size.SetHeight(size.GetHeight() * 10);
468 size.SetWidth(size.GetWidth() * 5 / 2);
469 SetMinClientSize(size);
470 std::thread worker([=]() {
Worker(); });
477 Bind(CHANNELS_DL_DONE,
478 [=](wxCommandEvent& ev) { m_grid->CellDone(ev, 1); });
479 Bind(CHANNELS_PARSE_DONE,
480 [=](wxCommandEvent& ev) { m_grid->CellDone(ev, 3); });
481 Bind(CATALOG_DL_DONE, [=](wxCommandEvent& ev) { m_grid->CellDone(ev, 5); });
482 Bind(CATALOG_PARSE_DONE, [=](wxCommandEvent& ev) {
workerDone(ev); });
485 void PostEvent(
int evt_id, catalog_status status, std::string message) {
486 wxCommandEvent evt(evt_id);
487 evt.SetInt(
static_cast<int>(status));
488 evt.SetString(message);
489 wxPostEvent(
this, evt);
494 m_grid->CellDone(ev, 5);
496 std::string message = _(
"Catalog updated").ToStdString();
499 path +=
"ocpn-plugins.xml";
500 std::ofstream stream;
501 stream.open(path, std::ios::out);
502 if (stream.is_open()) {
503 stream << m_xml.str();
505 ev.SetString(
"Catalog updated");
506 ev.SetInt(
static_cast<int>(catalog_status::OK_MSG));
508 ev.SetString(
"Update error (cannot writé to file)");
509 ev.SetInt(
static_cast<int>(catalog_status::OS_ERROR));
510 wxLogMessage(
"Update error: Cannot write to %s", path.c_str());
512 m_grid->CellDone(ev, 6);
513 m_buttons->ActivateOk();
516 auto handler = CatalogHandler::getInstance();
517 catalog_data = handler->LatestCatalogData();
527 auto catalog = CatalogHandler::getInstance();
528 std::ostringstream json;
530 auto status = catalog->LoadChannels(&json);
531 PostEvent(CHANNELS_DL_DONE, status, catalog->LastErrorMsg());
533 status = catalog->LoadChannels(json.str());
534 PostEvent(CHANNELS_PARSE_DONE, status, catalog->LastErrorMsg());
536 if (status == catalog_status::OK) {
537 auto channel = catalog->GetActiveChannel();
538 status = catalog_status::OK_MSG;
539 PostEvent(CHANNELS_PARSE_DONE, status, channel);
542 status = catalog->DownloadCatalog(&m_xml);
543 PostEvent(CATALOG_DL_DONE, status, catalog->LastErrorMsg());
545 status = catalog->ParseCatalog(m_xml.str(),
true);
546 if (status == catalog_status::OK) {
547 PostEvent(CATALOG_PARSE_DONE, catalog_status::OK_MSG,
548 catalog->LatestCatalogData().version);
550 PostEvent(CATALOG_PARSE_DONE, status, catalog->LastErrorMsg());
557 auto grid =
new wxFlexGridSizer(2, 0, 0);
559 wxSizerFlags().Expand().Border().Align(wxALIGN_CENTER_VERTICAL);
560 grid->Add(staticText(_(
"Server is reachable...")), flags);
561 grid->Add(staticText(
""), flags);
562 grid->Add(staticText(_(
"Check channel...")), flags);
563 grid->Add(staticText(
""), flags);
564 grid->Add(staticText(_(
"Check latest release...")), flags);
565 grid->Add(staticText(
""), flags);
566 grid->Add(staticText(
""), flags);
567 grid->Add(staticText(
""), flags);
568 auto url = CatalogHandler::getInstance()->
GetCustomUrl();
570 grid->Add(staticText(_(
"Custom URL")), flags);
571 grid->Add(staticText(url.c_str()), flags);
579 void CellDone(
const wxCommandEvent& event,
size_t index) {
581 auto cell = GetSizer()->GetItem(index)->GetWindow();
582 auto code =
static_cast<catalog_status
>(
event.GetInt());
583 if (code == catalog_status::OK) {
584 cell->SetLabel(_(
"OK"));
585 }
else if (code == catalog_status::OK_MSG) {
586 cell->SetLabel(event.GetString().ToStdString());
588 auto msg = std::string(_(
"Fail: ")) +
event.GetString().ToStdString();
589 cell->SetLabel(msg.c_str());
597 Buttons(wxWindow* parent) : wxPanel(parent) {
598 using CmdEvt = wxCommandEvent;
600 auto sizer =
new wxBoxSizer(wxHORIZONTAL);
601 auto flags = wxSizerFlags().Right().Bottom().Border();
602 sizer->Add(1, 1, 100, wxEXPAND);
603 auto cancel =
new wxButton(
this, wxID_CANCEL, _(
"Cancel"));
604 sizer->Add(cancel, flags);
605 m_ok =
new wxButton(
this, wxID_OK, _(
"OK"));
607 sizer->Add(m_ok, flags);
614 void ActivateOk() { m_ok->Enable(
true); }
619 std::ostringstream m_xml;
629 : wxFrame(parent, wxID_ANY, _(
"Catalog Manager"), wxDefaultPosition,
630 wxDefaultSize, wxDEFAULT_FRAME_STYLE | wxRESIZE_BORDER) {
631 auto vbox =
new wxBoxSizer(wxHORIZONTAL);
633 wxSizerFlags(1).Expand());
634 Bind(wxEVT_CLOSE_WINDOW, [
this](wxCloseEvent&) {
635 wxCommandEvent evt(EVT_PLUGINS_RELOAD);
636 wxPostEvent(GetParent(), evt);
639 Bind(CATALOG_DLG_CLOSE, [
this](wxCommandEvent&) {
640 wxCommandEvent evt(EVT_PLUGINS_RELOAD);
641 wxPostEvent(GetParent(), evt);
654 : wxDialog(parent, wxID_ANY, _(
"Catalog Manager"), wxDefaultPosition,
655 wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) {
656 auto vbox =
new wxBoxSizer(wxHORIZONTAL);
658 Bind(wxEVT_CLOSE_WINDOW, [&](wxCloseEvent& e) { EndModal(wxID_OK); });
659 Bind(CATALOG_DLG_CLOSE, [&](wxCommandEvent& ev) { EndModal(wxID_OK); });
Plugin catalog management: Build the runtime catalog, handling downloads as required.
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.
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.
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.