1 /******************************************************************************
2 * $Id: chartdldr_pi.cpp,v 1.0 2011/02/26 01:54:37 nohal Exp $
3 *
4 * Project: OpenCPN
5 * Purpose: Chart downloader Plugin
6 * Author: Pavel Kalian
7 *
8 ***************************************************************************
9 * Copyright (C) 2011 by Pavel Kalian *
10 * $EMAIL$ *
11 * *
12 * This program is free software; you can redistribute it and/or modify *
13 * it under the terms of the GNU General Public License as published by *
14 * the Free Software Foundation; either version 2 of the License, or *
15 * (at your option) any later version. *
16 * *
17 * This program is distributed in the hope that it will be useful, *
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
20 * GNU General Public License for more details. *
21 * *
22 * You should have received a copy of the GNU General Public License *
23 * along with this program; if not, write to the *
24 * Free Software Foundation, Inc., *
25 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
26 ***************************************************************************
27 */
28
29
30 #include "wx/wxprec.h"
31
32 #ifndef WX_PRECOMP
33 #include "wx/wx.h"
34 #endif //precompiled headers
35
36 #include "chartdldr_pi.h"
37 #include "wxWTranslateCatalog.h"
38 #include <wx/stdpaths.h>
39 #include <wx/url.h>
40 #include <wx/progdlg.h>
41 #include <wx/sstream.h>
42 #include <wx/wfstream.h>
43 #include <wx/filename.h>
44 #include <wx/listctrl.h>
45 #include <wx/dir.h>
46 #include <wx/filesys.h>
47 #include <wx/zipstrm.h>
48 #include <wx/wfstream.h>
49 #include <memory>
50 #include <wx/regex.h>
51 #ifdef DLDR_USE_LIBARCHIVE
52 #include <archive.h>
53 #include <archive_entry.h>
54 #ifdef CHARTDLDR_RAR_UNARR
55 #include "unarr.h"
56 #endif
57 #else
58 #include "unarr.h"
59 #endif
60
61 #include <wx/arrimpl.cpp>
62 WX_DEFINE_OBJARRAY(wxArrayOfDateTime);
63 WX_DEFINE_OBJARRAY(ArrayOfChartPanels);
64
65 #ifdef __OCPN__ANDROID__
66 #define _LIBCPP_HAS_NO_OFF_T_FUNCTIONS
67 #endif
68
69 #include <fstream>
70
71 #ifdef __OCPN__ANDROID__
72 #include "androidSupport.h"
73 #endif
74
75 #ifdef __WXMAC__
76 #define CATALOGS_NAME_WIDTH 300
77 #define CATALOGS_DATE_WIDTH 120
78 #define CATALOGS_PATH_WIDTH 100
79 #define CHARTS_NAME_WIDTH 300
80 #define CHARTS_STATUS_WIDTH 100
81 #define CHARTS_DATE_WIDTH 120
82 #else
83 #ifdef __OCPN__ANDROID__
84
85 #define CATALOGS_NAME_WIDTH 350
86 #define CATALOGS_DATE_WIDTH 230
87 #define CATALOGS_PATH_WIDTH 1000
88 #define CHARTS_NAME_WIDTH 520
89 #define CHARTS_STATUS_WIDTH 150
90 #define CHARTS_DATE_WIDTH 200
91
92
93 #else
94
95 #define CATALOGS_NAME_WIDTH 200
96 #define CATALOGS_DATE_WIDTH 130
97 #define CATALOGS_PATH_WIDTH 250
98 #define CHARTS_NAME_WIDTH 320
99 #define CHARTS_STATUS_WIDTH 150
100 #define CHARTS_DATE_WIDTH 130
101
102 #endif
103 #endif // __WXMAC__
104
105 #ifdef __OCPN__ANDROID__
106 #include <QtAndroidExtras/QAndroidJniObject>
107 #include "qdebug.h"
108
109 extern JavaVM *java_vm; // found in androidUtil.cpp, accidentally exported....
110 JNIEnv* jenv;
111
112 #endif
113
114 bool getDisplayMetrics();
115
116 #define CHART_DIR "Charts"
117
118
write_file(const wxString extract_file,char * data,unsigned long datasize)119 void write_file( const wxString extract_file, char *data, unsigned long datasize )
120 {
121 wxFileName fn(extract_file);
122 if( wxDirExists( fn.GetPath() ) )
123 {
124 if( !wxFileName::Mkdir(fn.GetPath(), 0755, wxPATH_MKDIR_FULL) )
125 {
126 wxLogError(_T("Can not create directory '") + fn.GetPath() + _T("'."));
127 return;
128 }
129 }
130 wxFileOutputStream f(extract_file);
131 f.Write(data, datasize);
132 f.Close();
133 }
134
135 // the class factories, used to create and destroy instances of the PlugIn
136
create_pi(void * ppimgr)137 extern "C" DECL_EXP opencpn_plugin* create_pi(void *ppimgr)
138 {
139 return new chartdldr_pi(ppimgr);
140 }
141
destroy_pi(opencpn_plugin * p)142 extern "C" DECL_EXP void destroy_pi(opencpn_plugin* p)
143 {
144 delete p;
145 }
146
147 double g_androidDPmm;
148 chartdldr_pi *g_pi;
149
150 //---------------------------------------------------------------------------------------------------------
151 //
152 // ChartDldr PlugIn Implementation
153 //
154 //---------------------------------------------------------------------------------------------------------
155
156 #include "icons.h"
157
158 //---------------------------------------------------------------------------------------------------------
159 //
160 // PlugIn initialization and de-init
161 //
162 //---------------------------------------------------------------------------------------------------------
163
chartdldr_pi(void * ppimgr)164 chartdldr_pi::chartdldr_pi( void *ppimgr ) : opencpn_plugin_113( ppimgr )
165 {
166 // Create the PlugIn icons
167 initialize_images();
168
169 m_pChartSources = NULL;
170 m_parent_window = NULL;
171 m_pChartCatalog = NULL;
172 m_pChartSource = NULL;
173 m_pconfig = 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;
179 m_dldrpanel = NULL;
180 m_leftclick_tool_id = -1;
181 m_schartdldr_sources = wxEmptyString;
182
183 g_pi = this;
184 }
185
Init(void)186 int chartdldr_pi::Init( void )
187 {
188 AddLocaleCatalog( PLUGIN_CATALOG_NAME );
189
190 // Get a pointer to the opencpn display canvas, to use as a parent for the POI Manager dialog
191 m_parent_window = GetOCPNCanvasWindow();
192
193 // Get a pointer to the opencpn configuration object
194 m_pconfig = GetOCPNConfigObject();
195 m_pOptionsPage = NULL;
196
197 m_pChartSources = new wxArrayOfChartSources();
198 m_pChartCatalog = new ChartCatalog;
199 m_pChartSource = NULL;
200
201 // And load the configuration items
202 LoadConfig();
203
204 getDisplayMetrics();
205
206 wxStringTokenizer st(m_schartdldr_sources, _T("|"), wxTOKEN_DEFAULT);
207 while( st.HasMoreTokens() )
208 {
209 wxString s1 = st.GetNextToken();
210 wxString s2 = st.GetNextToken();
211 wxString s3 = st.GetNextToken();
212 if(!s2.IsEmpty()) // scrub empty sources.
213 m_pChartSources->Add(new ChartSource(s1, s2, s3));
214 }
215 return (
216 WANTS_PREFERENCES |
217 WANTS_CONFIG |
218 INSTALLS_TOOLBOX_PAGE
219 );
220 }
221
DeInit(void)222 bool chartdldr_pi::DeInit( void )
223 {
224 wxLogMessage(_T("chartdldr_pi: DeInit"));
225
226 m_pChartSources->Clear();
227 wxDELETE(m_pChartSources);
228 wxDELETE(m_pChartCatalog);
229 //wxDELETE(m_pChartSource);
230 /* TODO: Seth */
231 // dialog->Close();
232 // dialog->Destroy();
233 // wxDELETE(dialog);
234 /* We must delete remaining page if the plugin is disabled while in Options dialog */
235 if( m_pOptionsPage )
236 {
237 if( DeleteOptionsPage( m_pOptionsPage ) )
238 m_pOptionsPage = NULL;
239 // TODO: any other memory leak?
240 }
241 return true;
242 }
243
GetAPIVersionMajor()244 int chartdldr_pi::GetAPIVersionMajor()
245 {
246 return MY_API_VERSION_MAJOR;
247 }
248
GetAPIVersionMinor()249 int chartdldr_pi::GetAPIVersionMinor()
250 {
251 return MY_API_VERSION_MINOR;
252 }
253
GetPlugInVersionMajor()254 int chartdldr_pi::GetPlugInVersionMajor()
255 {
256 return PLUGIN_VERSION_MAJOR;
257 }
258
GetPlugInVersionMinor()259 int chartdldr_pi::GetPlugInVersionMinor()
260 {
261 return PLUGIN_VERSION_MINOR;
262 }
263
GetPlugInBitmap()264 wxBitmap *chartdldr_pi::GetPlugInBitmap()
265 {
266 return _img_chartdldr_pi;
267 }
268
GetCommonName()269 wxString chartdldr_pi::GetCommonName()
270 {
271 return _("ChartDownloader");
272 }
273
GetShortDescription()274 wxString chartdldr_pi::GetShortDescription()
275 {
276 return _("Chart Downloader PlugIn for OpenCPN");
277 }
278
GetLongDescription()279 wxString chartdldr_pi::GetLongDescription()
280 {
281 return _("Chart Downloader PlugIn for OpenCPN\n\
282 Manages chart downloads and updates from sources supporting\n\
283 NOAA Chart Catalog format");
284 }
285
OnSetupOptions(void)286 void chartdldr_pi::OnSetupOptions( void )
287 {
288 m_pOptionsPage = AddOptionsPage( PI_OPTIONS_PARENT_CHARTS, _("Chart Downloader") );
289 if( ! m_pOptionsPage )
290 {
291 wxLogMessage( _T("Error: chartdldr_pi::OnSetupOptions AddOptionsPage failed!") );
292 return;
293 }
294 wxBoxSizer *sizer = new wxBoxSizer( wxVERTICAL );
295 m_pOptionsPage->SetSizer( sizer );
296
297 m_dldrpanel = new ChartDldrPanelImpl( this, m_pOptionsPage, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE );
298
299 m_pOptionsPage->InvalidateBestSize();
300 sizer->Add( m_dldrpanel, 1, wxALL | wxEXPAND );
301 m_dldrpanel->SetBulkUpdate( m_allow_bulk_update );
302 m_dldrpanel->FitInside();
303 }
304
OnCloseToolboxPanel(int page_sel,int ok_apply_cancel)305 void chartdldr_pi::OnCloseToolboxPanel( int page_sel, int ok_apply_cancel )
306 {
307 /* TODO: Seth */
308 m_dldrpanel->CancelDownload();
309 #ifndef __OCPN__ANDROID__
310 OCPN_cancelDownloadFileBackground( 0 ); //Stop the thread, is something like this needed on Android as well?
311 #endif
312 m_selected_source = m_dldrpanel->GetSelectedCatalog();
313 SaveConfig();
314 }
315
LoadConfig(void)316 bool chartdldr_pi::LoadConfig( void )
317 {
318 wxFileConfig *pConf = (wxFileConfig *)m_pconfig;
319
320 if( pConf )
321 {
322 pConf->SetPath ( _T ( "/Settings/ChartDnldr" ) );
323 pConf->Read ( _T ( "ChartSources" ), &m_schartdldr_sources, wxEmptyString );
324 pConf->Read ( _T ( "Source" ), &m_selected_source, -1 );
325
326 // wxFileName fn(wxStandardPaths::Get().GetDocumentsDir(), wxEmptyString);
327 wxFileName fn(GetWritableDocumentsDir(), wxEmptyString);
328 fn.AppendDir(_T(CHART_DIR));
329
330 pConf->Read ( _T ( "BaseChartDir" ), &m_base_chart_dir, fn.GetPath() );
331 wxLogMessage( _T ( "chartdldr_pi: " ) + m_base_chart_dir );
332 wxLogMessage( _T ( "chartdldr_pi: " ) + fn.GetPath() );
333
334 pConf->Read ( _T ( "PreselectNew" ), &m_preselect_new, true );
335 pConf->Read ( _T ( "PreselectUpdated" ), &m_preselect_updated, true );
336 pConf->Read ( _T ( "AllowBulkUpdate" ), &m_allow_bulk_update, false );
337 return true;
338 }
339 else
340 return false;
341 }
342
SaveConfig(void)343 bool chartdldr_pi::SaveConfig(void)
344 {
345 wxFileConfig *pConf = (wxFileConfig *)m_pconfig;
346
347 m_schartdldr_sources.Clear();
348
349 for( size_t i = 0; i < m_pChartSources->GetCount(); i++ )
350 {
351 ChartSource *cs = m_pChartSources->Item(i);
352 m_schartdldr_sources.Append(wxString::Format(_T("%s|%s|%s|"), cs->GetName().c_str(), cs->GetUrl().c_str(), cs->GetDir().c_str()));
353 }
354
355 if( pConf )
356 {
357 pConf->SetPath ( _T ( "/Settings/ChartDnldr" ) );
358 pConf->Write ( _T ( "ChartSources" ), m_schartdldr_sources );
359 pConf->Write ( _T ( "Source" ), m_selected_source );
360 pConf->Write ( _T ( "BaseChartDir" ), m_base_chart_dir );
361 pConf->Write ( _T ( "PreselectNew" ), m_preselect_new );
362 pConf->Write ( _T ( "PreselectUpdated" ), m_preselect_updated );
363 pConf->Write ( _T ( "AllowBulkUpdate" ), m_allow_bulk_update );
364
365 return true;
366 }
367 else
368 return false;
369 }
370
SetBackColor(wxWindow * ctrl,wxColour col)371 void SetBackColor( wxWindow* ctrl, wxColour col)
372 {
373 static int depth = 0; // recursion count
374 if ( depth == 0 ) { // only for the window root, not for every child
375
376 ctrl->SetBackgroundColour( col );
377 }
378
379 wxWindowList kids = ctrl->GetChildren();
380 for( unsigned int i = 0; i < kids.GetCount(); i++ ) {
381 wxWindowListNode *node = kids.Item( i );
382 wxWindow *win = node->GetData();
383
384 if( win->IsKindOf( CLASSINFO(wxListBox) ) )
385 ( (wxListBox*) win )->SetBackgroundColour( col );
386
387 else if( win->IsKindOf( CLASSINFO(wxTextCtrl) ) )
388 ( (wxTextCtrl*) win )->SetBackgroundColour( col );
389
390 // else if( win->IsKindOf( CLASSINFO(wxStaticText) ) )
391 // ( (wxStaticText*) win )->SetForegroundColour( uitext );
392
393 else if( win->IsKindOf( CLASSINFO(wxChoice) ) )
394 ( (wxChoice*) win )->SetBackgroundColour( col );
395
396 else if( win->IsKindOf( CLASSINFO(wxComboBox) ) )
397 ( (wxComboBox*) win )->SetBackgroundColour( col );
398
399 else if( win->IsKindOf( CLASSINFO(wxRadioButton) ) )
400 ( (wxRadioButton*) win )->SetBackgroundColour( col );
401
402 else if( win->IsKindOf( CLASSINFO(wxScrolledWindow) ) ) {
403 ( (wxScrolledWindow*) win )->SetBackgroundColour( col );
404 }
405
406
407 else if( win->IsKindOf( CLASSINFO(wxButton) ) ) {
408 ( (wxButton*) win )->SetBackgroundColour( col );
409 }
410
411 else {
412 ;
413 }
414
415 if( win->GetChildren().GetCount() > 0 ) {
416 depth++;
417 wxWindow * w = win;
418 SetBackColor( w, col );
419 depth--;
420 }
421 }
422 }
423
424
ShowPreferencesDialog(wxWindow * parent)425 void chartdldr_pi::ShowPreferencesDialog( wxWindow* parent )
426 {
427 ChartDldrPrefsDlgImpl *dialog = new ChartDldrPrefsDlgImpl(parent);
428
429 wxFont fo = GetOCPNGUIScaledFont_PlugIn(_T("Dialog"));
430 dialog->SetFont(fo);
431
432 #ifdef __OCPN__ANDROID__
433 if( m_parent_window ){
434 int xmax = m_parent_window->GetSize().GetWidth();
435 int ymax = m_parent_window->GetParent()->GetSize().GetHeight(); // This would be the Options dialog itself
436 dialog->SetSize( xmax, ymax );
437 dialog->Layout();
438
439 dialog->Move(0,0);
440 }
441
442 wxColour cl = wxColour(214,218,222);
443 SetBackColor( dialog, cl );
444 #endif
445
446 dialog->SetPath(m_base_chart_dir);
447 dialog->SetPreferences(m_preselect_new, m_preselect_updated, m_allow_bulk_update);
448
449 dialog->ShowModal();
450 dialog->Destroy();
451 }
452
UpdatePrefs(ChartDldrPrefsDlgImpl * dialog)453 void chartdldr_pi::UpdatePrefs(ChartDldrPrefsDlgImpl *dialog)
454 {
455 m_base_chart_dir = dialog->GetPath();
456 dialog->GetPreferences(m_preselect_new, m_preselect_updated, m_allow_bulk_update);
457 SaveConfig();
458 if(m_dldrpanel)
459 m_dldrpanel->SetBulkUpdate( m_allow_bulk_update );
460 }
461
getDisplayMetrics()462 bool getDisplayMetrics()
463 {
464 #ifdef __OCPN__ANDROID__
465
466 g_androidDPmm = 4.0; // nominal default
467
468 // Get a reference to the running native activity
469 QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative",
470 "activity", "()Landroid/app/Activity;");
471
472 if ( !activity.isValid() ){
473 return false;
474 }
475
476 // Call the desired method
477 QAndroidJniObject data = activity.callObjectMethod("getDisplayMetrics", "()Ljava/lang/String;");
478
479 wxString return_string;
480 jstring s = data.object<jstring>();
481
482 // Need a Java environment to decode the resulting string
483 if (java_vm->GetEnv( (void **) &jenv, JNI_VERSION_1_6) != JNI_OK) {
484 //qDebug() << "GetEnv failed.";
485 }
486 else {
487 const char *ret_string = (jenv)->GetStringUTFChars(s, NULL);
488 return_string = wxString(ret_string, wxConvUTF8);
489 }
490
491 // Return string may have commas instead of periods, if using Euro locale
492 // We just fix it here...
493 return_string.Replace( _T(","), _T(".") );
494
495
496 // wxLogMessage(_T("Metrics:") + return_string);
497 // wxSize screen_size = ::wxGetDisplaySize();
498 // wxString msg;
499 // msg.Printf(_T("wxGetDisplaySize(): %d %d"), screen_size.x, screen_size.y);
500 // wxLogMessage(msg);
501
502 double density = 1.0;
503 wxStringTokenizer tk(return_string, _T(";"));
504 if( tk.HasMoreTokens() ){
505 wxString token = tk.GetNextToken(); // xdpi
506 token = tk.GetNextToken(); // density
507
508 long b = ::wxGetDisplaySize().y;
509 token.ToDouble( &density );
510
511 token = tk.GetNextToken(); // ldpi
512
513 token = tk.GetNextToken(); // width
514 token = tk.GetNextToken(); // height - statusBarHeight
515 token = tk.GetNextToken(); // width
516 token = tk.GetNextToken(); // height
517 token = tk.GetNextToken(); // dm.widthPixels
518 token = tk.GetNextToken(); // dm.heightPixels
519
520 token = tk.GetNextToken(); // actionBarHeight
521 long abh;
522 token.ToLong( &abh );
523 // g_ActionBarHeight = wxMax(abh, 50);
524
525 // qDebug() << "g_ActionBarHeight" << abh << g_ActionBarHeight;
526
527 }
528
529 double ldpi = 160. * density;
530
531 // double maxDim = wxMax(::wxGetDisplaySize().x, ::wxGetDisplaySize().y);
532 // ret = (maxDim / ldpi) * 25.4;
533
534 // msg.Printf(_T("Android Auto Display Size (mm, est.): %g"), ret);
535 // wxLogMessage(msg);
536
537 // Save some items as global statics for convenience
538 g_androidDPmm = ldpi / 25.4;
539 // g_androidDensity = density;
540
541 //qDebug() << "PI Metrics" << g_androidDPmm << density;
542 return true;
543 #else
544
545 return true;
546 #endif
547
548
549 }
550
ChartSource(wxString name,wxString url,wxString localdir)551 ChartSource::ChartSource( wxString name, wxString url, wxString localdir )
552 {
553 m_name = name;
554 m_url = url;
555 m_dir = localdir;
556 m_update_data.clear();
557 }
558
~ChartSource()559 ChartSource::~ChartSource()
560 {
561 m_update_data.clear();
562 }
563
564 #define ID_MNU_SELALL 2001
565 #define ID_MNU_DELALL 2002
566 #define ID_MNU_INVSEL 2003
567 #define ID_MNU_SELUPD 2004
568 #define ID_MNU_SELNEW 2005
569
570 enum
571 {
572 ThreadId = wxID_HIGHEST+1
573 };
574
IMPLEMENT_DYNAMIC_CLASS(ChartDldrPanelImpl,ChartDldrPanel)575 IMPLEMENT_DYNAMIC_CLASS( ChartDldrPanelImpl, ChartDldrPanel )
576 BEGIN_EVENT_TABLE( ChartDldrPanelImpl, ChartDldrPanel )
577 END_EVENT_TABLE()
578
579 void ChartDldrPanelImpl::OnPopupClick( wxCommandEvent &evt )
580 {
581 switch( evt.GetId() )
582 {
583 case ID_MNU_SELALL:
584 CheckAllCharts( true );
585 break;
586 case ID_MNU_DELALL:
587 CheckAllCharts( false );
588 break;
589 case ID_MNU_INVSEL:
590 InvertCheckAllCharts( );
591 break;
592 case ID_MNU_SELUPD:
593 CheckUpdatedCharts(true);
594 break;
595 case ID_MNU_SELNEW:
596 CheckNewCharts(true);
597 break;
598 }
599 }
600
OnContextMenu(wxMouseEvent & event)601 void ChartDldrPanelImpl::OnContextMenu( wxMouseEvent& event )
602 {
603
604 wxMenu menu;
605
606 wxPoint mouseScreen = wxGetMousePosition();
607 wxPoint mouseClient = ScreenToClient(mouseScreen);
608
609 #ifdef __OCPN__ANDROID__
610 wxFont *pf = OCPNGetFont(_T("Menu"), 0);
611
612 // add stuff
613 wxMenuItem *item1 = new wxMenuItem(&menu, ID_MNU_SELALL, _("Select all"));
614 item1->SetFont(*pf);
615 menu.Append(item1);
616
617 wxMenuItem *item2 = new wxMenuItem(&menu, ID_MNU_DELALL, _("Deselect all"));
618 item2->SetFont(*pf);
619 menu.Append(item2);
620
621 wxMenuItem *item3 = new wxMenuItem(&menu, ID_MNU_INVSEL, _("Invert selection"));
622 item3->SetFont(*pf);
623 menu.Append(item3);
624
625 wxMenuItem *item4 = new wxMenuItem(&menu, ID_MNU_SELUPD, _("Select updated"));
626 item4->SetFont(*pf);
627 menu.Append(item4);
628
629 wxMenuItem *item5 = new wxMenuItem(&menu, ID_MNU_SELNEW, _("Select new"));
630 item5->SetFont(*pf);
631 menu.Append(item5);
632
633 #else
634
635 menu.Append(ID_MNU_SELALL, _("Select all"), wxT(""));
636 menu.Append(ID_MNU_DELALL, _("Deselect all"), wxT(""));
637 menu.Append(ID_MNU_INVSEL, _("Invert selection"), wxT(""));
638 menu.Append(ID_MNU_SELUPD, _("Select updated"), wxT(""));
639 menu.Append(ID_MNU_SELNEW, _("Select new"), wxT(""));
640
641 #endif
642
643 menu.Connect(wxEVT_COMMAND_MENU_SELECTED, (wxObjectEventFunction)&ChartDldrPanelImpl::OnPopupClick, NULL, this);
644 // and then display
645 PopupMenu(&menu, mouseClient.x, mouseClient.y);
646
647 }
648
OnShowLocalDir(wxCommandEvent & event)649 void ChartDldrPanelImpl::OnShowLocalDir( wxCommandEvent& event )
650 {
651 if (pPlugIn->m_pChartSource == NULL)
652 return;
653 #ifdef __WXGTK__
654 wxExecute(wxString::Format(_T("xdg-open %s"), pPlugIn->m_pChartSource->GetDir().c_str()));
655 #endif
656 #ifdef __WXMAC__
657 wxExecute(wxString::Format(_T("open %s"), pPlugIn->m_pChartSource->GetDir().c_str()));
658 #endif
659 #ifdef __WXMSW__
660 wxExecute(wxString::Format(_T("explorer %s"), pPlugIn->m_pChartSource->GetDir().c_str()));
661 #endif
662 }
663
SetSource(int id)664 void ChartDldrPanelImpl::SetSource( int id )
665 {
666 pPlugIn->SetSourceId( id );
667
668 m_bDeleteSource->Enable( id >= 0 );
669 m_bUpdateChartList->Enable( id >= 0 );
670 m_bEditSource->Enable( id >= 0 );
671
672 // TODO: DAN - Need to optimze to only update the chart list if needed.
673 // Right now it updates multiple times unnecessarily.
674 CleanForm();
675 if( id >= 0 && id < (int)pPlugIn->m_pChartSources->Count() )
676 {
677 ::wxBeginBusyCursor(); //wxSetCursor(wxCURSOR_WAIT);
678 wxYield();
679 ChartSource *cs = pPlugIn->m_pChartSources->Item(id);
680 cs->LoadUpdateData();
681 cs->UpdateLocalFiles();
682 pPlugIn->m_pChartSource = cs;
683 FillFromFile(cs->GetUrl(), cs->GetDir(), pPlugIn->m_preselect_new, pPlugIn->m_preselect_updated);
684 m_chartsLabel->SetLabel(wxString::Format(_("Charts: %s"), cs->GetName().c_str()));
685 if (::wxIsBusy()) ::wxEndBusyCursor();
686 }
687 else
688 {
689 pPlugIn->m_pChartSource = NULL;
690 m_chartsLabel->SetLabel( _("Charts"));
691 }
692 }
693
SelectSource(wxListEvent & event)694 void ChartDldrPanelImpl::SelectSource( wxListEvent& event )
695 {
696 SetSource(GetSelectedCatalog());
697 event.Skip();
698 }
699
SetBulkUpdate(bool bulk_update)700 void ChartDldrPanelImpl::SetBulkUpdate( bool bulk_update )
701 {
702 m_bUpdateAllCharts->Enable( bulk_update );
703 m_bUpdateAllCharts->Show( bulk_update );
704 Layout();
705 m_parent->Layout();
706 }
707
CleanForm()708 void ChartDldrPanelImpl::CleanForm()
709 {
710 #ifdef NEW_LIST
711 m_scrollWinChartList->ClearBackground();
712 #else
713 m_clCharts->Freeze();
714 m_clCharts->DeleteAllItems();
715 m_clCharts->Thaw();
716 #endif
717 //m_stCatalogInfo->Show( false );
718 }
719
FillFromFile(wxString url,wxString dir,bool selnew,bool selupd)720 void ChartDldrPanelImpl::FillFromFile( wxString url, wxString dir, bool selnew, bool selupd )
721 {
722 //load if exists
723 wxStringTokenizer tk(url, _T("/"));
724 wxString file;
725 do
726 {
727 file = tk.GetNextToken();
728 }
729 while( tk.HasMoreTokens() );
730 wxFileName fn;
731 fn.SetFullName(file);
732 fn.SetPath(dir);
733 wxString path = fn.GetFullPath();
734 if( wxFileExists(path) )
735 {
736 pPlugIn->m_pChartCatalog->LoadFromFile(path);
737 // m_tChartSourceInfo->SetValue(pPlugIn->m_pChartCatalog->GetDescription());
738 //fill in the rest of the form
739
740 #ifdef NEW_LIST
741 size_t updated_charts = 0;
742 size_t new_charts = 0;
743
744 // Clear any existing panels
745 for(unsigned int i = 0 ; i < m_panelArray.GetCount() ; i++){
746 delete m_panelArray.Item(i);
747 }
748 m_panelArray.Clear();
749
750 m_scrollWinChartList->ClearBackground();
751
752 for( size_t i = 0; i < pPlugIn->m_pChartCatalog->charts.Count(); i++ )
753 {
754 wxString status;
755 wxString latest;
756 bool bcheck = false;
757 wxString file = pPlugIn->m_pChartCatalog->charts.Item(i).GetChartFilename(true);
758 if( !pPlugIn->m_pChartSource->ExistsLocaly(pPlugIn->m_pChartCatalog->charts.Item(i).number, file) )
759 {
760 new_charts++;
761 status = _("New");
762 if (selnew)
763 bcheck = true;
764 }
765 else
766 {
767 if( pPlugIn->m_pChartSource->IsNewerThanLocal(pPlugIn->m_pChartCatalog->charts.Item(i).number, file, pPlugIn->m_pChartCatalog->charts.Item(i).GetUpdateDatetime()) )
768 {
769 updated_charts++;
770 status = _("Update available");
771 if (selupd)
772 bcheck = true;
773 }
774 else
775 {
776 status = _("Up to date");
777 }
778 }
779 latest = pPlugIn->m_pChartCatalog->charts.Item(i).GetUpdateDatetime().Format(_T("%Y-%m-%d"));
780
781 ChartPanel *pC = new ChartPanel(m_scrollWinChartList, wxID_ANY, wxDefaultPosition, wxSize(-1, -1),
782 pPlugIn->m_pChartCatalog->charts.Item(i).GetChartTitle(), status, latest, this, bcheck );
783 pC->Connect( wxEVT_RIGHT_DOWN, wxMouseEventHandler( ChartDldrPanel::OnContextMenu ), NULL, this );
784
785 m_boxSizerCharts->Add( pC, 0, wxEXPAND | wxLEFT | wxRIGHT, 2 );
786 m_panelArray.Add( pC );
787 }
788
789
790 m_scrollWinChartList->ClearBackground();
791 m_scrollWinChartList->FitInside();
792 m_scrollWinChartList->GetSizer()->Layout();
793
794 Layout();
795 m_scrollWinChartList->ClearBackground();
796
797
798 #else
799 m_clCharts->Freeze();
800 m_clCharts->DeleteAllItems();
801 size_t updated_charts = 0;
802 size_t new_charts = 0;
803 for( size_t i = 0; i < pPlugIn->m_pChartCatalog->charts.Count(); i++ )
804 {
805 wxListItem li;
806 li.SetId(i);
807 li.SetText(pPlugIn->m_pChartCatalog->charts.Item(i).GetChartTitle());
808 long x = m_clCharts->InsertItem(li);
809 m_clCharts->SetItem(x, 0, pPlugIn->m_pChartCatalog->charts.Item(i).GetChartTitle());
810 wxString file = pPlugIn->m_pChartCatalog->charts.Item(i).GetChartFilename(true);
811 if( !pPlugIn->m_pChartSource->ExistsLocaly(pPlugIn->m_pChartCatalog->charts.Item(i).number, file) )
812 {
813 new_charts++;
814 m_clCharts->SetItem(x, 1, _("New"));
815 if (selnew)
816 m_clCharts->Check(x, true);
817 }
818 else
819 {
820 if( pPlugIn->m_pChartSource->IsNewerThanLocal(pPlugIn->m_pChartCatalog->charts.Item(i).number, file, pPlugIn->m_pChartCatalog->charts.Item(i).GetUpdateDatetime()) )
821 {
822 updated_charts++;
823 m_clCharts->SetItem(x, 1, _("Update available"));
824 if (selupd)
825 m_clCharts->Check(x, true);
826 }
827 else
828 {
829 m_clCharts->SetItem(x, 1, _("Up to date"));
830 }
831 }
832 m_clCharts->SetItem(x, 2, pPlugIn->m_pChartCatalog->charts.Item(i).GetUpdateDatetime().Format(_T("%Y-%m-%d %H:%M")));
833 }
834 m_clCharts->Thaw();
835 #endif
836
837 m_stCatalogInfo->SetLabel( wxString::Format( _("%lu charts total, %lu updated, %lu new"), pPlugIn->m_pChartCatalog->charts.Count(), updated_charts, new_charts ) );
838 m_stCatalogInfo->Show( true );
839 }
840 }
841
ExistsLocaly(wxString chart_number,wxString filename)842 bool ChartSource::ExistsLocaly( wxString chart_number, wxString filename )
843 {
844 wxASSERT(this);
845
846 wxStringTokenizer tk(filename, _T("."));
847 wxString file = tk.GetNextToken().MakeLower();
848
849 if( !m_update_data.empty() )
850 {
851 return m_update_data.find(std::string(chart_number.Lower().mb_str())) != m_update_data.end() || m_update_data.find(std::string(file.mb_str())) != m_update_data.end();
852 }
853 for( size_t i = 0; i < m_localfiles.Count(); i++ )
854 {
855 if (m_localfiles.Item(i) == file)
856 return true;
857 }
858 return false;
859 }
860
IsNewerThanLocal(wxString chart_number,wxString filename,wxDateTime validDate)861 bool ChartSource::IsNewerThanLocal( wxString chart_number, wxString filename, wxDateTime validDate )
862 {
863 wxStringTokenizer tk(filename, _T("."));
864 wxString file = tk.GetNextToken().MakeLower();
865 if( !m_update_data.empty() )
866 {
867 if( m_update_data[std::string(chart_number.Lower().mbc_str())] < validDate.GetTicks() && m_update_data[std::string(file.mbc_str())] < validDate.GetTicks() )
868 return true;
869 else
870 return false;
871 }
872 bool update_candidate = false;
873
874 for( size_t i = 0; i < m_localfiles.Count(); i++ )
875 {
876 if( m_localfiles.Item(i) == file )
877 {
878 if( validDate.IsLaterThan(m_localdt.Item(i)) )
879 {
880 update_candidate = true;
881 }
882 else
883 return false;
884 }
885 }
886 return update_candidate;
887 }
888
GetSelectedCatalog()889 int ChartDldrPanelImpl::GetSelectedCatalog()
890 {
891 long item = m_lbChartSources->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
892 return item;
893 }
894
SelectCatalog(int item)895 void ChartDldrPanelImpl::SelectCatalog( int item )
896 {
897 if( item >= 0 )
898 {
899 m_bDeleteSource->Enable();
900 m_bEditSource->Enable();
901 m_bUpdateChartList->Enable();
902 }
903 else
904 {
905 m_bDeleteSource->Disable();
906 m_bEditSource->Disable();
907 m_bUpdateChartList->Disable();
908 }
909 m_lbChartSources->SetItemState(item, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
910 }
911
AppendCatalog(ChartSource * cs)912 void ChartDldrPanelImpl::AppendCatalog( ChartSource *cs )
913 {
914 long id = m_lbChartSources->GetItemCount();
915 m_lbChartSources->InsertItem(id, cs->GetName());
916 m_lbChartSources->SetItem(id, 1, _("(Please update first)"));
917 m_lbChartSources->SetItem(id, 2, cs->GetDir());
918 wxURI url(cs->GetUrl());
919 if( url.IsReference() )
920 {
921 OCPNMessageBox_PlugIn(this,_("Error, the URL to the chart source data seems wrong."), _("Error"));
922 return;
923 }
924 wxFileName fn(url.GetPath());
925 fn.SetPath(cs->GetDir());
926 wxString path = fn.GetFullPath();
927 if( wxFileExists(path) )
928 {
929 if( pPlugIn->m_pChartCatalog->LoadFromFile(path, true) )
930 {
931 m_lbChartSources->SetItem(id, 0, pPlugIn->m_pChartCatalog->title);
932 m_lbChartSources->SetItem(id, 1, pPlugIn->m_pChartCatalog->GetReleaseDate().Format(_T("%Y-%m-%d %H:%M")));
933 m_lbChartSources->SetItem(id, 2, path);
934 #ifdef __OCPN__ANDROID__
935 m_lbChartSources->GetHandle()->resizeColumnToContents(0);
936 m_lbChartSources->GetHandle()->resizeColumnToContents(1);
937 m_lbChartSources->GetHandle()->resizeColumnToContents(2);
938 #endif
939 }
940 }
941 }
942
UpdateAllCharts(wxCommandEvent & event)943 void ChartDldrPanelImpl::UpdateAllCharts( wxCommandEvent& event )
944 {
945 int failed_to_update = 0;
946 int attempted_to_update = 0;
947 if( (pPlugIn->m_preselect_new) && (pPlugIn->m_preselect_updated) )
948 {
949 wxMessageDialog mess(this, _("You have chosen to update all chart catalogs.\nThen download all new and updated charts.\nThis may take a long time."),
950 _("Chart Downloader"), wxOK | wxCANCEL);
951 if( mess.ShowModal() == wxID_CANCEL )
952 return;
953 }
954 else if( pPlugIn->m_preselect_new )
955 {
956 wxMessageDialog mess(this, _("You have chosen to update all chart catalogs.\nThen download only new (but not updated) charts.\nThis may take a long time."),
957 _("Chart Downloader"), wxOK | wxCANCEL);
958 if (mess.ShowModal() == wxID_CANCEL) return;
959 }
960 else if( pPlugIn->m_preselect_updated )
961 {
962 wxMessageDialog mess(this, _("You have chosen to update all chart catalogs.\nThen download only updated (but not new) charts.\nThis may take a long time."),
963 _("Chart Downloader"), wxOK | wxCANCEL);
964 if (mess.ShowModal() == wxID_CANCEL) return;
965 }
966 updatingAll = true;
967 cancelled = false;
968 // Flip to the list of charts so user can observe the download progress
969 int oldPage = m_DLoadNB->SetSelection(1);
970 for( long chartIndex = 0; chartIndex < m_lbChartSources->GetItemCount(); chartIndex++ )
971 {
972 m_lbChartSources->SetItemState(chartIndex, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
973 if( cancelled )
974 break;
975 UpdateChartList( event );
976 DownloadCharts();
977 attempted_to_update += m_downloading;
978 failed_to_update += m_failed_downloads;
979 }
980 wxLogMessage( wxString::Format(_T("chartdldr_pi::UpdateAllCharts() downloaded %d out of %d charts."), attempted_to_update - failed_to_update, attempted_to_update) );
981 if( failed_to_update > 0 )
982 OCPNMessageBox_PlugIn( this, wxString::Format( _("%d out of %d charts failed to download.\nCheck the list, verify there is a working Internet connection and repeat the operation if needed."),
983 failed_to_update, attempted_to_update ), _("Chart Downloader"), wxOK | wxICON_ERROR );
984 if( attempted_to_update > failed_to_update )
985 ForceChartDBUpdate();
986 updatingAll = false;
987 cancelled = false;
988 // Flip back to the original page
989 m_DLoadNB->SetSelection(oldPage);
990 }
991
992
UpdateChartList(wxCommandEvent & event)993 void ChartDldrPanelImpl::UpdateChartList( wxCommandEvent& event )
994 {
995 //TODO: check if everything exists and we can write to the output dir etc.
996 if( !m_lbChartSources->GetSelectedItemCount() )
997 return;
998 ChartSource *cs = pPlugIn->m_pChartSources->Item(GetSelectedCatalog());
999 wxURI url(cs->GetUrl());
1000 if( url.IsReference() )
1001 {
1002 OCPNMessageBox_PlugIn(this, _("Error, the URL to the chart source data seems wrong."), _("Error"));
1003 return;
1004 }
1005
1006 wxStringTokenizer tk(url.GetPath(), _T("/"));
1007 wxString file;
1008 do
1009 {
1010 file = tk.GetNextToken();
1011 }
1012 while( tk.HasMoreTokens() );
1013 wxFileName fn;
1014 fn.SetFullName(file);
1015 fn.SetPath(cs->GetDir());
1016 if( !wxDirExists(cs->GetDir()) )
1017 {
1018 if( !wxFileName::Mkdir(cs->GetDir(), 0755, wxPATH_MKDIR_FULL) )
1019 {
1020 OCPNMessageBox_PlugIn(this, wxString::Format(_("Directory %s can't be created."), cs->GetDir().c_str()), _("Chart Downloader"));
1021 return;
1022 }
1023 }
1024
1025 bool bok = false;
1026
1027 #ifdef __OCPN__ANDROID__
1028 wxString file_URI = _T("file://") + fn.GetFullPath();
1029
1030 // wxFile testFile(tfn.GetFullPath().c_str(), wxFile::write);
1031 // if(!testFile.IsOpened()){
1032 // wxMessageBox(this, wxString::Format(_("File %s can't be written. \nChoose a writable folder for Chart Downloader file storage."), tfn.GetFullPath().c_str()), _("Chart Downloader"));
1033 // return;
1034 // }
1035 // testFile.Close();
1036 // ::wxRemoveFile(tfn.GetFullPath());
1037
1038 _OCPN_DLStatus ret = OCPN_downloadFile( cs->GetUrl(), file_URI,
1039 _("Downloading file"),
1040 _("Reading Headers: ") + url.BuildURI(), wxNullBitmap, this,
1041 OCPN_DLDS_ELAPSED_TIME|OCPN_DLDS_ESTIMATED_TIME|OCPN_DLDS_REMAINING_TIME|OCPN_DLDS_SPEED|OCPN_DLDS_SIZE|OCPN_DLDS_URL|OCPN_DLDS_CAN_PAUSE|OCPN_DLDS_CAN_ABORT|OCPN_DLDS_AUTO_CLOSE,
1042 10);
1043 bok = true;
1044
1045
1046 #else
1047 wxFileName tfn = wxFileName::CreateTempFileName( fn.GetFullPath() );
1048 wxString file_URI = tfn.GetFullPath();
1049
1050 _OCPN_DLStatus ret = OCPN_downloadFile( cs->GetUrl(), file_URI,
1051 _("Downloading file"),
1052 _("Reading Headers: ") + url.BuildURI(), wxNullBitmap, this,
1053 OCPN_DLDS_ELAPSED_TIME|OCPN_DLDS_ESTIMATED_TIME|OCPN_DLDS_REMAINING_TIME|OCPN_DLDS_SPEED|OCPN_DLDS_SIZE|OCPN_DLDS_URL|OCPN_DLDS_CAN_PAUSE|OCPN_DLDS_CAN_ABORT|OCPN_DLDS_AUTO_CLOSE,
1054 10);
1055
1056 bok = wxCopyFile( tfn.GetFullPath(), fn.GetFullPath() );
1057 wxRemoveFile ( tfn.GetFullPath() );
1058
1059 #endif
1060
1061 // wxLogMessage(_T("chartdldr_pi: OCPN_downloadFile done:"));
1062
1063 switch( ret )
1064 {
1065 case OCPN_DL_NO_ERROR:
1066 {
1067 if( bok )
1068 {
1069 long id = GetSelectedCatalog();
1070 SetSource(id);
1071
1072 m_lbChartSources->SetItem(id, 0, pPlugIn->m_pChartCatalog->title);
1073 m_lbChartSources->SetItem(id, 1, pPlugIn->m_pChartCatalog->GetReleaseDate().Format(_T("%Y-%m-%d %H:%M")));
1074 m_lbChartSources->SetItem(id, 2, cs->GetDir());
1075
1076 }
1077 else
1078 OCPNMessageBox_PlugIn(this, wxString::Format( _("Failed to Find New Catalog: %s "), url.BuildURI().c_str() ),
1079 _("Chart Downloader"), wxOK | wxICON_ERROR);
1080 break;
1081 }
1082 case OCPN_DL_FAILED:
1083 {
1084 OCPNMessageBox_PlugIn(this, wxString::Format( _("Failed to Download Catalog: %s \nVerify there is a working Internet connection."), url.BuildURI().c_str() ),
1085 _("Chart Downloader"), wxOK | wxICON_ERROR);
1086 break;
1087 }
1088
1089 case OCPN_DL_USER_TIMEOUT:
1090 case OCPN_DL_ABORTED:
1091 {
1092 cancelled = true;
1093 break;
1094 }
1095
1096 case OCPN_DL_UNKNOWN:
1097 case OCPN_DL_STARTED:
1098 {
1099 break;
1100 }
1101
1102 default:
1103 wxASSERT( false ); // This should never happen because we handle all possible cases of ret
1104 }
1105
1106
1107
1108 }
1109
GetLocalFiles()1110 void ChartSource::GetLocalFiles()
1111 {
1112 if( !UpdateDataExists() || m_update_data.empty() )
1113 {
1114 wxArrayString *allFiles = new wxArrayString;
1115 if( wxDirExists(GetDir()) )
1116 wxDir::GetAllFiles(GetDir(), allFiles);
1117 m_localdt.Clear();
1118 m_localfiles.Clear();
1119 wxDateTime ct, mt, at;
1120 wxString name;
1121 for( size_t i = 0; i < allFiles->Count(); i++ )
1122 {
1123 wxFileName fn(allFiles->Item(i));
1124 name = fn.GetFullName().Lower();
1125 // Only add unique files names to the local list.
1126 // This is safe because all chart names within a catalog
1127 // are necessarily unique.
1128 if( !ExistsLocaly(wxEmptyString, name) )
1129 {
1130 fn.GetTimes(&at, &mt, &ct);
1131 m_localdt.Add(mt);
1132 m_localfiles.Add(fn.GetName().Lower());
1133
1134 wxStringTokenizer tk(name, _T("."));
1135 wxString file = tk.GetNextToken().MakeLower();
1136 m_update_data[std::string(file.mbc_str())] = mt.GetTicks();
1137 }
1138 }
1139 allFiles->Clear();
1140 wxDELETE(allFiles);
1141 SaveUpdateData();
1142 }
1143 else
1144 {
1145 LoadUpdateData();
1146 }
1147 }
1148
UpdateDataExists()1149 bool ChartSource::UpdateDataExists()
1150 {
1151 return wxFileExists( GetDir() + wxFileName::GetPathSeparator() + _T(UPDATE_DATA_FILENAME) );
1152 }
1153
LoadUpdateData()1154 void ChartSource::LoadUpdateData()
1155 {
1156 m_update_data.clear();
1157 wxString fn = GetDir() + wxFileName::GetPathSeparator() + _T(UPDATE_DATA_FILENAME);
1158
1159 if( !wxFileExists( fn ) )
1160 return;
1161
1162 std::ifstream infile( fn.mb_str() );
1163
1164 std::string key;
1165 long value;
1166
1167 while( infile >> key >> value )
1168 m_update_data[key] = value;
1169
1170 infile.close();
1171 }
1172
SaveUpdateData()1173 void ChartSource::SaveUpdateData()
1174 {
1175 wxString fn;
1176 fn = GetDir() + wxFileName::GetPathSeparator() + _T(UPDATE_DATA_FILENAME);
1177
1178 #ifdef __OCPN__ANDROID__
1179 fn = AndroidGetCacheDir() + wxFileName::GetPathSeparator() + _T(UPDATE_DATA_FILENAME);
1180 #endif
1181
1182 std::ofstream outfile( fn.mb_str() );
1183 if( !outfile.is_open() )
1184 return;
1185
1186 std::map<std::string, time_t>::iterator iter;
1187 for( iter = m_update_data.begin(); iter != m_update_data.end(); ++iter )
1188 {
1189 if( iter->first.find(" ") == std::string::npos )
1190 if( !iter->first.empty() )
1191 outfile << iter->first << " " << iter->second << "\n";
1192 }
1193
1194 outfile.close();
1195
1196 #ifdef __OCPN__ANDROID__
1197 AndroidSecureCopyFile(fn, GetDir() + wxFileName::GetPathSeparator() + _T(UPDATE_DATA_FILENAME));
1198 #endif
1199
1200 }
1201
ChartUpdated(wxString chart_number,time_t timestamp)1202 void ChartSource::ChartUpdated( wxString chart_number, time_t timestamp )
1203 {
1204 m_update_data[std::string(chart_number.Lower().mb_str())] = timestamp;
1205 SaveUpdateData();
1206 }
1207
DownloadChart(wxString url,wxString file,wxString title)1208 bool ChartDldrPanelImpl::DownloadChart( wxString url, wxString file, wxString title )
1209 {
1210 return false;
1211 }
1212
DisableForDownload(bool enabled)1213 void ChartDldrPanelImpl::DisableForDownload( bool enabled )
1214 {
1215 m_bAddSource->Enable( enabled );
1216 m_bDeleteSource->Enable( enabled );
1217 m_bEditSource->Enable( enabled );
1218 m_bUpdateAllCharts->Enable( enabled );
1219 m_bUpdateChartList->Enable( enabled );
1220 #ifndef NEW_LIST
1221 m_clCharts->Enable( enabled );
1222 #endif /* NEW_LIST */
1223 m_lbChartSources->Enable( enabled );
1224 }
1225
OnDownloadCharts(wxCommandEvent & event)1226 void ChartDldrPanelImpl::OnDownloadCharts( wxCommandEvent& event )
1227 {
1228 if( DownloadIsCancel )
1229 {
1230 cancelled = true;
1231 return;
1232 }
1233 DownloadCharts();
1234 }
1235
GetChartCount()1236 int ChartDldrPanelImpl::GetChartCount()
1237 {
1238 #ifndef NEW_LIST
1239 return m_clCharts->GetItemCount();
1240 #else
1241 return m_panelArray.GetCount();
1242 #endif /* NEW_LIST */
1243 }
1244
GetCheckedChartCount()1245 int ChartDldrPanelImpl::GetCheckedChartCount()
1246 {
1247 #ifndef NEW_LIST
1248 return m_lbChartSources->GetSelectedItemCount();
1249 #else
1250 int cnt = 0;
1251 for( int i=0 ; i < GetChartCount() ; i++){
1252 if(m_panelArray.Item(i)->GetCB()->IsChecked())
1253 cnt++;
1254 }
1255 return cnt;
1256 #endif /* NEW_LIST */
1257 }
1258
isChartChecked(int i)1259 bool ChartDldrPanelImpl::isChartChecked( int i )
1260 {
1261 #ifndef NEW_LIST
1262 return m_clCharts->IsChecked(i);
1263 #else
1264 if(i <= GetChartCount())
1265 return m_panelArray.Item(i)->GetCB()->IsChecked();
1266 else
1267 return false;
1268 #endif /* NEW_LIST */
1269 }
1270
CheckAllCharts(bool value)1271 void ChartDldrPanelImpl::CheckAllCharts( bool value )
1272 {
1273 #ifndef NEW_LIST
1274 m_clCharts->CheckAll(value);
1275
1276 #else
1277 for( int i=0 ; i < GetChartCount() ; i++){
1278 m_panelArray.Item(i)->GetCB()->SetValue( value );
1279 }
1280 #endif /* NEW_LIST */
1281 }
1282
CheckNewCharts(bool value)1283 void ChartDldrPanelImpl::CheckNewCharts(bool value)
1284 {
1285 for (int i = 0; i < GetChartCount(); i++) {
1286 if (m_panelArray.Item(i)->isNew())
1287 m_panelArray.Item(i)->GetCB()->SetValue(value);
1288 }
1289 }
1290
CheckUpdatedCharts(bool value)1291 void ChartDldrPanelImpl::CheckUpdatedCharts(bool value)
1292 {
1293 for (int i = 0; i < GetChartCount(); i++) {
1294 if (m_panelArray.Item(i)->isUpdated())
1295 m_panelArray.Item(i)->GetCB()->SetValue(value);
1296 }
1297 }
1298
InvertCheckAllCharts()1299 void ChartDldrPanelImpl::InvertCheckAllCharts( )
1300 {
1301 #ifndef NEW_LIST
1302 for (int i = 0; i < GetChartCount(); i++)
1303 m_clCharts->Check(i, !m_clCharts->IsChecked(i));
1304
1305 #else
1306 for (int i = 0; i < GetChartCount(); i++)
1307 m_panelArray.Item(i)->GetCB()->SetValue( !isChartChecked(i) );
1308 #endif /* NEW_LIST */
1309 }
1310
1311
1312
DownloadCharts()1313 void ChartDldrPanelImpl::DownloadCharts()
1314 {
1315 if(!m_bconnected){
1316 Connect(wxEVT_DOWNLOAD_EVENT, (wxObjectEventFunction)(wxEventFunction)&ChartDldrPanelImpl::onDLEvent);
1317 m_bconnected = true;
1318 }
1319
1320 if( !GetCheckedChartCount() && !updatingAll )
1321 {
1322 OCPNMessageBox_PlugIn(this, _("No charts selected for download."));
1323 return;
1324 }
1325 ChartSource *cs = pPlugIn->m_pChartSources->Item(GetSelectedCatalog());
1326
1327 cancelled = false;
1328 to_download = GetCheckedChartCount();
1329 m_downloading = 0;
1330 m_failed_downloads = 0;
1331 DisableForDownload( false );
1332 //wxString old_label = m_bDnldCharts->GetLabel(); // Broken on Android??
1333 m_bDnldCharts->SetLabel( _("Abort download") );
1334 DownloadIsCancel = true;
1335
1336 wxFileName downloaded_p;
1337 int idx = -1;
1338
1339 for( int i = 0; i < GetChartCount() && to_download; i++ )
1340 {
1341 if( cancelled )
1342 break;
1343 //Prepare download queues
1344 if( !isChartChecked(i) )
1345 continue;
1346
1347 m_bTransferComplete = false;
1348 m_bTransferSuccess = true;
1349 m_totalsize = _("Unknown");
1350 m_transferredsize = _T("0");
1351 m_downloading++;
1352 if( pPlugIn->m_pChartCatalog->charts.Item(i).NeedsManualDownload() )
1353 {
1354 if( wxID_YES == OCPNMessageBox_PlugIn(this,
1355 wxString::Format( _("The selected chart '%s' can't be downloaded automatically, do you want me to open a browser window and download them manually?\n\n \
1356 After downloading the charts, please extract them to %s"), pPlugIn->m_pChartCatalog->charts.Item(i).title.c_str(), pPlugIn->m_pChartSource->GetDir().c_str() ), _("Chart Downloader"), wxYES_NO | wxCENTRE | wxICON_QUESTION ) )
1357 {
1358 wxLaunchDefaultBrowser( pPlugIn->m_pChartCatalog->charts.Item(i).GetManualDownloadUrl() );
1359 }
1360 continue;
1361 }
1362
1363 //download queue
1364 wxURI url(pPlugIn->m_pChartCatalog->charts.Item(i).GetDownloadLocation());
1365 if( url.IsReference() )
1366 {
1367 OCPNMessageBox_PlugIn(this, wxString::Format(_("Error, the URL to the chart (%s) data seems wrong."), url.BuildURI().c_str()), _("Error"));
1368 this->Enable();
1369 /// XXX undo anything? return or break?
1370 return;
1371 }
1372 //construct local file path
1373 wxString file = pPlugIn->m_pChartCatalog->charts.Item(i).GetChartFilename();
1374 wxFileName fn;
1375 fn.SetFullName(file);
1376 fn.SetPath(cs->GetDir());
1377 wxString path = fn.GetFullPath();
1378 if( wxFileExists( path ) )
1379 wxRemoveFile( path );
1380 wxString title = pPlugIn->m_pChartCatalog->charts.Item(i).GetChartTitle();
1381
1382 // Ready to start download
1383 #ifdef __OCPN__ANDROID__
1384 wxString file_path = _T("file://") + fn.GetFullPath();
1385 #else
1386 wxString file_path = fn.GetFullPath();
1387 #endif
1388
1389 long handle;
1390 OCPN_downloadFileBackground( url.BuildURI(), file_path, this, &handle);
1391
1392 if (idx >= 0) {
1393 pPlugIn->ProcessFile(downloaded_p.GetFullPath(), downloaded_p.GetPath(), true,
1394 pPlugIn->m_pChartCatalog->charts.Item(idx).GetUpdateDatetime());
1395
1396 cs->ChartUpdated( pPlugIn->m_pChartCatalog->charts.Item(idx).number,
1397 pPlugIn->m_pChartCatalog->charts.Item(idx).GetUpdateDatetime().GetTicks() );
1398 idx = -1;
1399 }
1400
1401 while( !m_bTransferComplete && m_bTransferSuccess && !cancelled )
1402 {
1403 if(m_failed_downloads)
1404 m_stCatalogInfo->SetLabel( wxString::Format( _("Downloading chart %u of %u, %u downloads failed (%s / %s)"),
1405 m_downloading, to_download, m_failed_downloads,
1406 m_transferredsize.c_str(), m_totalsize.c_str() ) );
1407 else
1408 m_stCatalogInfo->SetLabel( wxString::Format( _("Downloading chart %u of %u (%s / %s)"),
1409 m_downloading, to_download,
1410 m_transferredsize.c_str(), m_totalsize.c_str() ) );
1411
1412 //if(g_pi && g_pi->m_dldrpanel)
1413 //g_pi->m_dldrpanel->Raise();
1414 wxYield();
1415 wxMilliSleep(30);
1416 }
1417
1418 if(cancelled){
1419 idx = -1;
1420 OCPN_cancelDownloadFileBackground( handle );
1421 }
1422
1423 if( m_bTransferSuccess && !cancelled )
1424 {
1425 idx = i;
1426 downloaded_p = path;
1427 } else {
1428 idx = -1;
1429 if( wxFileExists( path ) )
1430 wxRemoveFile( path );
1431 m_failed_downloads++;
1432 }
1433 }
1434 if (idx >= 0) {
1435 pPlugIn->ProcessFile(downloaded_p.GetFullPath(), downloaded_p.GetPath(), true,
1436 pPlugIn->m_pChartCatalog->charts.Item(idx).GetUpdateDatetime());
1437
1438 cs->ChartUpdated( pPlugIn->m_pChartCatalog->charts.Item(idx).number,
1439 pPlugIn->m_pChartCatalog->charts.Item(idx).GetUpdateDatetime().GetTicks() );
1440 }
1441 DisableForDownload( true );
1442 m_bDnldCharts->SetLabel( _("Download selected charts") );
1443 DownloadIsCancel = false;
1444 SetSource(GetSelectedCatalog());
1445 if( m_failed_downloads > 0 && !updatingAll && !cancelled )
1446 OCPNMessageBox_PlugIn( this, wxString::Format( _("%d out of %d charts failed to download.\nCheck the list, verify there is a working Internet connection and repeat the operation if needed.")
1447 , m_failed_downloads, m_downloading ),
1448 _("Chart Downloader"), wxOK | wxICON_ERROR );
1449
1450 if( cancelled )
1451 OCPNMessageBox_PlugIn( this, _("Chart download cancelled."), _("Chart Downloader"), wxOK | wxICON_INFORMATION );
1452
1453 if( (m_downloading - m_failed_downloads > 0) && !updatingAll )
1454 ForceChartDBUpdate();
1455 }
1456
~ChartDldrPanelImpl()1457 ChartDldrPanelImpl::~ChartDldrPanelImpl()
1458 {
1459 Disconnect(wxEVT_DOWNLOAD_EVENT, (wxObjectEventFunction)(wxEventFunction)&ChartDldrPanelImpl::onDLEvent);
1460 m_bconnected = false;
1461
1462 #ifndef __OCPN__ANDROID__
1463 OCPN_cancelDownloadFileBackground( 0 ); //Stop the thread, is something like this needed on Android as well?
1464 #endif
1465
1466 #ifndef NEW_LIST
1467 m_lbChartSources->ClearAll();
1468 ((wxListCtrl *)m_clCharts)->DeleteAllItems();
1469 #else
1470 for(unsigned int i = 0 ; i < m_panelArray.GetCount() ; i++){
1471 delete m_panelArray.Item(i);
1472 }
1473 m_panelArray.Clear();
1474
1475 #endif
1476 }
1477
ChartDldrPanelImpl(chartdldr_pi * plugin,wxWindow * parent,wxWindowID id,const wxPoint & pos,const wxSize & size,long style)1478 ChartDldrPanelImpl::ChartDldrPanelImpl( chartdldr_pi* plugin, wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style )
1479 : ChartDldrPanel( parent, id, pos, size, style )
1480 {
1481 m_bDeleteSource->Disable();
1482 m_bUpdateChartList->Disable();
1483 m_bEditSource->Disable();
1484 m_lbChartSources->InsertColumn (0, _("Catalog"), wxLIST_FORMAT_LEFT, CATALOGS_NAME_WIDTH);
1485 m_lbChartSources->InsertColumn (1, _("Released"), wxLIST_FORMAT_LEFT, CATALOGS_DATE_WIDTH);
1486 m_lbChartSources->InsertColumn (2, _("Local path"), wxLIST_FORMAT_LEFT, CATALOGS_PATH_WIDTH);
1487 m_lbChartSources->Enable();
1488
1489 #ifndef NEW_LIST
1490 // Add columns
1491 ((wxListCtrl *)m_clCharts)->InsertColumn(0, _("Title"), wxLIST_FORMAT_LEFT, CHARTS_NAME_WIDTH);
1492 ((wxListCtrl *)m_clCharts)->InsertColumn(1, _("Status"), wxLIST_FORMAT_LEFT, CHARTS_STATUS_WIDTH);
1493 ((wxListCtrl *)m_clCharts)->InsertColumn(2, _("Latest"), wxLIST_FORMAT_LEFT, CHARTS_DATE_WIDTH);
1494 #endif
1495
1496 downloadInProgress = false;
1497 cancelled = true;
1498 to_download = -1;
1499 m_downloading = -1;
1500 updatingAll = false;
1501 pPlugIn = plugin;
1502 m_populated = false;
1503 DownloadIsCancel = false;
1504 m_failed_downloads = 0;
1505 m_stCatalogInfo->SetLabel( wxEmptyString );
1506 m_bTransferComplete = true;
1507 m_bTransferSuccess = true;
1508
1509 Connect(wxEVT_DOWNLOAD_EVENT, (wxObjectEventFunction)(wxEventFunction)&ChartDldrPanelImpl::onDLEvent);
1510 m_bconnected = true;
1511
1512 for (size_t i = 0; i < pPlugIn->m_pChartSources->GetCount(); i++)
1513 {
1514 AppendCatalog(pPlugIn->m_pChartSources->Item(i));
1515 }
1516 m_populated = true;
1517 }
1518
OnPaint(wxPaintEvent & event)1519 void ChartDldrPanelImpl::OnPaint( wxPaintEvent& event )
1520 {
1521 if( !m_populated )
1522 {
1523 m_populated = true;
1524 for (size_t i = 0; i < pPlugIn->m_pChartSources->GetCount(); i++)
1525 {
1526 AppendCatalog(pPlugIn->m_pChartSources->Item(i));
1527 }
1528 }
1529 #ifdef __WXMAC__
1530 // Mojave does not paint the controls correctly without this.
1531 m_lbChartSources->Refresh(true);
1532 #ifndef NEW_LIST
1533 m_clCharts->Refresh(true);
1534 #endif
1535 #endif
1536 event.Skip();
1537 }
1538
DeleteSource(wxCommandEvent & event)1539 void ChartDldrPanelImpl::DeleteSource( wxCommandEvent& event )
1540 {
1541 if( !m_lbChartSources->GetSelectedItemCount() )
1542 return;
1543 if( wxID_YES != OCPNMessageBox_PlugIn(this, _("Do you really want to remove the chart source?\nThe local chart files will not be removed,\nbut you will not be able to update the charts anymore."),
1544 _("Chart Downloader"), wxYES_NO | wxCENTRE) )
1545 return;
1546 int ToBeRemoved = GetSelectedCatalog();
1547 m_lbChartSources->SetItemState(ToBeRemoved, 0, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
1548 pPlugIn->m_pChartSources->RemoveAt(ToBeRemoved);
1549 m_lbChartSources->DeleteItem(ToBeRemoved);
1550 CleanForm();
1551 pPlugIn->SetSourceId(-1);
1552 SelectCatalog(-1);
1553 pPlugIn->SaveConfig();
1554 event.Skip();
1555 }
1556
AddSource(wxCommandEvent & event)1557 void ChartDldrPanelImpl::AddSource( wxCommandEvent& event )
1558 {
1559
1560 ChartDldrGuiAddSourceDlg *dialog = new ChartDldrGuiAddSourceDlg(this);
1561 dialog->SetBasePath(pPlugIn->GetBaseChartDir());
1562
1563 //#ifdef __OCPN__ANDROID__
1564 wxSize sz = GetParent()->GetGrandParent()->GetSize(); // This is the options panel true size
1565 dialog->SetSize(sz.GetWidth(), sz.GetHeight());
1566 dialog->Center();
1567 //#endif
1568
1569 dialog->ShowWindowModalThenDo([this,dialog](int retcode){
1570 if ( retcode == wxID_OK ) {
1571 ChartSource *cs = new ChartSource(dialog->m_tSourceName->GetValue(), dialog->m_tChartSourceUrl->GetValue(),
1572 dialog->m_tcChartDirectory->GetValue());
1573 pPlugIn->m_pChartSources->Add(cs);
1574 AppendCatalog(cs);
1575 bool covered = false;
1576 for( size_t i = 0; i < GetChartDBDirArrayString().GetCount(); i++ )
1577 {
1578 if( cs->GetDir().StartsWith((GetChartDBDirArrayString().Item(i))) )
1579 {
1580 covered = true;
1581 break;
1582 }
1583 }
1584 if( !covered )
1585 {
1586 wxString dir = cs->GetDir();
1587 AddChartDirectory( dir );
1588 }
1589 SelectCatalog(m_lbChartSources->GetItemCount() - 1);
1590 pPlugIn->SaveConfig();
1591 }
1592 });
1593 event.Skip();
1594 }
1595
DoEditSource()1596 void ChartDldrPanelImpl::DoEditSource()
1597 {
1598 if( !m_lbChartSources->GetSelectedItemCount() )
1599 return;
1600 int cat = GetSelectedCatalog();
1601 ChartDldrGuiAddSourceDlg *dialog = new ChartDldrGuiAddSourceDlg(this);
1602 dialog->SetBasePath(pPlugIn->GetBaseChartDir());
1603 dialog->SetSourceEdit(pPlugIn->m_pChartSources->Item(cat));
1604 dialog->SetTitle(_("Edit Chart Source"));
1605
1606 dialog->ShowWindowModalThenDo([this,dialog,cat](int retcode){
1607 if ( retcode == wxID_OK ) {
1608 pPlugIn->m_pChartSources->Item(cat)->SetName(dialog->m_tSourceName->GetValue());
1609 pPlugIn->m_pChartSources->Item(cat)->SetUrl(dialog->m_tChartSourceUrl->GetValue());
1610 pPlugIn->m_pChartSources->Item(cat)->SetDir(dialog->m_tcChartDirectory->GetValue());
1611
1612 m_lbChartSources->SetItem(cat, 0, pPlugIn->m_pChartSources->Item(cat)->GetName());
1613 m_lbChartSources->SetItem(cat, 1, _("(Please update first)"));
1614 m_lbChartSources->SetItem(cat, 2, pPlugIn->m_pChartSources->Item(cat)->GetDir());
1615 wxURI url(pPlugIn->m_pChartSources->Item(cat)->GetUrl());
1616 wxFileName fn(url.GetPath());
1617 fn.SetPath(pPlugIn->m_pChartSources->Item(cat)->GetDir());
1618 wxString path = fn.GetFullPath();
1619 if( wxFileExists(path) )
1620 {
1621 if( pPlugIn->m_pChartCatalog->LoadFromFile(path, true) )
1622 {
1623 m_lbChartSources->SetItem(cat, 0, pPlugIn->m_pChartCatalog->title);
1624 m_lbChartSources->SetItem(cat, 1, pPlugIn->m_pChartCatalog->GetReleaseDate().Format(_T("%Y-%m-%d %H:%M")));
1625 m_lbChartSources->SetItem(cat, 2, path);
1626 }
1627 }
1628 bool covered = false;
1629 for( size_t i = 0; i < GetChartDBDirArrayString().GetCount(); i++ )
1630 {
1631 if( pPlugIn->m_pChartSources->Item(cat)->GetDir().StartsWith((GetChartDBDirArrayString().Item(i))) )
1632 {
1633 covered = true;
1634 break;
1635 }
1636 }
1637 if( !covered )
1638 OCPNMessageBox_PlugIn( this, wxString::Format(_("Path %s seems not to be covered by your configured Chart Directories.\nTo see the charts you have to adjust the configuration on the 'Chart Files' tab."), pPlugIn->m_pChartSources->Item(cat)->GetDir().c_str()),
1639 _("Chart Downloader") );
1640
1641 pPlugIn->SaveConfig();
1642 SetSource(cat);
1643 }
1644 });
1645 }
1646
EditSource(wxCommandEvent & event)1647 void ChartDldrPanelImpl::EditSource( wxCommandEvent& event )
1648 {
1649 DoEditSource();
1650 event.Skip();
1651 }
1652
OnLeftDClick(wxMouseEvent & event)1653 void ChartDldrPanelImpl::OnLeftDClick( wxMouseEvent& event )
1654 {
1655 DoEditSource();
1656 event.Skip();
1657 }
1658
ProcessFile(const wxString & aFile,const wxString & aTargetDir,bool aStripPath,wxDateTime aMTime)1659 bool chartdldr_pi::ProcessFile( const wxString& aFile, const wxString& aTargetDir, bool aStripPath, wxDateTime aMTime )
1660 {
1661 if( aFile.Lower().EndsWith(_T("zip")) ) //Zip compressed
1662 {
1663 bool ret = ExtractZipFiles( aFile, aTargetDir, aStripPath, aMTime, false);
1664 if( ret )
1665 wxRemoveFile(aFile);
1666 else
1667 wxLogError(_T("chartdldr_pi: Unable to extract: ") + aFile );
1668 return ret;
1669 }
1670 #ifdef DLDR_USE_LIBARCHIVE
1671 else if( aFile.Lower().EndsWith(_T("rar")) )
1672 {
1673 #ifdef CHARTDLDR_RAR_UNARR
1674 bool ret = ExtractUnarrFiles( aFile, aTargetDir, aStripPath, aMTime, false);
1675 #else
1676 bool ret = ExtractLibArchiveFiles( aFile, aTargetDir, aStripPath, aMTime, false);
1677 #endif
1678 if( ret )
1679 wxRemoveFile(aFile);
1680 else
1681 wxLogError(_T("chartdldr_pi: Unable to extract: ") + aFile );
1682 return ret;
1683 }
1684 else if( aFile.Lower().EndsWith(_T("tar")) ||
1685 aFile.Lower().EndsWith(_T("gz")) ||
1686 aFile.Lower().EndsWith(_T("bz2")) ||
1687 aFile.Lower().EndsWith(_T("lzma")) ||
1688 aFile.Lower().EndsWith(_T("7z")) ||
1689 aFile.Lower().EndsWith(_T("xz"))
1690 )
1691 {
1692 bool ret = ExtractLibArchiveFiles( aFile, aTargetDir, aStripPath, aMTime, false);
1693 if( ret )
1694 wxRemoveFile(aFile);
1695 else
1696 wxLogError(_T("chartdldr_pi: Unable to extract: ") + aFile );
1697 return ret;
1698 }
1699 #else
1700 else if( aFile.Lower().EndsWith(_T("rar"))
1701 || aFile.Lower().EndsWith(_T("tar"))
1702 #ifdef HAVE_BZIP2
1703 || aFile.Lower().EndsWith(_T("bz2"))
1704 #endif
1705 #ifdef HAVE_ZLIB
1706 || aFile.Lower().EndsWith(_T("gz"))
1707 #endif
1708 #ifdef HAVE_7Z
1709 || aFile.Lower().EndsWith(_T("7z")) //TODO: Could it actually extract more formats the LZMA SDK supports?
1710 #endif
1711 )
1712 {
1713 bool ret = ExtractUnarrFiles( aFile, aTargetDir, aStripPath, aMTime, false);
1714 if( ret )
1715 wxRemoveFile(aFile);
1716 else
1717 wxLogError(_T("chartdldr_pi: Unable to extract: ") + aFile );
1718 return ret;
1719 }
1720 #endif
1721
1722 #ifdef __OCPN__ANDROID__
1723 else if( aFile.Lower().EndsWith(_T("tar")) ||
1724 aFile.Lower().EndsWith(_T("gz")) ||
1725 aFile.Lower().EndsWith(_T("bz2")) ||
1726 aFile.Lower().EndsWith(_T("lzma")) ||
1727 aFile.Lower().EndsWith(_T("7z")) ||
1728 aFile.Lower().EndsWith(_T("xz"))
1729 )
1730 {
1731 int nStrip = 0;
1732 if(aStripPath)
1733 nStrip = 1;
1734
1735 if(m_dldrpanel)
1736 m_dldrpanel->m_stCatalogInfo->SetLabel( _("Installing charts."));
1737
1738 androidShowBusyIcon();
1739 bool ret = AndroidUnzip(aFile, aTargetDir, nStrip, true);
1740 androidHideBusyIcon();
1741
1742 return ret;
1743 }
1744 #endif
1745
1746 else //Uncompressed
1747 {
1748 wxFileName fn(aFile);
1749 if( fn.GetPath() != aTargetDir ) //We have to move the file somewhere
1750 {
1751 if( !wxDirExists(aTargetDir) )
1752 {
1753 if( wxFileName::Mkdir(aTargetDir, 0755, wxPATH_MKDIR_FULL) )
1754 {
1755 if( !wxRenameFile(aFile, aTargetDir) )
1756 return false;
1757 }
1758 else
1759 return false;
1760 }
1761 }
1762 wxString name = fn.GetFullName();
1763 fn.Clear();
1764 fn.Assign(aTargetDir, name);
1765 fn.SetTimes(&aMTime, &aMTime, &aMTime);
1766 }
1767 return true;
1768 }
1769
1770 #ifdef DLDR_USE_LIBARCHIVE
1771 #ifndef __OCPN__ANDROID__
copy_data(struct archive * ar,struct archive * aw)1772 static int copy_data(struct archive *ar, struct archive *aw)
1773 {
1774 int r;
1775 const void *buff;
1776 size_t size;
1777 __LA_INT64_T offset;
1778
1779 for (;;) {
1780 r = archive_read_data_block(ar, &buff, &size, &offset);
1781 if (r == ARCHIVE_EOF)
1782 return (ARCHIVE_OK);
1783 if (r < ARCHIVE_OK)
1784 return (r);
1785 r = archive_write_data_block(aw, buff, size, offset);
1786 if (r < ARCHIVE_OK) {
1787 //fprintf(stderr, "%s\n", archive_error_string(aw));
1788 wxLogError(wxString::Format("Chartdldr_pi: LibArchive error: %s", archive_error_string(aw)));
1789 return (r);
1790 }
1791 }
1792 }
1793 #endif
1794
ExtractLibArchiveFiles(const wxString & aArchiveFile,const wxString & aTargetDir,bool aStripPath,wxDateTime aMTime,bool aRemoveArchive)1795 bool chartdldr_pi::ExtractLibArchiveFiles(const wxString& aArchiveFile, const wxString& aTargetDir, bool aStripPath, wxDateTime aMTime, bool aRemoveArchive)
1796 {
1797 #ifndef __OCPN__ANDROID__
1798 struct archive *a;
1799 struct archive *ext;
1800 struct archive_entry *entry;
1801 int flags;
1802 int r;
1803
1804 /* Select which attributes we want to restore. */
1805 flags = ARCHIVE_EXTRACT_TIME;
1806 /*
1807 flags |= ARCHIVE_EXTRACT_PERM;
1808 flags |= ARCHIVE_EXTRACT_ACL;
1809 flags |= ARCHIVE_EXTRACT_FFLAGS;
1810 */
1811
1812 a = archive_read_new();
1813 archive_read_support_format_all(a);
1814 archive_read_support_filter_all(a);
1815 archive_read_support_compression_all(a);
1816 ext = archive_write_disk_new();
1817 archive_write_disk_set_options(ext, flags);
1818 archive_write_disk_set_standard_lookup(ext);
1819 if ((r = archive_read_open_filename(a, aArchiveFile.c_str(), 10240))){
1820 return false;
1821 }
1822 for (;;) {
1823 r = archive_read_next_header(a, &entry);
1824 if (r == ARCHIVE_EOF)
1825 break;
1826 if (r < ARCHIVE_OK)
1827 //fprintf(stderr, "%s\n", archive_error_string(a));
1828 wxLogError(wxString::Format("Chartdldr_pi: LibArchive error: %s", archive_error_string(a)));
1829 if (r < ARCHIVE_WARN)
1830 return false;
1831 if (aStripPath) {
1832 const char* currentFile = archive_entry_pathname(entry);
1833 std::string fullOutputPath = currentFile;
1834 size_t sep = fullOutputPath.find_last_of("\\/");
1835 if (sep != std::string::npos)
1836 fullOutputPath = fullOutputPath.substr(sep + 1, fullOutputPath.size() - sep - 1);
1837 archive_entry_set_pathname(entry, fullOutputPath.c_str());
1838 }
1839 if (aTargetDir != wxEmptyString) {
1840 const char* currentFile = archive_entry_pathname(entry);
1841 const std::string fullOutputPath = aTargetDir.ToStdString() + wxString(wxFileName::GetPathSeparator()).ToStdString() + currentFile;
1842 archive_entry_set_pathname(entry, fullOutputPath.c_str());
1843 }
1844 r = archive_write_header(ext, entry);
1845 if (r < ARCHIVE_OK)
1846 //fprintf(stderr, "%s\n", archive_error_string(ext));
1847 wxLogError(wxString::Format("Chartdldr_pi: LibArchive error: %s", archive_error_string(ext)));
1848 else if (archive_entry_size(entry) > 0) {
1849 r = copy_data(a, ext);
1850 if (r < ARCHIVE_OK)
1851 //fprintf(stderr, "%s\n", archive_error_string(ext));
1852 wxLogError(wxString::Format("Chartdldr_pi: LibArchive error: %s", archive_error_string(ext)));
1853 if (r < ARCHIVE_WARN)
1854 return false;
1855 }
1856 r = archive_write_finish_entry(ext);
1857
1858 if (r < ARCHIVE_OK)
1859 //fprintf(stderr, "%s\n", archive_error_string(ext));
1860 wxLogError(wxString::Format("Chartdldr_pi: LibArchive error: %s", archive_error_string(ext)));
1861 if (r < ARCHIVE_WARN)
1862 return false;
1863 }
1864 archive_read_close(a);
1865 archive_read_free(a);
1866 archive_write_close(ext);
1867 archive_write_free(ext);
1868
1869 if( aRemoveArchive )
1870 wxRemoveFile(aArchiveFile);
1871 return true;
1872
1873 #else
1874
1875 return rv;
1876
1877 #endif // Android
1878 }
1879 #endif
1880
1881 #if defined(CHARTDLDR_RAR_UNARR) || !defined(DLDR_USE_LIBARCHIVE)
ar_open_any_archive(ar_stream * stream,const char * fileext)1882 ar_archive *ar_open_any_archive(ar_stream *stream, const char *fileext)
1883 {
1884 ar_archive *ar = ar_open_rar_archive(stream);
1885 if (!ar)
1886 ar = ar_open_zip_archive(stream, fileext && (strcmp(fileext, ".xps") == 0 || strcmp(fileext, ".epub") == 0));
1887 if (!ar)
1888 ar = ar_open_7z_archive(stream);
1889 if (!ar)
1890 ar = ar_open_tar_archive(stream);
1891 return ar;
1892 }
1893
ExtractUnarrFiles(const wxString & aRarFile,const wxString & aTargetDir,bool aStripPath,wxDateTime aMTime,bool aRemoveRar)1894 bool chartdldr_pi::ExtractUnarrFiles( const wxString& aRarFile, const wxString& aTargetDir, bool aStripPath, wxDateTime aMTime, bool aRemoveRar )
1895 {
1896 ar_stream *stream = NULL;
1897 ar_archive *ar = NULL;
1898 int entry_count = 1;
1899 int entry_skips = 0;
1900 int error_step = 1;
1901 bool ret = true;
1902
1903 stream = ar_open_file(aRarFile.c_str());
1904 if( !stream ) {
1905 wxLogError(_T("Can not open file '")+aRarFile+_T("'."));
1906 ar_close_archive(ar);
1907 ar_close(stream);
1908 return false;
1909 }
1910 ar = ar_open_any_archive(stream, strrchr(aRarFile.c_str(), '.'));
1911 if( !ar ) {
1912 wxLogError(_T("Can not open archive '")+aRarFile+_T("'."));
1913 ar_close_archive(ar);
1914 ar_close(stream);
1915 return false;
1916 }
1917 while (ar_parse_entry(ar)) {
1918 size_t size = ar_entry_get_size(ar);
1919 wxString name = ar_entry_get_name(ar);
1920 if( aStripPath )
1921 {
1922 wxFileName fn(name);
1923 /* We can completly replace the entry path */
1924 //fn.SetPath(aTargetDir);
1925 //name = fn.GetFullPath();
1926 /* Or only remove the first dir (eg. ENC_ROOT) */
1927 if (fn.GetDirCount() > 0)
1928 {
1929 fn.RemoveDir(0);
1930 name = aTargetDir + wxFileName::GetPathSeparator() + fn.GetFullPath();
1931 }
1932 else
1933 {
1934 name = aTargetDir + wxFileName::GetPathSeparator() + name;
1935 }
1936 }
1937 wxFileName fn(name);
1938 if( !fn.DirExists() )
1939 {
1940 if( !wxFileName::Mkdir(fn.GetPath()) )
1941 {
1942 wxLogError(_T("Can not create directory '") + fn.GetPath() + _T("'."));
1943 ret = false;
1944 break;
1945 }
1946 }
1947 wxFileOutputStream file(name);
1948 if( !file )
1949 {
1950 wxLogError(_T("Can not create file '")+name+_T("'."));
1951 ret = false;
1952 break;
1953 }
1954 while (size > 0) {
1955 unsigned char buffer[1024];
1956 size_t count = size < sizeof(buffer) ? size : sizeof(buffer);
1957 if (!ar_entry_uncompress(ar, buffer, count))
1958 break;
1959 file.Write(buffer, count);
1960 size -= count;
1961 }
1962 file.Close();
1963 fn.SetTimes(&aMTime, &aMTime, &aMTime);
1964 if (size > 0) {
1965 wxLogError("Warning: Failed to uncompress... skipping");
1966 entry_skips++;
1967 ret = false;
1968 }
1969 }
1970 if( !ar_at_eof(ar) ) {
1971 wxLogError("Error: Failed to parse entry %d!", entry_count);
1972 ret = false;
1973 }
1974 ar_close_archive(ar);
1975 ar_close(stream);
1976
1977 if( aRemoveRar )
1978 wxRemoveFile(aRarFile);
1979
1980 #ifdef _UNIX
1981 // reset LC_NUMERIC locale, some locales use a comma for decimal point
1982 // and it corrupts navobj.xml file
1983 setlocale(LC_NUMERIC, "C");
1984 #endif
1985
1986 return ret;
1987 }
1988 #endif
1989
ExtractZipFiles(const wxString & aZipFile,const wxString & aTargetDir,bool aStripPath,wxDateTime aMTime,bool aRemoveZip)1990 bool chartdldr_pi::ExtractZipFiles( const wxString& aZipFile, const wxString& aTargetDir, bool aStripPath, wxDateTime aMTime, bool aRemoveZip )
1991 {
1992 bool ret = true;
1993
1994 #ifdef __OCPN__ANDROID__
1995 int nStrip = 0;
1996 if(aStripPath)
1997 nStrip = 1;
1998
1999 ret = AndroidUnzip(aZipFile, aTargetDir, nStrip, true);
2000 #else
2001 std::unique_ptr<wxZipEntry> entry(new wxZipEntry());
2002
2003 do
2004 {
2005 wxLogMessage(_T("chartdldr_pi: Going to extract '")+aZipFile+_T("'."));
2006 wxFileInputStream in(aZipFile);
2007
2008 if( !in )
2009 {
2010 wxLogMessage(_T("Can not open file '")+aZipFile+_T("'."));
2011 ret = false;
2012 break;
2013 }
2014 wxZipInputStream zip(in);
2015 ret = false;
2016
2017 while( entry.reset(zip.GetNextEntry()), entry.get() != NULL )
2018 {
2019 // access meta-data
2020 wxString name = entry->GetName();
2021 if( aStripPath )
2022 {
2023 wxFileName fn(name);
2024 /* We can completly replace the entry path */
2025 //fn.SetPath(aTargetDir);
2026 //name = fn.GetFullPath();
2027 /* Or only remove the first dir (eg. ENC_ROOT) */
2028 if (fn.GetDirCount() > 0)
2029 fn.RemoveDir(0);
2030 name = aTargetDir + wxFileName::GetPathSeparator() + fn.GetFullPath();
2031 }
2032 else
2033 {
2034 name = aTargetDir + wxFileName::GetPathSeparator() + name;
2035 }
2036
2037 // read 'zip' to access the entry's data
2038 if( entry->IsDir() )
2039 {
2040 int perm = entry->GetMode();
2041 if( !wxFileName::Mkdir(name, perm, wxPATH_MKDIR_FULL) )
2042 {
2043 wxLogMessage(_T("Can not create directory '") + name + _T("'."));
2044 ret = false;
2045 break;
2046 }
2047 }
2048 else
2049 {
2050 if( !zip.OpenEntry(*entry.get()) )
2051 {
2052 wxLogMessage(_T("Can not open zip entry '") + entry->GetName() + _T("'."));
2053 ret = false;
2054 break;
2055 }
2056 if( !zip.CanRead() )
2057 {
2058 wxLogMessage(_T("Can not read zip entry '") + entry->GetName() + _T("'."));
2059 ret = false;
2060 break;
2061 }
2062
2063 wxFileName fn(name);
2064 if( !fn.DirExists() )
2065 {
2066 if( !wxFileName::Mkdir(fn.GetPath()) )
2067 {
2068 wxLogMessage(_T("Can not create directory '") + fn.GetPath() + _T("'."));
2069 ret = false;
2070 break;
2071 }
2072 }
2073
2074 wxFileOutputStream file(name);
2075
2076 if( !file )
2077 {
2078 wxLogMessage(_T("Can not create file '")+name+_T("'."));
2079 ret = false;
2080 break;
2081 }
2082 zip.Read(file);
2083 fn.SetTimes(&aMTime, &aMTime, &aMTime);
2084 ret = true;
2085 }
2086
2087 }
2088
2089 }
2090 while(false);
2091
2092 if( aRemoveZip )
2093 wxRemoveFile(aZipFile);
2094 #endif //Android
2095
2096 return ret;
2097 }
2098
ChartDldrGuiAddSourceDlg(wxWindow * parent)2099 ChartDldrGuiAddSourceDlg::ChartDldrGuiAddSourceDlg( wxWindow* parent ) : AddSourceDlg( parent )
2100 {
2101 wxFileName fn;
2102 fn.SetPath(*GetpSharedDataLocation());
2103 fn.AppendDir(_T("plugins"));
2104 fn.AppendDir(_T("chartdldr_pi"));
2105 fn.AppendDir(_T("data"));
2106
2107 int w = 16; // default for desktop
2108 int h = 16;
2109
2110 #ifdef __OCPN__ANDROID__
2111 w = 6 * g_androidDPmm; // mm nominal size
2112 h = w;
2113
2114 p_buttonIconList = new wxImageList(w, h);
2115
2116 fn.SetFullName(_T("button_right.png"));
2117 wxImage im1(fn.GetFullPath(), wxBITMAP_TYPE_PNG);
2118 im1.Rescale(w, h, wxIMAGE_QUALITY_HIGH);
2119 p_buttonIconList->Add(im1);
2120
2121 fn.SetFullName(_T("button_right.png"));
2122 wxImage im2(fn.GetFullPath(), wxBITMAP_TYPE_PNG);
2123 im2.Rescale(w, h, wxIMAGE_QUALITY_HIGH);
2124 p_buttonIconList->Add(im2);
2125
2126 fn.SetFullName(_T("button_down.png"));
2127 wxImage im3(fn.GetFullPath(), wxBITMAP_TYPE_PNG);
2128 im3.Rescale(w, h, wxIMAGE_QUALITY_HIGH);
2129 p_buttonIconList->Add(im3);
2130
2131 fn.SetFullName(_T("button_down.png"));
2132 wxImage im4(fn.GetFullPath(), wxBITMAP_TYPE_PNG);
2133 im4.Rescale(w, h, wxIMAGE_QUALITY_HIGH);
2134 p_buttonIconList->Add(im4);
2135
2136 m_treeCtrlPredefSrcs->AssignButtonsImageList(p_buttonIconList);
2137 #else
2138 p_iconList = new wxImageList(w, h);
2139
2140 fn.SetFullName(_T("folder.png"));
2141 wxImage ima(fn.GetFullPath(), wxBITMAP_TYPE_PNG);
2142 ima.Rescale(w, h, wxIMAGE_QUALITY_HIGH);
2143 p_iconList->Add(ima);
2144
2145 fn.SetFullName(_T("file.png"));
2146 wxImage imb(fn.GetFullPath(), wxBITMAP_TYPE_PNG);
2147 imb.Rescale(w, h, wxIMAGE_QUALITY_HIGH);
2148 p_iconList->Add(imb);
2149
2150 m_treeCtrlPredefSrcs->AssignImageList(p_iconList);
2151 #endif /* __OCPN__ANDROID__ */
2152
2153 m_treeCtrlPredefSrcs->SetIndent( w );
2154
2155 m_base_path = wxEmptyString;
2156 m_last_path = wxEmptyString;
2157 LoadSources();
2158 m_nbChoice->SetSelection(0);
2159 //m_treeCtrlPredefSrcs->ExpandAll();
2160
2161 Fit();
2162
2163 applyStyle();
2164 }
2165
LoadSources()2166 bool ChartDldrGuiAddSourceDlg::LoadSources()
2167 {
2168 wxTreeItemId tree = m_treeCtrlPredefSrcs->AddRoot(_T("root"));
2169
2170 wxFileName fn;
2171 fn.SetPath(*GetpPrivateApplicationDataLocation());
2172 fn.SetFullName(_T("chartdldr_pi-chart_sources.xml"));
2173 if( !fn.FileExists() )
2174 {
2175 fn.SetPath(*GetpSharedDataLocation());
2176 fn.AppendDir(_T("plugins"));
2177 fn.AppendDir(_T("chartdldr_pi"));
2178 fn.AppendDir(_T("data"));
2179 fn.SetFullName(_T("chart_sources.xml"));
2180 if( !fn.FileExists() )
2181 {
2182 wxLogMessage( wxString::Format(_T("Error: chartdldr_pi::LoadSources() %s not found!"), fn.GetFullPath().c_str()) );
2183 return false;
2184 }
2185 }
2186 wxString path = fn.GetFullPath();
2187
2188 pugi::xml_document *doc = new pugi::xml_document;
2189 bool ret = doc->load_file( path.mb_str() );
2190 if( ret )
2191 {
2192 pugi::xml_node root = doc->first_child();
2193
2194 for (pugi::xml_node element = root.first_child(); element; element = element.next_sibling()){
2195 if( !strcmp(element.name(), "sections") ){
2196 LoadSections(tree, element);
2197 }
2198 }
2199 }
2200 wxDELETE(doc);
2201 return true;
2202 }
2203
LoadSections(const wxTreeItemId & root,pugi::xml_node & node)2204 bool ChartDldrGuiAddSourceDlg::LoadSections( const wxTreeItemId &root, pugi::xml_node &node )
2205 {
2206 for (pugi::xml_node element = node.first_child(); element; element = element.next_sibling()){
2207 if( !strcmp(element.name(), "section") ){
2208 LoadSection(root, element);
2209 }
2210 }
2211 return true;
2212 }
2213
LoadSection(const wxTreeItemId & root,pugi::xml_node & node)2214 bool ChartDldrGuiAddSourceDlg::LoadSection( const wxTreeItemId &root, pugi::xml_node &node )
2215 {
2216 wxTreeItemId item;
2217 for (pugi::xml_node element = node.first_child(); element; element = element.next_sibling()){
2218 if( !strcmp(element.name(), "name") ){
2219 item = m_treeCtrlPredefSrcs->AppendItem(root, wxString::FromUTF8(element.first_child().value()), 0, 0);
2220
2221 wxFont *pFont = OCPNGetFont(_T("Dialog"), 0);
2222 if( pFont ) m_treeCtrlPredefSrcs->SetItemFont( item, *pFont );
2223 }
2224 if( !strcmp(element.name(), "sections") )
2225 LoadSections(item, element);
2226 if( !strcmp(element.name(), "catalogs") )
2227 LoadCatalogs(item, element);
2228
2229 }
2230
2231 return true;
2232 }
2233
LoadCatalogs(const wxTreeItemId & root,pugi::xml_node & node)2234 bool ChartDldrGuiAddSourceDlg::LoadCatalogs( const wxTreeItemId &root, pugi::xml_node &node )
2235 {
2236 for (pugi::xml_node element = node.first_child(); element; element = element.next_sibling()){
2237 if( !strcmp(element.name(), "catalog") )
2238 LoadCatalog(root, element);
2239 }
2240
2241 return true;
2242 }
2243
LoadCatalog(const wxTreeItemId & root,pugi::xml_node & node)2244 bool ChartDldrGuiAddSourceDlg::LoadCatalog( const wxTreeItemId &root, pugi::xml_node &node )
2245 {
2246 wxString name, type, location, dir;
2247 for (pugi::xml_node element = node.first_child(); element; element = element.next_sibling()){
2248 if( !strcmp(element.name(), "name") )
2249 name = wxString::FromUTF8(element.first_child().value());
2250 else if( !strcmp(element.name(), "type") )
2251 type = wxString::FromUTF8(element.first_child().value());
2252 else if( !strcmp(element.name(), "location") )
2253 location = wxString::FromUTF8(element.first_child().value());
2254 else if( !strcmp(element.name(), "dir") )
2255 dir = wxString::FromUTF8(element.first_child().value());
2256 }
2257 ChartSource *cs = new ChartSource(name, location, dir);
2258 wxTreeItemId id = m_treeCtrlPredefSrcs->AppendItem(root, name, 1, 1, cs);
2259
2260 wxFont *pFont = OCPNGetFont(_T("Dialog"), 0);
2261 if( pFont ) m_treeCtrlPredefSrcs->SetItemFont( id, *pFont );
2262
2263 return true;
2264 }
2265
~ChartDldrGuiAddSourceDlg()2266 ChartDldrGuiAddSourceDlg::~ChartDldrGuiAddSourceDlg()
2267 {
2268 }
2269
FixPath(wxString path)2270 wxString ChartDldrGuiAddSourceDlg::FixPath( wxString path )
2271 {
2272 wxString sep( wxFileName::GetPathSeparator() );
2273 wxString s = path;
2274 s.Replace(_T("/"), sep, true);
2275 s.Replace(_T(USERDATA), m_base_path);
2276 s.Replace(sep + sep, sep);
2277 return s;
2278 }
2279
OnChangeType(wxCommandEvent & event)2280 void ChartDldrGuiAddSourceDlg::OnChangeType( wxCommandEvent& event )
2281 {
2282 m_treeCtrlPredefSrcs->Enable(m_nbChoice->GetSelection()==0);
2283 m_tSourceName->Enable(m_nbChoice->GetSelection()==1);
2284 m_tChartSourceUrl->Enable(m_nbChoice->GetSelection()==1);
2285 }
2286
OnSourceSelected(wxTreeEvent & event)2287 void ChartDldrGuiAddSourceDlg::OnSourceSelected( wxTreeEvent& event )
2288 {
2289 wxTreeItemId item = m_treeCtrlPredefSrcs->GetSelection();
2290 ChartSource *cs = (ChartSource *)(m_treeCtrlPredefSrcs->GetItemData(item));
2291 if( cs ) {
2292 m_tSourceName->SetValue(cs->GetName());
2293 m_tChartSourceUrl->SetValue(cs->GetUrl());
2294 if( m_tcChartDirectory->GetValue() == m_last_path )
2295 {
2296 m_tcChartDirectory->SetValue(FixPath(cs->GetDir()));
2297 m_last_path = m_tcChartDirectory->GetValue();
2298 }
2299 }
2300 event.Skip();
2301 }
2302
SetSourceEdit(ChartSource * cs)2303 void ChartDldrGuiAddSourceDlg::SetSourceEdit( ChartSource* cs )
2304 {
2305 m_nbChoice->SetSelection(1);
2306 m_tChartSourceUrl->Enable();
2307 m_treeCtrlPredefSrcs->Disable();
2308 m_tSourceName->SetValue(cs->GetName());
2309 m_tChartSourceUrl->SetValue(cs->GetUrl());
2310 m_tcChartDirectory->SetValue(FixPath(cs->GetDir()));
2311 }
2312
ChartDldrPrefsDlgImpl(wxWindow * parent)2313 ChartDldrPrefsDlgImpl::ChartDldrPrefsDlgImpl( wxWindow* parent ) : ChartDldrPrefsDlg( parent )
2314 {
2315 }
2316
~ChartDldrPrefsDlgImpl()2317 ChartDldrPrefsDlgImpl::~ChartDldrPrefsDlgImpl()
2318 {
2319 }
2320
SetPath(const wxString path)2321 void ChartDldrPrefsDlgImpl::SetPath( const wxString path )
2322 {
2323 //if( !wxDirExists(path) )
2324 //if( !wxFileName::Mkdir(path, 0755, wxPATH_MKDIR_FULL) )
2325 //{
2326 // OCPNMessageBox_PlugIn(this, wxString::Format(_("Directory %s can't be created."), m_dpDefaultDir->GetTextCtrlValue().c_str()), _("Chart Downloader"));
2327 // return;
2328 //}
2329 m_tcDefaultDir->SetValue(path);
2330 }
2331
GetPreferences(bool & preselect_new,bool & preselect_updated,bool & bulk_update)2332 void ChartDldrPrefsDlgImpl::GetPreferences( bool &preselect_new, bool &preselect_updated, bool &bulk_update )
2333 {
2334 preselect_new = m_cbSelectNew->GetValue();
2335 preselect_updated = m_cbSelectUpdated->GetValue();
2336 bulk_update = m_cbBulkUpdate->GetValue();
2337 }
SetPreferences(bool preselect_new,bool preselect_updated,bool bulk_update)2338 void ChartDldrPrefsDlgImpl::SetPreferences( bool preselect_new, bool preselect_updated, bool bulk_update )
2339 {
2340 m_cbSelectNew->SetValue(preselect_new);
2341 m_cbSelectUpdated->SetValue(preselect_updated);
2342 m_cbBulkUpdate->SetValue(bulk_update);
2343 }
2344
OnOkClick(wxCommandEvent & event)2345 void ChartDldrGuiAddSourceDlg::OnOkClick( wxCommandEvent& event )
2346 {
2347 wxString msg = wxEmptyString;
2348
2349 if( m_nbChoice->GetSelection()==0 )
2350 {
2351 wxTreeItemId item = m_treeCtrlPredefSrcs->GetSelection();
2352 if( m_treeCtrlPredefSrcs->GetSelection().IsOk() )
2353 {
2354 ChartSource *cs = (ChartSource *)(m_treeCtrlPredefSrcs->GetItemData(item));
2355 if ( !cs )
2356 msg += _("You must select one of the predefined chart sources or create one of your own.\n");
2357 }
2358 else
2359 msg += _("You must select one of the predefined chart sources or create one of your own.\n");
2360 }
2361 if( m_nbChoice->GetSelection()==1 && m_tSourceName->GetValue() == wxEmptyString )
2362 msg += _("The chart source must have a name.\n");
2363 wxURI url(m_tChartSourceUrl->GetValue());
2364 if( m_nbChoice->GetSelection()==1 && ( m_tChartSourceUrl->GetValue() == wxEmptyString || !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
2369 if( !wxDirExists(m_tcChartDirectory->GetValue()) )
2370 if( !wxFileName::Mkdir(m_tcChartDirectory->GetValue(), 0755, wxPATH_MKDIR_FULL) )
2371 msg += wxString::Format(_("Directory %s can't be created."), m_tcChartDirectory->GetValue().c_str()) + _T("\n");
2372
2373 if( msg != wxEmptyString )
2374 OCPNMessageBox_PlugIn( this, msg, _("Chart source definition problem"), wxOK | wxCENTRE | wxICON_ERROR );
2375 else {
2376 event.Skip();
2377 SetReturnCode(wxID_OK);
2378 EndModal( wxID_OK );
2379 }
2380 }
2381
OnCancelClick(wxCommandEvent & event)2382 void ChartDldrGuiAddSourceDlg::OnCancelClick( wxCommandEvent& event )
2383 {
2384 SetReturnCode(wxID_CANCEL);
2385 EndModal( wxID_CANCEL );
2386 }
2387
2388
OnOkClick(wxCommandEvent & event)2389 void ChartDldrPrefsDlgImpl::OnOkClick( wxCommandEvent& event )
2390 {
2391
2392 if( !wxDirExists(m_tcDefaultDir->GetValue()) ){
2393 if( !wxFileName::Mkdir(m_tcDefaultDir->GetValue(), 0755, wxPATH_MKDIR_FULL) )
2394 {
2395 OCPNMessageBox_PlugIn(this, wxString::Format(_("Directory %s can't be created."), m_tcDefaultDir->GetValue().c_str()), _("Chart Downloader"));
2396 return;
2397 }
2398 }
2399
2400 if(g_pi){
2401 g_pi->UpdatePrefs(this);
2402 }
2403
2404 event.Skip();
2405 EndModal( wxID_OK );
2406
2407 //Hide();
2408 //Close();
2409 }
2410
OnCancelClick(wxCommandEvent & event)2411 void ChartDldrPrefsDlg::OnCancelClick( wxCommandEvent& event )
2412 {
2413 event.Skip();
2414 EndModal( wxID_CANCEL );
2415 //Close();
2416 }
2417
OnOkClick(wxCommandEvent & event)2418 void ChartDldrPrefsDlg::OnOkClick( wxCommandEvent& event )
2419 {
2420 event.Skip();
2421 //Close();
2422 }
2423
2424
ValidateUrl(const wxString Url,bool catalog_xml)2425 bool ChartDldrGuiAddSourceDlg::ValidateUrl( const wxString Url, bool catalog_xml )
2426 {
2427 wxRegEx re;
2428 if( catalog_xml )
2429 re.Compile( _T("^https?\\://[a-zA-Z0-9\\./_-]*\\.[xX][mM][lL]$") ); //TODO: wxRegEx sucks a bit, this RE is way too naive
2430 else
2431 re.Compile( _T("^https?\\://[a-zA-Z0-9\\./_-]*$") ); //TODO: wxRegEx sucks a bit, this RE is way too naive
2432 return re.Matches(Url);
2433 }
2434
FormatBytes(double bytes)2435 static wxString FormatBytes(double bytes)
2436 {
2437 return wxString::Format( _T("%.1fMB"), bytes / 1024 / 1024 );
2438 }
2439
onDLEvent(OCPN_downloadEvent & ev)2440 void ChartDldrPanelImpl::onDLEvent(OCPN_downloadEvent &ev)
2441 {
2442 // wxString msg;
2443 // msg.Printf(_T("onDLEvent %d %d"),ev.getDLEventCondition(), ev.getDLEventStatus());
2444 // wxLogMessage(msg);
2445
2446 switch(ev.getDLEventCondition()){
2447 case OCPN_DL_EVENT_TYPE_END:
2448 m_bTransferComplete = true;
2449 m_bTransferSuccess = (ev.getDLEventStatus() == OCPN_DL_NO_ERROR) ? true : false;
2450 break;
2451
2452 case OCPN_DL_EVENT_TYPE_PROGRESS:
2453 m_totalsize = FormatBytes( ev.getTotal() );
2454 m_transferredsize = FormatBytes( ev.getTransferred() );
2455
2456 break;
2457 default:
2458 break;
2459 }
2460 }
2461
2462
2463
2464
2465
2466
2467
2468