KiCad PCB EDA Suite
wizard_3DShape_Libs_downloader.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2015 CERN
5  * Code derived from "wizard_add_fplib.cpp" ( author Maciej Suminski <maciej.suminski@cern.ch> )
6  * Copyright (C) 2014-2015 Jean-Pierre Charras, jp.charras at wanadoo.fr
7  * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors.
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License
11  * as published by the Free Software Foundation; either version 2
12  * of the License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, you may find one here:
21  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
22  * or you may search the http://www.gnu.org website for the version 2 license,
23  * or you may write to the Free Software Foundation, Inc.,
24  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
25  */
26 
35 #include <wx/wx.h>
36 #include <wx/uri.h>
37 #include <wx/dir.h>
38 #include <wx/progdlg.h>
39 #include <wx/config.h>
40 
41 #include <pgm_base.h>
42 #include <project.h>
44 #include <confirm.h>
45 #include <3d_viewer.h>
46 #include <bitmaps.h>
47 
48 #include <../github/github_getliblist.h>
49 
50 // a key to store the default Kicad Github 3D libs URL
51 #define KICAD_3DLIBS_URL_KEY wxT( "kicad_3Dlib_url" )
52 #define KICAD_3DLIBS_LAST_DOWNLOAD_DIR wxT( "kicad_3Dlib_last_download_dir" )
53 
54 #define DEFAULT_GITHUB_3DSHAPES_LIBS_URL \
55  wxT( "https://github.com/KiCad/kicad-library/tree/master/modules/packages3d" )
56 
57 void Invoke3DShapeLibsDownloaderWizard( wxTopLevelWindow* aParent )
58 {
59  WIZARD_3DSHAPE_LIBS_DOWNLOADER wizard( aParent );
60  wizard.RunWizard( wizard.GetFirstPage() );
61 }
62 
63 
66 {
67  m_welcomeDlg = m_pages[0];
69  m_reviewDlg = m_pages[2];
70 
71  // Initialize default download dir (local target folder of 3D shapes libs)
72  wxString default_path;
73  wxGetEnv( KISYS3DMOD, &default_path );
74 
75  wxConfigBase* cfg = Pgm().CommonSettings();
76  wxString tmp;
77  cfg->Read( KICAD_3DLIBS_LAST_DOWNLOAD_DIR, &tmp, default_path );
78  setDownloadDir( tmp );
79 
80  // Restore the Github 3D shapes libs url
81  wxString githubUrl;
82  cfg->Read( KICAD_3DLIBS_URL_KEY, &githubUrl );
83 
84  if( githubUrl.IsEmpty() )
86 
87  SetGithubURL( githubUrl );
88 
89 
90  // Give the minimal size to the dialog, which allows displaying any page
91  wxSize minsize;
92 
93  for( unsigned ii = 0; ii < m_pages.size(); ii++ )
94  {
95  wxSize size = m_pages[ii]->GetSizer()->CalcMin();
96  minsize.x = std::max( minsize.x, size.x );
97  minsize.y = std::max( minsize.y, size.y );
98  }
99 
100  SetMinSize( minsize );
101  SetPageSize( minsize );
102  GetSizer()->SetSizeHints( this );
103  Center();
104 
107 
108  // When starting m_textCtrlGithubURL has the focus, and the text is selected,
109  // and not fully visible.
110  // Forcing deselection does not work, at least on W7 with wxWidgets 3.0.2
111  // So (and also because m_textCtrlGithubURL and m_downloadDir are rarely modified
112  // the focus is given to an other widget.
113  m_hyperlinkGithubKicad->SetFocus();
114 
115  Connect( wxEVT_RADIOBUTTON, wxCommandEventHandler( WIZARD_3DSHAPE_LIBS_DOWNLOADER::OnSourceCheck ), NULL, this );
116  Connect( wxEVT_CHECKLISTBOX, wxCommandEventHandler( WIZARD_3DSHAPE_LIBS_DOWNLOADER::OnCheckGithubList ), NULL, this );
117 }
118 
119 
121 {
122  // Use this if you want to store kicad lib URL in pcbnew/cvpcb section config:
123  // wxConfigBase* cfg = Kiface().KifaceSettings();
124 
125  // Use this if you want to store kicad lib URL in common section config:
126  wxConfigBase* cfg = Pgm().CommonSettings();
127  cfg->Write( KICAD_3DLIBS_URL_KEY, GetGithubURL() );
129 }
130 
131 
132 
134 {
135  SetBitmap( KiBitmap( wizard_add_fplib_icon_xpm ) );
136  enableNext( true );
137 
138  if( GetCurrentPage() == m_githubListDlg )
139  setupGithubList();
140  else if( GetCurrentPage() == m_reviewDlg )
141  setupReview();
142 }
143 
144 
146 {
147  wxArrayInt dummy;
148 
149  enableNext( m_checkList3Dlibnames->GetCheckedItems( dummy ) > 0 );
150 }
151 
152 
153 void WIZARD_3DSHAPE_LIBS_DOWNLOADER::OnSourceCheck( wxCommandEvent& aEvent )
154 {
157 }
158 
160 {
161  // Adjust the width of the column 1 afo m_gridLibReview (library names) to the
162  // max available width.
163  int gridwidth = m_gridLibReview->GetClientSize().x;
164  gridwidth -= m_gridLibReview->GetColSize( 0 ) + m_gridLibReview->GetColLabelSize();
165 
166  if( gridwidth < 200 )
167  gridwidth = 200;
168 
169  m_gridLibReview->SetColSize( 1, gridwidth );
170 
171  event.Skip();
172 }
173 
174 
176 {
177  // Prepare the last page of the wizard.
178 
179  m_LocalFolderInfo->SetLabel( getDownloadDir() );
180 
181  wxArrayInt checkedIndices;
182  m_checkList3Dlibnames->GetCheckedItems( checkedIndices );
183 
184  m_libraries.Clear();
185 
186  // populate m_libraries with the name of libraries, without the github path:
187  for( unsigned int ii = 0; ii < checkedIndices.GetCount(); ++ii )
188  {
189  m_libraries.Add( m_checkList3Dlibnames->GetString( checkedIndices[ii] ).AfterLast( '/' ) );
190  }
191 
192  // Adjust number of rows in m_gridLibReview:
193  int delta = m_libraries.GetCount() - m_gridLibReview->GetNumberRows();
194 
195  if( delta < 0 )
196  m_gridLibReview->DeleteRows( -delta );
197  else if( delta > 0 )
198  m_gridLibReview->AppendRows( delta );
199 
200  // For user info, verify the existence of these libs in local folder
201  wxArrayString liblist;
202  wxFileName fn;
203  fn.AssignDir( getDownloadDir() );
204 
205  for( unsigned int ii = 0; ii < m_libraries.GetCount(); ++ii )
206  {
207  fn.SetName( m_libraries[ii] );
208 
209  wxDir dirs;
210  bool isNew = ! dirs.Exists( fn.GetFullPath() );
211  wxString info = isNew ? _( "New" ) : _( "Update" );
212 
213  liblist.Add( info + wxT(" ") + m_libraries[ii] );
214 
215  m_gridLibReview->SetCellValue( ii, 0, info );
216  m_gridLibReview->SetCellValue( ii, 1, m_libraries[ii] );
217  }
218 
219  m_gridLibReview->AutoSizeColumn( 0 );
220 }
221 
222 
224 {
225  for( unsigned int i = 0; i < m_checkList3Dlibnames->GetCount(); ++i )
226  m_checkList3Dlibnames->Check( i, true );
227 
228  // The list might be empty, e.g. in case of download error
229  wxArrayInt dummy;
230  enableNext( m_checkList3Dlibnames->GetCheckedItems( dummy ) > 0 );
231 }
232 
233 
235 {
236  for( unsigned int i = 0; i < m_checkList3Dlibnames->GetCount(); ++i )
237  m_checkList3Dlibnames->Check( i, false );
238 
239  enableNext( false );
240 }
241 
242 
244 {
245  wxString searchPhrase = m_searchCtrl3Dlibs->GetValue().Lower();
246 
247  // Store the current selection
248  wxArrayInt checkedIndices;
249  m_checkList3Dlibnames->GetCheckedItems( checkedIndices );
250  wxArrayString checkedStrings;
251 
252  for( unsigned int i = 0; i < checkedIndices.GetCount(); ++i )
253  checkedStrings.Add( m_checkList3Dlibnames->GetString( checkedIndices[i] ).AfterLast( '/' ) );
254 
255  m_checkList3Dlibnames->Clear();
256 
257  // Rebuild the list, putting the matching entries on the top
258  int matching = 0; // number of entries matching the search phrase
259  for( unsigned int i = 0; i < m_githubLibs.GetCount(); ++i )
260  {
261  const wxString& lib = m_githubLibs[i].AfterLast( '/' );
262  bool wasChecked = ( checkedStrings.Index( lib ) != wxNOT_FOUND );
263  int insertedIdx = -1;
264 
265  if( !searchPhrase.IsEmpty() && lib.Lower().Contains( searchPhrase ) )
266  {
267  insertedIdx = m_checkList3Dlibnames->Insert( lib, matching++ );
268  m_checkList3Dlibnames->SetSelection( insertedIdx );
269  }
270  else
271  insertedIdx = m_checkList3Dlibnames->Append( lib );
272 
273  if( wasChecked )
274  m_checkList3Dlibnames->Check( insertedIdx );
275  }
276 
277  if( !m_checkList3Dlibnames->IsEmpty() )
278  m_checkList3Dlibnames->EnsureVisible( 0 );
279 }
280 
281 
283 {
284  // we download a localy copy of the libraries
285  wxString error;
286 
287  if( !downloadGithubLibsFromList( m_libraries, &error ) )
288  {
289  DisplayError( GetParent(), error );
290  }
291 }
292 
293 
295 {
296  wxString path = getDownloadDir();
297 
298  path = wxDirSelector( _("Choose a folder to save the downloaded libraries" ),
299  path, 0, wxDefaultPosition, this );
300 
301  if( !path.IsEmpty() && wxDirExists( path ) )
302  {
303  setDownloadDir( path );
305  }
306 }
307 
308 
310 {
311  wxString default_path;
312  wxGetEnv( KISYS3DMOD, &default_path );
313 
314  if( !default_path.IsEmpty() && wxDirExists( default_path ) )
315  {
316  setDownloadDir( default_path );
318  }
319  else
320  wxMessageBox( _( "KISYS3DMOD path not defined , or not existing" ) );
321 }
322 
323 
325 {
327 }
328 
329 
331 {
332  wxBeginBusyCursor();
333 
334  // Be sure there is no trailing '/' at the end of the repo name
335  wxString git_url = m_textCtrlGithubURL->GetValue();
336 
337  if( git_url.EndsWith( wxT( "/" ) ) )
338  {
339  git_url.RemoveLast();
340  m_textCtrlGithubURL->SetValue( git_url );
341  }
342 
343  GITHUB_GETLIBLIST getter( git_url );
345 
346  wxEndBusyCursor();
347 }
348 
349 
350 // Download the .pretty libraries folders found in aUrlList and store them on disk
351 // in a master folder
353  wxString* aErrorMessage )
354 {
355  // Display a progress bar to show the download state
356  // The title is updated for each downloaded library.
357  // the state will be updated by downloadOneLib() for each file.
358  wxProgressDialog pdlg( _( "Downloading 3D libraries" ), wxEmptyString,
359  aUrlList.GetCount(), GetParent(),
360  wxPD_CAN_ABORT | wxPD_APP_MODAL | wxPD_AUTO_HIDE );
361 
362  wxString url_base = GetGithubURL();
363 
364  // Download libs:
365  for( unsigned ii = 0; ii < aUrlList.GetCount(); ii++ )
366  {
367  wxString& libsrc_name = aUrlList[ii];
368 
369  // Extract the lib name from the full URL:
370  wxString url = GetGithubURL() + wxT( "/" ) + libsrc_name;
371  wxFileName fn( libsrc_name );
372  // Set our local path
373  fn.SetPath( getDownloadDir() );
374  wxString libdst_name = fn.GetFullPath();
375 
376  // Display the name of the library to download in the wxProgressDialog
377  pdlg.SetTitle( wxString::Format( wxT("%s [%d/%d]" ),
378  libsrc_name.AfterLast( '/' ).GetData(),
379  ii + 1, aUrlList.GetCount() ) );
380 
381  if( !wxDirExists( libdst_name ) )
382  wxMkdir( libdst_name );
383 
384  if( !downloadOneLib( url, libdst_name, &pdlg, aErrorMessage ) )
385  return false;
386  }
387 
388  return true;
389 }
390 
391 
393  const wxString& aLocalLibName, wxProgressDialog * aIndicator,
394  wxString* aErrorMessage )
395 {
396  wxArrayString fileslist;
397 
398  bool success;
399 
400  // Get the list of candidate files: with ext .wrl or .wings
401  do
402  {
403  GITHUB_GETLIBLIST getter( aLibURL );
404  success = getter.Get3DshapesLibsList( &fileslist, filter3dshapesfiles );
405  } while( 0 );
406 
407  if( !success )
408  return false;
409 
410  // Load each file in list:
411  wxURI repo( aLibURL );
412 
413  wxString server = repo.GetServer();
414 
415  // Github gives the current url of files inside .3dshapes folders like:
416  // "https://github.com/KiCad/kicad-library/blob/master/modules/packages3d/Capacitors_SMD.3dshapes/C_0402.wrl"
417  // which displays a html page showing the file in html form.
418  //
419  // the URL of the corresponding raw file is
420  // "https://github.com/KiCad/kicad-library/raw/master/modules/packages3d/Capacitors_SMD.3dshapes/C_0402.wrl"
421  //
422  // However Github redirects this current url to raw.githubusercontent.com/fullfilename
423  // when trying to download raw files.
424  // "https://github.com/KiCad/kicad-library/raw/master/modules/packages3d/Capacitors_SMD.3dshapes/C_0402.wrl"
425  // would be redirected to:
426  // "https://raw.githubusercontent.com/KiCad/kicad-library/master/modules/packages3d/Capacitors_SMD.3dshapes/C_0402.wrl"
427  // So use raw.githubusercontent.com instead of github.com
428  // (and removes the "/raw" in path) speed up the downloads (x2 faster).
429  //
430  // wxURI has no way to change the server name, so we need to use tricks to make the URL.
431  //
432  // Comment this next line to use the github.com URL
433 #define FORCE_GITHUB_RAW_URL
434 
435 #ifdef FORCE_GITHUB_RAW_URL
436  if( server.Cmp( wxT( "github.com" ) ) == 0 )
437  server = wxT( "raw.githubusercontent.com" );
438 #endif
439 
440  wxString full_url_base = repo.GetScheme() + wxT( "://" ) + server;
441  wxString target_full_url;
442 
443  for( unsigned ii = 0; ii < fileslist.GetCount(); ii++ )
444  {
445  target_full_url = full_url_base + fileslist[ii];
446 
447 #ifdef FORCE_GITHUB_RAW_URL
448  // Remove "blob/" in URL string to build the URL on "raw.githubusercontent.com"
449  // server from "github.com" URL string:
450  target_full_url.Replace( wxT( "blob/" ), wxT( "" ) );
451 #else
452  // Replace "blob" by "raw" in URL to access the raw file itself, not the html page
453  // on "github.com" server
454  target_full_url.Replace( wxT( "blob" ), wxT( "raw" ) );
455 #endif
456  aIndicator->SetRange( fileslist.GetCount() );
457  bool abort = !aIndicator->Update( ii, target_full_url.AfterLast( '/' ) );
458 
459  if( abort )
460  {
461  if( aErrorMessage )
462  *aErrorMessage << _( "Aborted by user" );
463  return false;
464  }
465 
466  // Download the current file.
467  // Get3DshapesLibsList actually downloads and stores the target_full_url content.
468  GITHUB_GETLIBLIST getter( target_full_url );
469  success = getter.Get3DshapesLibsList( NULL, NULL );
470 
471  if( !success )
472  break;
473 
474  wxFileName fn;
475  fn.AssignDir( aLocalLibName );
476  fn.SetFullName( fileslist[ii].AfterLast( '/' ) );
477 
478  // The entire downloaded file is stored in getter buffer
479  const std::string& buffer = getter.GetBuffer();
480 
481  // Write is "as this". It can be a binary file.
482  wxFile file(fn.GetFullPath(), wxFile::write);
483  file.Write( &buffer[0], buffer.size() );
484  }
485 
486  return success;
487 }
488 
489 
491 {
492  // Enable 'Next' only if there is at least one library selected
493  wxArrayInt checkedIndices;
494  m_checkList3Dlibnames->GetCheckedItems( checkedIndices );
495  enableNext( checkedIndices.GetCount() > 0 );
496 
497  // Update only if necessary
498  if( m_githubLibs.GetCount() == 0 )
500 
501  m_searchCtrl3Dlibs->Clear();
502 
503  // Clear the review list so it will be reloaded
504  m_libraries.clear();
505 }
506 
507 
509 {
510  bool valid = wxFileName::IsDirWritable( getDownloadDir() );
511 
512  // Shows or not the warning text if the target 3d folder does not exist, or is not
513  // writable.
514  m_invalidDirWarningText->Show( !valid );
515  m_bitmapDirWarn->Show( !valid );
516 
517  // If the dialog starts with m_invalidDirWarningText and m_bitmapDirWarn not shown
518  // the size and position of the sizer containing these widgets can be incorrect,
519  // until a wxSizeEvent is fired, and the widgets are not shown, or truncated,
520  // at least on Windows. So fire a dummy wxSizeEvent if the size looks bad
521  if( m_invalidDirWarningText->IsShown() && m_invalidDirWarningText->GetSize().x < 2 )
522  {
523  wxSizeEvent event( GetSize() );
524  wxPostEvent( this, event );
525  }
526 
527  // Allow to go further only if there is a valid target directory selected
528  enableNext( valid );
529 }
530 
531 // Called when the local folder name is edited.
533 {
535 }
536 
537 
539 {
540  m_welcomeDlg->SetNext( m_githubListDlg );
541  m_githubListDlg->SetPrev( m_welcomeDlg );
542  m_githubListDlg->SetNext( m_reviewDlg );
543  m_reviewDlg->SetPrev( m_githubListDlg );
544 }
545 
wxArrayString m_libraries
Libraries names selected in the wizard
void OnWizardFinished(wxWizardEvent &aEvent) override
void Invoke3DShapeLibsDownloaderWizard(wxTopLevelWindow *aParent)
Function Invoke3DShapeLibsDownloaderWizard Runs the downloader wizard for easy 3D shape libraries dow...
bool downloadGithubLibsFromList(wxArrayString &aUrlList, wxString *aErrorMessage)
Saves a list of Github libraries locally.
This file is part of the common library.
void OnUnselectAll3Dlibs(wxCommandEvent &aEvent) override
void OnChangeSearch(wxCommandEvent &aEvent) override
PGM_BASE & Pgm()
The global Program "get" accessor.
Definition: kicad.cpp:65
void OnBrowseButtonClick(wxCommandEvent &aEvent) override
wxArrayString m_githubLibs
Cache for the downloaded Github library list
wxWizardPage * GetFirstPage() const
Function GetFirstPage Returns the welcoming page for the wizard.
static const int delta[8][2]
Definition: solve.cpp:112
wxString getDownloadDir()
Gets the current target for downloaded libraries
wxString GetGithubURL() const
Function GetGithubURL Returns the current Github repository URL set in the wizard.
void OnCheckGithubList(wxCommandEvent &aEvent)
void updateGithubControls()
Enables Github widgets depending on the selected options.
void enableNext(bool aEnable)
Enables/disable 'Next' button
wxBitmap KiBitmap(BITMAP_DEF aBitmap)
Function KiBitmap constructs a wxBitmap from a memory record, held in a BITMAP_DEF.
Definition: bitmap.cpp:36
void getLibsListGithub(wxArrayString &aList)
Downloads the list of Github libraries
bool Get3DshapesLibsList(wxArrayString *aList, bool(*aFilter)(const wxString &aData))
Fills aList by the URL of libraries found on the github repo.
#define KICAD_3DLIBS_LAST_DOWNLOAD_DIR
#define KISYS3DMOD
A variable name whose value holds the path of 3D shape files.
Definition: 3d_viewer.h:38
#define KICAD_3DLIBS_URL_KEY
Wizard for selecting and dowloading D shapes libraries of footprints consisting of 3 steps: ...
void OnSelectAll3Dlibs(wxCommandEvent &aEvent) override
static bool filter3dshapesfiles(const wxString &aData)
void OnGridLibReviewSize(wxSizeEvent &event) override
bool downloadOneLib(const wxString &aLibURL, const wxString &aLocalLibName, wxProgressDialog *aIndicator, wxString *aErrorMessage)
Saves a Github library aLibURL locally in aLocalLibName.
Class WIZARD_3DSHAPE_LIBS_DOWNLOADER_BASE.
void OnLocalFolderChange(wxCommandEvent &event) override
void SetGithubURL(const wxString &aUrl)
Function SetGithubURL Sets the current Github repository URL used by the wizard.
void OnPageChanged(wxWizardEvent &aEvent) override
static bool filter3dshapeslibraries(const wxString &aData)
see class PGM_BASE
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, CPTREE &aTree)
Function Format outputs a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:205
static LIB_PART * dummy()
Used when a LIB_PART is not found in library to draw a dummy shape This component is a 400 mils squar...
#define max(a, b)
Definition: auxiliary.h:86
VTBL_ENTRY wxConfigBase * CommonSettings() const
Definition: pgm_base.h:145
void setDownloadDir(const wxString &aDir)
Sets the target directory for libraries downloaded from Github
void OnDefault3DPathButtonClick(wxCommandEvent &event) override
#define DEFAULT_GITHUB_3DSHAPES_LIBS_URL
void DisplayError(wxWindow *parent, const wxString &text, int displaytime)
Function DisplayError displays an error or warning message box with aMessage.
Definition: confirm.cpp:69
std::string & GetBuffer()
Declaration of the eda_3d_viewer class.
Class GITHUB_GETLIBLIST implements a portion of pcbnew's PLUGIN interface to provide read only access...