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 an other 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( GetParent(), 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 .pretty 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  wxProgressDialog pdlg( _( "Downloading 3D libraries" ), wxEmptyString,
360  aUrlList.GetCount(), GetParent(),
361  wxPD_CAN_ABORT | wxPD_APP_MODAL | wxPD_AUTO_HIDE );
362 
363  // Built the full server name string:
364  wxURI repo( GetGithubURL() );
365  wxString server = repo.GetScheme() + "://" + repo.GetServer();
366 
367  // Download libs:
368  for( unsigned ii = 0; ii < aUrlList.GetCount(); ii++ )
369  {
370  wxString& libsrc_name = aUrlList[ii];
371 
372  // Recover the full URL lib from short name:
373  // (note: m_githubLibs stores the URL relative to the server name)
374  wxString url;
375 
376  for( unsigned jj = 0; jj < m_githubLibs.GetCount(); jj++ )
377  {
378  if( m_githubLibs[jj].EndsWith( libsrc_name ) )
379  {
380  url = server + m_githubLibs[jj];
381  break;
382  }
383  }
384 
385  wxFileName fn( libsrc_name );
386  // Set our local path
387  fn.SetPath( getDownloadDir() );
388  wxString libdst_name = fn.GetFullPath();
389 
390  // Display the name of the library to download in the wxProgressDialog
391  pdlg.SetTitle( wxString::Format( wxT("%s [%d/%d]" ),
392  libsrc_name.AfterLast( '/' ).GetData(),
393  ii + 1, aUrlList.GetCount() ) );
394 
395  if( !wxDirExists( libdst_name ) )
396  wxMkdir( libdst_name );
397 
398  if( !downloadOneLib( url, libdst_name, &pdlg, aErrorMessage ) )
399  return false;
400  }
401 
402  return true;
403 }
404 
405 
407  const wxString& aLocalLibName, wxProgressDialog * aIndicator,
408  wxString* aErrorMessage )
409 {
410  wxArrayString fileslist;
411 
412  bool success;
413 
414  // Get the list of candidate files: with ext .wrl .stp .step .STEP .STP or .wings
415  do
416  {
417  GITHUB_GETLIBLIST getter( aLibURL );
418  success = getter.Get3DshapesLibsList( &fileslist, filter3dshapesfiles );
419  } while( 0 );
420 
421  if( !success )
422  return false;
423 
424  // Load each file in list:
425  wxURI repo( aLibURL );
426 
427  wxString server = repo.GetServer();
428 
429  // Github gives the current url of files inside .3dshapes folders like:
430  // "https://github.com/KiCad/kicad-library/blob/master/modules/packages3d/Capacitors_SMD.3dshapes/C_0402.wrl"
431  // which displays a html page showing the file in html form.
432  //
433  // the URL of the corresponding raw file is
434  // "https://github.com/KiCad/kicad-library/raw/master/modules/packages3d/Capacitors_SMD.3dshapes/C_0402.wrl"
435  //
436  // However Github redirects this current url to raw.githubusercontent.com/fullfilename
437  // when trying to download raw files.
438  // "https://github.com/KiCad/kicad-library/raw/master/modules/packages3d/Capacitors_SMD.3dshapes/C_0402.wrl"
439  // would be redirected to:
440  // "https://raw.githubusercontent.com/KiCad/kicad-library/master/modules/packages3d/Capacitors_SMD.3dshapes/C_0402.wrl"
441  // So use raw.githubusercontent.com instead of github.com
442  // (and removes the "/raw" in path) speed up the downloads (x2 faster).
443  //
444  // wxURI has no way to change the server name, so we need to use tricks to make the URL.
445  //
446  // Comment this next line to use the github.com URL
447 #define FORCE_GITHUB_RAW_URL
448 
449 #ifdef FORCE_GITHUB_RAW_URL
450  if( server.Cmp( wxT( "github.com" ) ) == 0 )
451  server = wxT( "raw.githubusercontent.com" );
452 #endif
453 
454  wxString full_url_base = repo.GetScheme() + wxT( "://" ) + server;
455  wxString target_full_url;
456 
457  for( unsigned ii = 0; ii < fileslist.GetCount(); ii++ )
458  {
459  target_full_url = full_url_base + fileslist[ii];
460 
461 #ifdef FORCE_GITHUB_RAW_URL
462  // Remove "blob/" in URL string to build the URL on "raw.githubusercontent.com"
463  // server from "github.com" URL string:
464  target_full_url.Replace( wxT( "blob/" ), wxT( "" ) );
465 #else
466  // Replace "blob" by "raw" in URL to access the raw file itself, not the html page
467  // on "github.com" server
468  target_full_url.Replace( wxT( "blob" ), wxT( "raw" ) );
469 #endif
470  aIndicator->SetRange( fileslist.GetCount() );
471  bool abort = !aIndicator->Update( ii, target_full_url.AfterLast( '/' ) );
472 
473  if( abort )
474  {
475  if( aErrorMessage )
476  *aErrorMessage << _( "Aborted by user" );
477  return false;
478  }
479 
480  // Download the current file.
481  // Get3DshapesLibsList actually downloads and stores the target_full_url content.
482  GITHUB_GETLIBLIST getter( target_full_url );
483  success = getter.Get3DshapesLibsList( NULL, NULL );
484 
485  if( !success )
486  break;
487 
488  wxFileName fn;
489  fn.AssignDir( aLocalLibName );
490  fn.SetFullName( fileslist[ii].AfterLast( '/' ) );
491 
492  // The entire downloaded file is stored in getter buffer
493  const std::string& buffer = getter.GetBuffer();
494 
495  // Write is "as this". It can be a binary file.
496  wxFile file(fn.GetFullPath(), wxFile::write);
497  file.Write( &buffer[0], buffer.size() );
498  }
499 
500  return success;
501 }
502 
503 
505 {
506  // Enable 'Next' only if there is at least one library selected
507  wxArrayInt checkedIndices;
508  m_checkList3Dlibnames->GetCheckedItems( checkedIndices );
509  enableNext( checkedIndices.GetCount() > 0 );
510 
511  // Update only if necessary
512  if( m_githubLibs.GetCount() == 0 )
514 
515  m_searchCtrl3Dlibs->Clear();
516 
517  // Clear the review list so it will be reloaded
518  m_libraries.clear();
519 }
520 
521 
523 {
524  bool valid = wxFileName::IsDirWritable( getDownloadDir() );
525 
526  // Shows or not the warning text if the target 3d folder does not exist, or is not
527  // writable.
528  m_invalidDirWarningText->Show( !valid );
529  m_bitmapDirWarn->Show( !valid );
530 
531  // If the dialog starts with m_invalidDirWarningText and m_bitmapDirWarn not shown
532  // the size and position of the sizer containing these widgets can be incorrect,
533  // until a wxSizeEvent is fired, and the widgets are not shown, or truncated,
534  // at least on Windows. So fire a dummy wxSizeEvent if the size looks bad
535  if( m_invalidDirWarningText->IsShown() && m_invalidDirWarningText->GetSize().x < 2 )
536  {
537  wxSizeEvent event( GetSize() );
538  wxPostEvent( this, event );
539  }
540 
541  // Allow to go further only if there is a valid target directory selected
542  enableNext( valid );
543 }
544 
545 // Called when the local folder name is edited.
547 {
549 }
550 
551 
553 {
554  m_welcomeDlg->SetNext( m_githubListDlg );
555  m_githubListDlg->SetPrev( m_welcomeDlg );
556  m_githubListDlg->SetNext( m_reviewDlg );
557  m_reviewDlg->SetPrev( m_githubListDlg );
558 }
559 
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: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 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
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:73
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...