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  "https://github.com/KiCad/kicad-packages3d"
56 // wxT( "https://github.com/KiCad/kicad-library/tree/master/modules/packages3d" )
57 
58 void Invoke3DShapeLibsDownloaderWizard( wxTopLevelWindow* aCaller )
59 {
60  WIZARD_3DSHAPE_LIBS_DOWNLOADER wizard( aCaller );
61  wizard.RunWizard( wizard.GetFirstPage() );
62 }
63 
64 
67 {
68  m_welcomeDlg = m_pages[0];
70  m_reviewDlg = m_pages[2];
71 
72  // Initialize default download dir (local target folder of 3D shapes libs)
73  wxString default_path;
74  wxGetEnv( KISYS3DMOD, &default_path );
75 
76  wxConfigBase* cfg = Pgm().CommonSettings();
77  wxString tmp;
78  cfg->Read( KICAD_3DLIBS_LAST_DOWNLOAD_DIR, &tmp, default_path );
79  setDownloadDir( tmp );
80 
81  // Restore the Github 3D shapes libs url
82  wxString githubUrl;
83  cfg->Read( KICAD_3DLIBS_URL_KEY, &githubUrl );
84 
85  if( githubUrl.IsEmpty() )
87 
88  SetGithubURL( githubUrl );
89 
90 
91  // Give the minimal size to the dialog, which allows displaying any page
92  wxSize minsize;
93 
94  for( unsigned ii = 0; ii < m_pages.size(); ii++ )
95  {
96  wxSize size = m_pages[ii]->GetSizer()->CalcMin();
97  minsize.x = std::max( minsize.x, size.x );
98  minsize.y = std::max( minsize.y, size.y );
99  }
100 
101  SetMinSize( minsize );
102  SetPageSize( minsize );
103  GetSizer()->SetSizeHints( this );
104  Center();
105 
108 
109  // When starting m_textCtrlGithubURL has the focus, and the text is selected,
110  // and not fully visible.
111  // Forcing deselection does not work, at least on W7 with wxWidgets 3.0.2
112  // So (and also because m_textCtrlGithubURL and m_downloadDir are rarely modified
113  // the focus is given to another widget.
114  m_hyperlinkGithubKicad->SetFocus();
115 
116  Connect( wxEVT_RADIOBUTTON, wxCommandEventHandler( WIZARD_3DSHAPE_LIBS_DOWNLOADER::OnSourceCheck ), NULL, this );
117  Connect( wxEVT_CHECKLISTBOX, wxCommandEventHandler( WIZARD_3DSHAPE_LIBS_DOWNLOADER::OnCheckGithubList ), NULL, this );
118 }
119 
120 
122 {
123  // Use this if you want to store kicad lib URL in pcbnew/cvpcb section config:
124  // wxConfigBase* cfg = Kiface().KifaceSettings();
125 
126  // Use this if you want to store kicad lib URL in common section config:
127  wxConfigBase* cfg = Pgm().CommonSettings();
128  cfg->Write( KICAD_3DLIBS_URL_KEY, GetGithubURL() );
130 }
131 
132 
133 
135 {
136  SetBitmap( KiBitmap( wizard_add_fplib_icon_xpm ) );
137  enableNext( true );
138 
139  if( GetCurrentPage() == m_githubListDlg )
140  setupGithubList();
141  else if( GetCurrentPage() == m_reviewDlg )
142  setupReview();
143 }
144 
145 
147 {
148  wxArrayInt dummy;
149 
150  enableNext( m_checkList3Dlibnames->GetCheckedItems( dummy ) > 0 );
151 }
152 
153 
154 void WIZARD_3DSHAPE_LIBS_DOWNLOADER::OnSourceCheck( wxCommandEvent& aEvent )
155 {
158 }
159 
161 {
162  // Adjust the width of the column 1 of m_gridLibReview (library names) to the
163  // max available width.
164  int gridwidth = m_gridLibReview->GetClientSize().x;
165  gridwidth -= m_gridLibReview->GetColSize( 0 ) + m_gridLibReview->GetColLabelSize();
166 
167  if( gridwidth < 200 )
168  gridwidth = 200;
169 
170  m_gridLibReview->SetColSize( 1, gridwidth );
171 
172  event.Skip();
173 }
174 
175 
177 {
178  // Prepare the last page of the wizard.
179 
180  m_LocalFolderInfo->SetLabel( getDownloadDir() );
181 
182  wxArrayInt checkedIndices;
183  m_checkList3Dlibnames->GetCheckedItems( checkedIndices );
184 
185  m_libraries.Clear();
186 
187  // populate m_libraries with the name of libraries, without the github path:
188  for( unsigned int ii = 0; ii < checkedIndices.GetCount(); ++ii )
189  {
190  m_libraries.Add( m_checkList3Dlibnames->GetString( checkedIndices[ii] ).AfterLast( '/' ) );
191  }
192 
193  // Adjust number of rows in m_gridLibReview:
194  int delta = m_libraries.GetCount() - m_gridLibReview->GetNumberRows();
195 
196  if( delta < 0 )
197  m_gridLibReview->DeleteRows( -delta );
198  else if( delta > 0 )
199  m_gridLibReview->AppendRows( delta );
200 
201  // For user info, verify the existence of these libs in local folder
202  wxArrayString liblist;
203  wxFileName fn;
204  fn.AssignDir( getDownloadDir() );
205 
206  for( unsigned int ii = 0; ii < m_libraries.GetCount(); ++ii )
207  {
208  fn.SetName( m_libraries[ii] );
209 
210  wxDir dirs;
211  bool isNew = ! dirs.Exists( fn.GetFullPath() );
212  wxString info = isNew ? _( "New" ) : _( "Update" );
213 
214  liblist.Add( info + wxT(" ") + m_libraries[ii] );
215 
216  m_gridLibReview->SetCellValue( ii, 0, info );
217  m_gridLibReview->SetCellValue( ii, 1, m_libraries[ii] );
218  }
219 
220  m_gridLibReview->AutoSizeColumn( 0 );
221 }
222 
223 
225 {
226  for( unsigned int i = 0; i < m_checkList3Dlibnames->GetCount(); ++i )
227  m_checkList3Dlibnames->Check( i, true );
228 
229  // The list might be empty, e.g. in case of download error
230  wxArrayInt dummy;
231  enableNext( m_checkList3Dlibnames->GetCheckedItems( dummy ) > 0 );
232 }
233 
234 
236 {
237  for( unsigned int i = 0; i < m_checkList3Dlibnames->GetCount(); ++i )
238  m_checkList3Dlibnames->Check( i, false );
239 
240  enableNext( false );
241 }
242 
243 
245 {
246  wxString searchPhrase = m_searchCtrl3Dlibs->GetValue().Lower();
247 
248  // Store the current selection
249  wxArrayInt checkedIndices;
250  m_checkList3Dlibnames->GetCheckedItems( checkedIndices );
251  wxArrayString checkedStrings;
252 
253  for( unsigned int i = 0; i < checkedIndices.GetCount(); ++i )
254  checkedStrings.Add( m_checkList3Dlibnames->GetString( checkedIndices[i] ).AfterLast( '/' ) );
255 
256  m_checkList3Dlibnames->Clear();
257 
258  // Rebuild the list, putting the matching entries on the top
259  int matching = 0; // number of entries matching the search phrase
260  for( unsigned int i = 0; i < m_githubLibs.GetCount(); ++i )
261  {
262  const wxString& lib = m_githubLibs[i].AfterLast( '/' );
263  bool wasChecked = ( checkedStrings.Index( lib ) != wxNOT_FOUND );
264  int insertedIdx = -1;
265 
266  if( !searchPhrase.IsEmpty() && lib.Lower().BeforeLast( '.' ).Contains( searchPhrase ) )
267  {
268  insertedIdx = m_checkList3Dlibnames->Insert( lib, matching++ );
269  m_checkList3Dlibnames->SetSelection( insertedIdx );
270  }
271  else
272  insertedIdx = m_checkList3Dlibnames->Append( lib );
273 
274  if( wasChecked )
275  m_checkList3Dlibnames->Check( insertedIdx );
276  }
277 
278  if( !m_checkList3Dlibnames->IsEmpty() )
279  m_checkList3Dlibnames->EnsureVisible( 0 );
280 }
281 
282 
284 {
285  // we download a localy copy of the libraries
286  wxString error;
287 
288  if( !downloadGithubLibsFromList( m_libraries, &error ) )
289  {
290  DisplayError( this, error );
291  }
292 }
293 
294 
296 {
297  wxString path = getDownloadDir();
298 
299  path = wxDirSelector( _("Choose a folder to save the downloaded libraries" ),
300  path, 0, wxDefaultPosition, this );
301 
302  if( !path.IsEmpty() && wxDirExists( path ) )
303  {
304  setDownloadDir( path );
306  }
307 }
308 
309 
311 {
312  wxString default_path;
313  wxGetEnv( KISYS3DMOD, &default_path );
314 
315  if( !default_path.IsEmpty() && wxDirExists( default_path ) )
316  {
317  setDownloadDir( default_path );
319  }
320  else
321  wxMessageBox( _( "KISYS3DMOD path not defined , or not existing" ) );
322 }
323 
324 
326 {
328 }
329 
330 
332 {
333  wxBeginBusyCursor();
334 
335  // Be sure there is no trailing '/' at the end of the repo name
336  wxString git_url = m_textCtrlGithubURL->GetValue();
337 
338  if( git_url.EndsWith( wxT( "/" ) ) )
339  {
340  git_url.RemoveLast();
341  m_textCtrlGithubURL->SetValue( git_url );
342  }
343 
344  GITHUB_GETLIBLIST getter( git_url );
346 
347  wxEndBusyCursor();
348 }
349 
350 
351 // Download the .3Dshapes libraries folders found in aUrlList and store them on disk
352 // in a master folder
354  wxString* aErrorMessage )
355 {
356  // Display a progress bar to show the download state
357  // The title is updated for each downloaded library.
358  // the state will be updated by downloadOneLib() for each file.
359  // for OSX do not enable wPD_APP_MODAL, keep wxPD_AUTO_HIDE
360  wxProgressDialog pdlg( _( "Downloading 3D libraries" ), wxEmptyString,
361  aUrlList.GetCount(), this,
362 #ifndef __WXMAC__
363  wxPD_APP_MODAL |
364 #endif
365  wxPD_CAN_ABORT | wxPD_AUTO_HIDE );
366 
367  // Built the full server name string:
368  wxURI repo( GetGithubURL() );
369  wxString server = repo.GetScheme() + "://" + repo.GetServer();
370 
371  // Download libs:
372  for( size_t ii = 0; ii < aUrlList.GetCount(); ii++ )
373  {
374  wxString& libsrc_name = aUrlList[ii];
375 
376  // Recover the full URL lib from short name:
377  // (note: m_githubLibs stores the URL relative to the server name)
378  wxString url;
379 
380  for( unsigned jj = 0; jj < m_githubLibs.GetCount(); jj++ )
381  {
382  if( m_githubLibs[jj].EndsWith( libsrc_name ) )
383  {
384  url = server + m_githubLibs[jj];
385  break;
386  }
387  }
388 
389  wxFileName fn( libsrc_name );
390  // Set our local path
391  fn.SetPath( getDownloadDir() );
392  wxString libdst_name = fn.GetFullPath();
393 
394  // Display the name of the library to download in the wxProgressDialog
395  pdlg.SetTitle( wxString::Format( wxT("%s [%lu/%lu]" ),
396  libsrc_name.AfterLast( '/' ).GetData(),
397  ii + 1, aUrlList.GetCount() ) );
398 
399  if( !wxDirExists( libdst_name ) )
400  wxMkdir( libdst_name );
401 
402  if( !downloadOneLib( url, libdst_name, &pdlg, aErrorMessage ) )
403  return false;
404  }
405 
406  return true;
407 }
408 
409 
411  const wxString& aLocalLibName, wxProgressDialog* aIndicator,
412  wxString* aErrorMessage )
413 {
414  wxArrayString fileslist;
415 
416  bool success;
417 
418  // Get the list of candidate files: with ext .wrl .stp .step .STEP .STP or .wings
419  do
420  {
421  GITHUB_GETLIBLIST getter( aLibURL );
422  success = getter.Get3DshapesLibsList( &fileslist, filter3dshapesfiles );
423  } while( 0 );
424 
425  if( !success )
426  return false;
427 
428  // Load each file in list:
429  wxURI repo( aLibURL );
430 
431  wxString server = repo.GetServer();
432 
433  // Github gives the current url of files inside .3dshapes folders like:
434  // "https://github.com/KiCad/kicad-library/blob/master/modules/packages3d/Capacitors_SMD.3dshapes/C_0402.wrl"
435  // which displays a html page showing the file in html form.
436  //
437  // the URL of the corresponding raw file is
438  // "https://github.com/KiCad/kicad-library/raw/master/modules/packages3d/Capacitors_SMD.3dshapes/C_0402.wrl"
439  //
440  // However Github redirects this current url to raw.githubusercontent.com/fullfilename
441  // when trying to download raw files.
442  // "https://github.com/KiCad/kicad-library/raw/master/modules/packages3d/Capacitors_SMD.3dshapes/C_0402.wrl"
443  // would be redirected to:
444  // "https://raw.githubusercontent.com/KiCad/kicad-library/master/modules/packages3d/Capacitors_SMD.3dshapes/C_0402.wrl"
445  // So use raw.githubusercontent.com instead of github.com
446  // (and removes the "/raw" in path) speed up the downloads (x2 faster).
447  //
448  // wxURI has no way to change the server name, so we need to use tricks to make the URL.
449  //
450  // Comment this next line to use the github.com URL
451 #define FORCE_GITHUB_RAW_URL
452 
453 #ifdef FORCE_GITHUB_RAW_URL
454  if( server.Cmp( wxT( "github.com" ) ) == 0 )
455  server = wxT( "raw.githubusercontent.com" );
456 #endif
457 
458  wxString full_url_base = repo.GetScheme() + wxT( "://" ) + server;
459  wxString target_full_url;
460 
461  for( unsigned ii = 0; ii < fileslist.GetCount(); ii++ )
462  {
463  target_full_url = full_url_base + fileslist[ii];
464 
465 #ifdef FORCE_GITHUB_RAW_URL
466  // Remove "blob/" in URL string to build the URL on "raw.githubusercontent.com"
467  // server from "github.com" URL string:
468  target_full_url.Replace( wxT( "blob/" ), wxT( "" ) );
469 #else
470  // Replace "blob" by "raw" in URL to access the raw file itself, not the html page
471  // on "github.com" server
472  target_full_url.Replace( wxT( "blob" ), wxT( "raw" ) );
473 #endif
474  aIndicator->SetRange( fileslist.GetCount() );
475  bool abort = !aIndicator->Update( ii, target_full_url.AfterLast( '/' ) );
476 
477  if( abort )
478  {
479  if( aErrorMessage )
480  *aErrorMessage << _( "Aborted by user" );
481  return false;
482  }
483 
484  // Download the current file.
485  // Get3DshapesLibsList actually downloads and stores the target_full_url content.
486  GITHUB_GETLIBLIST getter( target_full_url );
487  success = getter.Get3DshapesLibsList( NULL, NULL );
488 
489  if( !success )
490  break;
491 
492  wxFileName fn;
493  fn.AssignDir( aLocalLibName );
494  fn.SetFullName( fileslist[ii].AfterLast( '/' ) );
495 
496  // The entire downloaded file is stored in getter buffer
497  const std::string& buffer = getter.GetBuffer();
498 
499  // Write is "as this". It can be a binary file.
500  wxFile file(fn.GetFullPath(), wxFile::write);
501  file.Write( &buffer[0], buffer.size() );
502  }
503 
504  return success;
505 }
506 
507 
509 {
510  // Enable 'Next' only if there is at least one library selected
511  wxArrayInt checkedIndices;
512  m_checkList3Dlibnames->GetCheckedItems( checkedIndices );
513  enableNext( checkedIndices.GetCount() > 0 );
514 
515  // Update only if necessary
516  if( m_githubLibs.GetCount() == 0 )
518 
519  m_searchCtrl3Dlibs->Clear();
520 
521  // Clear the review list so it will be reloaded
522  m_libraries.clear();
523 }
524 
525 
527 {
528  bool valid = wxFileName::IsDirWritable( getDownloadDir() );
529 
530  // Shows or not the warning text if the target 3d folder does not exist, or is not
531  // writable.
532  m_invalidDirWarningText->Show( !valid );
533  m_bitmapDirWarn->Show( !valid );
534 
535  // If the dialog starts with m_invalidDirWarningText and m_bitmapDirWarn not shown
536  // the size and position of the sizer containing these widgets can be incorrect,
537  // until a wxSizeEvent is fired, and the widgets are not shown, or truncated,
538  // at least on Windows. So fire a dummy wxSizeEvent if the size looks bad
539  if( m_invalidDirWarningText->IsShown() && m_invalidDirWarningText->GetSize().x < 2 )
540  {
541  wxSizeEvent event( GetSize() );
542  wxPostEvent( this, event );
543  }
544 
545  // Allow to go further only if there is a valid target directory selected
546  enableNext( valid );
547 }
548 
549 // Called when the local folder name is edited.
551 {
553 }
554 
555 
557 {
558  m_welcomeDlg->SetNext( m_githubListDlg );
559  m_githubListDlg->SetPrev( m_welcomeDlg );
560  m_githubListDlg->SetNext( m_reviewDlg );
561  m_reviewDlg->SetPrev( m_githubListDlg );
562 }
563 
wxArrayString m_libraries
Libraries names selected in the wizard
void OnWizardFinished(wxWizardEvent &aEvent) override
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
Called when the content of m_searchCtrl3Dlibs has changed.
PGM_BASE & Pgm()
The global Program "get" accessor.
Definition: kicad.cpp:66
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 &#39;Next&#39; button
wxBitmap KiBitmap(BITMAP_DEF aBitmap)
Function KiBitmap constructs a wxBitmap from a memory record, held in a BITMAP_DEF.
Definition: bitmap.cpp:78
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 3D 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 Invoke3DShapeLibsDownloaderWizard(wxTopLevelWindow *aCaller)
Function Invoke3DShapeLibsDownloaderWizard Runs the downloader wizard for easy 3D shape libraries dow...
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
size_t i
Definition: json11.cpp:597
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:185
std::string & GetBuffer()
Declaration of the eda_3d_viewer class.
Class GITHUB_GETLIBLIST implements a portion of pcbnew&#39;s PLUGIN interface to provide read only access...