35#include "chartdldr_pi.h"
36#include "wxWTranslateCatalog.h"
37#include <wx/stdpaths.h>
39#include <wx/progdlg.h>
40#include <wx/sstream.h>
41#include <wx/wfstream.h>
42#include <wx/filename.h>
43#include <wx/listctrl.h>
45#include <wx/filesys.h>
46#include <wx/zipstrm.h>
47#include <wx/wfstream.h>
52#ifdef DLDR_USE_LIBARCHIVE
54#include <archive_entry.h>
55#ifdef CHARTDLDR_RAR_UNARR
62#ifdef __OCPN__ANDROID__
63#define _LIBCPP_HAS_NO_OFF_T_FUNCTIONS
69#include "androidSupport.h"
70#include "android_jvm.h"
75#define CATALOGS_NAME_WIDTH 300
76#define CATALOGS_DATE_WIDTH 120
77#define CATALOGS_PATH_WIDTH 100
78#define CHARTS_NAME_WIDTH 300
79#define CHARTS_STATUS_WIDTH 100
80#define CHARTS_DATE_WIDTH 120
82#ifdef __OCPN__ANDROID__
84#define CATALOGS_NAME_WIDTH 350
85#define CATALOGS_DATE_WIDTH 500
86#define CATALOGS_PATH_WIDTH 1000
87#define CHARTS_NAME_WIDTH 520
88#define CHARTS_STATUS_WIDTH 150
89#define CHARTS_DATE_WIDTH 200
93#define CATALOGS_NAME_WIDTH 200
94#define CATALOGS_DATE_WIDTH 130
95#define CATALOGS_PATH_WIDTH 250
96#define CHARTS_NAME_WIDTH 320
97#define CHARTS_STATUS_WIDTH 150
98#define CHARTS_DATE_WIDTH 130
103#ifdef __OCPN__ANDROID__
104#include <QtAndroidExtras/QAndroidJniObject>
109bool getDisplayMetrics();
111#define CHART_DIR "Charts"
113void write_file(
const wxString extract_file,
char *data,
114 unsigned long datasize) {
115 wxFileName fn(extract_file);
116 if (wxDirExists(fn.GetPath())) {
117 if (!wxFileName::Mkdir(fn.GetPath(), 0755, wxPATH_MKDIR_FULL)) {
118 wxLogError(_T(
"Can not create directory '") + fn.GetPath() + _T(
"'."));
122 wxFileOutputStream f(extract_file);
123 f.Write(data, datasize);
127int g_Android_SDK_Version;
129bool IsDLDirWritable(wxFileName fn) {
130#ifndef __OCPN__ANDROID__
131 return fn.IsDirWritable();
133 if (g_Android_SDK_Version >= 30) {
135 return (fn.GetFullPath().Contains(
"org.opencpn.opencpn"));
137 return fn.IsDirWritable();
148extern "C" DECL_EXP
void destroy_pi(
opencpn_plugin *p) {
delete p; }
171 m_parent_window = NULL;
172 m_pChartSource = NULL;
174 m_preselect_new =
false;
175 m_preselect_updated =
false;
176 m_allow_bulk_update =
false;
177 m_pOptionsPage = NULL;
178 m_selected_source = -1;
180 m_leftclick_tool_id = -1;
181 m_schartdldr_sources = wxEmptyString;
186int chartdldr_pi::Init(
void) {
187 AddLocaleCatalog(PLUGIN_CATALOG_NAME);
191 m_parent_window = GetOCPNCanvasWindow();
194 m_pconfig = GetOCPNConfigObject();
195 m_pOptionsPage = NULL;
197 m_pChartSource = NULL;
199#ifdef __OCPN__ANDROID__
200 androidGetSDKVersion();
208 wxStringTokenizer st(m_schartdldr_sources, _T(
"|"), wxTOKEN_DEFAULT);
209 while (st.HasMoreTokens()) {
210 wxString s1 = st.GetNextToken();
211 wxString s2 = st.GetNextToken();
212 wxString s3 = st.GetNextToken();
214 m_ChartSources.push_back(std::make_unique<ChartSource>(s1, s2, s3));
219bool chartdldr_pi::DeInit(
void) {
220 wxLogMessage(_T(
"chartdldr_pi: DeInit"));
222 m_ChartSources.clear();
230 if (m_pOptionsPage) {
231 if (DeleteOptionsPage(m_pOptionsPage)) m_pOptionsPage = NULL;
237int chartdldr_pi::GetAPIVersionMajor() {
return MY_API_VERSION_MAJOR; }
239int chartdldr_pi::GetAPIVersionMinor() {
return MY_API_VERSION_MINOR; }
241int chartdldr_pi::GetPlugInVersionMajor() {
return PLUGIN_VERSION_MAJOR; }
243int chartdldr_pi::GetPlugInVersionMinor() {
return PLUGIN_VERSION_MINOR; }
247wxString chartdldr_pi::GetCommonName() {
return _(
"ChartDownloader"); }
249wxString chartdldr_pi::GetShortDescription() {
250 return _(
"Chart Downloader PlugIn for OpenCPN");
253wxString chartdldr_pi::GetLongDescription() {
255 "Chart Downloader PlugIn for OpenCPN\n\
256Manages chart downloads and updates from sources supporting\n\
257NOAA Chart Catalog format");
260void chartdldr_pi::OnSetupOptions(
void) {
262 AddOptionsPage(PI_OPTIONS_PARENT_CHARTS, _(
"Chart Downloader"));
263 if (!m_pOptionsPage) {
265 _T(
"Error: chartdldr_pi::OnSetupOptions AddOptionsPage failed!"));
268 wxBoxSizer *sizer =
new wxBoxSizer(wxVERTICAL);
269 m_pOptionsPage->SetSizer(sizer);
273 wxDefaultSize, wxDEFAULT_DIALOG_STYLE);
275 m_pOptionsPage->InvalidateBestSize();
276 sizer->Add(m_dldrpanel, 1, wxALL | wxEXPAND);
277 m_dldrpanel->SetBulkUpdate(m_allow_bulk_update);
278 m_dldrpanel->FitInside();
281void chartdldr_pi::OnCloseToolboxPanel(
int page_sel,
int ok_apply_cancel) {
283 m_dldrpanel->CancelDownload();
284#ifndef __OCPN__ANDROID__
285 OCPN_cancelDownloadFileBackground(
288 m_selected_source = m_dldrpanel->GetSelectedCatalog();
292bool chartdldr_pi::LoadConfig(
void) {
293 wxFileConfig *pConf = (wxFileConfig *)m_pconfig;
296 pConf->SetPath(_T (
"/Settings/ChartDnldr" ));
297 pConf->Read(_T (
"ChartSources" ), &m_schartdldr_sources, wxEmptyString);
298 pConf->Read(_T (
"Source" ), &m_selected_source, -1);
300 wxFileName fn(GetWritableDocumentsDir(), wxEmptyString);
301 fn.AppendDir(_T(CHART_DIR));
303 pConf->Read(_T (
"BaseChartDir" ), &m_base_chart_dir, fn.GetPath());
304 wxLogMessage(_T (
"chartdldr_pi:m_base_chart_dir: " ) + m_base_chart_dir);
307 wxFileName testFN(m_base_chart_dir);
308 if (!IsDLDirWritable(testFN)) {
310 "Cannot write to m_base_chart_dir, override to "
311 "GetWritableDocumentsDir()");
312 m_base_chart_dir = fn.GetPath();
313 wxLogMessage(_T (
"chartdldr_pi: Corrected: " ) + m_base_chart_dir);
316 pConf->Read(_T (
"PreselectNew" ), &m_preselect_new,
true);
317 pConf->Read(_T (
"PreselectUpdated" ), &m_preselect_updated,
true);
318 pConf->Read(_T (
"AllowBulkUpdate" ), &m_allow_bulk_update,
false);
324bool chartdldr_pi::SaveConfig(
void) {
325 wxFileConfig *pConf = (wxFileConfig *)m_pconfig;
327 m_schartdldr_sources.Clear();
329 for (
size_t i = 0; i < m_ChartSources.size(); i++) {
330 std::unique_ptr<ChartSource> &cs = m_ChartSources.at(i);
331 m_schartdldr_sources.Append(
332 wxString::Format(_T(
"%s|%s|%s|"), cs->GetName().c_str(),
333 cs->GetUrl().c_str(), cs->GetDir().c_str()));
337 pConf->SetPath(_T (
"/Settings/ChartDnldr" ));
338 pConf->Write(_T (
"ChartSources" ), m_schartdldr_sources);
339 pConf->Write(_T (
"Source" ), m_selected_source);
340 pConf->Write(_T (
"BaseChartDir" ), m_base_chart_dir);
341 pConf->Write(_T (
"PreselectNew" ), m_preselect_new);
342 pConf->Write(_T (
"PreselectUpdated" ), m_preselect_updated);
343 pConf->Write(_T (
"AllowBulkUpdate" ), m_allow_bulk_update);
350void SetBackColor(wxWindow *ctrl, wxColour col) {
351 static int depth = 0;
354 ctrl->SetBackgroundColour(col);
357 wxWindowList kids = ctrl->GetChildren();
358 for (
unsigned int i = 0; i < kids.GetCount(); i++) {
359 wxWindowListNode *node = kids.Item(i);
360 wxWindow *win = node->GetData();
362 if (
dynamic_cast<wxListBox *
>(win))
363 dynamic_cast<wxListBox *
>(win)->SetBackgroundColour(col);
365 else if (
dynamic_cast<wxTextCtrl *
>(win))
366 dynamic_cast<wxTextCtrl *
>(win)->SetBackgroundColour(col);
371 else if (
dynamic_cast<wxChoice *
>(win))
372 dynamic_cast<wxChoice *
>(win)->SetBackgroundColour(col);
374 else if (
dynamic_cast<wxComboBox *
>(win))
375 dynamic_cast<wxComboBox *
>(win)->SetBackgroundColour(col);
377 else if (
dynamic_cast<wxRadioButton *
>(win))
378 dynamic_cast<wxRadioButton *
>(win)->SetBackgroundColour(col);
380 else if (
dynamic_cast<wxScrolledWindow *
>(win)) {
381 dynamic_cast<wxScrolledWindow *
>(win)->SetBackgroundColour(col);
384 else if (
dynamic_cast<wxButton *
>(win)) {
385 dynamic_cast<wxButton *
>(win)->SetBackgroundColour(col);
392 if (win->GetChildren().GetCount() > 0) {
395 SetBackColor(w, col);
401void chartdldr_pi::ShowPreferencesDialog(wxWindow *parent) {
407#ifdef __OCPN__ANDROID__
408 if (m_parent_window) {
409 int xmax = m_parent_window->GetSize().GetWidth();
410 int ymax = m_parent_window->GetParent()
413 dialog->SetSize(xmax, ymax);
419 wxColour cl = wxColour(214, 218, 222);
420 SetBackColor(dialog, cl);
423 dialog->SetPath(m_base_chart_dir);
424 dialog->SetPreferences(m_preselect_new, m_preselect_updated,
425 m_allow_bulk_update);
432 m_base_chart_dir = dialog->GetPath();
433 dialog->GetPreferences(m_preselect_new, m_preselect_updated,
434 m_allow_bulk_update);
436 if (m_dldrpanel) m_dldrpanel->SetBulkUpdate(m_allow_bulk_update);
439bool getDisplayMetrics() {
440#ifdef __OCPN__ANDROID__
445 QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod(
446 "org/qtproject/qt5/android/QtNative",
"activity",
447 "()Landroid/app/Activity;");
449 if (!activity.isValid()) {
454 QAndroidJniObject data =
455 activity.callObjectMethod(
"getDisplayMetrics",
"()Ljava/lang/String;");
457 wxString return_string;
458 jstring s = data.object<jstring>();
462 if (java_vm->GetEnv((
void **)&jenv, JNI_VERSION_1_6) != JNI_OK) {
465 const char *ret_string = (jenv)->GetStringUTFChars(s, NULL);
466 return_string = wxString(ret_string, wxConvUTF8);
471 return_string.Replace(_T(
","), _T(
"."));
479 double density = 1.0;
480 wxStringTokenizer tk(return_string, _T(
";"));
481 if (tk.HasMoreTokens()) {
482 wxString token = tk.GetNextToken();
483 token = tk.GetNextToken();
485 long b = ::wxGetDisplaySize().y;
486 token.ToDouble(&density);
488 token = tk.GetNextToken();
490 token = tk.GetNextToken();
491 token = tk.GetNextToken();
492 token = tk.GetNextToken();
493 token = tk.GetNextToken();
494 token = tk.GetNextToken();
495 token = tk.GetNextToken();
497 token = tk.GetNextToken();
505 double ldpi = 160. * density;
514 g_androidDPmm = ldpi / 25.4;
525ChartSource::ChartSource(wxString name, wxString url, wxString localdir) {
529 m_update_data.clear();
532ChartSource::~ChartSource() { m_update_data.clear(); }
534#define ID_MNU_SELALL 2001
535#define ID_MNU_DELALL 2002
536#define ID_MNU_INVSEL 2003
537#define ID_MNU_SELUPD 2004
538#define ID_MNU_SELNEW 2005
540enum { ThreadId = wxID_HIGHEST + 1 };
546 switch (evt.GetId()) {
548 CheckAllCharts(
true);
551 CheckAllCharts(
false);
554 InvertCheckAllCharts();
557 CheckUpdatedCharts(
true);
560 CheckNewCharts(
true);
565void ChartDldrPanelImpl::OnContextMenu(wxMouseEvent &event) {
568 wxPoint mouseScreen = wxGetMousePosition();
569 wxPoint mouseClient = ScreenToClient(mouseScreen);
571#ifdef __OCPN__ANDROID__
575 wxMenuItem *item1 =
new wxMenuItem(&menu, ID_MNU_SELALL, _(
"Select all"));
579 wxMenuItem *item2 =
new wxMenuItem(&menu, ID_MNU_DELALL, _(
"Deselect all"));
584 new wxMenuItem(&menu, ID_MNU_INVSEL, _(
"Invert selection"));
588 wxMenuItem *item4 =
new wxMenuItem(&menu, ID_MNU_SELUPD, _(
"Select updated"));
592 wxMenuItem *item5 =
new wxMenuItem(&menu, ID_MNU_SELNEW, _(
"Select new"));
598 menu.Append(ID_MNU_SELALL, _(
"Select all"), wxT(
""));
599 menu.Append(ID_MNU_DELALL, _(
"Deselect all"), wxT(
""));
600 menu.Append(ID_MNU_INVSEL, _(
"Invert selection"), wxT(
""));
601 menu.Append(ID_MNU_SELUPD, _(
"Select updated"), wxT(
""));
602 menu.Append(ID_MNU_SELNEW, _(
"Select new"), wxT(
""));
606 menu.Connect(wxEVT_COMMAND_MENU_SELECTED,
607 (wxObjectEventFunction)&ChartDldrPanelImpl::OnPopupClick, NULL,
610 PopupMenu(&menu, mouseClient.x, mouseClient.y);
613void ChartDldrPanelImpl::OnShowLocalDir(wxCommandEvent &event) {
614 if (pPlugIn->m_pChartSource == NULL)
return;
616 wxExecute(wxString::Format(_T(
"xdg-open %s"),
617 pPlugIn->m_pChartSource->GetDir().c_str()));
620 wxExecute(wxString::Format(_T(
"open %s"),
621 pPlugIn->m_pChartSource->GetDir().c_str()));
624 wxExecute(wxString::Format(_T(
"explorer %s"),
625 pPlugIn->m_pChartSource->GetDir().c_str()));
629void ChartDldrPanelImpl::SetSource(
int id) {
630 pPlugIn->SetSourceId(
id);
632 m_bDeleteSource->Enable(
id >= 0);
633 m_bUpdateChartList->Enable(
id >= 0);
634 m_bEditSource->Enable(
id >= 0);
639 if (
id >= 0 &&
id < (
int)pPlugIn->m_ChartSources.size()) {
640 ::wxBeginBusyCursor();
642 std::unique_ptr<ChartSource> &cs = pPlugIn->m_ChartSources.at(
id);
643 cs->LoadUpdateData();
644 cs->UpdateLocalFiles();
645 pPlugIn->m_pChartSource = cs.get();
646 FillFromFile(cs->GetUrl(), cs->GetDir(), pPlugIn->m_preselect_new,
647 pPlugIn->m_preselect_updated);
648 wxURI url(cs->GetUrl());
649 m_chartsLabel->SetLabel(wxString::Format(
650 _(
"Charts: %s"), (cs->GetName() + _(
" from ") + url.BuildURI() +
651 _T(
" -> ") + cs->GetDir())
653 if (::wxIsBusy()) ::wxEndBusyCursor();
655 pPlugIn->m_pChartSource = NULL;
656 m_chartsLabel->SetLabel(_(
"Charts"));
660void ChartDldrPanelImpl::SelectSource(wxListEvent &event) {
661 int i = GetSelectedCatalog();
662 if (i >= 0) SetSource(i);
666void ChartDldrPanelImpl::SetBulkUpdate(
bool bulk_update) {
667 m_bUpdateAllCharts->Enable(bulk_update);
668 m_bUpdateAllCharts->Show(bulk_update);
673void ChartDldrPanelImpl::CleanForm() {
674#if defined(CHART_LIST)
677 m_scrollWinChartList->ClearBackground();
682void ChartDldrPanelImpl::FillFromFile(wxString url, wxString dir,
bool selnew,
685 wxStringTokenizer tk(url, _T(
"/"));
688 file = tk.GetNextToken();
689 }
while (tk.HasMoreTokens());
691 fn.SetFullName(file);
693 wxString path = fn.GetFullPath();
694 if (wxFileExists(path)) {
695 pPlugIn->m_pChartCatalog.LoadFromFile(path);
702#if !defined(CHART_LIST)
704 m_panelArray.clear();
705 m_scrollWinChartList->ClearBackground();
708 for (
size_t i = 0; i < pPlugIn->m_pChartCatalog.charts.size(); i++) {
713 pPlugIn->m_pChartCatalog.charts.at(i)->GetChartFilename(
true);
714 if (!pPlugIn->m_pChartSource->ExistsLocaly(
715 pPlugIn->m_pChartCatalog.charts.at(i)->number, file)) {
718 if (selnew) bcheck =
true;
720 if (pPlugIn->m_pChartSource->IsNewerThanLocal(
721 pPlugIn->m_pChartCatalog.charts.at(i)->number, file,
722 pPlugIn->m_pChartCatalog.charts.at(i)->GetUpdateDatetime())) {
724 status = _(
"Out of date");
725 if (selupd) bcheck =
true;
727 status = _(
"Up to date");
731 pPlugIn->m_pChartCatalog.charts.at(i)->GetUpdateDatetime().Format(
734#if defined(CHART_LIST)
735 wxVector<wxVariant> data;
736 data.push_back(wxVariant(bcheck));
737 data.push_back(wxVariant(status));
738 data.push_back(wxVariant(latest));
740 wxVariant(pPlugIn->m_pChartCatalog.charts.at(i)->GetChartTitle()));
741 getChartList()->AppendItem(data);
743 auto pC = std::make_unique<ChartPanel>(
744 m_scrollWinChartList, wxID_ANY, wxDefaultPosition, wxSize(-1, -1),
745 pPlugIn->m_pChartCatalog.charts.at(i)->GetChartTitle(), status,
746 latest,
this, bcheck);
747 pC->Connect(wxEVT_RIGHT_DOWN,
748 wxMouseEventHandler(ChartDldrPanel::OnContextMenu), NULL,
751 m_boxSizerCharts->Add(pC.get(), 0, wxEXPAND | wxLEFT | wxRIGHT, 2);
752 m_panelArray.push_back(std::move(pC));
756#if !defined(CHART_LIST)
757 m_scrollWinChartList->ClearBackground();
758 m_scrollWinChartList->FitInside();
759 m_scrollWinChartList->GetSizer()->Layout();
761 m_scrollWinChartList->ClearBackground();
762 SetChartInfo(wxString::Format(_(
"%lu charts total, %lu updated, %lu new"),
763 pPlugIn->m_pChartCatalog.charts.size(),
764 m_updatedCharts, m_newCharts));
766 SetChartInfo(wxString::Format(
767 _(
"%lu charts total, %lu updated, %lu new, %lu selected"),
768 pPlugIn->m_pChartCatalog.charts.size(), m_updatedCharts, m_newCharts,
769 GetCheckedChartCount()));
774bool ChartSource::ExistsLocaly(wxString chart_number, wxString filename) {
777 wxStringTokenizer tk(filename, _T(
"."));
778 wxString file = tk.GetNextToken().MakeLower();
780 if (!m_update_data.empty()) {
781 return m_update_data.find(std::string(chart_number.Lower().mb_str())) !=
782 m_update_data.end() ||
783 m_update_data.find(std::string(file.mb_str())) !=
786 for (
size_t i = 0; i < m_localfiles.Count(); i++) {
787 if (m_localfiles.Item(i) == file)
return true;
792bool ChartSource::IsNewerThanLocal(wxString chart_number, wxString filename,
793 wxDateTime validDate) {
794 wxStringTokenizer tk(filename, _T(
"."));
795 wxString file = tk.GetNextToken().MakeLower();
796 if (!m_update_data.empty()) {
797 if (m_update_data[std::string(chart_number.Lower().mbc_str())] <
798 validDate.GetTicks() &&
799 m_update_data[std::string(file.mbc_str())] < validDate.GetTicks())
804 bool update_candidate =
false;
806 for (
size_t i = 0; i < m_localfiles.Count(); i++) {
807 if (m_localfiles.Item(i) == file) {
808 if (validDate.IsLaterThan(m_localdt.at(i))) {
809 update_candidate =
true;
814 return update_candidate;
817int ChartDldrPanelImpl::GetSelectedCatalog() {
819 m_lbChartSources->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
823void ChartDldrPanelImpl::SelectCatalog(
int item) {
825 m_bDeleteSource->Enable();
826 m_bEditSource->Enable();
827 m_bUpdateChartList->Enable();
829 m_bDeleteSource->Disable();
830 m_bEditSource->Disable();
831 m_bUpdateChartList->Disable();
833 m_lbChartSources->SetItemState(item, wxLIST_STATE_SELECTED,
834 wxLIST_STATE_SELECTED);
837void ChartDldrPanelImpl::AppendCatalog(std::unique_ptr<ChartSource> &cs) {
838 long id = m_lbChartSources->GetItemCount();
839 m_lbChartSources->InsertItem(
id, cs->GetName());
840 m_lbChartSources->SetItem(
id, 1, _(
"(Please update first)"));
841 m_lbChartSources->SetItem(
id, 2, cs->GetDir());
842 wxURI url(cs->GetUrl());
843 if (url.IsReference()) {
844 OCPNMessageBox_PlugIn(
845 this, _(
"Error, the URL to the chart source data seems wrong."),
849 wxFileName fn(url.GetPath());
850 fn.SetPath(cs->GetDir());
851 wxString path = fn.GetFullPath();
852 if (wxFileExists(path)) {
853 if (pPlugIn->m_pChartCatalog.LoadFromFile(path,
true)) {
854 m_lbChartSources->SetItem(
id, 0, pPlugIn->m_pChartCatalog.title);
855 m_lbChartSources->SetItem(
857 pPlugIn->m_pChartCatalog.GetReleaseDate().Format(
858 _T(
"%Y-%m-%d %H:%M")));
859 m_lbChartSources->SetItem(
id, 2, path);
860#ifdef __OCPN__ANDROID__
861 m_lbChartSources->GetHandle()->resizeColumnToContents(0);
862 m_lbChartSources->GetHandle()->resizeColumnToContents(1);
863 m_lbChartSources->GetHandle()->resizeColumnToContents(2);
869void ChartDldrPanelImpl::UpdateAllCharts(wxCommandEvent &event) {
870 int failed_to_update = 0;
871 int attempted_to_update = 0;
872 if ((pPlugIn->m_preselect_new) && (pPlugIn->m_preselect_updated)) {
873 wxMessageDialog mess(
875 _(
"You have chosen to update all chart catalogs.\nThen download all "
876 "new and updated charts.\nThis may take a long time."),
877 _(
"Chart Downloader"), wxOK | wxCANCEL);
878 if (mess.ShowModal() == wxID_CANCEL)
return;
879 }
else if (pPlugIn->m_preselect_new) {
880 wxMessageDialog mess(
882 _(
"You have chosen to update all chart catalogs.\nThen download only "
883 "new (but not updated) charts.\nThis may take a long time."),
884 _(
"Chart Downloader"), wxOK | wxCANCEL);
885 if (mess.ShowModal() == wxID_CANCEL)
return;
886 }
else if (pPlugIn->m_preselect_updated) {
887 wxMessageDialog mess(
889 _(
"You have chosen to update all chart catalogs.\nThen download only "
890 "updated (but not new) charts.\nThis may take a long time."),
891 _(
"Chart Downloader"), wxOK | wxCANCEL);
892 if (mess.ShowModal() == wxID_CANCEL)
return;
897 int oldPage = m_DLoadNB->SetSelection(1);
898 for (
long chartIndex = 0; chartIndex < m_lbChartSources->GetItemCount();
900 m_lbChartSources->SetItemState(chartIndex, wxLIST_STATE_SELECTED,
901 wxLIST_STATE_SELECTED);
902 if (cancelled)
break;
903 UpdateChartList(event);
905 attempted_to_update += m_downloading;
906 failed_to_update += m_failed_downloads;
908 wxLogMessage(wxString::Format(
909 _T(
"chartdldr_pi::UpdateAllCharts() downloaded %d out of %d charts."),
910 attempted_to_update - failed_to_update, attempted_to_update));
911 if (failed_to_update > 0)
912 OCPNMessageBox_PlugIn(
914 wxString::Format(_(
"%d out of %d charts failed to download.\nCheck the "
915 "list, verify there is a working Internet "
916 "connection and repeat the operation if needed."),
917 failed_to_update, attempted_to_update),
918 _(
"Chart Downloader"), wxOK | wxICON_ERROR);
919 if (attempted_to_update > failed_to_update) ForceChartDBUpdate();
923 m_DLoadNB->SetSelection(oldPage);
926void ChartDldrPanelImpl::UpdateChartList(wxCommandEvent &event) {
928 if (!m_lbChartSources->GetSelectedItemCount())
return;
929 std::unique_ptr<ChartSource> &cs =
930 pPlugIn->m_ChartSources.at(GetSelectedCatalog());
931 wxURI url(cs->GetUrl());
932 if (url.IsReference()) {
933 OCPNMessageBox_PlugIn(
934 this, _(
"Error, the URL to the chart source data seems wrong."),
939 wxStringTokenizer tk(url.GetPath(), _T(
"/"));
942 file = tk.GetNextToken();
943 }
while (tk.HasMoreTokens());
945 fn.SetFullName(file);
946 fn.SetPath(cs->GetDir());
947 if (!wxDirExists(cs->GetDir())) {
948 if (!wxFileName::Mkdir(cs->GetDir(), 0755, wxPATH_MKDIR_FULL)) {
949 OCPNMessageBox_PlugIn(
951 wxString::Format(_(
"Directory %s can't be created."),
952 cs->GetDir().c_str()),
953 _(
"Chart Downloader"));
960#ifdef __OCPN__ANDROID__
961 wxString file_URI = _T(
"file://") + fn.GetFullPath();
972 _OCPN_DLStatus ret = OCPN_downloadFile(
973 cs->GetUrl(), file_URI, _(
"Downloading file"),
974 _(
"Reading Headers: ") + url.BuildURI(), wxNullBitmap,
this,
983 wxFileName tfn = wxFileName::CreateTempFileName(fn.GetFullPath());
984 wxString file_URI = tfn.GetFullPath();
986 _OCPN_DLStatus ret = OCPN_downloadFile(
987 cs->GetUrl(), file_URI, _(
"Downloading file"),
988 _(
"Reading Headers: ") + url.BuildURI(), wxNullBitmap,
this,
995 bok = wxCopyFile(tfn.GetFullPath(), fn.GetFullPath());
996 wxRemoveFile(tfn.GetFullPath());
1003 case OCPN_DL_NO_ERROR: {
1005 long id = GetSelectedCatalog();
1008 m_lbChartSources->SetItem(
id, 0, pPlugIn->m_pChartCatalog.title);
1009 m_lbChartSources->SetItem(
1011 pPlugIn->m_pChartCatalog.GetReleaseDate().Format(
1012 _T(
"%Y-%m-%d %H:%M")));
1013 m_lbChartSources->SetItem(
id, 2, cs->GetDir());
1016 OCPNMessageBox_PlugIn(
1018 wxString::Format(_(
"Failed to Find New Catalog: %s "),
1019 url.BuildURI().c_str()),
1020 _(
"Chart Downloader"), wxOK | wxICON_ERROR);
1023 case OCPN_DL_FAILED: {
1024 OCPNMessageBox_PlugIn(
1026 wxString::Format(_(
"Failed to Download Catalog: %s \nVerify there is "
1027 "a working Internet connection."),
1028 url.BuildURI().c_str()),
1029 _(
"Chart Downloader"), wxOK | wxICON_ERROR);
1033 case OCPN_DL_USER_TIMEOUT:
1034 case OCPN_DL_ABORTED: {
1039 case OCPN_DL_UNKNOWN:
1040 case OCPN_DL_STARTED: {
1049 if ((ret == OCPN_DL_NO_ERROR) && bok) m_DLoadNB->SetSelection(1);
1052void ChartSource::GetLocalFiles() {
1053 if (!UpdateDataExists() || m_update_data.empty()) {
1054 wxArrayString *allFiles =
new wxArrayString;
1055 if (wxDirExists(GetDir())) wxDir::GetAllFiles(GetDir(), allFiles);
1057 m_localfiles.Clear();
1058 wxDateTime ct, mt, at;
1060 for (
size_t i = 0; i < allFiles->Count(); i++) {
1061 wxFileName fn(allFiles->Item(i));
1062 name = fn.GetFullName().Lower();
1066 if (!ExistsLocaly(wxEmptyString, name)) {
1067 fn.GetTimes(&at, &mt, &ct);
1068 m_localdt.push_back(mt);
1069 m_localfiles.Add(fn.GetName().Lower());
1071 wxStringTokenizer tk(name, _T(
"."));
1072 wxString file = tk.GetNextToken().MakeLower();
1073 m_update_data[std::string(file.mbc_str())] = mt.GetTicks();
1084bool ChartSource::UpdateDataExists() {
1085 return wxFileExists(GetDir() + wxFileName::GetPathSeparator() +
1086 _T(UPDATE_DATA_FILENAME));
1089void ChartSource::LoadUpdateData() {
1090 m_update_data.clear();
1092 GetDir() + wxFileName::GetPathSeparator() + _T(UPDATE_DATA_FILENAME);
1094 if (!wxFileExists(fn))
return;
1096 std::ifstream infile(fn.mb_str());
1101 while (infile >> key >> value) m_update_data[key] = value;
1106void ChartSource::SaveUpdateData() {
1108 fn = GetDir() + wxFileName::GetPathSeparator() + _T(UPDATE_DATA_FILENAME);
1110#ifdef __OCPN__ANDROID__
1111 fn = AndroidGetCacheDir() + wxFileName::GetPathSeparator() +
1112 _T(UPDATE_DATA_FILENAME);
1115 std::ofstream outfile(fn.mb_str());
1116 if (!outfile.is_open())
return;
1118 std::map<std::string, time_t>::iterator iter;
1119 for (iter = m_update_data.begin(); iter != m_update_data.end(); ++iter) {
1120 if (iter->first.find(
" ") == std::string::npos)
1121 if (!iter->first.empty())
1122 outfile << iter->first <<
" " << iter->second <<
"\n";
1127#ifdef __OCPN__ANDROID__
1128 AndroidSecureCopyFile(
1129 fn, GetDir() + wxFileName::GetPathSeparator() + _T(UPDATE_DATA_FILENAME));
1133void ChartSource::ChartUpdated(wxString chart_number, time_t timestamp) {
1134 m_update_data[std::string(chart_number.Lower().mb_str())] = timestamp;
1138bool ChartDldrPanelImpl::DownloadChart(wxString url, wxString file,
1143void ChartDldrPanelImpl::DisableForDownload(
bool enabled) {
1144 m_bAddSource->Enable(enabled);
1145 m_bDeleteSource->Enable(enabled);
1146 m_bEditSource->Enable(enabled);
1147 m_bUpdateAllCharts->Enable(enabled);
1148 m_bUpdateChartList->Enable(enabled);
1149 m_lbChartSources->Enable(enabled);
1150#if defined(CHART_LIST)
1151 m_bSelectNew->Enable(enabled);
1152 m_bSelectUpdated->Enable(enabled);
1153 m_bSelectAll->Enable(enabled);
1157void ChartDldrPanelImpl::OnDownloadCharts(wxCommandEvent &event) {
1158 if (DownloadIsCancel) {
1164#if defined(CHART_LIST)
1165void ChartDldrPanelImpl::OnSelectChartItem(wxCommandEvent &event) {
1167 SetChartInfo(wxString::Format(
1168 _(
"%lu charts total, %lu updated, %lu new, %lu selected"),
1169 pPlugIn->m_pChartCatalog.charts.size(), m_updatedCharts, m_newCharts,
1170 GetCheckedChartCount()));
1175#if defined(CHART_LIST)
1176void ChartDldrPanelImpl::OnSelectNewCharts(wxCommandEvent &event) {
1177 CheckNewCharts(
true);
1181#if defined(CHART_LIST)
1182void ChartDldrPanelImpl::OnSelectUpdatedCharts(wxCommandEvent &event) {
1183 CheckUpdatedCharts(
true);
1187#if defined(CHART_LIST)
1188void ChartDldrPanelImpl::OnSelectAllCharts(wxCommandEvent &event) {
1189 if (m_bSelectAll->GetLabel() == _(
"Select All")) {
1190 CheckAllCharts(
true);
1191 m_bSelectAll->SetLabel(_(
"Select None"));
1192 m_bSelectAll->SetToolTip(_(
"De-select all charts in the list."));
1194 CheckAllCharts(
false);
1195 m_bSelectAll->SetLabel(_(
"Select All"));
1196 m_bSelectAll->SetToolTip(_(
"Select all charts in the list."));
1201int ChartDldrPanelImpl::GetChartCount() {
1202#if defined(CHART_LIST)
1203 return getChartList()->GetItemCount();
1205 return m_panelArray.size();
1209int ChartDldrPanelImpl::GetCheckedChartCount() {
1210#if defined(CHART_LIST)
1212 int chartCnt = GetChartCount();
1213 for (
int i = 0; i < chartCnt; i++)
1214 if (isChartChecked(i)) cnt++;
1217 for (
int i = 0; i < GetChartCount(); i++) {
1218 if (m_panelArray.at(i)->GetCB()->IsChecked()) cnt++;
1224bool ChartDldrPanelImpl::isChartChecked(
int i) {
1225 wxASSERT_MSG(i >= 0,
1226 wxT(
"This function should be called with non-negative index."));
1227 if (i <= GetChartCount())
1228#if defined(CHART_LIST)
1229 return getChartList()->GetToggleValue(i, 0);
1231 return m_panelArray.at(i)->GetCB()->IsChecked();
1237void ChartDldrPanelImpl::CheckAllCharts(
bool value) {
1238#if defined(CHART_LIST)
1242 for (
int i = 0; i < GetChartCount(); i++) {
1243#if defined(CHART_LIST)
1244 getChartList()->SetToggleValue(value, i, 0);
1246 m_panelArray.at(i)->GetCB()->SetValue(value);
1249#if defined(CHART_LIST)
1250 SetChartInfo(wxString::Format(
1251 _(
"%lu charts total, %lu updated, %lu new, %lu selected"),
1252 pPlugIn->m_pChartCatalog.charts.size(), m_updatedCharts, m_newCharts,
1253 GetCheckedChartCount()));
1254 m_bInfoHold =
false;
1258void ChartDldrPanelImpl::CheckNewCharts(
bool value) {
1259 for (
int i = 0; i < GetChartCount(); i++) {
1260#if defined(CHART_LIST)
1261 if (isNew(i)) getChartList()->SetToggleValue(
true, i, 0);
1263 if (m_panelArray.at(i)->isNew())
1264 m_panelArray.at(i)->GetCB()->SetValue(value);
1267#if defined(CHART_LIST)
1268 SetChartInfo(wxString::Format(
1269 _(
"%lu charts total, %lu updated, %lu new, %lu selected"),
1270 pPlugIn->m_pChartCatalog.charts.size(), m_updatedCharts, m_newCharts,
1271 GetCheckedChartCount()));
1275void ChartDldrPanelImpl::CheckUpdatedCharts(
bool value) {
1276 for (
int i = 0; i < GetChartCount(); i++) {
1277#if defined(CHART_LIST)
1278 if (isUpdated(i)) getChartList()->SetToggleValue(value, i, 0);
1280 if (m_panelArray.at(i)->isUpdated())
1281 m_panelArray.at(i)->GetCB()->SetValue(value);
1284#if defined(CHART_LIST)
1285 SetChartInfo(wxString::Format(
1286 _(
"%lu charts total, %lu updated, %lu new, %lu selected"),
1287 pPlugIn->m_pChartCatalog.charts.size(), m_updatedCharts, m_newCharts,
1288 GetCheckedChartCount()));
1292void ChartDldrPanelImpl::InvertCheckAllCharts() {
1293#if defined(CHART_LIST)
1296 for (
int i = 0; i < GetChartCount(); i++)
1297#
if defined(CHART_LIST)
1298 getChartList()->SetToggleValue(!isChartChecked(i), i, 0);
1300 m_panelArray.at(i)->GetCB()->SetValue(!isChartChecked(i));
1302#if defined(CHART_LIST)
1303 m_bInfoHold =
false;
1304 SetChartInfo(wxString::Format(
1305 _(
"%lu charts total, %lu updated, %lu new, %lu selected"),
1306 pPlugIn->m_pChartCatalog.charts.size(), m_updatedCharts, m_newCharts,
1307 GetCheckedChartCount()));
1312 if (!m_bconnected) {
1314 wxEVT_DOWNLOAD_EVENT,
1315 (wxObjectEventFunction)(wxEventFunction)&ChartDldrPanelImpl::onDLEvent);
1316 m_bconnected =
true;
1319 if (!GetCheckedChartCount() && !updatingAll) {
1320 OCPNMessageBox_PlugIn(
this, _(
"No charts selected for download."));
1323 std::unique_ptr<ChartSource> &cs =
1324 pPlugIn->m_ChartSources.at(GetSelectedCatalog());
1327 to_download = GetCheckedChartCount();
1329 m_failed_downloads = 0;
1330 DisableForDownload(
false);
1332 m_bDnldCharts->SetLabel(_(
"Abort download"));
1333 DownloadIsCancel =
true;
1335 wxFileName downloaded_p;
1338 for (
int i = 0; i < GetChartCount() && to_download; i++) {
1340 if (cancelled)
break;
1342 if (!isChartChecked(i))
continue;
1343 m_bTransferComplete =
false;
1344 m_bTransferSuccess =
true;
1345 m_totalsize = _(
"Unknown");
1346 m_transferredsize = _T(
"0");
1348 if (pPlugIn->m_pChartCatalog.charts.at(index)->NeedsManualDownload()) {
1350 OCPNMessageBox_PlugIn(
1353 _(
"The selected chart '%s' can't be downloaded automatically, do you want me to open a browser window and download them manually?\n\n \
1354After downloading the charts, please extract them to %s"),
1355 pPlugIn->m_pChartCatalog.charts.at(index)->title.c_str(),
1356 pPlugIn->m_pChartSource->GetDir().c_str()),
1357 _(
"Chart Downloader"), wxYES_NO | wxCENTRE | wxICON_QUESTION)) {
1358 wxLaunchDefaultBrowser(
1359 pPlugIn->m_pChartCatalog.charts.at(index)->GetManualDownloadUrl());
1365 wxURI url(pPlugIn->m_pChartCatalog.charts.at(index)->GetDownloadLocation());
1366 if (url.IsReference()) {
1367 OCPNMessageBox_PlugIn(
1370 _(
"Error, the URL to the chart (%s) data seems wrong."),
1371 url.BuildURI().c_str()),
1379 pPlugIn->m_pChartCatalog.charts.at(index)->GetChartFilename();
1381 fn.SetFullName(file);
1382 fn.SetPath(cs->GetDir());
1383 wxString path = fn.GetFullPath();
1384 if (wxFileExists(path)) wxRemoveFile(path);
1385 wxString title = pPlugIn->m_pChartCatalog.charts.at(index)->GetChartTitle();
1388#ifdef __OCPN__ANDROID__
1389 wxString file_path = _T(
"file://") + fn.GetFullPath();
1391 wxString file_path = fn.GetFullPath();
1395 OCPN_downloadFileBackground(url.BuildURI(), file_path,
this, &handle);
1398 if (pPlugIn->ProcessFile(
1399 downloaded_p.GetFullPath(), downloaded_p.GetPath(),
true,
1400 pPlugIn->m_pChartCatalog.charts.at(idx)->GetUpdateDatetime())) {
1401 cs->ChartUpdated(pPlugIn->m_pChartCatalog.charts.at(idx)->number,
1402 pPlugIn->m_pChartCatalog.charts.at(idx)
1403 ->GetUpdateDatetime()
1406 m_failed_downloads++;
1411 while (!m_bTransferComplete && m_bTransferSuccess && !cancelled) {
1412 if (m_failed_downloads)
1413 SetChartInfo(wxString::Format(
1414 _(
"Downloading chart %u of %u, %u downloads failed (%s / %s)"),
1415 m_downloading, to_download, m_failed_downloads,
1416 m_transferredsize.c_str(), m_totalsize.c_str()));
1418 SetChartInfo(wxString::Format(
1419 _(
"Downloading chart %u of %u (%s / %s)"), m_downloading,
1420 to_download, m_transferredsize.c_str(), m_totalsize.c_str()));
1425 wxTheApp->ProcessPendingEvents();
1432 OCPN_cancelDownloadFileBackground(handle);
1435 if (m_bTransferSuccess && !cancelled) {
1437 downloaded_p = path;
1440 if (wxFileExists(path)) wxRemoveFile(path);
1441 m_failed_downloads++;
1445 if (pPlugIn->ProcessFile(
1446 downloaded_p.GetFullPath(), downloaded_p.GetPath(),
true,
1447 pPlugIn->m_pChartCatalog.charts.at(idx)->GetUpdateDatetime())) {
1448 cs->ChartUpdated(pPlugIn->m_pChartCatalog.charts.at(idx)->number,
1449 pPlugIn->m_pChartCatalog.charts.at(idx)
1450 ->GetUpdateDatetime()
1453 m_failed_downloads++;
1456 DisableForDownload(
true);
1457 m_bDnldCharts->SetLabel(_(
"Download selected charts"));
1458 DownloadIsCancel =
false;
1459 SetSource(GetSelectedCatalog());
1460 if (m_failed_downloads > 0 && !updatingAll && !cancelled)
1461 OCPNMessageBox_PlugIn(
1463 wxString::Format(_(
"%d out of %d charts failed to download.\nCheck the "
1464 "list, verify there is a working Internet "
1465 "connection and repeat the operation if needed."),
1466 m_failed_downloads, m_downloading),
1467 _(
"Chart Downloader"), wxOK | wxICON_ERROR);
1470 OCPNMessageBox_PlugIn(
this, _(
"Chart download cancelled."),
1471 _(
"Chart Downloader"), wxOK | wxICON_INFORMATION);
1473 if ((m_downloading - m_failed_downloads > 0) && !updatingAll)
1474 ForceChartDBUpdate();
1477ChartDldrPanelImpl::~ChartDldrPanelImpl() {
1479 wxEVT_DOWNLOAD_EVENT,
1480 (wxObjectEventFunction)(wxEventFunction)&ChartDldrPanelImpl::onDLEvent);
1481 m_bconnected =
false;
1483#ifndef __OCPN__ANDROID__
1484 OCPN_cancelDownloadFileBackground(
1487#if defined(CHART_LIST)
1492ChartDldrPanelImpl::ChartDldrPanelImpl(
chartdldr_pi *plugin, wxWindow *parent,
1493 wxWindowID
id,
const wxPoint &pos,
1494 const wxSize &size,
long style)
1496 m_bDeleteSource->Disable();
1497 m_bUpdateChartList->Disable();
1498 m_bEditSource->Disable();
1499 m_lbChartSources->InsertColumn(0, _(
"Catalog"), wxLIST_FORMAT_LEFT,
1500 CATALOGS_NAME_WIDTH);
1501 m_lbChartSources->InsertColumn(1, _(
"Released"), wxLIST_FORMAT_LEFT,
1502 CATALOGS_DATE_WIDTH);
1503 m_lbChartSources->InsertColumn(2, _(
"Local path"), wxLIST_FORMAT_LEFT,
1504 CATALOGS_PATH_WIDTH);
1505 m_lbChartSources->Enable();
1506 m_bInfoHold =
false;
1507 downloadInProgress =
false;
1511 updatingAll =
false;
1513 m_populated =
false;
1514 DownloadIsCancel =
false;
1515 m_failed_downloads = 0;
1516 SetChartInfo(wxEmptyString);
1517 m_bTransferComplete =
true;
1518 m_bTransferSuccess =
true;
1521 wxEVT_DOWNLOAD_EVENT,
1522 (wxObjectEventFunction)(wxEventFunction)&ChartDldrPanelImpl::onDLEvent);
1523 m_bconnected =
true;
1525 for (
size_t i = 0; i < pPlugIn->m_ChartSources.size(); i++) {
1526 AppendCatalog(pPlugIn->m_ChartSources.at(i));
1531void ChartDldrPanelImpl::OnPaint(wxPaintEvent &event) {
1534 for (
size_t i = 0; i < pPlugIn->m_ChartSources.size(); i++) {
1535 AppendCatalog(pPlugIn->m_ChartSources.at(i));
1540 m_lbChartSources->Refresh(
true);
1545void ChartDldrPanelImpl::DeleteSource(wxCommandEvent &event) {
1546 if (!m_lbChartSources->GetSelectedItemCount())
return;
1547 if (wxID_YES != OCPNMessageBox_PlugIn(
1549 _(
"Do you really want to remove the chart source?\nThe "
1550 "local chart files will not be removed,\nbut you will "
1551 "not be able to update the charts anymore."),
1552 _(
"Chart Downloader"), wxYES_NO | wxCENTRE))
1554 int ToBeRemoved = GetSelectedCatalog();
1555 m_lbChartSources->SetItemState(ToBeRemoved, 0,
1556 wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED);
1557 pPlugIn->m_ChartSources.erase(pPlugIn->m_ChartSources.begin() + ToBeRemoved);
1558 m_lbChartSources->DeleteItem(ToBeRemoved);
1560 pPlugIn->SetSourceId(-1);
1562 pPlugIn->SaveConfig();
1566void ChartDldrPanelImpl::AddSource(wxCommandEvent &event) {
1568 dialog->SetBasePath(pPlugIn->GetBaseChartDir());
1570 wxSize sz = GetParent()
1573 dialog->SetSize(sz.GetWidth(), sz.GetHeight());
1576#ifdef __OCPN__ANDROID__
1577 androidDisableRotation();
1580 if (dialog->ShowModal() == wxID_OK) {
1581 std::unique_ptr<ChartSource> cs =
1582 std::make_unique<ChartSource>(dialog->m_tSourceName->GetValue(),
1583 dialog->m_tChartSourceUrl->GetValue(),
1584 dialog->m_tcChartDirectory->GetValue());
1587 bool covered =
false;
1588 for (
size_t i = 0; i < GetChartDBDirArrayString().GetCount(); i++) {
1589 if (cs->GetDir().StartsWith((GetChartDBDirArrayString().Item(i)))) {
1595 wxString dir = cs->GetDir();
1596 AddChartDirectory(dir);
1599 long itemSelectedNow = GetSelectedCatalog();
1600 m_lbChartSources->SetItemState(itemSelectedNow, 0, wxLIST_STATE_SELECTED);
1602 SelectCatalog(m_lbChartSources->GetItemCount() - 1);
1603 pPlugIn->m_ChartSources.push_back(std::move(cs));
1604 pPlugIn->SaveConfig();
1606#ifdef __OCPN__ANDROID__
1607 androidEnableRotation();
1613void ChartDldrPanelImpl::DoEditSource() {
1614 if (!m_lbChartSources->GetSelectedItemCount())
return;
1615 int cat = GetSelectedCatalog();
1617 dialog->SetBasePath(pPlugIn->GetBaseChartDir());
1618 dialog->SetSourceEdit(pPlugIn->m_ChartSources.at(cat));
1619 dialog->SetTitle(_(
"Edit Chart Source"));
1621 dialog->ShowModal();
1622 int retcode = dialog->GetReturnCode();
1624 if (retcode == wxID_OK) {
1625 pPlugIn->m_ChartSources.at(cat)->SetName(
1626 dialog->m_tSourceName->GetValue());
1627 pPlugIn->m_ChartSources.at(cat)->SetUrl(
1628 dialog->m_tChartSourceUrl->GetValue());
1629 pPlugIn->m_ChartSources.at(cat)->SetDir(
1630 dialog->m_tcChartDirectory->GetValue());
1632 m_lbChartSources->SetItem(cat, 0,
1633 pPlugIn->m_ChartSources.at(cat)->GetName());
1634 m_lbChartSources->SetItem(cat, 1, _(
"(Please update first)"));
1635 m_lbChartSources->SetItem(cat, 2,
1636 pPlugIn->m_ChartSources.at(cat)->GetDir());
1637 wxURI url(pPlugIn->m_ChartSources.at(cat)->GetUrl());
1638 wxFileName fn(url.GetPath());
1639 fn.SetPath(pPlugIn->m_ChartSources.at(cat)->GetDir());
1640 wxString path = fn.GetFullPath();
1641 if (wxFileExists(path)) {
1642 if (pPlugIn->m_pChartCatalog.LoadFromFile(path,
true)) {
1643 m_lbChartSources->SetItem(cat, 0, pPlugIn->m_pChartCatalog.title);
1644 m_lbChartSources->SetItem(
1646 pPlugIn->m_pChartCatalog.GetReleaseDate().Format(
1647 _T(
"%Y-%m-%d %H:%M")));
1648 m_lbChartSources->SetItem(cat, 2, path);
1651 bool covered =
false;
1652 for (
size_t i = 0; i < GetChartDBDirArrayString().GetCount(); i++) {
1653 if (pPlugIn->m_ChartSources.at(cat)->GetDir().StartsWith(
1654 (GetChartDBDirArrayString().Item(i)))) {
1660 OCPNMessageBox_PlugIn(
1663 _(
"Path %s seems not to be covered by your configured Chart "
1664 "Directories.\nTo see the charts you have to adjust the "
1665 "configuration on the 'Chart Files' tab."),
1666 pPlugIn->m_ChartSources.at(cat)->GetDir().c_str()),
1667 _(
"Chart Downloader"));
1669 pPlugIn->SaveConfig();
1675void ChartDldrPanelImpl::EditSource(wxCommandEvent &event) {
1680void ChartDldrPanelImpl::OnLeftDClick(wxMouseEvent &event) {
1685bool chartdldr_pi::ProcessFile(
const wxString &aFile,
1686 const wxString &aTargetDir,
bool aStripPath,
1687 wxDateTime aMTime) {
1688 if (aFile.Lower().EndsWith(_T(
"zip")))
1690 bool ret = ExtractZipFiles(aFile, aTargetDir, aStripPath, aMTime,
false);
1692 wxRemoveFile(aFile);
1694 wxLogError(_T(
"chartdldr_pi: Unable to extract: ") + aFile);
1697#ifdef DLDR_USE_LIBARCHIVE
1698 else if (aFile.Lower().EndsWith(_T(
"rar"))) {
1699#ifdef CHARTDLDR_RAR_UNARR
1700 bool ret = ExtractUnarrFiles(aFile, aTargetDir, aStripPath, aMTime,
false);
1703 ExtractLibArchiveFiles(aFile, aTargetDir, aStripPath, aMTime,
false);
1706 wxRemoveFile(aFile);
1708 wxLogError(_T(
"chartdldr_pi: Unable to extract: ") + aFile);
1710 }
else if (aFile.Lower().EndsWith(_T(
"tar")) ||
1711 aFile.Lower().EndsWith(_T(
"gz")) ||
1712 aFile.Lower().EndsWith(_T(
"bz2")) ||
1713 aFile.Lower().EndsWith(_T(
"lzma")) ||
1714 aFile.Lower().EndsWith(_T(
"7z")) ||
1715 aFile.Lower().EndsWith(_T(
"xz"))) {
1717 ExtractLibArchiveFiles(aFile, aTargetDir, aStripPath, aMTime,
false);
1719 wxRemoveFile(aFile);
1721 wxLogError(_T(
"chartdldr_pi: Unable to extract: ") + aFile);
1725 else if (aFile.Lower().EndsWith(_T(
"rar")) ||
1726 aFile.Lower().EndsWith(_T(
"tar"))
1728 || aFile.Lower().EndsWith(_T(
"bz2"))
1731 || aFile.Lower().EndsWith(_T(
"gz"))
1734 || aFile.Lower().EndsWith(
1739 bool ret = ExtractUnarrFiles(aFile, aTargetDir, aStripPath, aMTime,
false);
1741 wxRemoveFile(aFile);
1743 wxLogError(_T(
"chartdldr_pi: Unable to extract: ") + aFile);
1748#ifdef __OCPN__ANDROID__
1749 else if (aFile.Lower().EndsWith(_T(
"tar")) ||
1750 aFile.Lower().EndsWith(_T(
"gz")) ||
1751 aFile.Lower().EndsWith(_T(
"bz2")) ||
1752 aFile.Lower().EndsWith(_T(
"lzma")) ||
1753 aFile.Lower().EndsWith(_T(
"7z")) ||
1754 aFile.Lower().EndsWith(_T(
"xz"))) {
1756 if (aStripPath) nStrip = 1;
1758 if (m_dldrpanel) m_dldrpanel->SetChartInfo(_(
"Installing charts."));
1760 androidShowBusyIcon();
1761 bool ret = AndroidUnzip(aFile, aTargetDir, nStrip,
true);
1762 androidHideBusyIcon();
1770 wxFileName fn(aFile);
1771 if (fn.GetPath() != aTargetDir)
1773 if (!wxDirExists(aTargetDir)) {
1774 if (wxFileName::Mkdir(aTargetDir, 0755, wxPATH_MKDIR_FULL)) {
1775 if (!wxRenameFile(aFile, aTargetDir))
return false;
1780 wxString name = fn.GetFullName();
1782 fn.Assign(aTargetDir, name);
1783 fn.SetTimes(&aMTime, &aMTime, &aMTime);
1788#ifdef DLDR_USE_LIBARCHIVE
1789#ifndef __OCPN__ANDROID__
1790static int copy_data(
struct archive *ar,
struct archive *aw) {
1794 __LA_INT64_T offset;
1797 r = archive_read_data_block(ar, &buff, &size, &offset);
1798 if (r == ARCHIVE_EOF)
return (ARCHIVE_OK);
1799 if (r < ARCHIVE_OK)
return (r);
1800 r = archive_write_data_block(aw, buff, size, offset);
1801 if (r < ARCHIVE_OK) {
1803 wxLogError(wxString::Format(
"Chartdldr_pi: LibArchive error: %s",
1804 archive_error_string(aw)));
1811bool chartdldr_pi::ExtractLibArchiveFiles(
const wxString &aArchiveFile,
1812 const wxString &aTargetDir,
1813 bool aStripPath, wxDateTime aMTime,
1814 bool aRemoveArchive) {
1815#ifndef __OCPN__ANDROID__
1817 struct archive *ext;
1818 struct archive_entry *entry;
1823 flags = ARCHIVE_EXTRACT_TIME;
1830 a = archive_read_new();
1831 archive_read_support_format_all(a);
1832 archive_read_support_filter_all(a);
1833 archive_read_support_compression_all(a);
1834 ext = archive_write_disk_new();
1835 archive_write_disk_set_options(ext, flags);
1836 archive_write_disk_set_standard_lookup(ext);
1837 if ((r = archive_read_open_filename(a, aArchiveFile.c_str(), 10240))) {
1841 r = archive_read_next_header(a, &entry);
1842 if (r == ARCHIVE_EOF)
break;
1845 wxLogError(wxString::Format(
"Chartdldr_pi: LibArchive error: %s",
1846 archive_error_string(a)));
1847 if (r < ARCHIVE_WARN)
return false;
1849 const char *currentFile = archive_entry_pathname(entry);
1850 std::string fullOutputPath = currentFile;
1851 size_t sep = fullOutputPath.find_last_of(
"\\/");
1852 if (sep != std::string::npos)
1854 fullOutputPath.substr(sep + 1, fullOutputPath.size() - sep - 1);
1855 archive_entry_set_pathname(entry, fullOutputPath.c_str());
1857 if (aTargetDir != wxEmptyString) {
1858 const char *currentFile = archive_entry_pathname(entry);
1859 const std::string fullOutputPath =
1860 aTargetDir.ToStdString() +
1861 wxString(wxFileName::GetPathSeparator()).ToStdString() + currentFile;
1862 archive_entry_set_pathname(entry, fullOutputPath.c_str());
1864 r = archive_write_header(ext, entry);
1867 wxLogError(wxString::Format(
"Chartdldr_pi: LibArchive error: %s",
1868 archive_error_string(ext)));
1869 else if (archive_entry_size(entry) > 0) {
1870 r = copy_data(a, ext);
1873 wxLogError(wxString::Format(
"Chartdldr_pi: LibArchive error: %s",
1874 archive_error_string(ext)));
1875 if (r < ARCHIVE_WARN)
return false;
1877 r = archive_write_finish_entry(ext);
1881 wxLogError(wxString::Format(
"Chartdldr_pi: LibArchive error: %s",
1882 archive_error_string(ext)));
1883 if (r < ARCHIVE_WARN)
return false;
1885 archive_read_close(a);
1887 archive_write_close(ext);
1890 if (aRemoveArchive) wxRemoveFile(aArchiveFile);
1901#if defined(CHARTDLDR_RAR_UNARR) || !defined(DLDR_USE_LIBARCHIVE)
1902ar_archive *ar_open_any_archive(ar_stream *stream,
const char *fileext) {
1903 ar_archive *ar = ar_open_rar_archive(stream);
1906 ar_open_zip_archive(stream, fileext && (strcmp(fileext,
".xps") == 0 ||
1907 strcmp(fileext,
".epub") == 0));
1908 if (!ar) ar = ar_open_7z_archive(stream);
1909 if (!ar) ar = ar_open_tar_archive(stream);
1913bool chartdldr_pi::ExtractUnarrFiles(
const wxString &aRarFile,
1914 const wxString &aTargetDir,
1915 bool aStripPath, wxDateTime aMTime,
1917 ar_stream *stream = NULL;
1918 ar_archive *ar = NULL;
1919 int entry_count = 1;
1920 int entry_skips = 0;
1924 stream = ar_open_file(aRarFile.c_str());
1926 wxLogError(_T(
"Can not open file '") + aRarFile + _T(
"'."));
1927 ar_close_archive(ar);
1931 ar = ar_open_any_archive(stream, strrchr(aRarFile.c_str(),
'.'));
1933 wxLogError(_T(
"Can not open archive '") + aRarFile + _T(
"'."));
1934 ar_close_archive(ar);
1938 while (ar_parse_entry(ar)) {
1939 size_t size = ar_entry_get_size(ar);
1940 wxString name = ar_entry_get_name(ar);
1942 wxFileName fn(name);
1947 if (fn.GetDirCount() > 0) {
1949 name = aTargetDir + wxFileName::GetPathSeparator() + fn.GetFullPath();
1951 name = aTargetDir + wxFileName::GetPathSeparator() + name;
1954 wxFileName fn(name);
1955 if (!fn.DirExists()) {
1956 if (!wxFileName::Mkdir(fn.GetPath())) {
1957 wxLogError(_T(
"Can not create directory '") + fn.GetPath() + _T(
"'."));
1962 wxFileOutputStream file(name);
1964 wxLogError(_T(
"Can not create file '") + name + _T(
"'."));
1969 unsigned char buffer[1024];
1970 size_t count = size <
sizeof(buffer) ? size : sizeof(buffer);
1971 if (!ar_entry_uncompress(ar, buffer, count))
break;
1972 file.Write(buffer, count);
1976 fn.SetTimes(&aMTime, &aMTime, &aMTime);
1978 wxLogError(
"Warning: Failed to uncompress... skipping");
1983 if (!ar_at_eof(ar)) {
1984 wxLogError(
"Error: Failed to parse entry %d!", entry_count);
1987 ar_close_archive(ar);
1990 if (aRemoveRar) wxRemoveFile(aRarFile);
1995 setlocale(LC_NUMERIC,
"C");
2002bool chartdldr_pi::ExtractZipFiles(
const wxString &aZipFile,
2003 const wxString &aTargetDir,
bool aStripPath,
2004 wxDateTime aMTime,
bool aRemoveZip) {
2007#ifdef __OCPN__ANDROID__
2009 if (aStripPath) nStrip = 1;
2011 ret = AndroidUnzip(aZipFile, aTargetDir, nStrip,
true);
2013 std::unique_ptr<wxZipEntry> entry(
new wxZipEntry());
2016 wxLogMessage(_T(
"chartdldr_pi: Going to extract '") + aZipFile + _T(
"'."));
2017 wxFileInputStream in(aZipFile);
2020 wxLogMessage(_T(
"Can not open file '") + aZipFile + _T(
"'."));
2024 wxZipInputStream zip(in);
2027 while (entry.reset(zip.GetNextEntry()), entry.get() != NULL) {
2029 wxString name = entry->GetName();
2031 wxFileName fn(name);
2036 if (fn.GetDirCount() > 0) fn.RemoveDir(0);
2037 name = aTargetDir + wxFileName::GetPathSeparator() + fn.GetFullPath();
2039 name = aTargetDir + wxFileName::GetPathSeparator() + name;
2043 if (entry->IsDir()) {
2044 int perm = entry->GetMode();
2045 if (!wxFileName::Mkdir(name, perm, wxPATH_MKDIR_FULL)) {
2046 wxLogMessage(_T(
"Can not create directory '") + name + _T(
"'."));
2051 if (!zip.OpenEntry(*entry.get())) {
2052 wxLogMessage(_T(
"Can not open zip entry '") + entry->GetName() +
2057 if (!zip.CanRead()) {
2058 wxLogMessage(_T(
"Can not read zip entry '") + entry->GetName() +
2064 wxFileName fn(name);
2065 if (!fn.DirExists()) {
2066 if (!wxFileName::Mkdir(fn.GetPath())) {
2067 wxLogMessage(_T(
"Can not create directory '") + fn.GetPath() +
2074 wxFileOutputStream file(name);
2077 wxLogMessage(_T(
"Can not create file '") + name + _T(
"'."));
2082 fn.SetTimes(&aMTime, &aMTime, &aMTime);
2089 if (aRemoveZip) wxRemoveFile(aZipFile);
2095ChartDldrGuiAddSourceDlg::ChartDldrGuiAddSourceDlg(wxWindow *parent)
2098 fn.SetPath(*GetpSharedDataLocation());
2099 fn.AppendDir(_T(
"plugins"));
2100 fn.AppendDir(_T(
"chartdldr_pi"));
2101 fn.AppendDir(_T(
"data"));
2106#ifdef __OCPN__ANDROID__
2107 w = 6 * g_androidDPmm;
2110 p_buttonIconList =
new wxImageList(w, h);
2112 fn.SetFullName(_T(
"button_right.png"));
2113 wxImage im1(fn.GetFullPath(), wxBITMAP_TYPE_PNG);
2114 im1.Rescale(w, h, wxIMAGE_QUALITY_HIGH);
2115 p_buttonIconList->Add(im1);
2117 fn.SetFullName(_T(
"button_right.png"));
2118 wxImage im2(fn.GetFullPath(), wxBITMAP_TYPE_PNG);
2119 im2.Rescale(w, h, wxIMAGE_QUALITY_HIGH);
2120 p_buttonIconList->Add(im2);
2122 fn.SetFullName(_T(
"button_down.png"));
2123 wxImage im3(fn.GetFullPath(), wxBITMAP_TYPE_PNG);
2124 im3.Rescale(w, h, wxIMAGE_QUALITY_HIGH);
2125 p_buttonIconList->Add(im3);
2127 fn.SetFullName(_T(
"button_down.png"));
2128 wxImage im4(fn.GetFullPath(), wxBITMAP_TYPE_PNG);
2129 im4.Rescale(w, h, wxIMAGE_QUALITY_HIGH);
2130 p_buttonIconList->Add(im4);
2132 m_treeCtrlPredefSrcs->AssignButtonsImageList(p_buttonIconList);
2134 p_iconList =
new wxImageList(w, h);
2136 fn.SetFullName(_T(
"folder.png"));
2137 wxImage ima(fn.GetFullPath(), wxBITMAP_TYPE_PNG);
2138 ima.Rescale(w, h, wxIMAGE_QUALITY_HIGH);
2139 p_iconList->Add(ima);
2141 fn.SetFullName(_T(
"file.png"));
2142 wxImage imb(fn.GetFullPath(), wxBITMAP_TYPE_PNG);
2143 imb.Rescale(w, h, wxIMAGE_QUALITY_HIGH);
2144 p_iconList->Add(imb);
2146 m_treeCtrlPredefSrcs->AssignImageList(p_iconList);
2149 m_treeCtrlPredefSrcs->SetIndent(w);
2151 m_base_path = wxEmptyString;
2152 m_last_path = wxEmptyString;
2154 m_nbChoice->SetSelection(0);
2162bool ChartDldrGuiAddSourceDlg::LoadSources() {
2163 wxTreeItemId tree = m_treeCtrlPredefSrcs->AddRoot(_T(
"root"));
2166 fn.SetPath(*GetpPrivateApplicationDataLocation());
2167 fn.SetFullName(_T(
"chartdldr_pi-chart_sources.xml"));
2168 if (!fn.FileExists()) {
2169 fn.SetPath(*GetpSharedDataLocation());
2170 fn.AppendDir(_T(
"plugins"));
2171 fn.AppendDir(_T(
"chartdldr_pi"));
2172 fn.AppendDir(_T(
"data"));
2173 fn.SetFullName(_T(
"chart_sources.xml"));
2174 if (!fn.FileExists()) {
2175 wxLogMessage(wxString::Format(
2176 _T(
"Error: chartdldr_pi::LoadSources() %s not found!"),
2177 fn.GetFullPath().c_str()));
2181 wxString path = fn.GetFullPath();
2184 bool ret = doc->load_file(path.mb_str());
2189 element = element.next_sibling()) {
2190 if (!strcmp(element.name(),
"sections")) {
2191 LoadSections(tree, element);
2199bool ChartDldrGuiAddSourceDlg::LoadSections(
const wxTreeItemId &root,
2202 element = element.next_sibling()) {
2203 if (!strcmp(element.name(),
"section")) {
2204 LoadSection(root, element);
2210bool ChartDldrGuiAddSourceDlg::LoadSection(
const wxTreeItemId &root,
2214 element = element.next_sibling()) {
2215 if (!strcmp(element.name(),
"name")) {
2216 item = m_treeCtrlPredefSrcs->AppendItem(
2217 root, wxString::FromUTF8(element.first_child().value()), 0, 0);
2220 if (pFont) m_treeCtrlPredefSrcs->SetItemFont(item, *pFont);
2222 if (!strcmp(element.name(),
"sections")) LoadSections(item, element);
2223 if (!strcmp(element.name(),
"catalogs")) LoadCatalogs(item, element);
2229bool ChartDldrGuiAddSourceDlg::LoadCatalogs(
const wxTreeItemId &root,
2232 element = element.next_sibling()) {
2233 if (!strcmp(element.name(),
"catalog")) LoadCatalog(root, element);
2239bool ChartDldrGuiAddSourceDlg::LoadCatalog(
const wxTreeItemId &root,
2241 wxString name, type, location, dir;
2243 element = element.next_sibling()) {
2244 if (!strcmp(element.name(),
"name"))
2245 name = wxString::FromUTF8(element.first_child().value());
2246 else if (!strcmp(element.name(),
"type"))
2247 type = wxString::FromUTF8(element.first_child().value());
2248 else if (!strcmp(element.name(),
"location"))
2249 location = wxString::FromUTF8(element.first_child().value());
2250 else if (!strcmp(element.name(),
"dir"))
2251 dir = wxString::FromUTF8(element.first_child().value());
2254 wxTreeItemId
id = m_treeCtrlPredefSrcs->AppendItem(root, name, 1, 1, cs);
2257 if (pFont) m_treeCtrlPredefSrcs->SetItemFont(
id, *pFont);
2262ChartDldrGuiAddSourceDlg::~ChartDldrGuiAddSourceDlg() {}
2264wxString ChartDldrGuiAddSourceDlg::FixPath(wxString path) {
2265 wxString sep(wxFileName::GetPathSeparator());
2267 s.Replace(_T(
"/"), sep,
true);
2268 s.Replace(_T(USERDATA), m_base_path);
2269 s.Replace(sep + sep, sep);
2273void ChartDldrGuiAddSourceDlg::OnChangeType(wxCommandEvent &event) {
2274 m_treeCtrlPredefSrcs->Enable(m_nbChoice->GetSelection() == 0);
2275 m_tSourceName->Enable(m_nbChoice->GetSelection() == 1);
2276 m_tChartSourceUrl->Enable(m_nbChoice->GetSelection() == 1);
2279void ChartDldrGuiAddSourceDlg::OnSourceSelected(wxTreeEvent &event) {
2280 wxTreeItemId item = m_treeCtrlPredefSrcs->GetSelection();
2283 m_dirExpanded = FixPath(cs->GetDir());
2285 m_tSourceName->SetValue(cs->GetName());
2286 m_tChartSourceUrl->SetValue(cs->GetUrl());
2287 if (m_tcChartDirectory->GetValue() == m_last_path) {
2288 m_tcChartDirectory->SetValue(FixPath(cs->GetDir()));
2289 m_panelChartDirectory->SetText(FixPath(cs->GetDir()));
2291 m_buttonChartDirectory->Enable();
2292 m_last_path = m_tcChartDirectory->GetValue();
2298void ChartDldrGuiAddSourceDlg::SetSourceEdit(std::unique_ptr<ChartSource> &cs) {
2299 m_nbChoice->SetSelection(1);
2300 m_tChartSourceUrl->Enable();
2301 m_treeCtrlPredefSrcs->Disable();
2302 m_tSourceName->SetValue(cs->GetName());
2303 m_tChartSourceUrl->SetValue(cs->GetUrl());
2304 m_tcChartDirectory->SetValue(FixPath(cs->GetDir()));
2305 m_panelChartDirectory->SetText(FixPath(cs->GetDir()));
2307 m_buttonChartDirectory->Enable();
2310ChartDldrPrefsDlgImpl::ChartDldrPrefsDlgImpl(wxWindow *parent)
2313ChartDldrPrefsDlgImpl::~ChartDldrPrefsDlgImpl() {}
2315void ChartDldrPrefsDlgImpl::SetPath(
const wxString path) {
2323 m_tcDefaultDir->SetValue(path);
2326void ChartDldrPrefsDlgImpl::GetPreferences(
bool &preselect_new,
2327 bool &preselect_updated,
2328 bool &bulk_update) {
2329 preselect_new = m_cbSelectNew->GetValue();
2330 preselect_updated = m_cbSelectUpdated->GetValue();
2331 bulk_update = m_cbBulkUpdate->GetValue();
2333void ChartDldrPrefsDlgImpl::SetPreferences(
bool preselect_new,
2334 bool preselect_updated,
2336 m_cbSelectNew->SetValue(preselect_new);
2337 m_cbSelectUpdated->SetValue(preselect_updated);
2338 m_cbBulkUpdate->SetValue(bulk_update);
2341void ChartDldrGuiAddSourceDlg::OnOkClick(wxCommandEvent &event) {
2342 wxString msg = wxEmptyString;
2344 if (m_nbChoice->GetSelection() == 0) {
2345 wxTreeItemId item = m_treeCtrlPredefSrcs->GetSelection();
2346 if (m_treeCtrlPredefSrcs->GetSelection().IsOk()) {
2348 (
ChartSource *)(m_treeCtrlPredefSrcs->GetItemData(item));
2351 _(
"You must select one of the predefined chart sources or create "
2352 "one of your own.\n");
2355 _(
"You must select one of the predefined chart sources or create one "
2358 if (m_nbChoice->GetSelection() == 1 &&
2359 m_tSourceName->GetValue() == wxEmptyString)
2360 msg += _(
"The chart source must have a name.\n");
2361 wxURI url(m_tChartSourceUrl->GetValue());
2362 if (m_nbChoice->GetSelection() == 1 &&
2363 (m_tChartSourceUrl->GetValue() == wxEmptyString ||
2364 !ValidateUrl(m_tChartSourceUrl->GetValue())))
2365 msg += _(
"The chart source must have a valid URL.\n");
2366 if (m_tcChartDirectory->GetValue() == wxEmptyString)
2367 msg += _(
"You must select a local folder to store the charts.\n");
2368 else if (!wxDirExists(m_tcChartDirectory->GetValue()))
2369 if (!wxFileName::Mkdir(m_tcChartDirectory->GetValue(), 0755,
2371 msg += wxString::Format(_(
"Directory %s can't be created."),
2372 m_tcChartDirectory->GetValue().c_str()) +
2375 if (msg != wxEmptyString)
2376 OCPNMessageBox_PlugIn(
this, msg, _(
"Chart source definition problem"),
2377 wxOK | wxCENTRE | wxICON_ERROR);
2380 SetReturnCode(wxID_OK);
2385void ChartDldrGuiAddSourceDlg::OnCancelClick(wxCommandEvent &event) {
2386 SetReturnCode(wxID_CANCEL);
2387 EndModal(wxID_CANCEL);
2390void ChartDldrPrefsDlgImpl::OnOkClick(wxCommandEvent &event) {
2391 if (!wxDirExists(m_tcDefaultDir->GetValue())) {
2392 if (!wxFileName::Mkdir(m_tcDefaultDir->GetValue(), 0755,
2393 wxPATH_MKDIR_FULL)) {
2394 OCPNMessageBox_PlugIn(
2396 wxString::Format(_(
"Directory %s can't be created."),
2397 m_tcDefaultDir->GetValue().c_str()),
2398 _(
"Chart Downloader"));
2404 g_pi->UpdatePrefs(
this);
2414void ChartDldrPrefsDlg::OnCancelClick(wxCommandEvent &event) {
2416 EndModal(wxID_CANCEL);
2420void ChartDldrPrefsDlg::OnOkClick(wxCommandEvent &event) {
2425bool ChartDldrGuiAddSourceDlg::ValidateUrl(
const wxString Url,
2430 _T(
"^https?\\://[a-zA-Z0-9\\./_-]*\\.[xX][mM][lL]$"));
2436 _T(
"^https?\\://[a-zA-Z0-9\\./_-]*$"));
2438 return re.Matches(Url);
2441static wxString FormatBytes(
double bytes) {
2442 return wxString::Format(_T(
"%.1fMB"), bytes / 1024 / 1024);
2450 switch (ev.getDLEventCondition()) {
2451 case OCPN_DL_EVENT_TYPE_END:
2452 m_bTransferComplete =
true;
2453 m_bTransferSuccess =
2454 (ev.getDLEventStatus() == OCPN_DL_NO_ERROR) ?
true : false;
2457 case OCPN_DL_EVENT_TYPE_PROGRESS:
2458 m_totalsize = FormatBytes(ev.getTotal());
2459 m_transferredsize = FormatBytes(ev.getTransferred());
Implementing ChartDldrPanel.
wxBitmap * GetPlugInBitmap()
FIXME static wxBitmap* LoadSVG(const wxString filename, unsigned int width, ...
@ OCPN_DLDS_URL
The dialog shows the URL involved in the transfer.
@ OCPN_DLDS_AUTO_CLOSE
The dialog auto closes when transfer is complete.
@ OCPN_DLDS_CAN_ABORT
The transfer can be aborted by the user.
@ OCPN_DLDS_ESTIMATED_TIME
The dialog shows the estimated total time.
@ OCPN_DLDS_SIZE
The dialog shows the size of the resource to download/upload.
@ OCPN_DLDS_REMAINING_TIME
The dialog shows the remaining time.
@ OCPN_DLDS_SPEED
The dialog shows the transfer speed.
@ OCPN_DLDS_ELAPSED_TIME
The dialog shows the elapsed time.
@ OCPN_DLDS_CAN_PAUSE
The transfer can be paused.
#define INSTALLS_TOOLBOX_PAGE
Plugin will add pages to the toolbox/settings dialog.
#define WANTS_PREFERENCES
Plugin will add page(s) to global preferences dialog.
#define WANTS_CONFIG
Plugin requires persistent configuration storage.
wxFont * OCPNGetFont(wxString TextElement, int default_size)
Gets a font for UI elements.
wxFont GetOCPNGUIScaledFont_PlugIn(wxString item)
Gets a uniquely scaled font copy for responsive UI elements.