/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2018 Jean-Pierre Charras, jp.charras at wanadoo.fr * Copyright (C) 2011 Wayne Stambaugh * Copyright (C) 1992-2022 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, you may find one here: * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html * or you may search the http://www.gnu.org website for the version 2 license, * or you may write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define CVPCB_MAINFRAME_NAME wxT( "CvpcbFrame" ) CVPCB_MAINFRAME::CVPCB_MAINFRAME( KIWAY* aKiway, wxWindow* aParent ) : KIWAY_PLAYER( aKiway, aParent, FRAME_CVPCB, _( "Assign Footprints" ), wxDefaultPosition, wxDefaultSize, KICAD_DEFAULT_DRAWFRAME_STYLE, CVPCB_MAINFRAME_NAME, unityScale ), m_viewerPendingUpdate( false ) { m_symbolsListBox = nullptr; m_footprintListBox = nullptr; m_librariesListBox = nullptr; m_mainToolBar = nullptr; m_modified = false; m_cannotClose = false; m_skipComponentSelect = false; m_filteringOptions = FOOTPRINTS_LISTBOX::UNFILTERED_FP_LIST; m_tcFilterString = nullptr; m_FootprintsList = FOOTPRINT_LIST::GetInstance( Kiway() ); m_initialized = false; m_aboutTitle = "CvPcb"; // Give an icon wxIcon icon; icon.CopyFromBitmap( KiBitmap( BITMAPS::icon_cvpcb ) ); SetIcon( icon ); SetAutoLayout( true ); LoadSettings( config() ); setupTools(); setupUIConditions(); ReCreateMenuBar(); ReCreateHToolbar(); // Create list of available footprints and symbols of the schematic BuildSymbolsListBox(); BuildFootprintsListBox(); m_librariesListBox = new LIBRARY_LISTBOX( this, ID_CVPCB_LIBRARY_LIST ); m_librariesListBox->SetFont( KIUI::GetMonospacedUIFont() ); m_auimgr.SetManagedWindow( this ); m_auimgr.AddPane( m_mainToolBar, EDA_PANE().HToolbar().Name( "MainToolbar" ).Top().Layer(6) ); m_auimgr.AddPane( m_librariesListBox, EDA_PANE().Palette().Name( "Libraries" ).Left().Layer(1) .Caption( _( "Footprint Libraries" ) ) .BestSize((int) ( m_frameSize.x * 0.20 ), m_frameSize.y ) ); m_auimgr.AddPane( m_symbolsListBox, EDA_PANE().Palette().Name( "Symbols" ).Center().Layer(0) .Caption( _( "Symbol : Footprint Assignments" ) ) ); m_auimgr.AddPane( m_footprintListBox, EDA_PANE().Palette().Name( "Footprints" ).Right().Layer(1) .Caption( _( "Filtered Footprints" ) ) .BestSize((int) ( m_frameSize.x * 0.30 ), m_frameSize.y ) ); // Build the bottom panel, to display 2 status texts and the buttons: auto bottomPanel = new wxPanel( this ); auto panelSizer = new wxBoxSizer( wxVERTICAL ); wxFlexGridSizer* fgSizerStatus = new wxFlexGridSizer( 3, 1, 0, 0 ); fgSizerStatus->SetFlexibleDirection( wxBOTH ); fgSizerStatus->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); m_statusLine1 = new wxStaticText( bottomPanel, wxID_ANY, wxEmptyString ); fgSizerStatus->Add( m_statusLine1, 0, 0, 5 ); m_statusLine2 = new wxStaticText( bottomPanel, wxID_ANY, wxEmptyString ); fgSizerStatus->Add( m_statusLine2, 0, 0, 5 ); m_statusLine3 = new wxStaticText( bottomPanel, wxID_ANY, wxEmptyString ); fgSizerStatus->Add( m_statusLine3, 0, wxBOTTOM, 3 ); panelSizer->Add( fgSizerStatus, 1, wxEXPAND|wxLEFT, 2 ); wxStaticLine* staticline1 = new wxStaticLine( bottomPanel ); panelSizer->Add( staticline1, 0, wxEXPAND, 5 ); m_statusLine1->SetFont( KIUI::GetStatusFont( this ) ); m_statusLine2->SetFont( KIUI::GetStatusFont( this ) ); m_statusLine3->SetFont( KIUI::GetStatusFont( this ) ); // Add buttons: auto buttonsSizer = new wxBoxSizer( wxHORIZONTAL ); auto sdbSizer = new wxStdDialogButtonSizer(); m_saveAndContinue = new wxButton( bottomPanel, wxID_ANY, _( "Apply, Save Schematic && Continue" ) ); buttonsSizer->Add( m_saveAndContinue, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 20 ); auto sdbSizerOK = new wxButton( bottomPanel, wxID_OK ); sdbSizer->AddButton( sdbSizerOK ); auto sdbSizerCancel = new wxButton( bottomPanel, wxID_CANCEL ); sdbSizer->AddButton( sdbSizerCancel ); sdbSizer->Realize(); buttonsSizer->Add( sdbSizer, 0, 0, 5 ); panelSizer->Add( buttonsSizer, 0, wxALIGN_RIGHT|wxALL, 5 ); bottomPanel->SetSizer( panelSizer ); bottomPanel->Fit(); sdbSizerOK->SetDefault(); KIPLATFORM::UI::FixupCancelButtonCmdKeyCollision( this ); m_auimgr.AddPane( bottomPanel, EDA_PANE().HToolbar().Name( "Buttons" ).Bottom().Layer(6) ); m_auimgr.Update(); m_initialized = true; if( CVPCB_SETTINGS* cfg = dynamic_cast( config() ) ) { m_tcFilterString->ChangeValue( cfg->m_FilterString ); if( cfg->m_LibrariesWidth > 0 ) { wxAuiPaneInfo& librariesPane = m_auimgr.GetPane( "Libraries" ); // wxAUI hack: force width by setting MinSize() and then Fixed() // thanks to ZenJu http://trac.wxwidgets.org/ticket/13180 librariesPane.MinSize( cfg->m_LibrariesWidth, -1 ); librariesPane.BestSize( cfg->m_LibrariesWidth, -1 ); librariesPane.MaxSize( cfg->m_LibrariesWidth, -1 ); librariesPane.Fixed(); m_auimgr.Update(); // now make it resizable again librariesPane.MinSize( 20, -1 ); librariesPane.Resizable(); m_auimgr.Update(); } if( cfg->m_FootprintsWidth > 0 ) { wxAuiPaneInfo& footprintsPane = m_auimgr.GetPane( "Footprints" ); // wxAUI hack: force width by setting MinSize() and then Fixed() // thanks to ZenJu http://trac.wxwidgets.org/ticket/13180 footprintsPane.MinSize( cfg->m_FootprintsWidth, -1 ); footprintsPane.BestSize( cfg->m_FootprintsWidth, -1 ); footprintsPane.MaxSize( cfg->m_FootprintsWidth, -1 ); footprintsPane.Fixed(); m_auimgr.Update(); // now make it resizable again footprintsPane.MinSize( 20, -1 ); footprintsPane.Resizable(); m_auimgr.Update(); } } // Connect Events setupEventHandlers(); // Start the main processing loop m_toolManager->InvokeTool( "cvpcb.Control" ); m_filterTimer->StartOnce( 100 ); KIPLATFORM::APP::SetShutdownBlockReason( this, _( "Symbol to footprint changes are unsaved" ) ); } CVPCB_MAINFRAME::~CVPCB_MAINFRAME() { // Stop the timer during destruction early to avoid potential race conditions (that do happen) m_filterTimer->Stop(); // Shutdown all running tools if( m_toolManager ) m_toolManager->ShutdownAllTools(); // Clean up the tool infrastructure delete m_actions; delete m_toolManager; delete m_toolDispatcher; m_auimgr.UnInit(); } void CVPCB_MAINFRAME::setupTools() { // Create the manager m_actions = new CVPCB_ACTIONS(); m_toolManager = new TOOL_MANAGER; m_toolManager->SetEnvironment( nullptr, nullptr, nullptr, config(), this ); m_toolDispatcher = new TOOL_DISPATCHER( m_toolManager ); // Register tools m_toolManager->RegisterTool( new COMMON_CONTROL ); m_toolManager->RegisterTool( new CVPCB_CONTROL ); m_toolManager->RegisterTool( new CVPCB_ASSOCIATION_TOOL ); m_toolManager->InitTools(); CVPCB_CONTROL* tool = m_toolManager->GetTool(); // Even though these menus will open with the right-click, we treat them as a normal // menu instead of a context menu because we don't care about their position and want // to be able to tell the difference between a menu click and a hotkey activation. // Create the context menu for the symbols list box m_symbolsContextMenu = new ACTION_MENU( false, tool ); m_symbolsContextMenu->Add( CVPCB_ACTIONS::showFootprintViewer ); m_symbolsContextMenu->AppendSeparator(); m_symbolsContextMenu->Add( ACTIONS::cut ); m_symbolsContextMenu->Add( ACTIONS::copy ); m_symbolsContextMenu->Add( ACTIONS::paste ); m_symbolsContextMenu->AppendSeparator(); m_symbolsContextMenu->Add( CVPCB_ACTIONS::deleteAssoc ); // Create the context menu for the footprint list box m_footprintContextMenu = new ACTION_MENU( false, tool ); m_footprintContextMenu->Add( CVPCB_ACTIONS::showFootprintViewer ); } void CVPCB_MAINFRAME::setupUIConditions() { EDA_BASE_FRAME::setupUIConditions(); ACTION_MANAGER* mgr = m_toolManager->GetActionManager(); EDITOR_CONDITIONS cond( this ); wxASSERT( mgr ); #define ENABLE( x ) ACTION_CONDITIONS().Enable( x ) #define CHECK( x ) ACTION_CONDITIONS().Check( x ) mgr->SetConditions( CVPCB_ACTIONS::saveAssociationsToSchematic, ENABLE( cond.ContentModified() ) ); mgr->SetConditions( CVPCB_ACTIONS::saveAssociationsToFile, ENABLE( cond.ContentModified() ) ); mgr->SetConditions( ACTIONS::undo, ENABLE( cond.UndoAvailable() ) ); mgr->SetConditions( ACTIONS::redo, ENABLE( cond.RedoAvailable() ) ); auto compFilter = [this] ( const SELECTION& ) { return m_filteringOptions & FOOTPRINTS_LISTBOX::FILTERING_BY_COMPONENT_FP_FILTERS; }; auto libFilter = [this] ( const SELECTION& ) { return m_filteringOptions & FOOTPRINTS_LISTBOX::FILTERING_BY_LIBRARY; }; auto pinFilter = [this] ( const SELECTION& ) { return m_filteringOptions & FOOTPRINTS_LISTBOX::FILTERING_BY_PIN_COUNT; }; mgr->SetConditions( CVPCB_ACTIONS::FilterFPbyFPFilters, CHECK( compFilter ) ); mgr->SetConditions( CVPCB_ACTIONS::FilterFPbyLibrary, CHECK( libFilter ) ); mgr->SetConditions( CVPCB_ACTIONS::filterFPbyPin, CHECK( pinFilter ) ); #undef CHECK #undef ENABLE } void CVPCB_MAINFRAME::setupEventHandlers() { // Connect the handlers to launch the context menus in the listboxes m_footprintListBox->Bind( wxEVT_RIGHT_DOWN, [this]( wxMouseEvent& ) { PopupMenu( m_footprintContextMenu ); } ); m_symbolsListBox->Bind( wxEVT_RIGHT_DOWN, [this]( wxMouseEvent& ) { PopupMenu( m_symbolsContextMenu ); } ); // Connect the handler for the save button m_saveAndContinue->Bind( wxEVT_COMMAND_BUTTON_CLICKED, [this]( wxCommandEvent& ) { // saveAssociations must be run immediately GetToolManager()->RunAction( CVPCB_ACTIONS::saveAssociationsToFile, true ); } ); // Connect the handlers for the ok/cancel buttons Bind( wxEVT_BUTTON, [this]( wxCommandEvent& ) { // saveAssociations must be run immediately, before running Close( true ) GetToolManager()->RunAction( CVPCB_ACTIONS::saveAssociationsToSchematic, true ); Close( true ); }, wxID_OK ); Bind( wxEVT_BUTTON, [this]( wxCommandEvent& ) { Close( false ); }, wxID_CANCEL ); // Connect the handlers for the close events Bind( wxEVT_MENU, [this]( wxCommandEvent& ) { Close( false ); }, wxID_CLOSE ); Bind( wxEVT_MENU, [this]( wxCommandEvent& ) { Close( false ); }, wxID_EXIT ); // Toolbar events Bind( wxEVT_TEXT, &CVPCB_MAINFRAME::onTextFilterChanged, this ); // Just skip the resize events Bind( wxEVT_SIZE, []( wxSizeEvent& aEvent ) { aEvent.Skip(); } ); // Attach the events to the tool dispatcher Bind( wxEVT_CHAR, &TOOL_DISPATCHER::DispatchWxEvent, m_toolDispatcher ); Bind( wxEVT_CHAR_HOOK, &TOOL_DISPATCHER::DispatchWxEvent, m_toolDispatcher ); m_filterTimer = new wxTimer( this ); Bind( wxEVT_TIMER, &CVPCB_MAINFRAME::onTextFilterChangedTimer, this, m_filterTimer->GetId() ); } bool CVPCB_MAINFRAME::canCloseWindow( wxCloseEvent& aEvent ) { if( m_modified ) { // Shutdown blocks must be determined and vetoed as early as possible if( KIPLATFORM::APP::SupportsShutdownBlockReason() && aEvent.GetId() == wxEVT_QUERY_END_SESSION ) { return false; } if( !HandleUnsavedChanges( this, _( "Symbol to Footprint links have been modified. " "Save changes?" ), [&]() -> bool { return SaveFootprintAssociation( false ); } ) ) { return false; } } if( m_cannotClose ) return false; return true; } void CVPCB_MAINFRAME::doCloseWindow() { if( GetFootprintViewerFrame() ) GetFootprintViewerFrame()->Close( true ); m_modified = false; // clear symbol selection in schematic: SendComponentSelectionToSch( true ); } void CVPCB_MAINFRAME::onTextFilterChanged( wxCommandEvent& event ) { // Called when changing the filter string in main toolbar. // If the option FOOTPRINTS_LISTBOX::FILTERING_BY_TEXT_PATTERN is set, update the list // of available footprints which match the filter m_filterTimer->StartOnce( 200 ); } void CVPCB_MAINFRAME::onTextFilterChangedTimer( wxTimerEvent& aEvent ) { // GTK loses the search-control's focus on a Refresh event, so we record the focus and // insertion point here and then restore them at the end. bool searchCtrlHasFocus = m_tcFilterString->HasFocus(); long pos = m_tcFilterString->GetInsertionPoint(); COMPONENT* symbol = GetSelectedComponent(); wxString libraryName = m_librariesListBox->GetSelectedLibrary(); m_footprintListBox->SetFootprints( *m_FootprintsList, libraryName, symbol, m_tcFilterString->GetValue(), m_filteringOptions ); if( symbol && symbol->GetFPID().IsValid() ) m_footprintListBox->SetSelectedFootprint( symbol->GetFPID() ); else if( m_footprintListBox->GetSelection() >= 0 ) m_footprintListBox->SetSelection( m_footprintListBox->GetSelection(), false ); RefreshFootprintViewer(); DisplayStatus(); if( searchCtrlHasFocus ) { m_tcFilterString->SetFocus(); m_tcFilterString->SetInsertionPoint( pos ); } } void CVPCB_MAINFRAME::OnSelectComponent( wxListEvent& event ) { if( m_skipComponentSelect ) return; COMPONENT* symbol = GetSelectedComponent(); wxString libraryName = m_librariesListBox->GetSelectedLibrary(); m_footprintListBox->SetFootprints( *m_FootprintsList, libraryName, symbol, m_tcFilterString->GetValue(), m_filteringOptions ); if( symbol && symbol->GetFPID().IsValid() ) m_footprintListBox->SetSelectedFootprint( symbol->GetFPID() ); else if( m_footprintListBox->GetSelection() >= 0 ) m_footprintListBox->SetSelection( m_footprintListBox->GetSelection(), false ); RefreshFootprintViewer(); refreshAfterSymbolSearch( symbol ); } void CVPCB_MAINFRAME::RefreshFootprintViewer() { if( GetFootprintViewerFrame() && !m_viewerPendingUpdate ) { Bind( wxEVT_IDLE, &CVPCB_MAINFRAME::updateFootprintViewerOnIdle, this ); m_viewerPendingUpdate = true; } } void CVPCB_MAINFRAME::updateFootprintViewerOnIdle( wxIdleEvent& aEvent ) { Unbind( wxEVT_IDLE, &CVPCB_MAINFRAME::updateFootprintViewerOnIdle, this ); m_viewerPendingUpdate = false; // On some plateforms (OSX) the focus is lost when the viewers (fp and 3D viewers) // are opened and refreshed when a new footprint is selected. // If the listbox has the focus before selecting a new footprint, it will be forced // after selection. bool footprintListHasFocus = m_footprintListBox->HasFocus(); bool symbolListHasFocus = m_symbolsListBox->HasFocus(); // If the footprint view window is displayed, update the footprint. if( GetFootprintViewerFrame() ) GetToolManager()->RunAction( CVPCB_ACTIONS::showFootprintViewer, true ); DisplayStatus(); if( footprintListHasFocus ) m_footprintListBox->SetFocus(); else if( symbolListHasFocus ) m_symbolsListBox->SetFocus(); } void CVPCB_MAINFRAME::LoadSettings( APP_SETTINGS_BASE* aCfg ) { EDA_BASE_FRAME::LoadSettings( aCfg ); CVPCB_SETTINGS* cfg = static_cast( aCfg ); m_filteringOptions = cfg->m_FilterFlags; } void CVPCB_MAINFRAME::SaveSettings( APP_SETTINGS_BASE* aCfg ) { EDA_BASE_FRAME::SaveSettings( aCfg ); CVPCB_SETTINGS* cfg = static_cast( aCfg ); cfg->m_FilterFlags = m_filteringOptions; cfg->m_FilterString = m_tcFilterString->GetValue(); cfg->m_LibrariesWidth = m_librariesListBox->GetSize().x; cfg->m_FootprintsWidth = m_footprintListBox->GetSize().x; } void CVPCB_MAINFRAME::UndoAssociation() { if( m_undoList.size() == 0 ) return; CVPCB_UNDO_REDO_ENTRIES redoEntries; CVPCB_UNDO_REDO_ENTRIES curEntry = m_undoList.back(); m_undoList.pop_back(); // Iterate over the entries to undo for( const auto& assoc : curEntry ) { AssociateFootprint( assoc, true, false ); redoEntries.emplace_back( assoc.Reverse() ); } // Add the redo entries to the redo stack m_redoList.emplace_back( redoEntries ); } void CVPCB_MAINFRAME::RedoAssociation() { if( m_redoList.size() == 0 ) return; CVPCB_UNDO_REDO_ENTRIES curEntry = m_redoList.back(); m_redoList.pop_back(); // Iterate over the entries to undo bool firstAssoc = true; for( const auto& assoc : curEntry ) { AssociateFootprint( assoc, firstAssoc ); firstAssoc = false; } } wxString CVPCB_MAINFRAME::formatSymbolDesc( int idx, const wxString& aReference, const wxString& aValue, const wxString& aFootprint ) { // Work around a bug in wxString::Format with double-byte chars (and double-quote, for some // reason). wxString desc = wxString::Format( wxT( "%3d " ), idx ); for( int ii = aReference.Length(); ii < 8; ++ii ) desc += wxS( " " ); desc += aReference + wxT( " - " ); for( int ii = aValue.Length(); ii < 16; ++ii ) desc += wxS( " " ); desc += aValue + wxT( " : " ) + aFootprint; return desc; } void CVPCB_MAINFRAME::AssociateFootprint( const CVPCB_ASSOCIATION& aAssociation, bool aNewEntry, bool aAddUndoItem ) { if( m_netlist.IsEmpty() ) return; COMPONENT* symbol = m_netlist.GetComponent( aAssociation.GetComponentIndex() ); if( symbol == nullptr ) return; LIB_ID fpid = aAssociation.GetNewFootprint(); LIB_ID oldFpid = symbol->GetFPID(); // Test for validity of the requested footprint if( !fpid.empty() && !fpid.IsValid() ) { wxString msg = wxString::Format( _( "'%s' is not a valid footprint." ), fpid.Format().wx_str() ); DisplayErrorMessage( this, msg ); return; } const KIID& id = symbol->GetKIIDs().front(); // Set new footprint to all instances of the selected symbol for( unsigned int idx : GetComponentIndices() ) { COMPONENT* candidate = m_netlist.GetComponent( idx ); const std::vector& kiids = candidate->GetKIIDs(); if( std::find( kiids.begin(), kiids.end(), id ) != kiids.end() ) { // Set the new footprint candidate->SetFPID( fpid ); // create the new symbol description and set it wxString description = formatSymbolDesc( idx + 1, candidate->GetReference(), candidate->GetValue(), candidate->GetFPID().Format().wx_str() ); m_symbolsListBox->SetString( idx, description ); FOOTPRINT_INFO* fp = m_FootprintsList->GetFootprintInfo( symbol->GetFPID().Format().wx_str() ); if( !fp ) { m_symbolsListBox->AppendWarning( idx ); } else { m_symbolsListBox->RemoveWarning( idx ); } } } // Mark the data as being modified m_modified = true; // Update the statusbar and refresh the list DisplayStatus(); m_symbolsListBox->Refresh(); if( !aAddUndoItem ) return; // Update the undo list if ( aNewEntry ) { // Create a new entry for this association CVPCB_UNDO_REDO_ENTRIES newEntry; newEntry.emplace_back( CVPCB_ASSOCIATION( aAssociation.GetComponentIndex(), oldFpid, aAssociation.GetNewFootprint() ) ); m_undoList.emplace_back( newEntry ); // Clear the redo list m_redoList.clear(); } else { m_undoList.back().emplace_back( CVPCB_ASSOCIATION( aAssociation.GetComponentIndex(), oldFpid, aAssociation.GetNewFootprint() ) ); } } bool CVPCB_MAINFRAME::OpenProjectFiles( const std::vector& aFileSet, int aCtl ) { return true; } void CVPCB_MAINFRAME::refreshAfterSymbolSearch( COMPONENT* aSymbol ) { // Tell AuiMgr that objects are changed ! if( m_auimgr.GetManagedWindow() ) // Be sure Aui Manager is initialized m_auimgr.Update(); // (could be not the case when starting CvPcb) if( aSymbol == nullptr ) { DisplayStatus(); return; } // Preview of the already assigned footprint. // Find the footprint that was already chosen for this aSymbol and select it, // but only if the selection is made from the aSymbol list or the library list. // If the selection is made from the footprint list, do not change the current // selected footprint. if( FindFocus() == m_symbolsListBox || FindFocus() == m_librariesListBox ) { wxString footprintName = FROM_UTF8( aSymbol->GetFPID().Format().c_str() ); m_footprintListBox->SetSelection( m_footprintListBox->GetSelection(), false ); for( int ii = 0; ii < m_footprintListBox->GetCount(); ii++ ) { wxString candidateName; wxString msg = m_footprintListBox->OnGetItemText( ii, 0 ); msg.Trim( true ); msg.Trim( false ); candidateName = msg.AfterFirst( wxChar( ' ' ) ); if( footprintName.Cmp( candidateName ) == 0 ) { m_footprintListBox->SetSelection( ii, true ); break; } } } SendComponentSelectionToSch(); DisplayStatus(); } void CVPCB_MAINFRAME::SetFootprintFilter( FOOTPRINTS_LISTBOX::FP_FILTER_T aFilter, CVPCB_MAINFRAME::CVPCB_FILTER_ACTION aAction ) { int option = aFilter; // Apply the filter accordingly switch( aAction ) { case CVPCB_MAINFRAME::FILTER_DISABLE: m_filteringOptions &= ~option; break; case CVPCB_MAINFRAME::FILTER_ENABLE: m_filteringOptions |= option; break; case CVPCB_MAINFRAME::FILTER_TOGGLE: m_filteringOptions ^= option; break; } wxListEvent l_event; OnSelectComponent( l_event ); } void CVPCB_MAINFRAME::DisplayStatus() { if( !m_initialized ) return; wxString filters, msg; COMPONENT* symbol = GetSelectedComponent(); if( ( m_filteringOptions & FOOTPRINTS_LISTBOX::FILTERING_BY_COMPONENT_FP_FILTERS ) ) { msg.Empty(); if( symbol ) { for( unsigned ii = 0; ii < symbol->GetFootprintFilters().GetCount(); ii++ ) { if( msg.IsEmpty() ) msg += symbol->GetFootprintFilters()[ii]; else msg += wxT( ", " ) + symbol->GetFootprintFilters()[ii]; } } filters += _( "Keywords" ); if( !msg.IsEmpty() ) filters += wxString::Format( wxT( " (%s)" ), msg ); } if( ( m_filteringOptions & FOOTPRINTS_LISTBOX::FILTERING_BY_PIN_COUNT ) ) { msg.Empty(); if( symbol ) msg = wxString::Format( wxT( "%i" ), symbol->GetPinCount() ); if( !filters.IsEmpty() ) filters += wxT( ", " ); filters += _( "Pin Count" ); if( !msg.IsEmpty() ) filters += wxString::Format( wxT( " (%s)" ), msg ); } if( ( m_filteringOptions & FOOTPRINTS_LISTBOX::FILTERING_BY_LIBRARY ) ) { msg = m_librariesListBox->GetSelectedLibrary(); if( !filters.IsEmpty() ) filters += wxT( ", " ); filters += _( "Library" ); if( !msg.IsEmpty() ) filters += wxString::Format( wxT( " (%s)" ), msg ); } wxString textFilter = m_tcFilterString->GetValue(); if( !textFilter.IsEmpty() ) { if( !filters.IsEmpty() ) filters += wxT( ", " ); filters += _( "Search Text" ) + wxString::Format( wxT( " (%s)" ), textFilter ); } if( filters.IsEmpty() ) msg = _( "No Filtering" ); else msg.Printf( _( "Filtered by %s" ), filters ); msg << wxT( ": " ) << m_footprintListBox->GetCount(); SetStatusText( msg ); msg.Empty(); wxString footprintName = GetSelectedFootprint(); FOOTPRINT_INFO* fp = m_FootprintsList->GetFootprintInfo( footprintName ); if( fp ) // can be NULL if no netlist loaded { msg = wxString::Format( _( "Description: %s; Keywords: %s" ), fp->GetDescription(), fp->GetKeywords() ); } SetStatusText( msg, 1 ); msg.Empty(); wxString lib; // Choose the footprint to get the information on if( fp ) { // Use the footprint in the footprint viewer lib = fp->GetLibNickname(); } else if( GetFocusedControl() == CVPCB_MAINFRAME::CONTROL_COMPONENT ) { // Use the footprint of the selected symbol if( symbol ) lib = symbol->GetFPID().GetLibNickname(); } else if( GetFocusedControl() == CVPCB_MAINFRAME::CONTROL_LIBRARY ) { // Use the library that is selected lib = m_librariesListBox->GetSelectedLibrary(); } // Extract the library information FP_LIB_TABLE* fptbl = Prj().PcbFootprintLibs(); if( fptbl->HasLibrary( lib ) ) msg = wxString::Format( _( "Library location: %s" ), fptbl->GetFullURI( lib ) ); else msg = wxString::Format( _( "Library location: unknown" ) ); SetStatusText( msg, 2 ); } bool CVPCB_MAINFRAME::LoadFootprintFiles() { FP_LIB_TABLE* fptbl = Prj().PcbFootprintLibs(); // Check if there are footprint libraries in the footprint library table. if( !fptbl || !fptbl->GetLogicalLibs().size() ) { wxMessageBox( _( "No PCB footprint libraries are listed in the current footprint " "library table." ), _( "Configuration Error" ), wxOK | wxICON_ERROR ); return false; } WX_PROGRESS_REPORTER progressReporter( this, _( "Loading Footprint Libraries" ), 2 ); m_FootprintsList->ReadFootprintFiles( fptbl, nullptr, &progressReporter ); if( m_FootprintsList->GetErrorCount() ) { m_FootprintsList->DisplayErrors( this ); } return true; } void CVPCB_MAINFRAME::SendComponentSelectionToSch( bool aClearSelectionOnly ) { if( m_netlist.IsEmpty() ) return; std::string command = "$SELECT: "; if( aClearSelectionOnly ) { // Sending an empty list means clearing the selection. if( Kiface().IsSingle() ) SendCommand( MSG_TO_SCH, command ); else Kiway().ExpressMail( FRAME_SCH, MAIL_SELECTION, command, this ); return; } int selection = m_symbolsListBox->GetSelection(); if( selection < 0 ) // Nothing selected return; if( m_netlist.GetComponent( selection ) == nullptr ) return; // Now select the corresponding symbol on the schematic: wxString ref = m_netlist.GetComponent( selection )->GetReference(); // The prefix 0,F before the reference is for selecting the symbol // (one can select a pin with a different prefix) command += wxT( "0,F" ) + EscapeString( ref, CTX_IPC ); if( Kiface().IsSingle() ) SendCommand( MSG_TO_SCH, command ); else Kiway().ExpressMail( FRAME_SCH, MAIL_SELECTION, command, this ); } int CVPCB_MAINFRAME::readSchematicNetlist( const std::string& aNetlist ) { STRING_LINE_READER* stringReader = new STRING_LINE_READER( aNetlist, "Eeschema via Kiway" ); KICAD_NETLIST_READER netlistReader( stringReader, &m_netlist ); m_netlist.Clear(); try { netlistReader.LoadNetlist(); } catch( const IO_ERROR& ioe ) { wxString msg = wxString::Format( _( "Error loading schematic.\n%s" ), ioe.What().GetData() ); wxMessageBox( msg, _( "Load Error" ), wxOK | wxICON_ERROR ); return 1; } // We also remove footprint name if it is "$noname" because this is a dummy name, // not the actual name of the footprint. for( unsigned ii = 0; ii < m_netlist.GetCount(); ii++ ) { if( m_netlist.GetComponent( ii )->GetFPID().GetLibItemName() == std::string( "$noname" ) ) m_netlist.GetComponent( ii )->SetFPID( LIB_ID() ); } // Sort symbols by reference: m_netlist.SortByReference(); return 0; } void CVPCB_MAINFRAME::BuildFootprintsListBox() { wxFont guiFont = wxSystemSettings::GetFont( wxSYS_DEFAULT_GUI_FONT ); if( m_footprintListBox == nullptr ) { m_footprintListBox = new FOOTPRINTS_LISTBOX( this, ID_CVPCB_FOOTPRINT_LIST ); m_footprintListBox->SetFont( KIUI::GetMonospacedUIFont() ); } m_footprintListBox->SetFootprints( *m_FootprintsList, wxEmptyString, nullptr, wxEmptyString, FOOTPRINTS_LISTBOX::UNFILTERED_FP_LIST ); DisplayStatus(); } void CVPCB_MAINFRAME::BuildSymbolsListBox() { wxString msg; COMPONENT* symbol; wxFont guiFont = wxSystemSettings::GetFont( wxSYS_DEFAULT_GUI_FONT ); if( m_symbolsListBox == nullptr ) { m_symbolsListBox = new SYMBOLS_LISTBOX( this, ID_CVPCB_COMPONENT_LIST ); m_symbolsListBox->SetFont( KIUI::GetMonospacedUIFont() ); } m_symbolsListBox->m_SymbolList.Clear(); for( unsigned i = 0; i < m_netlist.GetCount(); i++ ) { symbol = m_netlist.GetComponent( i ); msg = formatSymbolDesc( m_symbolsListBox->GetCount() + 1, symbol->GetReference(), symbol->GetValue(), symbol->GetFPID().Format().wx_str() ); m_symbolsListBox->m_SymbolList.Add( msg ); FOOTPRINT_INFO* fp = m_FootprintsList->GetFootprintInfo( symbol->GetFPID().Format().wx_str() ); if( !fp ) { m_symbolsListBox->AppendWarning( i ); } } if( m_symbolsListBox->m_SymbolList.Count() ) { m_symbolsListBox->SetItemCount( m_symbolsListBox->m_SymbolList.Count() ); m_symbolsListBox->SetSelection( 0, true ); m_symbolsListBox->RefreshItems( 0L, m_symbolsListBox->m_SymbolList.Count() - 1 ); m_symbolsListBox->UpdateWidth(); } } void CVPCB_MAINFRAME::BuildLibrariesListBox() { COMMON_SETTINGS* cfg = Pgm().GetCommonSettings(); PROJECT_FILE& project = Kiway().Prj().GetProjectFile(); FP_LIB_TABLE* tbl = Prj().PcbFootprintLibs(); std::set pinnedMatches; std::set otherMatches; m_librariesListBox->ClearList(); auto process = [&]( const wxString& aNickname ) { if( alg::contains( project.m_PinnedFootprintLibs, aNickname ) || alg::contains( cfg->m_Session.pinned_fp_libs, aNickname ) ) { pinnedMatches.insert( aNickname ); } else { otherMatches.insert( aNickname ); } }; if( tbl ) { wxArrayString libNames; std::vector< wxString > libNickNames = tbl->GetLogicalLibs(); for( const wxString& libNickName : libNickNames ) process( libNickName ); } for( const wxString& nickname : pinnedMatches ) m_librariesListBox->AppendLine( LIB_TREE_MODEL_ADAPTER::GetPinningSymbol() + nickname ); for( const wxString& nickname : otherMatches ) m_librariesListBox->AppendLine( nickname ); m_librariesListBox->Finish(); } COMPONENT* CVPCB_MAINFRAME::GetSelectedComponent() { int selection = m_symbolsListBox->GetSelection(); if( selection >= 0 && selection < (int) m_netlist.GetCount() ) return m_netlist.GetComponent( selection ); return nullptr; } void CVPCB_MAINFRAME::SetSelectedComponent( int aIndex, bool aSkipUpdate ) { m_skipComponentSelect = aSkipUpdate; if( aIndex < 0 ) { m_symbolsListBox->DeselectAll(); } else if( aIndex < m_symbolsListBox->GetCount() ) { m_symbolsListBox->DeselectAll(); m_symbolsListBox->SetSelection( aIndex ); SendComponentSelectionToSch(); } m_skipComponentSelect = false; } std::vector CVPCB_MAINFRAME::GetComponentIndices( CVPCB_MAINFRAME::CRITERIA aCriteria ) { std::vector idx; int lastIdx; // Make sure a netlist has been loaded and the box has contents if( m_netlist.IsEmpty() || m_symbolsListBox->GetCount() == 0 ) return idx; switch( aCriteria ) { case CVPCB_MAINFRAME::ALL_COMPONENTS: idx.resize( m_netlist.GetCount() ); std::iota( idx.begin(), idx.end(), 0 ); break; case CVPCB_MAINFRAME::SEL_COMPONENTS: // Check to see if anything is selected if( m_symbolsListBox->GetSelectedItemCount() < 1 ) break; // Get the symbols lastIdx = m_symbolsListBox->GetFirstSelected(); idx.emplace_back( lastIdx ); lastIdx = m_symbolsListBox->GetNextSelected( lastIdx ); while( lastIdx > 0 ) { idx.emplace_back( lastIdx ); lastIdx = m_symbolsListBox->GetNextSelected( lastIdx ); } break; case CVPCB_MAINFRAME::NA_COMPONENTS: for( unsigned int i = 0; i < m_netlist.GetCount(); i++ ) { if( m_netlist.GetComponent( i )->GetFPID().empty() ) idx.emplace_back( i ); } break; case CVPCB_MAINFRAME::ASSOC_COMPONENTS: for( unsigned int i = 0; i < m_netlist.GetCount(); i++ ) { if( !m_netlist.GetComponent( i )->GetFPID().empty() ) idx.emplace_back( i ); } break; default: wxASSERT_MSG( false, "Invalid symbol selection criteria" ); } return idx; } DISPLAY_FOOTPRINTS_FRAME* CVPCB_MAINFRAME::GetFootprintViewerFrame() const { // returns the Footprint Viewer frame, if exists, or NULL wxWindow* window = wxWindow::FindWindowByName( FOOTPRINTVIEWER_FRAME_NAME ); return dynamic_cast( window ); } wxWindow* CVPCB_MAINFRAME::GetToolCanvas() const { return GetFootprintViewerFrame(); } CVPCB_MAINFRAME::CONTROL_TYPE CVPCB_MAINFRAME::GetFocusedControl() const { if( m_librariesListBox->HasFocus() ) return CVPCB_MAINFRAME::CONTROL_LIBRARY; else if( m_symbolsListBox->HasFocus() ) return CVPCB_MAINFRAME::CONTROL_COMPONENT; else if( m_footprintListBox->HasFocus() ) return CVPCB_MAINFRAME::CONTROL_FOOTPRINT; return CVPCB_MAINFRAME::CONTROL_NONE; } void CVPCB_MAINFRAME::SetFocusedControl( CVPCB_MAINFRAME::CONTROL_TYPE aLB ) { switch( aLB ) { case CVPCB_MAINFRAME::CONTROL_LIBRARY: m_librariesListBox->SetFocus(); break; case CVPCB_MAINFRAME::CONTROL_COMPONENT: m_symbolsListBox->SetFocus(); break; case CVPCB_MAINFRAME::CONTROL_FOOTPRINT: m_footprintListBox->SetFocus(); break; default: break; } } wxString CVPCB_MAINFRAME::GetSelectedFootprint() { // returns the LIB_ID of the selected footprint in footprint listview // or a empty string return m_footprintListBox->GetSelectedFootprint(); } void CVPCB_MAINFRAME::SetStatusText( const wxString& aText, int aNumber ) { switch( aNumber ) { case 0: m_statusLine1->SetLabel( aText ); break; case 1: m_statusLine2->SetLabel( aText ); break; case 2: m_statusLine3->SetLabel( aText ); break; default: wxFAIL_MSG( "Invalid status row number" ); break; } } void CVPCB_MAINFRAME::ShowChangedLanguage() { EDA_BASE_FRAME::ShowChangedLanguage(); ReCreateHToolbar(); DisplayStatus(); } void CVPCB_MAINFRAME::KiwayMailIn( KIWAY_EXPRESS& mail ) { const std::string& payload = mail.GetPayload(); switch( mail.Command() ) { case MAIL_EESCHEMA_NETLIST: // Disable Close events during readNetListAndFpFiles() to avoid crash when updating // widgets: m_cannotClose = true; readNetListAndFpFiles( payload ); m_cannotClose = false; /* @todo Go into SCH_EDIT_FRAME::OnOpenCvpcb( wxCommandEvent& event ) and trim GNL_ALL down. */ break; case MAIL_RELOAD_LIB: m_cannotClose = true; LoadFootprintFiles(); BuildFootprintsListBox(); BuildLibrariesListBox(); m_cannotClose = false; break; default: ; // ignore most } }