1 /******************************************************************************
2 *
3 * Project: OpenCPN
4 *
5 ***************************************************************************
6 * Copyright (C) 2019 Alec Leamas *
7 * *
8 * This program is free software; you can redistribute it and/or modify *
9 * it under the terms of the GNU General Public License as published by *
10 * the Free Software Foundation; either version 2 of the License, or *
11 * (at your option) any later version. *
12 * *
13 * This program is distributed in the hope that it will be useful, *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16 * GNU General Public License for more details. *
17 * *
18 * You should have received a copy of the GNU General Public License *
19 * along with this program; if not, write to the *
20 * Free Software Foundation, Inc., *
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
22 ***************************************************************************
23 */
24
25
26 #include "config.h"
27
28 #include <fstream>
29 #include <sstream>
30 #include <thread>
31
32 #include <wx/button.h>
33 #include <wx/debug.h>
34 #include <wx/filename.h>
35 #include <wx/log.h>
36 #include <wx/msgdlg.h>
37 #include <wx/panel.h>
38 #include <wx/progdlg.h>
39 #include <wx/sizer.h>
40 #include <wx/statline.h>
41
42 #include "catalog_handler.h"
43 #include "catalog_mgr.h"
44 #include "Downloader.h"
45 #include "ocpn_utils.h"
46 #include "OCPNPlatform.h"
47 #include "PluginHandler.h"
48 #include "download_mgr.h"
49
50 extern OCPNPlatform* g_Platform;
51
52
53 /** Posted by Worker() in CatalogLoad, consumed by CatalogLoad::DialogGrid. */
54 wxDEFINE_EVENT(CHANNELS_DL_DONE, wxCommandEvent);
55 wxDEFINE_EVENT(CHANNELS_PARSE_DONE, wxCommandEvent);
56 wxDEFINE_EVENT(CATALOG_DL_DONE, wxCommandEvent);
57 wxDEFINE_EVENT(CATALOG_PARSE_DONE, wxCommandEvent);
58
59
60 /** Posted by CatalogUpdate on close. */
61 wxDEFINE_EVENT(CATALOG_DLG_CLOSE, wxCommandEvent);
62
63 #ifdef _WIN32
64 static const std::string SEP("\\");
65 #else
66 static const std::string SEP("/");
67 #endif
68
69 namespace catalog_mgr
70 {
71
72 class Helpers
73 {
74 public:
Helpers(wxWindow * _parent)75 Helpers(wxWindow* _parent): parent(_parent)
76 {}
77
staticText(const char * text)78 wxStaticText* staticText(const char* text)
79 {
80 return new wxStaticText(parent, wxID_ANY, text);
81 }
82
makeButton(const char * label,int id=wxID_ANY)83 wxButton* makeButton(const char* label, int id = wxID_ANY)
84 {
85 return new wxButton(parent, wxID_ANY, label);
86 }
87
88 private:
89 wxWindow* parent;
90 };
91
92
93 /** The advanced dialog showing channels, possible updates, rollback etc. */
94 class CatalogUpdate: public wxDialog, Helpers
95 {
96 protected:
97 class UrlEdit; //forward
98 class ActiveCatalogGrid; //forward
99
100 public:
CatalogUpdate(wxWindow * parent)101 CatalogUpdate(wxWindow* parent)
102 :wxDialog(parent, wxID_ANY, _("Manage Plugin Catalog"),
103 wxDefaultPosition , wxDefaultSize,
104 wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxSTAY_ON_TOP),
105 Helpers(this),
106 m_show_edit(true)
107 {
108 auto sizer = new wxBoxSizer(wxVERTICAL);
109 auto flags = wxSizerFlags().Expand().Border();
110
111 m_catalog_grid = new ActiveCatalogGrid(this);
112 sizer->Add(new UrlStatus(this), flags);
113 sizer->Add(new UrlChannel(this, m_catalog_grid), flags);
114 sizer->Add(new wxStaticLine(this), flags);
115
116 sizer->Add(m_catalog_grid, flags);
117 sizer->Add(new wxStaticLine(this), flags);
118
119 m_advanced = new wxStaticText(this, wxID_ANY, "");
120 m_advanced->Bind(wxEVT_LEFT_DOWN,
121 [=](wxMouseEvent& e) { toggleUrlEdit(); } );
122 sizer->Add(m_advanced, flags);
123
124 m_url_box = new wxBoxSizer(wxVERTICAL);
125 m_url_edit = new UrlEdit(this);
126 m_url_box->Add(m_url_edit, flags);
127 m_url_box->Add(new Buttons(this, m_catalog_grid),
128 wxSizerFlags().Border().Right());
129 sizer->Add(m_url_box, flags);
130 sizer->Add(new wxStaticLine(this), flags);
131
132 auto done = makeButton(_("Done"));
133 sizer->Add(done, wxSizerFlags().Border().Right());
134 done->Bind(wxEVT_COMMAND_BUTTON_CLICKED, [=] (wxCommandEvent e) {
135 EndModal(wxID_OK);
136 wxCommandEvent evt(CATALOG_DLG_CLOSE);
137 wxPostEvent(GetParent(), evt);
138 e.Skip();
139 });
140 toggleUrlEdit();
141
142 Bind(wxEVT_CLOSE_WINDOW, [=](wxCloseEvent& e) {
143 EndModal(wxID_OK);
144 wxCommandEvent cmd_evt(CATALOG_DLG_CLOSE);
145 wxPostEvent(GetParent(), cmd_evt);
146 e.Skip();
147 });
148
149 SetSizer(sizer);
150 auto size = getWindowSize();
151 size.SetHeight(1);
152 SetMinClientSize(size);
153 Fit();
154 ShowModal();
155 }
156
157 protected:
158 const char* const HIDE =
159 _("<span foreground='blue'>Hide <<<</span>");
160 const char* const ADVANCED =
161 _("<span foreground='blue'>Ultra advanced >>></span>");
162
163 wxBoxSizer* m_url_box;
164 ActiveCatalogGrid* m_catalog_grid;
165 UrlEdit* m_url_edit;
166 wxStaticText* m_advanced;
167 bool m_show_edit;
168
169 /**
170 * The window width is determined by the normally hidden custom
171 * url text control. Return the size of the main window including
172 * the hidden part.
173 */
getWindowSize()174 wxSize getWindowSize()
175 {
176 auto uri = CatalogHandler::getInstance()->GetDefaultUrl();
177 auto size = GetTextExtent(uri);
178 size.SetWidth(size.GetWidth() * 120 / 100);
179 size.SetHeight(size.GetHeight() * 130 / 100);
180 return size;
181 }
182
toggleUrlEdit()183 void toggleUrlEdit()
184 {
185 m_show_edit = !m_show_edit;
186 m_url_box->GetItem((size_t) 0)->GetWindow()->Show(m_show_edit);
187 m_url_box->GetItem((size_t) 1)->GetWindow()->Show(m_show_edit);
188 m_advanced->SetLabelMarkup(m_show_edit ? HIDE : ADVANCED);
189 Fit();
190 }
191
192 /** The Url Status line at top */
193 struct UrlStatus: public wxPanel, public Helpers
194 {
UrlStatuscatalog_mgr::CatalogUpdate::UrlStatus195 UrlStatus(wxWindow* parent) : wxPanel(parent), Helpers(this)
196 {
197 auto sizer = new wxBoxSizer(wxHORIZONTAL);
198 auto flags = wxSizerFlags()
199 .Expand().Border().Align(wxALIGN_CENTER_VERTICAL);
200 sizer->Add(staticText(_("Catalog URL status: ")), flags);
201 auto catalog = CatalogHandler::getInstance();
202 int channels = catalog->GetChannels().size();
203 auto text = staticText(channels > 0? _("OK"): _("Error"));
204 sizer->Add(text, flags);
205
206 SetSizer(sizer);
207 Fit();
208 Show();
209 }
210 };
211
212 /**
213 * Active catalog: The current active, the default and latest
214 * downloaded + buttons to use default or latest.
215 */
216 struct ActiveCatalogGrid: public wxPanel, public Helpers
217 {
ActiveCatalogGridcatalog_mgr::CatalogUpdate::ActiveCatalogGrid218 ActiveCatalogGrid(wxWindow* parent)
219 :wxPanel(parent), Helpers(this)
220 {
221 using CmdEvt = wxCommandEvent;
222
223 auto grid = new wxFlexGridSizer(4, 0, 0);
224 auto plugin_handler = PluginHandler::getInstance();
225 grid->AddGrowableCol(0);
226 grid->AddGrowableCol(1);
227 grid->AddGrowableCol(2);
228 grid->AddGrowableCol(3);
229 auto flags = wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL);
230 flags = flags.DoubleBorder();
231
232 /* Cell 0..3 */
233 CatalogData catalog_data =
234 CatalogHandler::getInstance()->UserCatalogData();
235 grid->Add(staticText(
236 _("Current active plugin catalog")), flags);
237 grid->Add(staticText(""), flags);
238 grid->Add(staticText(""), flags);
239 grid->Add(staticText(""), flags);
240
241 /* Cell 4..7 */
242 catalog_data =
243 CatalogHandler::getInstance()->DefaultCatalogData();
244 grid->Add(staticText(_("Default catalog")), flags);
245 grid->Add(staticText(""), flags);
246 grid->Add(staticText(""), flags);
247 auto use_default = makeButton(_("Use as active catalog"));
248 grid->Add(use_default, wxSizerFlags().Border());
249 use_default->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
250 [=](CmdEvt& e) { useDefaultCatalog(); });
251
252 /* Cell 8..11 */
253 catalog_data =
254 CatalogHandler::getInstance()->LatestCatalogData();
255 grid->Add(staticText(_("Latest available catalog:")), flags);
256 grid->Add(staticText(""), flags);
257 grid->Add(staticText(""), flags);
258 auto use_latest = makeButton(_("Use as active catalog"));
259 use_latest->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
260 [=](CmdEvt& e) { UseLatestCatalog(); });
261
262 grid->Add(use_latest, wxSizerFlags().Border());
263
264 SetSizer(grid);
265 UpdateVersions();
266 Fit();
267 Show();
268 }
269
UpdateVersioncatalog_mgr::CatalogUpdate::ActiveCatalogGrid270 void UpdateVersion(wxSizer* grid, CatalogData data, size_t ix)
271 {
272 auto version = dynamic_cast<wxStaticText*>(
273 grid->GetItem(ix)->GetWindow());
274 version->SetLabel(data.version);
275 auto date = dynamic_cast<wxStaticText*>(
276 grid->GetItem(ix + 1)->GetWindow());
277 date->SetLabel(data.date);
278 }
279
280 /**
281 * Update version and date for default, latest and active
282 * catalog.
283 */
UpdateVersionscatalog_mgr::CatalogUpdate::ActiveCatalogGrid284 void UpdateVersions()
285 {
286 CatalogData data =
287 CatalogHandler::getInstance()->UserCatalogData();
288 auto grid = dynamic_cast<wxSizer*>(GetSizer());
289 UpdateVersion(grid, data, 1);
290 data = CatalogHandler::getInstance()->DefaultCatalogData();
291 UpdateVersion(grid, data, 5);
292 data = CatalogHandler::getInstance()->LatestCatalogData();
293 UpdateVersion(grid, data, 9);
294 Refresh(true);
295 Update();
296 }
297
GetDefaultCatalogPathcatalog_mgr::CatalogUpdate::ActiveCatalogGrid298 std::string GetDefaultCatalogPath()
299 {
300 std::string path
301 = g_Platform->GetSharedDataDir().ToStdString();
302 path += SEP ;
303 path += "ocpn-plugins.xml";
304 return path;
305 }
306
ReloadAvailableVersioncatalog_mgr::CatalogUpdate::ActiveCatalogGrid307 void ReloadAvailableVersion()
308 {
309 auto handler = CatalogHandler::getInstance();
310 std::ostringstream xml;
311 auto status = handler->DownloadCatalog(&xml);
312 std::string message;
313 if (status != CatalogHandler::ServerStatus::OK) {
314 message = _("Cannot download data from url");
315 }
316 status = handler->ParseCatalog(xml.str(), true);
317 if (status != CatalogHandler::ServerStatus::OK) {
318 message = _("Cannot parse downloaded data");
319 }
320 if (message != "") {
321 wxMessageBox(message,
322 _("Catalog update problem"),
323 wxICON_ERROR);
324 }
325 else {
326 UpdateVersions();
327 }
328 }
329
GetPrivateCatalogPathcatalog_mgr::CatalogUpdate::ActiveCatalogGrid330 std::string GetPrivateCatalogPath()
331 {
332 auto plugin_handler = PluginHandler::getInstance();
333 std::string path =
334 g_Platform->GetPrivateDataDir().ToStdString();
335 path += SEP + "ocpn-plugins.xml";
336 return path;
337 }
338
useDefaultCatalogcatalog_mgr::CatalogUpdate::ActiveCatalogGrid339 void useDefaultCatalog()
340 {
341 auto src = GetDefaultCatalogPath();
342 auto dest = GetPrivateCatalogPath();
343 ocpn::copy_file(src, dest );
344 CatalogHandler::getInstance()->ClearCatalogData();
345 UpdateVersions();
346 }
347
UseLatestCatalogcatalog_mgr::CatalogUpdate::ActiveCatalogGrid348 void UseLatestCatalog()
349 {
350 auto catalog = CatalogHandler::getInstance();
351 std::ofstream dest(GetPrivateCatalogPath());
352 catalog->DownloadCatalog(&dest);
353 dest.close();
354 catalog->ClearCatalogData();
355 UpdateVersions();
356 }
357 };
358
359 /** The buttons below custom url: Use Default and Update. */
360 struct Buttons: public wxPanel, public Helpers
361 {
Buttonscatalog_mgr::CatalogUpdate::Buttons362 Buttons(wxWindow* parent, ActiveCatalogGrid* catalog_grid)
363 : wxPanel(parent), Helpers(this),
364 m_catalog_grid(catalog_grid),
365 m_parent(dynamic_cast<CatalogUpdate*>(GetParent()))
366 {
367 auto sizer = new wxBoxSizer(wxHORIZONTAL);
368 auto flags = wxSizerFlags().Right().Bottom().Border();
369
370 auto clear = makeButton(_("Clear"));
371 clear->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
372 [=](wxCommandEvent& ev) { clearUrl(); });
373 sizer->Add(clear, flags);
374
375 auto use_default = makeButton(_("Use default location"));
376 use_default->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
377 [=](wxCommandEvent& ev) { useDefaultUrl(); });
378 sizer->Add(use_default, flags);
379
380 auto update = makeButton(_("Save"));
381 sizer->Add(update, flags);
382 update->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
383 [=](wxCommandEvent& ev) { updateUrl(); });
384
385 SetSizer(sizer);
386 Fit();
387 Show();
388 }
389
useDefaultUrlcatalog_mgr::CatalogUpdate::Buttons390 void useDefaultUrl()
391 {
392 auto url = CatalogHandler::getInstance()->GetDefaultUrl();
393 m_parent->m_url_edit->setText(url);
394 }
395
clearUrlcatalog_mgr::CatalogUpdate::Buttons396 void clearUrl()
397 {
398 m_parent->m_url_edit->clear();
399 }
400
updateUrlcatalog_mgr::CatalogUpdate::Buttons401 void updateUrl()
402 {
403 auto text = m_parent->m_url_edit->getText();
404 CatalogHandler::getInstance()->SetCustomUrl(text.c_str());
405 m_catalog_grid->ReloadAvailableVersion();
406 }
407
408 ActiveCatalogGrid* m_catalog_grid;
409 CatalogUpdate* m_parent;
410 };
411
412
413 /** Combobox where user selects active catalog channel. */
414 struct UrlChannel: public wxPanel, public Helpers
415 {
UrlChannelcatalog_mgr::CatalogUpdate::UrlChannel416 UrlChannel(wxWindow* parent, ActiveCatalogGrid* catalog_grid)
417 : wxPanel(parent), Helpers(this), m_catalog_grid(catalog_grid)
418 {
419 auto sizer = new wxBoxSizer(wxHORIZONTAL);
420 auto flags = wxSizerFlags()
421 .Expand().Border().Align(wxALIGN_CENTER_VERTICAL);
422 sizer->Add(staticText(_("Catalog channel: ")), flags);
423 auto catalog = CatalogHandler::getInstance();
424 wxArrayString channel_list;
425 for (auto channel: catalog->GetChannels()) {
426 channel_list.Add(channel.c_str());
427 }
428 auto channels = new wxChoice(this, wxID_ANY,
429 wxDefaultPosition,
430 wxDefaultSize,
431 channel_list);
432 auto current =
433 CatalogHandler::getInstance()->GetActiveChannel();
434 int ix = channels->FindString(current.c_str());
435 channels->SetSelection(ix != wxNOT_FOUND ? ix : 0);
436 channels->Bind(
437 wxEVT_CHOICE,
438 [=](wxCommandEvent& e) { onChannelChange(e); });
439 sizer->Add(channels, flags);
440 SetSizer(sizer);
441 Fit();
442 Show();
443 }
444
onChannelChangecatalog_mgr::CatalogUpdate::UrlChannel445 void onChannelChange(wxCommandEvent& ev)
446 {
447 CatalogHandler::getInstance()
448 ->SetActiveChannel(ev.GetString());
449 m_catalog_grid->ReloadAvailableVersion();
450 };
451
452 std::string m_active_channel;
453 ActiveCatalogGrid* m_catalog_grid;
454 };
455
456
457 /** Custom url edit control, a text line. */
458 struct UrlEdit: public wxPanel, public Helpers
459 {
UrlEditcatalog_mgr::CatalogUpdate::UrlEdit460 UrlEdit(wxWindow* parent): wxPanel(parent), Helpers(this)
461 {
462 auto sizer = new wxBoxSizer(wxVERTICAL);
463 auto url_location = new wxBoxSizer(wxHORIZONTAL);
464 auto flags = wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL);
465 url_location->Add(staticText(_("Custom catalog URL: ")),
466 flags);
467 flags = wxSizerFlags().Expand().Border();
468 sizer->Add(url_location, flags);
469
470 auto url_edit = new wxBoxSizer(wxHORIZONTAL);
471 auto uri = CatalogHandler::getInstance()->GetCustomUrl();
472 m_url_ctrl = new wxTextCtrl(this, wxID_ANY, uri);
473 auto the_parent = dynamic_cast<CatalogUpdate*>(GetParent());
474 m_url_ctrl->SetMinClientSize(the_parent->getWindowSize());
475 url_edit->Add(m_url_ctrl, wxSizerFlags().Expand());
476
477 sizer->Add(url_edit, flags);
478 SetSizer(sizer);
479 Fit();
480 Show();
481 }
482
setTextcatalog_mgr::CatalogUpdate::UrlEdit483 void setText(std::string text)
484 {
485 m_url_ctrl->Clear();
486 *m_url_ctrl << text;
487 m_url_ctrl->Refresh();
488 }
489
clearcatalog_mgr::CatalogUpdate::UrlEdit490 void clear() { m_url_ctrl->Clear(); }
491
getTextcatalog_mgr::CatalogUpdate::UrlEdit492 std::string getText()
493 {
494 return std::string(m_url_ctrl->GetLineText(0).ToStdString());
495 }
496
497 wxTextCtrl* m_url_ctrl;
498 };
499 };
500
501
502 /** Download status/progress window, always shown at start-up. */
503 class CatalogLoad: public wxPanel, public Helpers
504 {
505 public:
506
507 class DialogGrid; // forward
508
CatalogLoad(wxWindow * parent,bool use_latest=false)509 CatalogLoad(wxWindow* parent, bool use_latest = false)
510 :wxPanel(parent), Helpers(this), m_simple(use_latest)
511 {
512 auto sizer = new wxBoxSizer(wxVERTICAL);
513 auto flags = wxSizerFlags().Expand().Border() ;
514 m_grid = new DialogGrid(this);
515 sizer->Add(m_grid, flags);
516 sizer->Add(1, 1, 1, wxEXPAND); // Expanding spacer
517 if (m_simple) {
518 m_buttons = new Buttons(this);
519 sizer->Add(m_buttons, flags.Bottom().Right());
520 }
521 auto size = GetTextExtent(_("Check latest release..."));
522 size.SetHeight(size.GetHeight() * 10);
523 size.SetWidth(size.GetWidth() * 5 / 2);
524 SetMinClientSize(size);
525 std::thread worker([=]() { Worker(); });
526 worker.detach();
527
528 SetSizer(sizer);
529 Fit();
530 Show();
531
532 Bind(CHANNELS_DL_DONE,
533 [=](wxCommandEvent& ev) { m_grid->CellDone(ev, 1); });
534 Bind(CHANNELS_PARSE_DONE,
535 [=](wxCommandEvent& ev) { m_grid->CellDone(ev, 3); });
536 Bind(CATALOG_DL_DONE,
537 [=](wxCommandEvent& ev) { m_grid->CellDone(ev, 5); });
538 Bind(CATALOG_PARSE_DONE,
539 [=](wxCommandEvent& ev) { workerDone(ev); });
540 }
541
PostEvent(int evt_id,catalog_status status,std::string message)542 void PostEvent(int evt_id, catalog_status status, std::string message)
543 {
544 wxCommandEvent evt(evt_id);
545 evt.SetInt(static_cast<int>(status));
546 evt.SetString(message);
547 wxPostEvent(this, evt);
548 }
549
550 /** Last part of initial Worker() call, in main thread. */
workerDone(wxCommandEvent & ev)551 void workerDone(wxCommandEvent& ev)
552 {
553 m_grid->CellDone(ev, 5);
554 if (m_simple) {
555 std::string message =_("Catalog updated").ToStdString();
556 std::string path =
557 g_Platform->GetPrivateDataDir().ToStdString();
558 path += SEP;
559 path += "ocpn-plugins.xml";
560 std::ofstream stream;
561 stream.open(path, std::ios::out);
562 if (stream.is_open()) {
563 stream << m_xml.str();
564 stream.close();
565 ev.SetString("Catalog updated");
566 ev.SetInt(static_cast<int>(catalog_status::OK_MSG));
567 }
568 else {
569 ev.SetString("Update error (cannot writé to file)");
570 ev.SetInt(static_cast<int>(catalog_status::OS_ERROR));
571 wxLogMessage("Update error: Cannot write to %s",
572 path.c_str());
573 }
574 m_grid->CellDone(ev, 6);
575 m_buttons->ActivateOk();
576 }
577 else {
578 CatalogData catalog_data;
579 auto handler = CatalogHandler::getInstance();
580 catalog_data = handler->LatestCatalogData();
581 Hide();
582 Refresh(true);
583 Update();
584 new CatalogUpdate(this);
585 }
586 }
587
588 /** Runs in separate, detached thread, started from ctor. */
Worker()589 void Worker()
590 {
591 auto catalog = CatalogHandler::getInstance();
592 std::ostringstream json;
593
594 auto status = catalog->LoadChannels(&json);
595 PostEvent(CHANNELS_DL_DONE, status, catalog->LastErrorMsg());
596
597 status = catalog->LoadChannels(json.str());
598 PostEvent(CHANNELS_PARSE_DONE, status, catalog->LastErrorMsg());
599
600 if (status == catalog_status::OK) {
601 auto channel = catalog->GetActiveChannel();
602 status = catalog_status::OK_MSG;
603 PostEvent(CHANNELS_PARSE_DONE, status, channel);
604 }
605 m_xml.clear();
606 status = catalog->DownloadCatalog(&m_xml);
607 PostEvent(CATALOG_DL_DONE, status, catalog->LastErrorMsg());
608
609 status = catalog->ParseCatalog(m_xml.str(), true);
610 if (status == catalog_status::OK) {
611 PostEvent(CATALOG_PARSE_DONE,
612 catalog_status::OK_MSG,
613 catalog->LatestCatalogData().version);
614 }
615 else {
616 PostEvent(CATALOG_PARSE_DONE,
617 status,
618 catalog->LastErrorMsg());
619 }
620 }
621
622 /** Grid with Server is Reachable..., Check channel... etc. */
623 struct DialogGrid: public wxPanel, public Helpers
624 {
DialogGridcatalog_mgr::CatalogLoad::DialogGrid625 DialogGrid(wxWindow* parent): wxPanel(parent), Helpers(this)
626 {
627 auto grid = new wxFlexGridSizer(2, 0, 0);
628 auto flags = wxSizerFlags()
629 .Expand().Border().Align(wxALIGN_CENTER_VERTICAL);
630 grid->Add(staticText(_("Server is reachable...")), flags);
631 grid->Add(staticText(_("")), flags);
632 grid->Add(staticText(_("Check channel...")), flags);
633 grid->Add(staticText(_("")), flags);
634 grid->Add(staticText(_("Check latest release...")), flags);
635 grid->Add(staticText(_("")), flags);
636 grid->Add(staticText(_("")), flags);
637 grid->Add(staticText(_("")), flags);
638 auto url = CatalogHandler::getInstance()->GetCustomUrl();
639 if (url != "") {
640 grid->Add(staticText(_("Custom URL")), flags);
641 grid->Add(staticText(url.c_str()), flags);
642 }
643 SetSizer(grid);
644 Fit();
645 Show();
646 }
647
648 /* Update status values in grid. */
CellDonecatalog_mgr::CatalogLoad::DialogGrid649 void CellDone(const wxCommandEvent& event, size_t index)
650 {
651 //wxLogMessage("CellDone: event %d", event.GetInt());
652 auto cell = GetSizer()->GetItem(index)->GetWindow();
653 auto code = static_cast<catalog_status>(event.GetInt());
654 if (code == catalog_status::OK) {
655 cell->SetLabel(_("OK"));
656 }
657 else if (code == catalog_status::OK_MSG) {
658 cell->SetLabel(event.GetString().ToStdString());
659 }
660 else {
661 auto msg = std::string(_("Fail: "))
662 + event.GetString().ToStdString();
663 cell->SetLabel(_(msg.c_str()));
664 }
665 Fit();
666 }
667 };
668
669 /** OK/Cancel buttons for the download progress window. */
670 struct Buttons: public wxPanel
671 {
Buttonscatalog_mgr::CatalogLoad::Buttons672 Buttons(wxWindow* parent) : wxPanel(parent)
673 {
674 using CmdEvt = wxCommandEvent;
675
676 auto sizer = new wxBoxSizer(wxHORIZONTAL);
677 auto flags = wxSizerFlags().Right().Bottom().Border();
678 sizer->Add(1, 1, 100, wxEXPAND); // Expanding spacer
679 auto cancel = new wxButton(this, wxID_CANCEL, _("Cancel"));
680 sizer->Add(cancel, flags);
681 m_ok = new wxButton(this, wxID_OK, _("OK"));
682 m_ok->Enable(false);
683 sizer->Add(m_ok, flags);
684 SetSizer(sizer);
685 Fit();
686 SetFocus();
687 Show();
688 }
689
ActivateOkcatalog_mgr::CatalogLoad::Buttons690 void ActivateOk()
691 {
692 m_ok->Enable(true);
693 }
694
695 wxButton* m_ok;
696 };
697
698 std::ostringstream m_xml;
699 DialogGrid* m_grid;
700 Buttons* m_buttons;
701 const bool m_simple; // Simple means just install, no advanced dialog
702 };
703
704
705 } // namespace catalog_mgr
706
707
708 /** Top-level plugin catalog dialog. */
AdvancedCatalogDialog(wxWindow * parent)709 AdvancedCatalogDialog::AdvancedCatalogDialog(wxWindow* parent)
710 :wxFrame(parent, wxID_ANY, _("Catalog Manager"),
711 wxDefaultPosition , wxDefaultSize,
712 wxDEFAULT_FRAME_STYLE | wxRESIZE_BORDER)
713 {
714 auto vbox = new wxBoxSizer(wxHORIZONTAL);
715 vbox->Add(new catalog_mgr::CatalogLoad(this, false),
716 wxSizerFlags(1).Expand());
717 Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent&) {
718 wxCommandEvent evt(EVT_PLUGINS_RELOAD);
719 wxPostEvent(GetParent(), evt);
720 Destroy();
721 });
722 Bind(CATALOG_DLG_CLOSE, [this](wxCommandEvent&) {
723 wxCommandEvent evt(EVT_PLUGINS_RELOAD);
724 wxPostEvent(GetParent(), evt);
725 Destroy();
726 });
727 SetSizer(vbox);
728
729 Fit();
730 Center();
731 Raise();
732 SetFocus();
733 Show();
734 }
735
SimpleCatalogDialog(wxWindow * parent)736 SimpleCatalogDialog::SimpleCatalogDialog(wxWindow* parent)
737 :wxDialog(parent, wxID_ANY, _("Catalog Manager"),
738 wxDefaultPosition , wxDefaultSize,
739 wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
740 {
741 auto vbox = new wxBoxSizer(wxHORIZONTAL);
742 vbox->Add(new catalog_mgr::CatalogLoad(this, true),
743 wxSizerFlags(1).Expand());
744 Bind(wxEVT_CLOSE_WINDOW, [&](wxCloseEvent& e) { EndModal(wxID_OK); });
745 Bind(CATALOG_DLG_CLOSE, [&](wxCommandEvent& ev) { EndModal(wxID_OK); });
746 SetSizer(vbox);
747
748 Fit();
749 ShowModal();
750 Destroy();
751 }
752