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.

632 lines
18 KiB

Modular-Kicad milestone B), major portions: *) Rework the set language support, simplify it by using KIWAY. Now any major frame with a "change language" menu can change the language for all KIWAY_PLAYERs in the whole KIWAY. Multiple KIWAYs are not supported yet. *) Simplify "modal wxFrame" support, and add that support exclusively to KIWAY_PLAYER where it is inherited by all derivatives. The function KIWAY_PLAYER::ShowModal() is in the vtable and so is cross module capable. *) Remove the requirements and assumptions that the wxFrame hierarchy always had PCB_EDIT_FRAME and SCH_EDIT_FRAME as immediate parents of their viewers and editors. This is no longer the case, nor required. *) Use KIWAY::Player() everywhere to make KIWAY_PLAYERs, this registers the KIWAY_PLAYER within the KIWAY and makes it very easy to find an open frame quickly. It also gives control to the KIWAY as to frame hierarchical relationships. *) Change single_top to use the KIWAY for loading a KIFACE and instantiating the single KIWAY_PLAYER, see bullet immediately above. *) Add KIWAY::OnKiwayEnd() and call it from PGM_BASE at program termination, this gives the KIFACEs a chance to save their final configuration dope to disk. *) Add dedicated FRAME_T's for the modal frames, so m_Ident can be tested and these modal frames are distinctly different than their non-modal equivalents. KIWAY_PLAYER::IsModal() is !not! a valid test during the wxFrame's constructor, so this is another important reason for having a dedicated FRAME_T for each modal wxFrame. On balance, more lines were deleted than were added to achieve all this.
12 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
  5. * Copyright (C) 2015-2022 KiCad Developers, see change_log.txt for contributors.
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License
  9. * as published by the Free Software Foundation; either version 2
  10. * of the License, or (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, you may find one here:
  19. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  20. * or you may search the http://www.gnu.org website for the version 2 license,
  21. * or you may write to the Free Software Foundation, Inc.,
  22. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  23. */
  24. #include <base_units.h>
  25. #include <kiway.h>
  26. #include <lib_tree_model_adapter.h>
  27. #include <pgm_base.h>
  28. #include <eda_list_dialog.h>
  29. #include <eeschema_settings.h>
  30. #include <project/project_file.h>
  31. #include <symbol_editor/symbol_editor_settings.h>
  32. #include <sch_draw_panel.h>
  33. #include <sch_view.h>
  34. #include <sch_painter.h>
  35. #include <settings/settings_manager.h>
  36. #include <confirm.h>
  37. #include <preview_items/selection_area.h>
  38. #include <symbol_library.h>
  39. #include <sch_base_frame.h>
  40. #include <symbol_lib_table.h>
  41. #include <tool/action_toolbar.h>
  42. #include <tool/tool_manager.h>
  43. #include <tool/tool_dispatcher.h>
  44. #include <tools/ee_actions.h>
  45. #include <tools/ee_selection_tool.h>
  46. #include <wx/choicdlg.h>
  47. #if defined( KICAD_USE_3DCONNEXION )
  48. #include <navlib/nl_schematic_plugin.h>
  49. #endif
  50. LIB_SYMBOL* SchGetLibSymbol( const LIB_ID& aLibId, SYMBOL_LIB_TABLE* aLibTable,
  51. SYMBOL_LIB* aCacheLib, wxWindow* aParent, bool aShowErrorMsg )
  52. {
  53. wxCHECK_MSG( aLibTable, nullptr, "Invalid symbol library table." );
  54. LIB_SYMBOL* symbol = nullptr;
  55. try
  56. {
  57. symbol = aLibTable->LoadSymbol( aLibId );
  58. if( !symbol && aCacheLib )
  59. {
  60. wxCHECK_MSG( aCacheLib->IsCache(), nullptr, "Invalid cache library." );
  61. wxString cacheName = aLibId.GetLibNickname().wx_str();
  62. cacheName += "_" + aLibId.GetLibItemName();
  63. symbol = aCacheLib->FindSymbol( cacheName );
  64. }
  65. }
  66. catch( const IO_ERROR& ioe )
  67. {
  68. if( aShowErrorMsg )
  69. {
  70. wxString msg = wxString::Format( _( "Error loading symbol %s from library '%s'." ),
  71. aLibId.GetLibItemName().wx_str(),
  72. aLibId.GetLibNickname().wx_str() );
  73. DisplayErrorMessage( aParent, msg, ioe.What() );
  74. }
  75. }
  76. return symbol;
  77. }
  78. SCH_BASE_FRAME::SCH_BASE_FRAME( KIWAY* aKiway, wxWindow* aParent, FRAME_T aWindowType,
  79. const wxString& aTitle, const wxPoint& aPosition,
  80. const wxSize& aSize, long aStyle, const wxString& aFrameName ) :
  81. EDA_DRAW_FRAME( aKiway, aParent, aWindowType, aTitle, aPosition, aSize, aStyle,
  82. aFrameName, schIUScale ),
  83. m_base_frame_defaults( nullptr, "base_Frame_defaults" )
  84. #if defined( KICAD_USE_3DCONNEXION )
  85. ,m_spaceMouse( nullptr )
  86. #endif
  87. {
  88. createCanvas();
  89. Bind( wxEVT_IDLE,
  90. [this]( wxIdleEvent& aEvent )
  91. {
  92. // Handle cursor adjustments. While we can get motion and key events through
  93. // wxWidgets, we can't get modifier-key-up events.
  94. if( m_toolManager )
  95. {
  96. EE_SELECTION_TOOL* selTool = m_toolManager->GetTool<EE_SELECTION_TOOL>();
  97. if( selTool )
  98. selTool->OnIdle( aEvent );
  99. }
  100. } );
  101. }
  102. SCH_BASE_FRAME::~SCH_BASE_FRAME()
  103. {
  104. #if defined( KICAD_USE_3DCONNEXION )
  105. if( m_spaceMouse != nullptr )
  106. delete m_spaceMouse;
  107. #endif
  108. }
  109. SCH_SCREEN* SCH_BASE_FRAME::GetScreen() const
  110. {
  111. return (SCH_SCREEN*) EDA_DRAW_FRAME::GetScreen();
  112. }
  113. EESCHEMA_SETTINGS* SCH_BASE_FRAME::eeconfig() const
  114. {
  115. return dynamic_cast<EESCHEMA_SETTINGS*>( config() );
  116. }
  117. SYMBOL_EDITOR_SETTINGS* SCH_BASE_FRAME::libeditconfig() const
  118. {
  119. return dynamic_cast<SYMBOL_EDITOR_SETTINGS*>( config() );
  120. }
  121. void SCH_BASE_FRAME::SetPageSettings( const PAGE_INFO& aPageSettings )
  122. {
  123. GetScreen()->SetPageSettings( aPageSettings );
  124. }
  125. const PAGE_INFO& SCH_BASE_FRAME::GetPageSettings () const
  126. {
  127. return GetScreen()->GetPageSettings();
  128. }
  129. const wxSize SCH_BASE_FRAME::GetPageSizeIU() const
  130. {
  131. // GetSizeIU is compile time dependent:
  132. return GetScreen()->GetPageSettings().GetSizeIU( schIUScale.IU_PER_MILS );
  133. }
  134. const TITLE_BLOCK& SCH_BASE_FRAME::GetTitleBlock() const
  135. {
  136. wxASSERT( GetScreen() );
  137. return GetScreen()->GetTitleBlock();
  138. }
  139. void SCH_BASE_FRAME::SetTitleBlock( const TITLE_BLOCK& aTitleBlock )
  140. {
  141. wxASSERT( GetScreen() );
  142. GetScreen()->SetTitleBlock( aTitleBlock );
  143. }
  144. void SCH_BASE_FRAME::UpdateStatusBar()
  145. {
  146. wxString line;
  147. BASE_SCREEN* screen = GetScreen();
  148. if( !screen )
  149. return;
  150. EDA_DRAW_FRAME::UpdateStatusBar();
  151. // Display absolute and relative coordinates
  152. VECTOR2D cursorPos = GetCanvas()->GetViewControls()->GetCursorPosition();
  153. VECTOR2D d = cursorPos - screen->m_LocalOrigin;
  154. line.Printf( "X %s Y %s",
  155. MessageTextFromValue( cursorPos.x, false ),
  156. MessageTextFromValue( cursorPos.y, false ) );
  157. SetStatusText( line, 2 );
  158. line.Printf( "dx %s dy %s dist %s",
  159. MessageTextFromValue( d.x, false ),
  160. MessageTextFromValue( d.y, false ),
  161. MessageTextFromValue( hypot( d.x, d.y ), false ) );
  162. SetStatusText( line, 3 );
  163. DisplayGridMsg();
  164. DisplayUnitsMsg();
  165. }
  166. LIB_SYMBOL* SCH_BASE_FRAME::GetLibSymbol( const LIB_ID& aLibId, bool aUseCacheLib,
  167. bool aShowErrorMsg )
  168. {
  169. SYMBOL_LIB* cache = ( aUseCacheLib ) ? Prj().SchLibs()->GetCacheLibrary() : nullptr;
  170. return SchGetLibSymbol( aLibId, Prj().SchSymbolLibTable(), cache, this, aShowErrorMsg );
  171. }
  172. bool SCH_BASE_FRAME::saveSymbolLibTables( bool aGlobal, bool aProject )
  173. {
  174. wxString msg;
  175. bool success = true;
  176. if( aGlobal )
  177. {
  178. try
  179. {
  180. SYMBOL_LIB_TABLE::GetGlobalLibTable().Save( SYMBOL_LIB_TABLE::GetGlobalTableFileName() );
  181. }
  182. catch( const IO_ERROR& ioe )
  183. {
  184. success = false;
  185. msg.Printf( _( "Error saving global symbol library table:\n%s" ), ioe.What() );
  186. wxMessageBox( msg, _( "File Save Error" ), wxOK | wxICON_ERROR );
  187. }
  188. }
  189. if( aProject && !Prj().GetProjectName().IsEmpty() )
  190. {
  191. wxFileName fn( Prj().GetProjectPath(), SYMBOL_LIB_TABLE::GetSymbolLibTableFileName() );
  192. try
  193. {
  194. Prj().SchSymbolLibTable()->Save( fn.GetFullPath() );
  195. }
  196. catch( const IO_ERROR& ioe )
  197. {
  198. success = false;
  199. msg.Printf( _( "Error saving project-specific symbol library table:\n%s" ),
  200. ioe.What() );
  201. wxMessageBox( msg, _( "File Save Error" ), wxOK | wxICON_ERROR );
  202. }
  203. }
  204. return success;
  205. }
  206. SYMBOL_LIB_TABLE* SCH_BASE_FRAME::SelectSymLibTable( bool aOptional )
  207. {
  208. // If no project is loaded, always work with the global table
  209. if( Prj().IsNullProject() )
  210. {
  211. SYMBOL_LIB_TABLE* ret = &SYMBOL_LIB_TABLE::GetGlobalLibTable();
  212. if( aOptional )
  213. {
  214. wxMessageDialog dlg( this, _( "Add the library to the global library table?" ),
  215. _( "Add To Global Library Table" ), wxYES_NO );
  216. if( dlg.ShowModal() != wxID_OK )
  217. ret = nullptr;
  218. }
  219. return ret;
  220. }
  221. wxArrayString libTableNames;
  222. libTableNames.Add( _( "Global" ) );
  223. libTableNames.Add( _( "Project" ) );
  224. wxSingleChoiceDialog dlg( this, _( "Choose the Library Table to add the library to:" ),
  225. _( "Add To Library Table" ), libTableNames );
  226. if( aOptional )
  227. {
  228. dlg.FindWindow( wxID_CANCEL )->SetLabel( _( "Skip" ) );
  229. dlg.FindWindow( wxID_OK )->SetLabel( _( "Add" ) );
  230. }
  231. if( dlg.ShowModal() != wxID_OK )
  232. return nullptr;
  233. switch( dlg.GetSelection() )
  234. {
  235. case 0: return &SYMBOL_LIB_TABLE::GetGlobalLibTable();
  236. case 1: return Prj().SchSymbolLibTable();
  237. default: return nullptr;
  238. }
  239. }
  240. void SCH_BASE_FRAME::RedrawScreen( const VECTOR2I& aCenterPoint, bool aWarpPointer )
  241. {
  242. GetCanvas()->GetView()->SetCenter( aCenterPoint );
  243. if( aWarpPointer )
  244. GetCanvas()->GetViewControls()->CenterOnCursor();
  245. GetCanvas()->Refresh();
  246. }
  247. void SCH_BASE_FRAME::HardRedraw()
  248. {
  249. if( GetCanvas() && GetCanvas()->GetView() )
  250. {
  251. GetCanvas()->GetView()->UpdateAllItems( KIGFX::ALL );
  252. GetCanvas()->ForceRefresh();
  253. }
  254. }
  255. SCH_DRAW_PANEL* SCH_BASE_FRAME::GetCanvas() const
  256. {
  257. return static_cast<SCH_DRAW_PANEL*>( EDA_DRAW_FRAME::GetCanvas() );
  258. }
  259. KIGFX::SCH_RENDER_SETTINGS* SCH_BASE_FRAME::GetRenderSettings()
  260. {
  261. if( GetCanvas() && GetCanvas()->GetView() )
  262. {
  263. if( KIGFX::PAINTER* painter = GetCanvas()->GetView()->GetPainter() )
  264. return static_cast<KIGFX::SCH_RENDER_SETTINGS*>( painter->GetSettings() );
  265. }
  266. return nullptr;
  267. }
  268. void SCH_BASE_FRAME::createCanvas()
  269. {
  270. m_canvasType = loadCanvasTypeSetting();
  271. SetCanvas( new SCH_DRAW_PANEL( this, wxID_ANY, wxPoint( 0, 0 ), m_frameSize,
  272. GetGalDisplayOptions(), m_canvasType ) );
  273. ActivateGalCanvas();
  274. }
  275. void SCH_BASE_FRAME::ActivateGalCanvas()
  276. {
  277. EDA_DRAW_FRAME::ActivateGalCanvas();
  278. #if defined( KICAD_USE_3DCONNEXION )
  279. try
  280. {
  281. if( !m_spaceMouse )
  282. {
  283. m_spaceMouse = new NL_SCHEMATIC_PLUGIN();
  284. }
  285. m_spaceMouse->SetCanvas( GetCanvas() );
  286. }
  287. catch( const std::system_error& e )
  288. {
  289. wxLogTrace( wxT( "KI_TRACE_NAVLIB" ), e.what() );
  290. }
  291. #endif
  292. }
  293. void SCH_BASE_FRAME::UpdateItem( EDA_ITEM* aItem, bool isAddOrDelete, bool aUpdateRtree )
  294. {
  295. EDA_ITEM* parent = aItem->GetParent();
  296. if( aItem->Type() == SCH_SHEET_PIN_T )
  297. {
  298. // Sheet pins aren't in the view. Refresh their parent.
  299. if( parent )
  300. GetCanvas()->GetView()->Update( parent );
  301. }
  302. else
  303. {
  304. if( !isAddOrDelete )
  305. GetCanvas()->GetView()->Update( aItem );
  306. // Some children are drawn from their parents. Mark them for re-paint.
  307. if( parent && parent->IsType( { SCH_SYMBOL_T, SCH_SHEET_T, SCH_LABEL_LOCATE_ANY_T } ) )
  308. GetCanvas()->GetView()->Update( parent, KIGFX::REPAINT );
  309. }
  310. /**
  311. * Be careful when calling this. Update will invalidate RTree iterators, so you cannot call this
  312. * while doing things like `for( SCH_ITEM* item : screen->Items() )`
  313. */
  314. if( aUpdateRtree)
  315. GetScreen()->Update( static_cast<SCH_ITEM*>( aItem ) );
  316. // Calling Refresh() here introduces a bi-stable state: when doing operations on a
  317. // large number of items if at some point the refresh timer times out and does a
  318. // refresh it will take long enough that the next item will also time out, and the
  319. // next, and the next, etc.
  320. // GetCanvas()->Refresh();
  321. }
  322. void SCH_BASE_FRAME::RefreshZoomDependentItems()
  323. {
  324. // We currently have two zoom-dependent renderings: text, which is rendered as bitmap text
  325. // when too small to see the difference, and selection shadows.
  326. //
  327. // Because non-selected text is cached by OpenGL, we only apply the bitmap performance hack
  328. // to selected text items.
  329. //
  330. // Thus, as it currently stands, all zoom-dependent items can be found in the list of selected
  331. // items.
  332. if( m_toolManager )
  333. {
  334. EE_SELECTION_TOOL* selectionTool = m_toolManager->GetTool<EE_SELECTION_TOOL>();
  335. SELECTION& selection = selectionTool->GetSelection();
  336. KIGFX::SCH_VIEW* view = GetCanvas()->GetView();
  337. for( EDA_ITEM* item : selection )
  338. {
  339. if( item->RenderAsBitmap( view->GetGAL()->GetWorldScale() ) != item->IsShownAsBitmap()
  340. || item->IsType( KIGFX::SCH_PAINTER::g_ScaledSelectionTypes ) )
  341. {
  342. view->Update( item, KIGFX::REPAINT );
  343. EDA_ITEM* parent = item->GetParent();
  344. // Symbol children are drawn from their parents. Mark them for re-paint.
  345. if( parent && parent->Type() == SCH_SYMBOL_T )
  346. GetCanvas()->GetView()->Update( parent, KIGFX::REPAINT );
  347. }
  348. }
  349. }
  350. }
  351. void SCH_BASE_FRAME::AddToScreen( EDA_ITEM* aItem, SCH_SCREEN* aScreen )
  352. {
  353. // Null pointers will cause boost::ptr_vector to raise a boost::bad_pointer exception which
  354. // will be unhandled. There is no valid reason to pass an invalid EDA_ITEM pointer to the
  355. // screen append function.
  356. wxCHECK( aItem != nullptr, /* voide */ );
  357. auto screen = aScreen;
  358. if( aScreen == nullptr )
  359. screen = GetScreen();
  360. screen->Append( (SCH_ITEM*) aItem );
  361. if( screen == GetScreen() )
  362. {
  363. GetCanvas()->GetView()->Add( aItem );
  364. UpdateItem( aItem, true ); // handle any additional parent semantics
  365. }
  366. }
  367. void SCH_BASE_FRAME::RemoveFromScreen( EDA_ITEM* aItem, SCH_SCREEN* aScreen )
  368. {
  369. auto screen = aScreen;
  370. if( aScreen == nullptr )
  371. screen = GetScreen();
  372. if( screen == GetScreen() )
  373. GetCanvas()->GetView()->Remove( aItem );
  374. screen->Remove( (SCH_ITEM*) aItem );
  375. if( screen == GetScreen() )
  376. UpdateItem( aItem, true ); // handle any additional parent semantics
  377. }
  378. void SCH_BASE_FRAME::SyncView()
  379. {
  380. GetCanvas()->GetView()->UpdateAllItems( KIGFX::ALL );
  381. }
  382. COLOR4D SCH_BASE_FRAME::GetLayerColor( SCH_LAYER_ID aLayer )
  383. {
  384. return GetColorSettings()->GetColor( aLayer );
  385. }
  386. void SCH_BASE_FRAME::CommonSettingsChanged( bool aEnvVarsChanged, bool aTextVarsChanged )
  387. {
  388. EDA_DRAW_FRAME::CommonSettingsChanged( aEnvVarsChanged, aTextVarsChanged );
  389. COLOR_SETTINGS* colorSettings = GetColorSettings( true );
  390. GetCanvas()->GetView()->GetPainter()->GetSettings()->LoadColors( colorSettings );
  391. GetCanvas()->GetGAL()->SetAxesColor( colorSettings->GetColor( LAYER_SCHEMATIC_GRID_AXES ) );
  392. GetCanvas()->GetView()->UpdateAllItems( KIGFX::ALL );
  393. GetCanvas()->GetView()->RecacheAllItems();
  394. GetCanvas()->GetView()->MarkTargetDirty( KIGFX::TARGET_NONCACHED );
  395. }
  396. COLOR_SETTINGS* SCH_BASE_FRAME::GetColorSettings( bool aForceRefresh ) const
  397. {
  398. if( !m_colorSettings || aForceRefresh )
  399. {
  400. SETTINGS_MANAGER& mgr = Pgm().GetSettingsManager();
  401. EESCHEMA_SETTINGS* cfg = mgr.GetAppSettings<EESCHEMA_SETTINGS>();
  402. wxString colorTheme = cfg->m_ColorTheme;
  403. if( IsType( FRAME_SCH_SYMBOL_EDITOR ) )
  404. {
  405. SYMBOL_EDITOR_SETTINGS* symCfg = mgr.GetAppSettings<SYMBOL_EDITOR_SETTINGS>();
  406. if( !symCfg->m_UseEeschemaColorSettings )
  407. colorTheme = symCfg->m_ColorTheme;
  408. }
  409. COLOR_SETTINGS* colorSettings = mgr.GetColorSettings( colorTheme );
  410. const_cast<SCH_BASE_FRAME*>( this )->m_colorSettings = colorSettings;
  411. }
  412. return m_colorSettings;
  413. }
  414. COLOR4D SCH_BASE_FRAME::GetDrawBgColor() const
  415. {
  416. return GetColorSettings()->GetColor( LAYER_SCHEMATIC_BACKGROUND );
  417. }
  418. void SCH_BASE_FRAME::handleActivateEvent( wxActivateEvent& aEvent )
  419. {
  420. EDA_DRAW_FRAME::handleActivateEvent( aEvent );
  421. #if defined( KICAD_USE_3DCONNEXION )
  422. if( m_spaceMouse )
  423. {
  424. m_spaceMouse->SetFocus( aEvent.GetActive() );
  425. }
  426. #endif
  427. }
  428. void SCH_BASE_FRAME::handleIconizeEvent( wxIconizeEvent& aEvent )
  429. {
  430. EDA_DRAW_FRAME::handleIconizeEvent( aEvent );
  431. #if defined( KICAD_USE_3DCONNEXION )
  432. if( m_spaceMouse && aEvent.IsIconized() )
  433. {
  434. m_spaceMouse->SetFocus( false );
  435. }
  436. #endif
  437. }
  438. wxString SCH_BASE_FRAME::SelectLibraryFromList()
  439. {
  440. COMMON_SETTINGS* cfg = Pgm().GetCommonSettings();
  441. PROJECT& prj = Prj();
  442. if( prj.SchSymbolLibTable()->IsEmpty() )
  443. {
  444. ShowInfoBarError( _( "No symbol libraries are loaded." ) );
  445. return wxEmptyString;
  446. }
  447. wxArrayString headers;
  448. headers.Add( _( "Library" ) );
  449. std::vector< wxArrayString > itemsToDisplay;
  450. std::vector< wxString > libNicknames = prj.SchSymbolLibTable()->GetLogicalLibs();
  451. for( const wxString& name : libNicknames )
  452. {
  453. // Exclude read only libraries.
  454. if( !prj.SchSymbolLibTable()->IsSymbolLibWritable( name ) )
  455. continue;
  456. if( alg::contains( prj.GetProjectFile().m_PinnedSymbolLibs, name )
  457. || alg::contains( cfg->m_Session.pinned_symbol_libs, name ) )
  458. {
  459. wxArrayString item;
  460. item.Add( LIB_TREE_MODEL_ADAPTER::GetPinningSymbol() + name );
  461. itemsToDisplay.push_back( item );
  462. }
  463. }
  464. for( const wxString& name : libNicknames )
  465. {
  466. // Exclude read only libraries.
  467. if( !prj.SchSymbolLibTable()->IsSymbolLibWritable( name ) )
  468. continue;
  469. if( !alg::contains( prj.GetProjectFile().m_PinnedSymbolLibs, name )
  470. && !alg::contains( cfg->m_Session.pinned_symbol_libs, name ) )
  471. {
  472. wxArrayString item;
  473. item.Add( name );
  474. itemsToDisplay.push_back( item );
  475. }
  476. }
  477. wxString oldLibName = prj.GetRString( PROJECT::SCH_LIB_SELECT );
  478. EDA_LIST_DIALOG dlg( this, _( "Select Symbol Library" ), headers, itemsToDisplay, oldLibName,
  479. false );
  480. if( dlg.ShowModal() != wxID_OK )
  481. return wxEmptyString;
  482. wxString libName = dlg.GetTextSelection();
  483. if( !libName.empty() )
  484. {
  485. if( prj.SchSymbolLibTable()->HasLibrary( libName ) )
  486. prj.SetRString( PROJECT::SCH_LIB_SELECT, libName );
  487. else
  488. libName = wxEmptyString;
  489. }
  490. return libName;
  491. }