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