You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

867 lines
24 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2015 CERN
  5. * @author Maciej Suminski <maciej.suminski@cern.ch>
  6. * Copyright (C) 2014-2015 Jean-Pierre Charras, jp.charras at wanadoo.fr
  7. * Copyright (C) 1992-2017 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. /**
  27. * @brief Wizard for selecting footprint libraries consisting of 4 steps:
  28. * - select source (Github/local files)
  29. * - pick libraries
  30. * - present a review of libraries (including validation)
  31. * - select scope (global/project)
  32. */
  33. #include <memory>
  34. #include <wx/wx.h>
  35. #include <wx/uri.h>
  36. #include <wx/dir.h>
  37. #include <wx/progdlg.h>
  38. #include <pgm_base.h>
  39. #include <project.h>
  40. #include <wizard_add_fplib.h>
  41. #include <fp_lib_table.h>
  42. #include <confirm.h>
  43. #include <bitmaps.h>
  44. #include <class_module.h>
  45. #ifdef BUILD_GITHUB_PLUGIN
  46. #include <../github/github_getliblist.h>
  47. #endif
  48. // a key to store the default Kicad Github libs URL
  49. #define KICAD_FPLIBS_URL_KEY wxT( "kicad_fplib_url" )
  50. #define KICAD_FPLIBS_LAST_DOWNLOAD_DIR wxT( "kicad_fplib_last_download_dir" )
  51. // Filters for the file picker
  52. static const int FILTER_COUNT = 4;
  53. static const struct
  54. {
  55. wxString m_Description; ///< Description shown in the file picker dialog
  56. wxString m_Extension; ///< In case of folders it stands for extensions of files stored inside
  57. bool m_IsFile; ///< Whether it is a folder or a file
  58. IO_MGR::PCB_FILE_T m_Plugin;
  59. } fileFilters[FILTER_COUNT] =
  60. {
  61. { "KiCad (folder with .kicad_mod files)", "kicad_mod", false, IO_MGR::KICAD_SEXP },
  62. { "Eagle 6.x (*.lbr)", "lbr", true, IO_MGR::EAGLE },
  63. { "KiCad legacy (*.mod)", "mod", true, IO_MGR::LEGACY },
  64. { "Geda (folder with *.fp files)", "fp", false, IO_MGR::GEDA_PCB },
  65. };
  66. // Returns the filter string for the file picker
  67. static wxString getFilterString()
  68. {
  69. wxString filterInit = _( "All supported library formats|" );
  70. wxString filter;
  71. for( int i = 0; i < FILTER_COUNT; ++i )
  72. {
  73. // Init part
  74. if( i != 0 )
  75. filterInit += ";";
  76. filterInit += "*." + fileFilters[i].m_Extension;
  77. // Rest of the filter string
  78. filter += "|" + fileFilters[i].m_Description +
  79. "|" + ( fileFilters[i].m_IsFile ? "*." + fileFilters[i].m_Extension : "" );
  80. }
  81. return filterInit + filter;
  82. }
  83. // Tries to guess the plugin type basing on the path
  84. static OPT<IO_MGR::PCB_FILE_T> getPluginType( const wxString& aPath )
  85. {
  86. if( ( aPath.StartsWith( "http://" ) || aPath.StartsWith( "https://" ) ) )
  87. return OPT<IO_MGR::PCB_FILE_T>( IO_MGR::GITHUB );
  88. wxFileName path( aPath );
  89. for( int i = 0; i < FILTER_COUNT; ++i )
  90. {
  91. bool ok = false;
  92. if( fileFilters[i].m_IsFile )
  93. {
  94. ok = path.IsFileReadable() && path.GetExt() == fileFilters[i].m_Extension;
  95. }
  96. else if( path.IsDirReadable() )
  97. {
  98. // Plugin expects a directory containing files with a specific extension
  99. wxDir dir( aPath );
  100. if( dir.IsOpened() )
  101. {
  102. wxString filename;
  103. dir.GetFirst( &filename, "*." + fileFilters[i].m_Extension, wxDIR_FILES );
  104. ok = !filename.IsEmpty();
  105. }
  106. }
  107. if( ok )
  108. return OPT<IO_MGR::PCB_FILE_T>( fileFilters[i].m_Plugin );
  109. }
  110. return NULLOPT;
  111. }
  112. // Checks if a filename fits specific filter
  113. static bool passesFilter( const wxString& aFileName, int aFilterIndex )
  114. {
  115. wxASSERT( aFilterIndex <= FILTER_COUNT );
  116. wxFileName file( aFileName );
  117. OPT<IO_MGR::PCB_FILE_T> result = getPluginType( aFileName );
  118. if( !result ) // does not match any supported plugin
  119. return false;
  120. if( aFilterIndex == 0 ) // any plugin will do
  121. return true;
  122. return ( fileFilters[aFilterIndex - 1].m_Plugin == *result );
  123. }
  124. WIZARD_FPLIB_TABLE::LIBRARY::LIBRARY( const wxString& aPath, const wxString& aDescription ) :
  125. m_path( aPath ), m_description( aDescription ), m_status( NOT_CHECKED )
  126. {
  127. m_plugin = getPluginType( aPath );
  128. }
  129. bool WIZARD_FPLIB_TABLE::LIBRARY::Test()
  130. {
  131. if( !m_plugin )
  132. {
  133. m_status = LIBRARY::INVALID;
  134. return false;
  135. }
  136. PLUGIN* p = IO_MGR::PluginFind( *m_plugin );
  137. wxArrayString footprints;
  138. if( !p )
  139. {
  140. m_status = LIBRARY::INVALID;
  141. return false;
  142. }
  143. try
  144. {
  145. p->FootprintEnumerate( footprints, m_path );
  146. }
  147. catch( IO_ERROR& )
  148. {
  149. m_status = LIBRARY::INVALID;
  150. return false;
  151. }
  152. if( footprints.GetCount() == 0 )
  153. {
  154. m_status = LIBRARY::INVALID;
  155. return false;
  156. }
  157. m_status = LIBRARY::OK;
  158. return true;
  159. }
  160. wxString WIZARD_FPLIB_TABLE::LIBRARY::GetPluginName() const
  161. {
  162. if( !m_plugin )
  163. return _( "UNKNOWN" );
  164. switch( *m_plugin )
  165. {
  166. case IO_MGR::LEGACY:
  167. return wxT( "Legacy" );
  168. case IO_MGR::KICAD_SEXP:
  169. return wxT( "KiCad" );
  170. case IO_MGR::EAGLE:
  171. return wxT( "Eagle" );
  172. case IO_MGR::GEDA_PCB:
  173. return wxT( "Geda-PCB" );
  174. case IO_MGR::GITHUB:
  175. return wxT( "Github" );
  176. default:
  177. return _( "UNKNOWN" );
  178. }
  179. /*PLUGIN* p = IO_MGR::PluginFind( *m_plugin );
  180. if( !p )
  181. return _( "UNKNOWN" );
  182. return p->PluginName();*/
  183. }
  184. wxString WIZARD_FPLIB_TABLE::LIBRARY::GetRelativePath( const wxString& aBase, const wxString& aSubstitution ) const
  185. {
  186. wxFileName libPath( m_path );
  187. // Check if the library path belongs to the project folder
  188. if( libPath.MakeRelativeTo( aBase ) && !libPath.GetFullPath().StartsWith( ".." ) )
  189. {
  190. return wxString( aSubstitution + "/" + libPath.GetFullPath() );
  191. }
  192. // Probably on another drive, so the relative path will not work
  193. return wxEmptyString;
  194. }
  195. wxString WIZARD_FPLIB_TABLE::LIBRARY::GetAutoPath( LIB_SCOPE aScope ) const
  196. {
  197. const wxString& global_env = FP_LIB_TABLE::GlobalPathEnvVariableName();
  198. const wxString& project_env = PROJECT_VAR_NAME;
  199. const wxString& github_env( "KIGITHUB" );
  200. wxString rel_path;
  201. // KISYSMOD check
  202. rel_path = replaceEnv( global_env );
  203. if( !rel_path.IsEmpty() )
  204. return rel_path;
  205. // KIGITHUB check
  206. rel_path = replaceEnv( github_env, false );
  207. if( !rel_path.IsEmpty() )
  208. return rel_path;
  209. // KIPRJMOD check
  210. if( aScope == PROJECT )
  211. {
  212. rel_path = replaceEnv( project_env );
  213. if( !rel_path.IsEmpty() )
  214. return rel_path;
  215. }
  216. // Return the full path
  217. return m_path;
  218. }
  219. wxString WIZARD_FPLIB_TABLE::LIBRARY::GetDescription() const
  220. {
  221. if( !m_description.IsEmpty() )
  222. return m_description;
  223. wxFileName filename( m_path );
  224. return filename.GetName();
  225. }
  226. wxString WIZARD_FPLIB_TABLE::LIBRARY::replaceEnv( const wxString& aEnvVar, bool aFilePath ) const
  227. {
  228. wxString env_path;
  229. if( !wxGetEnv( aEnvVar, &env_path ) )
  230. return wxEmptyString;
  231. //return GetRelativePath( m_path, wxString( "$(" + aEnvVar + ")" ) );
  232. wxString result( m_path );
  233. if( result.Replace( env_path, wxString( "$(" + aEnvVar + ")" ) ) )
  234. return result;
  235. return wxEmptyString;
  236. }
  237. WIZARD_FPLIB_TABLE::WIZARD_FPLIB_TABLE( wxWindow* aParent ) :
  238. WIZARD_FPLIB_TABLE_BASE( aParent ), m_welcomeDlg( m_pages[0] ),
  239. m_fileSelectDlg( m_pages[1] ), m_githubListDlg( m_pages[2] ),
  240. m_reviewDlg( m_pages[3] ), m_targetDlg( m_pages[4] ), m_selectedFilter( 0 )
  241. {
  242. m_filePicker->SetFilter( getFilterString() );
  243. // Initialize default download dir
  244. wxString default_path;
  245. wxGetEnv( FP_LIB_TABLE::GlobalPathEnvVariableName(), &default_path );
  246. setDownloadDir( default_path );
  247. m_filePicker->SetPath( default_path );
  248. // Restore the Github url
  249. wxString githubUrl;
  250. wxConfigBase* cfg = Pgm().CommonSettings();
  251. cfg->Read( KICAD_FPLIBS_URL_KEY, &githubUrl );
  252. cfg->Read( KICAD_FPLIBS_LAST_DOWNLOAD_DIR, &m_lastGithubDownloadDirectory );
  253. if( !m_lastGithubDownloadDirectory.IsEmpty() )
  254. {
  255. setDownloadDir( m_lastGithubDownloadDirectory );
  256. m_filePicker->SetPath( m_lastGithubDownloadDirectory );
  257. } else {
  258. m_lastGithubDownloadDirectory = default_path;
  259. }
  260. if( githubUrl.IsEmpty() )
  261. githubUrl = wxT( "https://github.com/KiCad" );
  262. SetGithubURL( githubUrl );
  263. // Give the minimal size to the dialog, which allows displaying any page
  264. wxSize minsize;
  265. for( unsigned ii = 0; ii < m_pages.size(); ii++ )
  266. {
  267. wxSize size = m_pages[ii]->GetSizer()->CalcMin();
  268. minsize.x = std::max( minsize.x, size.x );
  269. minsize.y = std::max( minsize.y, size.y );
  270. }
  271. SetMinSize( minsize );
  272. SetPageSize( minsize );
  273. GetSizer()->SetSizeHints( this );
  274. Center();
  275. if( !m_radioAddGithub->GetValue() && !m_radioAddLocal->GetValue() )
  276. m_radioAddLocal->SetValue( true );
  277. setupDialogOrder();
  278. updateGithubControls();
  279. Connect( wxEVT_RADIOBUTTON, wxCommandEventHandler( WIZARD_FPLIB_TABLE::OnSourceCheck ), NULL, this );
  280. Connect( wxEVT_DIRCTRL_SELECTIONCHANGED, wxCommandEventHandler( WIZARD_FPLIB_TABLE::OnSelectFiles ), NULL, this );
  281. Connect( wxEVT_CHECKLISTBOX, wxCommandEventHandler( WIZARD_FPLIB_TABLE::OnCheckGithubList ), NULL, this );
  282. }
  283. WIZARD_FPLIB_TABLE::~WIZARD_FPLIB_TABLE()
  284. {
  285. // Use this if you want to store kicad lib URL in pcbnew/cvpcb section config:
  286. // wxConfigBase* cfg = Kiface().KifaceSettings();
  287. // Use this if you want to store kicad lib URL in common section config:
  288. wxConfigBase* cfg = Pgm().CommonSettings();
  289. cfg->Write( KICAD_FPLIBS_URL_KEY, GetGithubURL() );
  290. }
  291. WIZARD_FPLIB_TABLE::LIB_SOURCE WIZARD_FPLIB_TABLE::GetLibSource() const
  292. {
  293. if( m_radioAddGithub->GetValue() )
  294. return GITHUB;
  295. wxASSERT( m_radioAddLocal->GetValue() );
  296. return LOCAL;
  297. }
  298. WIZARD_FPLIB_TABLE::LIB_SCOPE WIZARD_FPLIB_TABLE::GetLibScope() const
  299. {
  300. if( m_radioGlobal->GetValue() )
  301. return GLOBAL;
  302. wxASSERT( m_radioProject->GetValue() );
  303. return PROJECT;
  304. }
  305. void WIZARD_FPLIB_TABLE::OnPageChanged( wxWizardEvent& aEvent )
  306. {
  307. SetBitmap( KiBitmap( wizard_add_fplib_icon_xpm ) );
  308. enableNext( true );
  309. #ifdef BUILD_GITHUB_PLUGIN
  310. if( GetCurrentPage() == m_githubListDlg )
  311. setupGithubList();
  312. else
  313. #endif
  314. if( GetCurrentPage() == m_fileSelectDlg )
  315. setupFileSelect();
  316. else if( GetCurrentPage() == m_reviewDlg )
  317. setupReview();
  318. }
  319. void WIZARD_FPLIB_TABLE::OnSelectFiles( wxCommandEvent& aEvent )
  320. {
  321. int filterIdx = m_filePicker->GetFilterIndex();
  322. if( m_selectedFilter != filterIdx )
  323. {
  324. m_selectedFilter = filterIdx;
  325. // Process the event again, as in the first iteration we cannot get the list of selected items
  326. wxCommandEvent ev( wxEVT_DIRCTRL_SELECTIONCHANGED );
  327. AddPendingEvent( ev );
  328. return;
  329. }
  330. enableNext( checkFiles() );
  331. }
  332. void WIZARD_FPLIB_TABLE::OnCheckGithubList( wxCommandEvent& aEvent )
  333. {
  334. wxArrayInt dummy;
  335. enableNext( m_checkListGH->GetCheckedItems( dummy ) > 0 );
  336. }
  337. void WIZARD_FPLIB_TABLE::OnSourceCheck( wxCommandEvent& aEvent )
  338. {
  339. updateGithubControls();
  340. setupDialogOrder();
  341. }
  342. void WIZARD_FPLIB_TABLE::OnSelectAllGH( wxCommandEvent& aEvent )
  343. {
  344. for( unsigned int i = 0; i < m_checkListGH->GetCount(); ++i )
  345. m_checkListGH->Check( i, true );
  346. // The list might be empty, e.g. in case of download error
  347. wxArrayInt dummy;
  348. enableNext( m_checkListGH->GetCheckedItems( dummy ) > 0 );
  349. }
  350. void WIZARD_FPLIB_TABLE::OnUnselectAllGH( wxCommandEvent& aEvent )
  351. {
  352. for( unsigned int i = 0; i < m_checkListGH->GetCount(); ++i )
  353. m_checkListGH->Check( i, false );
  354. enableNext( false );
  355. }
  356. void WIZARD_FPLIB_TABLE::OnChangeSearch( wxCommandEvent& aEvent )
  357. {
  358. wxString searchPhrase = m_searchCtrlGH->GetValue().Lower();
  359. // Store the current selection
  360. wxArrayInt checkedIndices;
  361. m_checkListGH->GetCheckedItems( checkedIndices );
  362. wxArrayString checkedStrings;
  363. for( unsigned int i = 0; i < checkedIndices.GetCount(); ++i )
  364. checkedStrings.Add( m_checkListGH->GetString( checkedIndices[i] ).AfterLast( '/' ) );
  365. m_checkListGH->Clear();
  366. // Rebuild the list, putting the matching entries on the top
  367. int matching = 0; // number of entries matching the search phrase
  368. for( unsigned int i = 0; i < m_githubLibs.GetCount(); ++i )
  369. {
  370. const wxString& lib = m_githubLibs[i].AfterLast( '/' );
  371. bool wasChecked = ( checkedStrings.Index( lib ) != wxNOT_FOUND );
  372. int insertedIdx = -1;
  373. if( !searchPhrase.IsEmpty() && lib.Lower().Contains( searchPhrase ) )
  374. {
  375. insertedIdx = m_checkListGH->Insert( lib, matching++ );
  376. m_checkListGH->SetSelection( insertedIdx );
  377. }
  378. else
  379. insertedIdx = m_checkListGH->Append( lib );
  380. if( wasChecked )
  381. m_checkListGH->Check( insertedIdx );
  382. }
  383. if( !m_checkListGH->IsEmpty() )
  384. m_checkListGH->EnsureVisible( 0 );
  385. }
  386. void WIZARD_FPLIB_TABLE::OnWizardFinished( wxWizardEvent& aEvent )
  387. {
  388. #ifdef BUILD_GITHUB_PLUGIN
  389. // Shall we download a localy copy of the libraries
  390. if( GetLibSource() == GITHUB && m_downloadGithub->GetValue() )
  391. {
  392. wxString error;
  393. wxArrayString libs;
  394. // Prepare a list of libraries to download
  395. for( std::vector<LIBRARY>::const_iterator it = m_libraries.begin();
  396. it != m_libraries.end(); ++it )
  397. {
  398. wxASSERT( it->GetPluginType() == IO_MGR::GITHUB );
  399. if( it->GetStatus() != LIBRARY::INVALID )
  400. libs.Add( it->GetAbsolutePath() );
  401. }
  402. if( !downloadGithubLibsFromList( libs, &error ) )
  403. {
  404. DisplayError( this, error );
  405. m_libraries.clear();
  406. }
  407. else
  408. {
  409. // Now libraries are stored locally, so update the paths to point to the download folder
  410. for( std::vector<LIBRARY>::iterator it = m_libraries.begin();
  411. it != m_libraries.end(); ++it )
  412. {
  413. wxString path = it->GetAbsolutePath();
  414. path.Replace( GetGithubURL(), getDownloadDir() );
  415. it->setPath( path );
  416. it->setPluginType( IO_MGR::KICAD_SEXP );
  417. }
  418. }
  419. }
  420. #endif
  421. }
  422. void WIZARD_FPLIB_TABLE::OnBrowseButtonClick( wxCommandEvent& aEvent )
  423. {
  424. wxString path = getDownloadDir();
  425. path = wxDirSelector( _("Choose a folder to save the downloaded libraries" ),
  426. path, 0, wxDefaultPosition, this );
  427. if( !path.IsEmpty() && wxDirExists( path ) )
  428. {
  429. setDownloadDir( path );
  430. wxConfigBase* cfg = Pgm().CommonSettings();
  431. cfg->Write( KICAD_FPLIBS_LAST_DOWNLOAD_DIR, path );
  432. updateGithubControls();
  433. }
  434. }
  435. void WIZARD_FPLIB_TABLE::OnCheckSaveCopy( wxCommandEvent& aEvent )
  436. {
  437. updateGithubControls();
  438. }
  439. bool WIZARD_FPLIB_TABLE::checkFiles() const
  440. {
  441. // Get current selection (files & directories)
  442. wxArrayString candidates;
  443. m_filePicker->GetPaths( candidates );
  444. // Workaround, when you change filters "/" is automatically selected
  445. int slash_index = candidates.Index( "/", true, true );
  446. if( slash_index != wxNOT_FOUND )
  447. candidates.RemoveAt( slash_index, 1 );
  448. if( candidates.IsEmpty() )
  449. return false;
  450. // Verify all the files/folders comply to the selected library type filter
  451. for( unsigned int i = 0; i < candidates.GetCount(); ++i )
  452. {
  453. if( !passesFilter( candidates[i], m_filePicker->GetFilterIndex() ) )
  454. return false;
  455. }
  456. return true;
  457. }
  458. #ifdef BUILD_GITHUB_PLUGIN
  459. void WIZARD_FPLIB_TABLE::getLibsListGithub( wxArrayString& aList )
  460. {
  461. wxBeginBusyCursor();
  462. // Be sure there is no trailing '/' at the end of the repo name
  463. wxString git_url = m_textCtrlGithubURL->GetValue();
  464. if( git_url.EndsWith( wxT( "/" ) ) )
  465. {
  466. git_url.RemoveLast();
  467. m_textCtrlGithubURL->SetValue( git_url );
  468. }
  469. GITHUB_GETLIBLIST getter( git_url );
  470. getter.GetFootprintLibraryList( aList );
  471. wxEndBusyCursor();
  472. }
  473. // Download the .pretty libraries found in aUrlLis and store them on disk
  474. // in a master folder
  475. bool WIZARD_FPLIB_TABLE::downloadGithubLibsFromList( wxArrayString& aUrlList,
  476. wxString* aErrorMessage )
  477. {
  478. // Display a progress bar to show the downlaod state
  479. wxProgressDialog pdlg( _( "Downloading libraries" ), wxEmptyString, aUrlList.GetCount() );
  480. // Download libs:
  481. for( unsigned ii = 0; ii < aUrlList.GetCount(); ii++ )
  482. {
  483. wxString& libsrc_name = aUrlList[ii];
  484. wxString libdst_name;
  485. // Extract the lib name from the full URL:
  486. wxURI url( libsrc_name );
  487. wxFileName fn( url.GetPath() );
  488. // Set our local path
  489. fn.SetPath( getDownloadDir() );
  490. libdst_name = fn.GetFullPath();
  491. if( !wxDirExists( libdst_name ) )
  492. wxMkdir( libdst_name );
  493. pdlg.Update( ii, libsrc_name );
  494. pdlg.Refresh();
  495. pdlg.Update();
  496. try
  497. {
  498. PLUGIN::RELEASER src( IO_MGR::PluginFind( IO_MGR::GITHUB ) );
  499. PLUGIN::RELEASER dst( IO_MGR::PluginFind( IO_MGR::KICAD_SEXP ) );
  500. wxArrayString footprints;
  501. src->FootprintEnumerate( footprints, libsrc_name );
  502. for( unsigned i = 0; i < footprints.size(); ++i )
  503. {
  504. std::unique_ptr<MODULE> m( src->FootprintLoad( libsrc_name, footprints[i] ) );
  505. dst->FootprintSave( libdst_name, m.get() );
  506. // m is deleted here by unique_ptr.
  507. }
  508. }
  509. catch( const IO_ERROR& ioe )
  510. {
  511. if( aErrorMessage )
  512. aErrorMessage->Printf( _( "Error:\n\"%s\"\nwhile downloading library:\n\"%s\"" ),
  513. GetChars( ioe.What() ), GetChars( libsrc_name ) );
  514. return false;
  515. }
  516. }
  517. return true;
  518. }
  519. void WIZARD_FPLIB_TABLE::setupGithubList()
  520. {
  521. // Enable 'Next' only if there is at least one library selected
  522. wxArrayInt checkedIndices;
  523. m_checkListGH->GetCheckedItems( checkedIndices );
  524. enableNext( checkedIndices.GetCount() > 0 );
  525. // Update only if necessary
  526. if( m_githubLibs.GetCount() == 0 )
  527. getLibsListGithub( m_githubLibs );
  528. m_searchCtrlGH->Clear();
  529. // Clear the review list so it will be reloaded
  530. m_libraries.clear();
  531. m_listCtrlReview->DeleteAllItems();
  532. }
  533. #endif /* BUILD_GITHUB_PLUGIN */
  534. void WIZARD_FPLIB_TABLE::updateGithubControls()
  535. {
  536. #ifndef BUILD_GITHUB_PLUGIN
  537. m_radioAddGithub->Enable( false );
  538. #endif
  539. // Disable inputs that have no meaning for the selected source
  540. bool githubEnabled = ( GetLibSource() == GITHUB );
  541. m_textCtrlGithubURL->Enable( githubEnabled );
  542. m_downloadGithub->Enable( githubEnabled );
  543. m_downloadDir->Enable( githubEnabled && wantLocalCopy() );
  544. m_btnBrowse->Enable( githubEnabled && wantLocalCopy() );
  545. bool valid = !( githubEnabled && wantLocalCopy() ) || wxFileName::IsDirWritable( getDownloadDir() );
  546. // Do not allow to go further unless there is a valid directory selected
  547. m_invalidDir->Show( !valid );
  548. enableNext( valid );
  549. }
  550. void WIZARD_FPLIB_TABLE::updateLibraries()
  551. {
  552. // No need to update, the review list is ready
  553. if( m_listCtrlReview->GetItemCount() != 0 )
  554. return;
  555. switch( GetLibSource() )
  556. {
  557. case LOCAL:
  558. {
  559. wxArrayString libs;
  560. m_filePicker->GetPaths( libs );
  561. // Workaround, when you change filters "/" is automatically selected
  562. int slash_index = libs.Index( "/", true, true );
  563. if( slash_index != wxNOT_FOUND )
  564. libs.RemoveAt( slash_index, 1 );
  565. m_libraries.reserve( libs.GetCount() );
  566. for( unsigned int i = 0; i < libs.GetCount(); ++i )
  567. m_libraries.push_back( libs[i] );
  568. }
  569. break;
  570. case GITHUB:
  571. {
  572. wxArrayInt checkedLibs;
  573. m_checkListGH->GetCheckedItems( checkedLibs );
  574. m_libraries.reserve( checkedLibs.GetCount() );
  575. for( unsigned int i = 0; i < checkedLibs.GetCount(); ++i )
  576. m_libraries.push_back( GetGithubURL() + "/" + m_checkListGH->GetString( checkedLibs[i] ) );
  577. }
  578. break;
  579. default:
  580. wxASSERT( false );
  581. break;
  582. }
  583. }
  584. void WIZARD_FPLIB_TABLE::setupDialogOrder()
  585. {
  586. // Alternate the wizard pages flow depending on the selected option
  587. switch( GetLibSource() )
  588. {
  589. case LOCAL:
  590. m_welcomeDlg->SetNext( m_fileSelectDlg );
  591. m_fileSelectDlg->SetPrev( m_welcomeDlg );
  592. m_fileSelectDlg->SetNext( m_reviewDlg );
  593. m_reviewDlg->SetPrev( m_fileSelectDlg );
  594. break;
  595. case GITHUB:
  596. m_welcomeDlg->SetNext( m_githubListDlg );
  597. m_githubListDlg->SetPrev( m_welcomeDlg );
  598. m_githubListDlg->SetNext( m_reviewDlg );
  599. m_reviewDlg->SetPrev( m_githubListDlg );
  600. break;
  601. default:
  602. wxASSERT( false );
  603. break;
  604. }
  605. }
  606. void WIZARD_FPLIB_TABLE::setupFileSelect()
  607. {
  608. // Disable the button until something is selected
  609. enableNext( checkFiles() );
  610. // Clear the review list so it will be reloaded
  611. m_libraries.clear();
  612. m_listCtrlReview->DeleteAllItems();
  613. }
  614. void WIZARD_FPLIB_TABLE::setupReview()
  615. {
  616. wxBeginBusyCursor();
  617. updateLibraries();
  618. int libTotalCount = m_libraries.size();
  619. int libCount = 0;
  620. bool validate = true;
  621. wxProgressDialog progressDlg( _( "Please wait..." ), _( "Validating libraries" ),
  622. libTotalCount, this,
  623. wxPD_APP_MODAL | wxPD_CAN_ABORT | wxPD_AUTO_HIDE );
  624. m_dvLibName->SetWidth( 280 );
  625. // Prepare the review list
  626. m_listCtrlReview->DeleteAllItems();
  627. for( std::vector<LIBRARY>::iterator it = m_libraries.begin(); it != m_libraries.end(); ++it )
  628. {
  629. wxVector<wxVariant> row;
  630. LIBRARY::STATUS status = it->GetStatus();
  631. // Check if the library contents is valid
  632. if( status == LIBRARY::NOT_CHECKED && validate )
  633. {
  634. it->Test();
  635. status = it->GetStatus();
  636. }
  637. row.push_back( wxVariant( it->GetDescription() ) );
  638. switch( it->GetStatus() )
  639. {
  640. case LIBRARY::NOT_CHECKED:
  641. row.push_back( wxVariant( _( "NOT CHECKED" ) ) );
  642. break;
  643. case LIBRARY::OK:
  644. row.push_back( wxVariant( _( "OK" ) ) );
  645. break;
  646. case LIBRARY::INVALID:
  647. row.push_back( wxVariant( _( "INVALID" ) ) );
  648. break;
  649. }
  650. row.push_back( wxVariant( it->GetPluginName() ) );
  651. m_listCtrlReview->AppendItem( row );
  652. ++libCount;
  653. if( !progressDlg.Update( libCount, wxString::Format( _( "Validating libraries %d/%d" ),
  654. libCount, libTotalCount ) ) )
  655. validate = false;
  656. }
  657. // The list should never be empty, but who knows?
  658. enableNext( m_listCtrlReview->GetItemCount() > 0 );
  659. wxEndBusyCursor();
  660. }