/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2017 Jean-Pierre Charras, jp.charras at wanadoo.fr * Copyright (C) 1992-2023 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 #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 DIFF_SYMBOLS_DIALOG_NAME wxT( "DiffSymbolsDialog" ) BEGIN_EVENT_TABLE( SCH_EDIT_FRAME, SCH_BASE_FRAME ) EVT_SOCKET( ID_EDA_SOCKET_EVENT_SERV, EDA_DRAW_FRAME::OnSockRequestServer ) EVT_SOCKET( ID_EDA_SOCKET_EVENT, EDA_DRAW_FRAME::OnSockRequest ) EVT_SIZE( SCH_EDIT_FRAME::OnSize ) EVT_MENU_RANGE( ID_FILE1, ID_FILEMAX, SCH_EDIT_FRAME::OnLoadFile ) EVT_MENU( ID_FILE_LIST_CLEAR, SCH_EDIT_FRAME::OnClearFileHistory ) EVT_MENU( ID_APPEND_PROJECT, SCH_EDIT_FRAME::OnAppendProject ) EVT_MENU( ID_IMPORT_NON_KICAD_SCH, SCH_EDIT_FRAME::OnImportProject ) EVT_MENU( wxID_EXIT, SCH_EDIT_FRAME::OnExit ) EVT_MENU( wxID_CLOSE, SCH_EDIT_FRAME::OnExit ) // Drop files event EVT_DROP_FILES( SCH_EDIT_FRAME::OnDropFiles ) END_EVENT_TABLE() wxDEFINE_EVENT( EDA_EVT_SCHEMATIC_CHANGED, wxCommandEvent ); SCH_EDIT_FRAME::SCH_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) : SCH_BASE_FRAME( aKiway, aParent, FRAME_SCH, wxT( "Eeschema" ), wxDefaultPosition, wxDefaultSize, KICAD_DEFAULT_DRAWFRAME_STYLE, SCH_EDIT_FRAME_NAME ), m_ercDialog( nullptr ), m_diffSymbolDialog( nullptr ), m_symbolFieldsTableDialog( nullptr ), m_netNavigator( nullptr ), m_highlightedConnChanged( false ) { m_maximizeByDefault = true; m_schematic = new SCHEMATIC( nullptr ); m_showBorderAndTitleBlock = true; // true to show sheet references m_supportsAutoSave = true; m_syncingPcbToSchSelection = false; m_aboutTitle = _HKI( "KiCad Schematic Editor" ); m_show_search = false; m_findReplaceDialog = nullptr; m_findReplaceData = std::make_unique(); // Give an icon wxIcon icon; wxIconBundle icon_bundle; icon.CopyFromBitmap( KiBitmap( BITMAPS::icon_eeschema ) ); icon_bundle.AddIcon( icon ); icon.CopyFromBitmap( KiBitmap( BITMAPS::icon_eeschema_32 ) ); icon_bundle.AddIcon( icon ); icon.CopyFromBitmap( KiBitmap( BITMAPS::icon_eeschema_16 ) ); icon_bundle.AddIcon( icon ); SetIcons( icon_bundle ); LoadSettings( eeconfig() ); // NB: also links the schematic to the loaded project CreateScreens(); SetCurrentSheet( Schematic().GetSheets()[0] ); setupTools(); setupUIConditions(); ReCreateMenuBar(); ReCreateHToolbar(); ReCreateVToolbar(); ReCreateOptToolbar(); m_hierarchy = new HIERARCHY_PANE( this ); // Initialize common print setup dialog settings. m_pageSetupData.GetPrintData().SetPrintMode( wxPRINT_MODE_PRINTER ); m_pageSetupData.GetPrintData().SetQuality( wxPRINT_QUALITY_MEDIUM ); m_pageSetupData.GetPrintData().SetBin( wxPRINTBIN_AUTO ); m_pageSetupData.GetPrintData().SetNoCopies( 1 ); m_searchPane = new SCH_SEARCH_PANE( this ); m_propertiesPanel = new SCH_PROPERTIES_PANEL( this, this ); m_propertiesPanel->SetSplitterProportion( eeconfig()->m_AuiPanels.properties_splitter_proportion ); m_auimgr.SetManagedWindow( this ); CreateInfoBar(); // Rows; layers 4 - 6 m_auimgr.AddPane( m_mainToolBar, EDA_PANE().HToolbar().Name( wxS( "MainToolbar" ) ) .Top().Layer( 6 ) ); m_auimgr.AddPane( m_messagePanel, EDA_PANE().Messages().Name( wxS( "MsgPanel" ) ) .Bottom().Layer( 6 ) ); // Columns; layers 1 - 3 m_auimgr.AddPane( m_hierarchy, EDA_PANE().Palette().Name( SchematicHierarchyPaneName() ) .Caption( _( "Schematic Hierarchy" ) ) .Left().Layer( 3 ) .TopDockable( false ) .BottomDockable( false ) .CloseButton( true ) .MinSize( 120, 60 ) .BestSize( 200, 200 ) .FloatingSize( 200, 200 ) .FloatingPosition( 50, 50 ) .Show( false ) ); m_auimgr.AddPane( m_propertiesPanel, defaultPropertiesPaneInfo() ); m_auimgr.AddPane( createHighlightedNetNavigator(), defaultNetNavigatorPaneInfo() ); m_auimgr.AddPane( m_optionsToolBar, EDA_PANE().VToolbar().Name( wxS( "OptToolbar" ) ) .Left().Layer( 2 ) ); m_auimgr.AddPane( m_drawToolBar, EDA_PANE().VToolbar().Name( wxS( "ToolsToolbar" ) ) .Right().Layer( 2 ) ); // Center m_auimgr.AddPane( GetCanvas(), EDA_PANE().Canvas().Name( wxS( "DrawFrame" ) ) .Center() ); m_auimgr.AddPane( m_searchPane, EDA_PANE() .Name( SearchPaneName() ) .Bottom() .Caption( _( "Search" ) ) .PaneBorder( false ) .MinSize( 180, -1 ) .BestSize( 180, -1 ) .FloatingSize( 480, 200 ) .CloseButton( true ) .DestroyOnClose( false ) .Show( m_show_search ) ); FinishAUIInitialization(); resolveCanvasType(); SwitchCanvas( m_canvasType ); GetCanvas()->GetGAL()->SetAxesEnabled( false ); KIGFX::SCH_VIEW* view = GetCanvas()->GetView(); static_cast( view->GetPainter() )->SetSchematic( m_schematic ); wxAuiPaneInfo& hierarchy_pane = m_auimgr.GetPane( SchematicHierarchyPaneName() ); wxAuiPaneInfo& netNavigatorPane = m_auimgr.GetPane( NetNavigatorPaneName() ); wxAuiPaneInfo& propertiesPane = m_auimgr.GetPane( PropertiesPaneName() ); EESCHEMA_SETTINGS* cfg = eeconfig(); hierarchy_pane.Show( cfg->m_AuiPanels.show_schematic_hierarchy ); netNavigatorPane.Show( cfg->m_AuiPanels.show_net_nav_panel ); propertiesPane.Show( cfg->m_AuiPanels.show_properties ); if( cfg->m_AuiPanels.hierarchy_panel_float_width > 0 && cfg->m_AuiPanels.hierarchy_panel_float_height > 0 ) { // Show at end, after positioning hierarchy_pane.FloatingSize( cfg->m_AuiPanels.hierarchy_panel_float_width, cfg->m_AuiPanels.hierarchy_panel_float_height ); } if( cfg->m_AuiPanels.net_nav_panel_float_size.GetWidth() > 0 && cfg->m_AuiPanels.net_nav_panel_float_size.GetHeight() > 0 ) { netNavigatorPane.FloatingSize( cfg->m_AuiPanels.net_nav_panel_float_size ); } if( cfg->m_AuiPanels.properties_panel_width > 0 ) SetAuiPaneSize( m_auimgr, propertiesPane, cfg->m_AuiPanels.properties_panel_width, -1 ); if( cfg->m_AuiPanels.schematic_hierarchy_float ) hierarchy_pane.Float(); if( cfg->m_AuiPanels.search_panel_height > 0 && ( cfg->m_AuiPanels.search_panel_dock_direction == wxAUI_DOCK_TOP || cfg->m_AuiPanels.search_panel_dock_direction == wxAUI_DOCK_BOTTOM ) ) { wxAuiPaneInfo& searchPane = m_auimgr.GetPane( SearchPaneName() ); searchPane.Direction( cfg->m_AuiPanels.search_panel_dock_direction ); SetAuiPaneSize( m_auimgr, searchPane, -1, cfg->m_AuiPanels.search_panel_height ); } else if( cfg->m_AuiPanels.search_panel_width > 0 && ( cfg->m_AuiPanels.search_panel_dock_direction == wxAUI_DOCK_LEFT || cfg->m_AuiPanels.search_panel_dock_direction == wxAUI_DOCK_RIGHT ) ) { wxAuiPaneInfo& searchPane = m_auimgr.GetPane( SearchPaneName() ); searchPane.Direction( cfg->m_AuiPanels.search_panel_dock_direction ); SetAuiPaneSize( m_auimgr, searchPane, cfg->m_AuiPanels.search_panel_width, -1 ); } if( cfg->m_AuiPanels.float_net_nav_panel ) netNavigatorPane.Float(); if( cfg->m_AuiPanels.hierarchy_panel_docked_width > 0 ) { // If the net navigator is not show, let the heirarchy navigator take all of the vertical // space. if( !cfg->m_AuiPanels.show_net_nav_panel ) { SetAuiPaneSize( m_auimgr, hierarchy_pane, cfg->m_AuiPanels.hierarchy_panel_docked_width, -1 ); } else { SetAuiPaneSize( m_auimgr, hierarchy_pane, cfg->m_AuiPanels.hierarchy_panel_docked_width, cfg->m_AuiPanels.hierarchy_panel_docked_height ); SetAuiPaneSize( m_auimgr, netNavigatorPane, cfg->m_AuiPanels.net_nav_panel_docked_size.GetWidth(), cfg->m_AuiPanels.net_nav_panel_docked_size.GetHeight() ); } // wxAUI hack: force width by setting MinSize() and then Fixed() // thanks to ZenJu https://github.com/wxWidgets/wxWidgets/issues/13180 hierarchy_pane.MinSize( cfg->m_AuiPanels.hierarchy_panel_docked_width, 60 ); hierarchy_pane.Fixed(); netNavigatorPane.MinSize( cfg->m_AuiPanels.net_nav_panel_docked_size.GetWidth(), 60 ); netNavigatorPane.Fixed(); m_auimgr.Update(); // now make it resizable again hierarchy_pane.Resizable(); netNavigatorPane.Resizable(); m_auimgr.Update(); // Note: DO NOT call m_auimgr.Update() anywhere after this; it will nuke the size // back to minimum. hierarchy_pane.MinSize( 120, 60 ); netNavigatorPane.MinSize( 120, 60 ); } else { m_auimgr.Update(); } LoadProjectSettings(); view->SetLayerVisible( LAYER_ERC_ERR, cfg->m_Appearance.show_erc_errors ); view->SetLayerVisible( LAYER_ERC_WARN, cfg->m_Appearance.show_erc_warnings ); view->SetLayerVisible( LAYER_ERC_EXCLUSION, cfg->m_Appearance.show_erc_exclusions ); view->SetLayerVisible( LAYER_OP_VOLTAGES, cfg->m_Appearance.show_op_voltages ); view->SetLayerVisible( LAYER_OP_CURRENTS, cfg->m_Appearance.show_op_currents ); initScreenZoom(); m_hierarchy->Bind( wxEVT_SIZE, &SCH_EDIT_FRAME::OnResizeHierarchyNavigator, this ); m_netNavigator->Bind( wxEVT_TREE_SEL_CHANGING, &SCH_EDIT_FRAME::onNetNavigatorSelChanging, this ); m_netNavigator->Bind( wxEVT_TREE_SEL_CHANGED, &SCH_EDIT_FRAME::onNetNavigatorSelection, this ); m_netNavigator->Bind( wxEVT_SIZE, &SCH_EDIT_FRAME::onResizeNetNavigator, this ); // This is used temporarily to fix a client size issue on GTK that causes zoom to fit // to calculate the wrong zoom size. See SCH_EDIT_FRAME::onSize(). Bind( wxEVT_SIZE, &SCH_EDIT_FRAME::onSize, this ); setupUnits( eeconfig() ); // Net list generator DefaultExecFlags(); updateTitle(); m_toolManager->GetTool()->ResetHistory(); // Default shutdown reason until a file is loaded KIPLATFORM::APP::SetShutdownBlockReason( this, _( "New schematic file is unsaved" ) ); // Init for dropping files m_acceptedExts.emplace( KiCadSchematicFileExtension, &EE_ACTIONS::ddAppendFile ); DragAcceptFiles( true ); // Ensure the window is on top Raise(); // Now that all sizes are fixed, set the initial hierarchy_pane floating position to the // top-left corner of the canvas wxPoint canvas_pos = GetCanvas()->GetScreenPosition(); hierarchy_pane.FloatingPosition( canvas_pos.x + 10, canvas_pos.y + 10 ); Bind( EDA_EVT_CLOSE_DIALOG_BOOK_REPORTER, &SCH_EDIT_FRAME::onCloseSymbolDiffDialog, this ); Bind( EDA_EVT_CLOSE_ERC_DIALOG, &SCH_EDIT_FRAME::onCloseErcDialog, this ); Bind( EDA_EVT_CLOSE_DIALOG_SYMBOL_FIELDS_TABLE, &SCH_EDIT_FRAME::onCloseSymbolFieldsTableDialog, this ); } SCH_EDIT_FRAME::~SCH_EDIT_FRAME() { m_hierarchy->Unbind( wxEVT_SIZE, &SCH_EDIT_FRAME::OnResizeHierarchyNavigator, this ); // Ensure m_canvasType is up to date, to save it in config m_canvasType = GetCanvas()->GetBackend(); SetScreen( nullptr ); if( m_schematic ) m_schematic->RemoveAllListeners(); delete m_schematic; m_schematic = nullptr; // Close the project if we are standalone, so it gets cleaned up properly if( Kiface().IsSingle() ) { try { GetSettingsManager()->UnloadProject( &Prj(), false ); } catch( const nlohmann::detail::type_error& exc ) { // This may be overkill and could be an assertion but we are more likely to // find any settings manager errors this way. wxLogError( wxT( "Settings exception '%s' occurred." ), exc.what() ); } } delete m_hierarchy; } void SCH_EDIT_FRAME::OnResizeHierarchyNavigator( wxSizeEvent& aEvent ) { aEvent.Skip(); // Called when resizing the Hierarchy Navigator panel // Store the current pane size // It allows to retrieve the last defined pane size when switching between // docked and floating pane state // Note: *DO NOT* call m_auimgr.Update() here: it crashes Kicad at least on Windows EESCHEMA_SETTINGS* cfg = dynamic_cast( Kiface().KifaceSettings() ); wxAuiPaneInfo& hierarchy_pane = m_auimgr.GetPane( SchematicHierarchyPaneName() ); if( cfg && m_hierarchy->IsShown() ) { cfg->m_AuiPanels.hierarchy_panel_float_width = hierarchy_pane.floating_size.x; cfg->m_AuiPanels.hierarchy_panel_float_height = hierarchy_pane.floating_size.y; // initialize hierarchy_panel_docked_width and best size only if the hierarchy_pane // width is > 0 (i.e. if its size is already set and has meaning) // if it is floating, its size is not initialized (only floating_size is initialized) // initializing hierarchy_pane.best_size is useful when switching to float pane and // after switching to the docked pane, to retrieve the last docked pane width if( hierarchy_pane.rect.width > 50 ) // 50 is a good margin { cfg->m_AuiPanels.hierarchy_panel_docked_width = hierarchy_pane.rect.width; hierarchy_pane.best_size.x = hierarchy_pane.rect.width; } } } void SCH_EDIT_FRAME::setupTools() { // Create the manager and dispatcher & route draw panel events to the dispatcher m_toolManager = new TOOL_MANAGER; m_toolManager->SetEnvironment( &Schematic(), GetCanvas()->GetView(), GetCanvas()->GetViewControls(), config(), this ); m_actions = new EE_ACTIONS(); m_toolDispatcher = new TOOL_DISPATCHER( m_toolManager ); // Register tools m_toolManager->RegisterTool( new COMMON_CONTROL ); m_toolManager->RegisterTool( new COMMON_TOOLS ); m_toolManager->RegisterTool( new ZOOM_TOOL ); m_toolManager->RegisterTool( new EE_SELECTION_TOOL ); m_toolManager->RegisterTool( new PICKER_TOOL ); m_toolManager->RegisterTool( new SCH_DRAWING_TOOLS ); m_toolManager->RegisterTool( new SCH_LINE_WIRE_BUS_TOOL ); m_toolManager->RegisterTool( new SCH_MOVE_TOOL ); m_toolManager->RegisterTool( new SCH_EDIT_TOOL ); m_toolManager->RegisterTool( new EE_INSPECTION_TOOL ); m_toolManager->RegisterTool( new SCH_EDITOR_CONTROL ); m_toolManager->RegisterTool( new SCH_FIND_REPLACE_TOOL ); m_toolManager->RegisterTool( new EE_POINT_EDITOR ); m_toolManager->RegisterTool( new SCH_NAVIGATE_TOOL ); m_toolManager->RegisterTool( new PROPERTIES_TOOL ); m_toolManager->InitTools(); // Run the selection tool, it is supposed to be always active m_toolManager->PostAction( EE_ACTIONS::selectionActivate ); GetCanvas()->SetEventDispatcher( m_toolDispatcher ); } void SCH_EDIT_FRAME::setupUIConditions() { SCH_BASE_FRAME::setupUIConditions(); ACTION_MANAGER* mgr = m_toolManager->GetActionManager(); SCH_EDITOR_CONDITIONS cond( this ); wxASSERT( mgr ); auto hasElements = [ this ] ( const SELECTION& aSel ) { return GetScreen() && ( !GetScreen()->Items().empty() || !SELECTION_CONDITIONS::Idle( aSel ) ); }; auto searchPaneCond = [this] ( const SELECTION& ) { return m_auimgr.GetPane( SearchPaneName() ).IsShown(); }; auto propertiesCond = [this] ( const SELECTION& ) { return m_auimgr.GetPane( PropertiesPaneName() ).IsShown(); }; auto hierarchyNavigatorCond = [ this ] ( const SELECTION& aSel ) { return m_auimgr.GetPane( SchematicHierarchyPaneName() ).IsShown(); }; auto netNavigatorCond = [ this ] (const SELECTION& aSel ) { return m_auimgr.GetPane( NetNavigatorPaneName() ).IsShown(); }; auto undoCond = [ this ] (const SELECTION& aSel ) { if( SCH_LINE_WIRE_BUS_TOOL::IsDrawingLineWireOrBus( aSel ) ) return true; return GetUndoCommandCount() > 0; }; #define ENABLE( x ) ACTION_CONDITIONS().Enable( x ) #define CHECK( x ) ACTION_CONDITIONS().Check( x ) mgr->SetConditions( ACTIONS::save, ENABLE( SELECTION_CONDITIONS::ShowAlways ) ); mgr->SetConditions( ACTIONS::undo, ENABLE( undoCond ) ); mgr->SetConditions( ACTIONS::redo, ENABLE( cond.RedoAvailable() ) ); mgr->SetConditions( EE_ACTIONS::showSearch, CHECK( searchPaneCond ) ); mgr->SetConditions( EE_ACTIONS::showHierarchy, CHECK( hierarchyNavigatorCond ) ); mgr->SetConditions( EE_ACTIONS::showNetNavigator, CHECK( netNavigatorCond ) ); mgr->SetConditions( ACTIONS::showProperties, CHECK( propertiesCond ) ); mgr->SetConditions( ACTIONS::toggleGrid, CHECK( cond.GridVisible() ) ); mgr->SetConditions( ACTIONS::toggleGridOverrides, CHECK( cond.GridOverrides() ) ); mgr->SetConditions( ACTIONS::toggleCursorStyle, CHECK( cond.FullscreenCursor() ) ); mgr->SetConditions( ACTIONS::millimetersUnits, CHECK( cond.Units( EDA_UNITS::MILLIMETRES ) ) ); mgr->SetConditions( ACTIONS::inchesUnits, CHECK( cond.Units( EDA_UNITS::INCHES ) ) ); mgr->SetConditions( ACTIONS::milsUnits, CHECK( cond.Units( EDA_UNITS::MILS ) ) ); mgr->SetConditions( EE_ACTIONS::lineModeFree, CHECK( cond.LineMode( LINE_MODE::LINE_MODE_FREE ) ) ); mgr->SetConditions( EE_ACTIONS::lineMode90, CHECK( cond.LineMode( LINE_MODE::LINE_MODE_90 ) ) ); mgr->SetConditions( EE_ACTIONS::lineMode45, CHECK( cond.LineMode( LINE_MODE::LINE_MODE_45 ) ) ); mgr->SetConditions( ACTIONS::cut, ENABLE( hasElements ) ); mgr->SetConditions( ACTIONS::copy, ENABLE( hasElements ) ); mgr->SetConditions( ACTIONS::paste, ENABLE( SELECTION_CONDITIONS::Idle ) ); mgr->SetConditions( ACTIONS::pasteSpecial, ENABLE( SELECTION_CONDITIONS::Idle ) ); mgr->SetConditions( ACTIONS::doDelete, ENABLE( hasElements ) ); mgr->SetConditions( ACTIONS::duplicate, ENABLE( hasElements ) ); mgr->SetConditions( ACTIONS::selectAll, ENABLE( hasElements ) ); mgr->SetConditions( EE_ACTIONS::rotateCW, ENABLE( hasElements ) ); mgr->SetConditions( EE_ACTIONS::rotateCCW, ENABLE( hasElements ) ); mgr->SetConditions( EE_ACTIONS::mirrorH, ENABLE( hasElements ) ); mgr->SetConditions( EE_ACTIONS::mirrorV, ENABLE( hasElements ) ); mgr->SetConditions( ACTIONS::zoomTool, CHECK( cond.CurrentTool( ACTIONS::zoomTool ) ) ); mgr->SetConditions( ACTIONS::selectionTool, CHECK( cond.CurrentTool( ACTIONS::selectionTool ) ) ); if( SCRIPTING::IsWxAvailable() ) { mgr->SetConditions( EE_ACTIONS::showPythonConsole, CHECK( cond.ScriptingConsoleVisible() ) ); } auto showHiddenPinsCond = [this]( const SELECTION& ) { return GetShowAllPins(); }; auto showHiddenFieldsCond = [this]( const SELECTION& ) { EESCHEMA_SETTINGS* cfg = eeconfig(); return cfg && cfg->m_Appearance.show_hidden_fields; }; auto showDirectiveLabelsCond = [this]( const SELECTION& ) { EESCHEMA_SETTINGS* cfg = eeconfig(); return cfg && cfg->m_Appearance.show_directive_labels; }; auto showERCErrorsCond = [this]( const SELECTION& ) { EESCHEMA_SETTINGS* cfg = eeconfig(); return cfg && cfg->m_Appearance.show_erc_errors; }; auto showERCWarningsCond = [this]( const SELECTION& ) { EESCHEMA_SETTINGS* cfg = eeconfig(); return cfg && cfg->m_Appearance.show_erc_warnings; }; auto showERCExclusionsCond = [this]( const SELECTION& ) { EESCHEMA_SETTINGS* cfg = eeconfig(); return cfg && cfg->m_Appearance.show_erc_exclusions; }; auto showOPVoltagesCond = [this]( const SELECTION& ) { EESCHEMA_SETTINGS* cfg = eeconfig(); return cfg && cfg->m_Appearance.show_op_voltages; }; auto showOPCurrentsCond = [this]( const SELECTION& ) { EESCHEMA_SETTINGS* cfg = eeconfig(); return cfg && cfg->m_Appearance.show_op_currents; }; auto showAnnotateAutomaticallyCond = [this]( const SELECTION& ) { EESCHEMA_SETTINGS* cfg = eeconfig(); return cfg && cfg->m_AnnotatePanel.automatic; }; auto remapSymbolsCondition = [&]( const SELECTION& aSel ) { SCH_SCREENS schematic( Schematic().Root() ); // The remapping can only be performed on legacy projects. return schematic.HasNoFullyDefinedLibIds(); }; auto belowRootSheetCondition = [this]( const SELECTION& aSel ) { SCH_NAVIGATE_TOOL* navigateTool = m_toolManager->GetTool(); return navigateTool && navigateTool->CanGoUp(); }; auto navSchematicHasPreviousSheet = [this]( const SELECTION& aSel ) { SCH_NAVIGATE_TOOL* navigateTool = m_toolManager->GetTool(); return navigateTool && navigateTool->CanGoPrevious(); }; auto navSchematicHasNextSheet = [this]( const SELECTION& aSel ) { SCH_NAVIGATE_TOOL* navigateTool = m_toolManager->GetTool(); return navigateTool && navigateTool->CanGoNext(); }; mgr->SetConditions( EE_ACTIONS::leaveSheet, ENABLE( belowRootSheetCondition ) ); /* Some of these are bound by default to arrow keys which will get a different action if we * disable the buttons. So always leave them enabled so the action is consistent. * https://gitlab.com/kicad/code/kicad/-/issues/14783 mgr->SetConditions( EE_ACTIONS::navigateUp, ENABLE( belowRootSheetCondition ) ); mgr->SetConditions( EE_ACTIONS::navigateForward, ENABLE( navHistoryHasForward ) ); mgr->SetConditions( EE_ACTIONS::navigateBack, ENABLE( navHistoryHsBackward ) ); */ mgr->SetConditions( EE_ACTIONS::navigatePrevious, ENABLE( navSchematicHasPreviousSheet ) ); mgr->SetConditions( EE_ACTIONS::navigateNext, ENABLE( navSchematicHasNextSheet ) ); mgr->SetConditions( EE_ACTIONS::remapSymbols, ENABLE( remapSymbolsCondition ) ); mgr->SetConditions( EE_ACTIONS::toggleHiddenPins, CHECK( showHiddenPinsCond ) ); mgr->SetConditions( EE_ACTIONS::toggleHiddenFields, CHECK( showHiddenFieldsCond ) ); mgr->SetConditions( EE_ACTIONS::toggleDirectiveLabels, CHECK( showDirectiveLabelsCond ) ); mgr->SetConditions( EE_ACTIONS::toggleERCErrors, CHECK( showERCErrorsCond ) ); mgr->SetConditions( EE_ACTIONS::toggleERCWarnings, CHECK( showERCWarningsCond ) ); mgr->SetConditions( EE_ACTIONS::toggleERCExclusions, CHECK( showERCExclusionsCond ) ); mgr->SetConditions( EE_ACTIONS::toggleOPVoltages, CHECK( showOPVoltagesCond ) ); mgr->SetConditions( EE_ACTIONS::toggleOPCurrents, CHECK( showOPCurrentsCond ) ); mgr->SetConditions( EE_ACTIONS::toggleAnnotateAuto, CHECK( showAnnotateAutomaticallyCond ) ); mgr->SetConditions( ACTIONS::toggleBoundingBoxes, CHECK( cond.BoundingBoxes() ) ); #define CURRENT_TOOL( action ) mgr->SetConditions( action, CHECK( cond.CurrentTool( action ) ) ) CURRENT_TOOL( ACTIONS::deleteTool ); CURRENT_TOOL( EE_ACTIONS::highlightNetTool ); CURRENT_TOOL( EE_ACTIONS::placeSymbol ); CURRENT_TOOL( EE_ACTIONS::placePower ); CURRENT_TOOL( EE_ACTIONS::drawWire ); CURRENT_TOOL( EE_ACTIONS::drawBus ); CURRENT_TOOL( EE_ACTIONS::placeBusWireEntry ); CURRENT_TOOL( EE_ACTIONS::placeNoConnect ); CURRENT_TOOL( EE_ACTIONS::placeJunction ); CURRENT_TOOL( EE_ACTIONS::placeLabel ); CURRENT_TOOL( EE_ACTIONS::placeClassLabel ); CURRENT_TOOL( EE_ACTIONS::placeGlobalLabel ); CURRENT_TOOL( EE_ACTIONS::placeHierLabel ); CURRENT_TOOL( EE_ACTIONS::drawSheet ); CURRENT_TOOL( EE_ACTIONS::importSheetPin ); CURRENT_TOOL( EE_ACTIONS::drawRectangle ); CURRENT_TOOL( EE_ACTIONS::drawCircle ); CURRENT_TOOL( EE_ACTIONS::drawArc ); CURRENT_TOOL( EE_ACTIONS::drawLines ); CURRENT_TOOL( EE_ACTIONS::placeSchematicText ); CURRENT_TOOL( EE_ACTIONS::drawTextBox ); CURRENT_TOOL( EE_ACTIONS::placeImage ); #undef CURRENT_TOOL #undef CHECK #undef ENABLE } void SCH_EDIT_FRAME::SaveCopyForRepeatItem( const SCH_ITEM* aItem ) { // we cannot store a pointer to an item in the display list here since // that item may be deleted, such as part of a line concatenation or other. // So simply always keep a copy of the object which is to be repeated. if( aItem ) { m_items_to_repeat.clear(); AddCopyForRepeatItem( aItem ); } } void SCH_EDIT_FRAME::AddCopyForRepeatItem( const SCH_ITEM* aItem ) { // we cannot store a pointer to an item in the display list here since // that item may be deleted, such as part of a line concatenation or other. // So simply always keep a copy of the object which is to be repeated. if( aItem ) { std::unique_ptr repeatItem( static_cast( aItem->Clone() ) ); // Clone() preserves the flags, we want 'em cleared. repeatItem->ClearFlags(); m_items_to_repeat.emplace_back( std::move( repeatItem ) ); } } EDA_ITEM* SCH_EDIT_FRAME::GetItem( const KIID& aId ) const { return Schematic().GetSheets().GetItem( aId ); } void SCH_EDIT_FRAME::SetSheetNumberAndCount() { Schematic().SetSheetNumberAndCount(); } SCH_SCREEN* SCH_EDIT_FRAME::GetScreen() const { return GetCurrentSheet().LastScreen(); } SCHEMATIC& SCH_EDIT_FRAME::Schematic() const { return *m_schematic; } wxString SCH_EDIT_FRAME::GetScreenDesc() const { return GetCurrentSheet().Last()->GetName(); } wxString SCH_EDIT_FRAME::GetFullScreenDesc() const { return GetCurrentSheet().PathHumanReadable(); } void SCH_EDIT_FRAME::CreateScreens() { m_schematic->Reset(); m_schematic->SetProject( &Prj() ); SCH_SHEET* rootSheet = new SCH_SHEET( m_schematic ); m_schematic->SetRoot( rootSheet ); SCH_SCREEN* rootScreen = new SCH_SCREEN( m_schematic ); const_cast( rootSheet->m_Uuid ) = rootScreen->GetUuid(); m_schematic->Root().SetScreen( rootScreen ); SetScreen( Schematic().RootScreen() ); m_schematic->RootScreen()->SetFileName( wxEmptyString ); // Don't leave root page number empty SCH_SHEET_PATH rootSheetPath; rootSheetPath.push_back( rootSheet ); m_schematic->RootScreen()->SetPageNumber( wxT( "1" ) ); rootSheetPath.SetPageNumber( wxT( "1" ) ); if( GetScreen() == nullptr ) { SCH_SCREEN* screen = new SCH_SCREEN( m_schematic ); SetScreen( screen ); } } SCH_SHEET_PATH& SCH_EDIT_FRAME::GetCurrentSheet() const { return m_schematic->CurrentSheet(); } void SCH_EDIT_FRAME::SetCurrentSheet( const SCH_SHEET_PATH& aSheet ) { if( aSheet != GetCurrentSheet() ) { FocusOnItem( nullptr ); Schematic().SetCurrentSheet( aSheet ); GetCanvas()->DisplaySheet( aSheet.LastScreen() ); } } void SCH_EDIT_FRAME::HardRedraw() { SCH_SCREEN* screen = GetCurrentSheet().LastScreen(); for( SCH_ITEM* item : screen->Items() ) item->ClearCaches(); for( const std::pair& libSymbol : screen->GetLibSymbols() ) { wxCHECK2( libSymbol.second, continue ); libSymbol.second->ClearCaches(); } if( Schematic().Settings().m_IntersheetRefsShow ) RecomputeIntersheetRefs(); FocusOnItem( nullptr ); GetCanvas()->DisplaySheet( GetCurrentSheet().LastScreen() ); if( EE_SELECTION_TOOL* selectionTool = m_toolManager->GetTool() ) selectionTool->Reset( TOOL_BASE::REDRAW ); GetCanvas()->ForceRefresh(); } bool SCH_EDIT_FRAME::canCloseWindow( wxCloseEvent& aEvent ) { // Exit interactive editing // Note this this will commit *some* pending changes. For instance, the EE_POINT_EDITOR // will cancel any drag currently in progress, but commit all changes from previous drags. if( m_toolManager ) m_toolManager->RunAction( ACTIONS::cancelInteractive ); // Shutdown blocks must be determined and vetoed as early as possible if( KIPLATFORM::APP::SupportsShutdownBlockReason() && aEvent.GetId() == wxEVT_QUERY_END_SESSION && Schematic().GetSheets().IsModified() ) { return false; } if( Kiface().IsSingle() ) { auto* symbolEditor = (SYMBOL_EDIT_FRAME*) Kiway().Player( FRAME_SCH_SYMBOL_EDITOR, false ); if( symbolEditor && !symbolEditor->Close() ) // Can close symbol editor? return false; auto* symbolViewer = (SYMBOL_VIEWER_FRAME*) Kiway().Player( FRAME_SCH_VIEWER, false ); if( symbolViewer && !symbolViewer->Close() ) // Can close symbol viewer? return false; symbolViewer = (SYMBOL_VIEWER_FRAME*) Kiway().Player( FRAME_SCH_VIEWER_MODAL, false ); if( symbolViewer && !symbolViewer->Close() ) // Can close modal symbol viewer? return false; } else { auto* symbolEditor = (SYMBOL_EDIT_FRAME*) Kiway().Player( FRAME_SCH_SYMBOL_EDITOR, false ); if( symbolEditor && symbolEditor->IsSymbolFromSchematic() ) { if( !symbolEditor->CanCloseSymbolFromSchematic( true ) ) return false; } } if( !Kiway().PlayerClose( FRAME_SIMULATOR, false ) ) // Can close the simulator? return false; if( m_symbolFieldsTableDialog && !m_symbolFieldsTableDialog->Close( false ) ) // Can close the symbol fields table? { return false; } // We may have gotten multiple events; don't clean up twice if( !Schematic().IsValid() ) return false; SCH_SHEET_LIST sheetlist = Schematic().GetSheets(); if( sheetlist.IsModified() ) { wxFileName fileName = Schematic().RootScreen()->GetFileName(); wxString msg = _( "Save changes to '%s' before closing?" ); if( !HandleUnsavedChanges( this, wxString::Format( msg, fileName.GetFullName() ), [&]() -> bool { return SaveProject(); } ) ) { return false; } } return true; } void SCH_EDIT_FRAME::doCloseWindow() { SCH_SHEET_LIST sheetlist = Schematic().GetSheets(); // Shutdown all running tools if( m_toolManager ) m_toolManager->ShutdownAllTools(); // Close modeless dialogs. They're trouble when they get destroyed after the frame. Unbind( EDA_EVT_CLOSE_DIALOG_BOOK_REPORTER, &SCH_EDIT_FRAME::onCloseSymbolDiffDialog, this ); Unbind( EDA_EVT_CLOSE_ERC_DIALOG, &SCH_EDIT_FRAME::onCloseErcDialog, this ); Unbind( EDA_EVT_CLOSE_DIALOG_SYMBOL_FIELDS_TABLE, &SCH_EDIT_FRAME::onCloseSymbolFieldsTableDialog, this ); m_netNavigator->Unbind( wxEVT_TREE_SEL_CHANGING, &SCH_EDIT_FRAME::onNetNavigatorSelChanging, this ); m_netNavigator->Unbind( wxEVT_TREE_SEL_CHANGED, &SCH_EDIT_FRAME::onNetNavigatorSelection, this ); // Close the find dialog and preserve its setting if it is displayed. if( m_findReplaceDialog ) { m_findStringHistoryList = m_findReplaceDialog->GetFindEntries(); m_replaceStringHistoryList = m_findReplaceDialog->GetReplaceEntries(); m_findReplaceDialog->Destroy(); m_findReplaceDialog = nullptr; } if( m_diffSymbolDialog ) { m_diffSymbolDialog->Destroy(); m_diffSymbolDialog = nullptr; } if( m_ercDialog ) { m_ercDialog->Destroy(); m_ercDialog = nullptr; } if( m_symbolFieldsTableDialog ) { m_symbolFieldsTableDialog->Destroy(); m_symbolFieldsTableDialog = nullptr; } // Shutdown all running tools if( m_toolManager ) { m_toolManager->ShutdownAllTools(); // prevent the canvas from trying to dispatch events during close GetCanvas()->SetEventDispatcher( nullptr ); delete m_toolManager; m_toolManager = nullptr; } wxAuiPaneInfo& hierarchy_pane = m_auimgr.GetPane( SchematicHierarchyPaneName() ); if( hierarchy_pane.IsShown() && hierarchy_pane.IsFloating() ) { hierarchy_pane.Show( false ); m_auimgr.Update(); } SCH_SCREENS screens( Schematic().Root() ); wxFileName fn; for( SCH_SCREEN* screen = screens.GetFirst(); screen != nullptr; screen = screens.GetNext() ) { fn = Prj().AbsolutePath( screen->GetFileName() ); // Auto save file name is the normal file name prepended with GetAutoSaveFilePrefix(). fn.SetName( GetAutoSaveFilePrefix() + fn.GetName() ); if( fn.IsFileWritable() ) wxRemoveFile( fn.GetFullPath() ); } wxFileName tmpFn = Prj().GetProjectFullName(); wxFileName autoSaveFileName( tmpFn.GetPath(), getAutoSaveFileName() ); if( autoSaveFileName.IsFileWritable() ) wxRemoveFile( autoSaveFileName.GetFullPath() ); sheetlist.ClearModifyStatus(); wxString fileName = Prj().AbsolutePath( Schematic().RootScreen()->GetFileName() ); if( !Schematic().GetFileName().IsEmpty() && !Schematic().RootScreen()->IsEmpty() ) UpdateFileHistory( fileName ); // Make sure local settings are persisted SaveProjectLocalSettings(); Schematic().RootScreen()->Clear(); // all sub sheets are deleted, only the main sheet is usable GetCurrentSheet().clear(); // Clear view before destroying schematic as repaints depend on schematic being valid SetScreen( nullptr ); Schematic().Reset(); // Prevents any rogue events from continuing (i.e. search panel tries to redraw) Show( false ); Destroy(); } SEVERITY SCH_EDIT_FRAME::GetSeverity( int aErrorCode ) const { return Schematic().ErcSettings().GetSeverity( aErrorCode ); } void SCH_EDIT_FRAME::OnModify() { EDA_BASE_FRAME::OnModify(); wxCHECK( GetScreen(), /* void */ ); GetScreen()->SetContentModified(); m_autoSaveRequired = true; GetCanvas()->Refresh(); UpdateHierarchyNavigator(); if( !GetTitle().StartsWith( wxS( "*" ) ) ) updateTitle(); } void SCH_EDIT_FRAME::OnUpdatePCB( wxCommandEvent& event ) { if( Kiface().IsSingle() ) { DisplayError( this, _( "Cannot update the PCB, because the Schematic Editor is opened" " in stand-alone mode. In order to create/update PCBs from" " schematics, launch the KiCad shell and create a project." ) ); return; } KIWAY_PLAYER* frame = Kiway().Player( FRAME_PCB_EDITOR, false ); if( !frame ) { wxFileName fn = Prj().GetProjectFullName(); fn.SetExt( PcbFileExtension ); frame = Kiway().Player( FRAME_PCB_EDITOR, true ); // If Kiway() cannot create the Pcbnew frame, it shows a error message, and // frame is null if( !frame ) return; frame->OpenProjectFiles( std::vector( 1, fn.GetFullPath() ) ); } if( !frame->IsVisible() ) frame->Show( true ); // On Windows, Raise() does not bring the window on screen, when iconized if( frame->IsIconized() ) frame->Iconize( false ); frame->Raise(); std::string payload; Kiway().ExpressMail( FRAME_PCB_EDITOR, MAIL_PCB_UPDATE, payload, this ); } void SCH_EDIT_FRAME::UpdateHierarchyNavigator() { m_toolManager->GetTool()->CleanHistory(); m_hierarchy->UpdateHierarchyTree(); } void SCH_EDIT_FRAME::UpdateHierarchySelection() { m_hierarchy->UpdateHierarchySelection(); } void SCH_EDIT_FRAME::ShowFindReplaceDialog( bool aReplace ) { wxString findString; EE_SELECTION& selection = m_toolManager->GetTool()->GetSelection(); if( selection.Size() == 1 ) { EDA_ITEM* front = selection.Front(); switch( front->Type() ) { case SCH_SYMBOL_T: { SCH_SYMBOL* symbol = static_cast( front ); findString = UnescapeString( symbol->GetField( VALUE_FIELD )->GetText() ); break; } case SCH_FIELD_T: findString = UnescapeString( static_cast( front )->GetText() ); break; case SCH_LABEL_T: case SCH_GLOBAL_LABEL_T: case SCH_HIER_LABEL_T: case SCH_SHEET_PIN_T: findString = UnescapeString( static_cast( front )->GetText() ); break; case SCH_TEXT_T: findString = UnescapeString( static_cast( front )->GetText() ); if( findString.Contains( wxT( "\n" ) ) ) findString = findString.Before( '\n' ); break; default: break; } } if( m_findReplaceDialog ) m_findReplaceDialog->Destroy(); m_findReplaceDialog = new DIALOG_SCH_FIND( this, static_cast( m_findReplaceData.get() ), wxDefaultPosition, wxDefaultSize, aReplace ? wxFR_REPLACEDIALOG : 0 ); m_findReplaceDialog->SetFindEntries( m_findStringHistoryList, findString ); m_findReplaceDialog->SetReplaceEntries( m_replaceStringHistoryList ); m_findReplaceDialog->Show( true ); } void SCH_EDIT_FRAME::ShowFindReplaceStatus( const wxString& aMsg, int aStatusTime ) { // Prepare the infobar, since we don't know its state m_infoBar->RemoveAllButtons(); m_infoBar->AddCloseButton(); m_infoBar->ShowMessageFor( aMsg, aStatusTime, wxICON_INFORMATION ); } void SCH_EDIT_FRAME::ClearFindReplaceStatus() { m_infoBar->Dismiss(); } void SCH_EDIT_FRAME::OnFindDialogClose() { m_findStringHistoryList = m_findReplaceDialog->GetFindEntries(); m_replaceStringHistoryList = m_findReplaceDialog->GetReplaceEntries(); m_findReplaceDialog->Destroy(); m_findReplaceDialog = nullptr; m_toolManager->RunAction( ACTIONS::updateFind ); } void SCH_EDIT_FRAME::OnLoadFile( wxCommandEvent& event ) { wxString fn = GetFileFromHistory( event.GetId(), _( "Schematic" ) ); if( fn.size() ) OpenProjectFiles( std::vector( 1, fn ) ); } void SCH_EDIT_FRAME::OnClearFileHistory( wxCommandEvent& aEvent ) { ClearFileHistory(); } void SCH_EDIT_FRAME::NewProject() { wxString pro_dir = m_mruPath; wxFileDialog dlg( this, _( "New Schematic" ), pro_dir, wxEmptyString, KiCadSchematicFileWildcard(), wxFD_SAVE ); if( dlg.ShowModal() != wxID_CANCEL ) { // Enforce the extension, wxFileDialog is inept. wxFileName create_me = EnsureFileExtension( dlg.GetPath(), KiCadSchematicFileExtension ); if( create_me.FileExists() ) { wxString msg; msg.Printf( _( "Schematic file '%s' already exists." ), create_me.GetFullName() ); DisplayError( this, msg ); return ; } // OpenProjectFiles() requires absolute wxASSERT_MSG( create_me.IsAbsolute(), wxS( "wxFileDialog returned non-absolute path" ) ); OpenProjectFiles( std::vector( 1, create_me.GetFullPath() ), KICTL_CREATE ); m_mruPath = create_me.GetPath(); } } void SCH_EDIT_FRAME::LoadProject() { wxString pro_dir = m_mruPath; wxString wildcards = AllSchematicFilesWildcard() + wxS( "|" ) + KiCadSchematicFileWildcard() + wxS( "|" ) + LegacySchematicFileWildcard(); wxFileDialog dlg( this, _( "Open Schematic" ), pro_dir, wxEmptyString, wildcards, wxFD_OPEN | wxFD_FILE_MUST_EXIST ); if( dlg.ShowModal() != wxID_CANCEL ) { OpenProjectFiles( std::vector( 1, dlg.GetPath() ) ); m_mruPath = Prj().GetProjectPath(); } } void SCH_EDIT_FRAME::OnOpenPcbnew( wxCommandEvent& event ) { wxFileName kicad_board = Prj().AbsolutePath( Schematic().GetFileName() ); if( kicad_board.IsOk() && !Schematic().GetFileName().IsEmpty() ) { kicad_board.SetExt( PcbFileExtension ); wxFileName legacy_board( kicad_board ); legacy_board.SetExt( LegacyPcbFileExtension ); wxFileName& boardfn = legacy_board; if( !legacy_board.FileExists() || kicad_board.FileExists() ) boardfn = kicad_board; if( Kiface().IsSingle() ) { ExecuteFile( PCBNEW_EXE, boardfn.GetFullPath() ); } else { KIWAY_PLAYER* frame = Kiway().Player( FRAME_PCB_EDITOR, false ); if( !frame ) { frame = Kiway().Player( FRAME_PCB_EDITOR, true ); // frame can be null if Cvpcb cannot be run. No need to show a warning // Kiway() generates the error messages if( !frame ) return; frame->OpenProjectFiles( std::vector( 1, boardfn.GetFullPath() ) ); } if( !frame->IsVisible() ) frame->Show( true ); // On Windows, Raise() does not bring the window on screen, when iconized if( frame->IsIconized() ) frame->Iconize( false ); frame->Raise(); } } else { // If we are running inside a project, it should be impossible for this case to happen wxASSERT( Kiface().IsSingle() ); ExecuteFile( PCBNEW_EXE ); } } void SCH_EDIT_FRAME::OnOpenCvpcb( wxCommandEvent& event ) { wxFileName fn = Prj().AbsolutePath( Schematic().GetFileName() ); fn.SetExt( NetlistFileExtension ); if( !ReadyToNetlist( _( "Assigning footprints requires a fully annotated schematic." ) ) ) return; try { KIWAY_PLAYER* player = Kiway().Player( FRAME_CVPCB, false ); // test open already. if( !player ) { player = Kiway().Player( FRAME_CVPCB, true ); // player can be null if Cvpcb cannot be run. No need to show a warning // Kiway() generates the error messages if( !player ) return; player->Show( true ); } sendNetlistToCvpcb(); player->Raise(); } catch( const IO_ERROR& ) { DisplayError( this, _( "Could not open CvPcb" ) ); } } void SCH_EDIT_FRAME::OnExit( wxCommandEvent& event ) { if( event.GetId() == wxID_EXIT ) Kiway().OnKiCadExit(); if( event.GetId() == wxID_CLOSE || Kiface().IsSingle() ) Close( false ); } void SCH_EDIT_FRAME::PrintPage( const RENDER_SETTINGS* aSettings ) { wxString fileName = Prj().AbsolutePath( GetScreen()->GetFileName() ); const wxBrush& brush = wxBrush( GetColorSettings()->GetColor( LAYER_SCHEMATIC_BACKGROUND ).ToColour() ); aSettings->GetPrintDC()->SetBackground( brush ); aSettings->GetPrintDC()->Clear(); aSettings->GetPrintDC()->SetLogicalFunction( wxCOPY ); GetScreen()->Print( aSettings ); PrintDrawingSheet( aSettings, GetScreen(), Schematic().GetProperties(), schIUScale.IU_PER_MILS, fileName ); } void SCH_EDIT_FRAME::RefreshOperatingPointDisplay() { SCHEMATIC_SETTINGS& settings = m_schematic->Settings(); SIM_LIB_MGR simLibMgr( &Prj() ); NULL_REPORTER devnull; simLibMgr.SetReporter( &devnull ); // Patch for bug early in V7.99 dev if( settings.m_OPO_VRange.EndsWith( 'A' ) ) settings.m_OPO_VRange[ settings.m_OPO_VRange.Length() - 1 ] = 'V'; // Update items which may have ${OP} text variables // GetCanvas()->GetView()->UpdateAllItemsConditionally( [&]( KIGFX::VIEW_ITEM* aItem ) -> int { int flags = 0; if( SCH_ITEM* item = dynamic_cast( aItem ) ) { item->RunOnChildren( [&flags]( SCH_ITEM* aChild ) { EDA_TEXT* text = dynamic_cast( aChild ); if( text && text->HasTextVars() ) { text->ClearRenderCache(); text->ClearBoundingBoxCache(); flags |= KIGFX::GEOMETRY | KIGFX::REPAINT; } } ); EDA_TEXT* text = dynamic_cast( aItem ); if( text && text->HasTextVars() ) { text->ClearRenderCache(); text->ClearBoundingBoxCache(); flags |= KIGFX::GEOMETRY | KIGFX::REPAINT; } } return flags; } ); // Update OP overlay items // for( SCH_ITEM* item : GetScreen()->Items() ) { if( item->Type() == SCH_LINE_T ) { SCH_LINE* line = static_cast( item ); if( !line->GetOperatingPoint().IsEmpty() ) GetCanvas()->GetView()->Update( line ); line->SetOperatingPoint( wxEmptyString ); // update value from netlist, below } else if( item->Type() == SCH_SYMBOL_T ) { SCH_SYMBOL* symbol = static_cast( item ); wxString ref = symbol->GetRef( &GetCurrentSheet() ); std::vector pins = symbol->GetPins( &GetCurrentSheet() ); // Power symbols and other symbols which have the reference starting with "#" are // not included in simulation if( ref.StartsWith( '#' ) || symbol->GetExcludedFromSim() ) continue; for( SCH_PIN* pin : pins ) { if( !pin->GetOperatingPoint().IsEmpty() ) GetCanvas()->GetView()->Update( pin ); pin->SetOperatingPoint( wxEmptyString ); } if( pins.size() == 2 ) { wxString op = m_schematic->GetOperatingPoint( ref, settings.m_OPO_IPrecision, settings.m_OPO_IRange ); if( !op.IsEmpty() && op != wxS( "--" ) && op != wxS( "?" ) ) { pins[0]->SetOperatingPoint( op ); GetCanvas()->GetView()->Update( symbol ); } } else { SIM_MODEL& model = simLibMgr.CreateModel( &GetCurrentSheet(), *symbol ).model; SPICE_ITEM spiceItem; spiceItem.refName = ref; ref = model.SpiceGenerator().ItemName( spiceItem ); for( const auto& modelPin : model.GetPins() ) { SCH_PIN* symbolPin = symbol->GetPin( modelPin.get().symbolPinNumber ); wxString signalName = ref + wxS( ":" ) + modelPin.get().name; wxString op = m_schematic->GetOperatingPoint( signalName, settings.m_OPO_IPrecision, settings.m_OPO_IRange ); if( symbolPin && !op.IsEmpty() && op != wxS( "--" ) && op != wxS( "?" ) ) { symbolPin->SetOperatingPoint( op ); GetCanvas()->GetView()->Update( symbol ); } } } } } for( const auto& [ key, subgraphList ] : m_schematic->m_connectionGraph->GetNetMap() ) { wxString op = m_schematic->GetOperatingPoint( key.Name, settings.m_OPO_VPrecision, settings.m_OPO_VRange ); if( !op.IsEmpty() && op != wxS( "--" ) && op != wxS( "?" ) ) { for( CONNECTION_SUBGRAPH* subgraph : subgraphList ) { SCH_LINE* longestWire = nullptr; double length = 0.0; for( SCH_ITEM* item : subgraph->GetItems() ) { if( item->IsType( { SCH_ITEM_LOCATE_WIRE_T } ) ) { SCH_LINE* wire = static_cast( item ); if( wire->GetLength() > length ) { longestWire = wire; length = wire->GetLength(); } } } if( longestWire ) { longestWire->SetOperatingPoint( op ); GetCanvas()->GetView()->Update( longestWire ); } } } } } void SCH_EDIT_FRAME::AutoRotateItem( SCH_SCREEN* aScreen, SCH_ITEM* aItem ) { if( aItem->IsType( { SCH_GLOBAL_LABEL_T, SCH_HIER_LABEL_T } ) ) { SCH_LABEL_BASE* label = static_cast( aItem ); if( label->AutoRotateOnPlacement() ) { TEXT_SPIN_STYLE spin = aScreen->GetLabelOrientationForPoint( label->GetPosition(), label->GetTextSpinStyle(), &GetCurrentSheet() ); if( spin != label->GetTextSpinStyle() ) { label->SetTextSpinStyle( spin ); for( SCH_ITEM* item : aScreen->Items().OfType( SCH_GLOBAL_LABEL_T ) ) { SCH_LABEL_BASE* otherLabel = static_cast( item ); if( otherLabel != label && otherLabel->GetText() == label->GetText() ) otherLabel->AutoplaceFields( aScreen, false ); } } } } } void SCH_EDIT_FRAME::updateTitle() { SCH_SCREEN* screen = GetScreen(); wxCHECK( screen, /* void */ ); wxString title; if( !screen->GetFileName().IsEmpty() ) { wxFileName fn( Prj().AbsolutePath( screen->GetFileName() ) ); bool readOnly = false; bool unsaved = false; if( fn.IsOk() && screen->FileExists() ) readOnly = screen->IsReadOnly(); else unsaved = true; if( IsContentModified() ) title = wxT( "*" ); title += fn.GetName(); wxString sheetPath = GetCurrentSheet().PathHumanReadable( false, true ); if( sheetPath != title ) title += wxString::Format( wxT( " [%s]" ), sheetPath ); if( readOnly ) title += wxS( " " ) + _( "[Read Only]" ); if( unsaved ) title += wxS( " " ) + _( "[Unsaved]" ); } else { title = _( "[no schematic loaded]" ); } title += wxT( " \u2014 " ) + _( "Schematic Editor" ); SetTitle( title ); } void SCH_EDIT_FRAME::initScreenZoom() { m_toolManager->RunAction( ACTIONS::zoomFitScreen ); GetScreen()->m_zoomInitialized = true; } void SCH_EDIT_FRAME::RecalculateConnections( SCH_COMMIT* aCommit, SCH_CLEANUP_FLAGS aCleanupFlags ) { wxString highlightedConn = GetHighlightedConnection(); SCHEMATIC_SETTINGS& settings = Schematic().Settings(); SCH_SHEET_LIST list = Schematic().GetSheets(); SCH_COMMIT localCommit( m_toolManager ); if( !aCommit ) aCommit = &localCommit; #ifdef PROFILE PROF_TIMER timer; #endif // Ensure schematic graph is accurate if( aCleanupFlags == LOCAL_CLEANUP ) { SchematicCleanUp( aCommit, GetScreen() ); } else if( aCleanupFlags == GLOBAL_CLEANUP ) { for( const SCH_SHEET_PATH& sheet : list ) SchematicCleanUp( aCommit, sheet.LastScreen() ); } #ifdef PROFILE timer.Stop(); wxLogTrace( "CONN_PROFILE", "SchematicCleanUp() %0.4f ms", timer.msecs() ); #endif if( settings.m_IntersheetRefsShow ) RecomputeIntersheetRefs(); std::function changeHandler = [&]( SCH_ITEM* aChangedItem ) -> void { GetCanvas()->GetView()->Update( aChangedItem, KIGFX::REPAINT ); SCH_CONNECTION* connection = aChangedItem->Connection(); if( connection && ( connection->Name() == highlightedConn ) ) m_highlightedConnChanged = true; }; if( !ADVANCED_CFG::GetCfg().m_IncrementalConnectivity || aCleanupFlags == GLOBAL_CLEANUP || m_undoList.m_CommandsList.empty() ) { Schematic().ConnectionGraph()->Recalculate( list, true, &changeHandler ); } else { auto& changed_list = m_undoList.m_CommandsList.back(); std::set changed_items; std::vector pts; for( unsigned ii = 0; ii < changed_list->GetCount(); ++ii ) { EDA_ITEM* item = changed_list->GetPickedItem( ii ); if( !item || !IsEeschemaType( item->Type() ) ) continue; SCH_ITEM* sch_item = static_cast( item ); std::vector tmp_pts = sch_item->GetConnectionPoints(); pts.insert( pts.end(), tmp_pts.begin(), tmp_pts.end() ); changed_items.insert( sch_item ); } for( VECTOR2I& pt: pts ) { for( SCH_ITEM* item : GetScreen()->Items().Overlapping( pt ) ) { if( item->Type() == SCH_LINE_T ) { if( item->HitTest( pt ) ) changed_items.insert( item ); } else if( item->Type() == SCH_SYMBOL_T ) { SCH_SYMBOL* symbol = static_cast( item ); std::vector pins = symbol->GetPins(); changed_items.insert( pins.begin(), pins.end() ); } else if( item->IsConnectable() ) { if( item->IsConnected( pt ) ) changed_items.insert( item ); } } } std::set> all_items = Schematic().ConnectionGraph()->ExtractAffectedItems( changed_items ); CONNECTION_GRAPH new_graph( &Schematic() ); new_graph.SetLastCodes( Schematic().ConnectionGraph() ); for( auto&[ path, item ] : all_items ) { switch( item->Type() ) { case SCH_FIELD_T: case SCH_PIN_T: case SCH_SHEET_PIN_T: static_cast( item->GetParent() )->SetConnectivityDirty(); break; default: item->SetConnectivityDirty(); } } new_graph.Recalculate( list, false, &changeHandler ); Schematic().ConnectionGraph()->Merge( new_graph ); } GetCanvas()->GetView()->UpdateAllItemsConditionally( [&]( KIGFX::VIEW_ITEM* aItem ) -> int { int flags = 0; SCH_ITEM* item = dynamic_cast( aItem ); SCH_CONNECTION* connection = item ? item->Connection() : nullptr; if( connection && connection->HasDriverChanged() ) { connection->ClearDriverChanged(); flags |= KIGFX::REPAINT; } if( item ) { item->RunOnChildren( [&flags]( SCH_ITEM* aChild ) { EDA_TEXT* text = dynamic_cast( aChild ); if( text && text->HasTextVars() ) { text->ClearRenderCache(); text->ClearBoundingBoxCache(); flags |= KIGFX::GEOMETRY | KIGFX::REPAINT; } } ); EDA_TEXT* text = dynamic_cast( aItem ); if( text && text->HasTextVars() ) { text->ClearRenderCache(); text->ClearBoundingBoxCache(); flags |= KIGFX::GEOMETRY | KIGFX::REPAINT; } if( flags & KIGFX::GEOMETRY ) GetScreen()->Update( item, false ); // Refresh RTree } return flags; } ); if( !highlightedConn.IsEmpty() ) { if( m_highlightedConnChanged || !Schematic().ConnectionGraph()->FindFirstSubgraphByName( highlightedConn ) ) RefreshNetNavigator(); m_highlightedConnChanged = false; } if( !localCommit.Empty() ) localCommit.Push( _( "Schematic Cleanup" ) ); } void SCH_EDIT_FRAME::RecomputeIntersheetRefs() { Schematic().RecomputeIntersheetRefs( [&]( SCH_GLOBALLABEL* label ) { for( SCH_FIELD& field : label->GetFields() ) field.ClearBoundingBoxCache(); label->ClearBoundingBoxCache(); GetCanvas()->GetView()->Update( label ); } ); } void SCH_EDIT_FRAME::ShowAllIntersheetRefs( bool aShow ) { RecomputeIntersheetRefs(); GetCanvas()->GetView()->SetLayerVisible( LAYER_INTERSHEET_REFS, aShow ); } void SCH_EDIT_FRAME::CommonSettingsChanged( bool aEnvVarsChanged, bool aTextVarsChanged ) { SCH_BASE_FRAME::CommonSettingsChanged( aEnvVarsChanged, aTextVarsChanged ); SCHEMATIC_SETTINGS& settings = Schematic().Settings(); settings.m_JunctionSize = GetSchematicJunctionSize(); ShowAllIntersheetRefs( settings.m_IntersheetRefsShow ); EESCHEMA_SETTINGS* cfg = Pgm().GetSettingsManager().GetAppSettings(); GetGalDisplayOptions().ReadWindowSettings( cfg->m_Window ); GetRenderSettings()->SetDefaultFont( cfg->m_Appearance.default_font ); KIGFX::VIEW* view = GetCanvas()->GetView(); view->SetLayerVisible( LAYER_ERC_ERR, cfg->m_Appearance.show_erc_errors ); view->SetLayerVisible( LAYER_ERC_WARN, cfg->m_Appearance.show_erc_warnings ); view->SetLayerVisible( LAYER_ERC_EXCLUSION, cfg->m_Appearance.show_erc_exclusions ); view->SetLayerVisible( LAYER_OP_VOLTAGES, cfg->m_Appearance.show_op_voltages ); view->SetLayerVisible( LAYER_OP_CURRENTS, cfg->m_Appearance.show_op_currents ); RefreshOperatingPointDisplay(); settings.m_TemplateFieldNames.DeleteAllFieldNameTemplates( true /* global */ ); if( !cfg->m_Drawing.field_names.IsEmpty() ) settings.m_TemplateFieldNames.AddTemplateFieldNames( cfg->m_Drawing.field_names ); SCH_SCREEN* screen = GetCurrentSheet().LastScreen(); for( SCH_ITEM* item : screen->Items() ) item->ClearCaches(); for( const auto& [ libItemName, libSymbol ] : screen->GetLibSymbols() ) libSymbol->ClearCaches(); GetCanvas()->ForceRefresh(); RecreateToolbars(); Layout(); SendSizeEvent(); } void SCH_EDIT_FRAME::OnPageSettingsChange() { // Store the current zoom level into the current screen before calling // DisplayCurrentSheet() that set the zoom to GetScreen()->m_LastZoomLevel GetScreen()->m_LastZoomLevel = GetCanvas()->GetView()->GetScale(); // Rebuild the sheet view (draw area and any other items): DisplayCurrentSheet(); } void SCH_EDIT_FRAME::ShowChangedLanguage() { // call my base class SCH_BASE_FRAME::ShowChangedLanguage(); // tooltips in toolbars RecreateToolbars(); m_auimgr.GetPane( m_hierarchy ).Caption( _( "Schematic Hierarchy" ) ); m_auimgr.Update(); m_hierarchy->UpdateHierarchyTree(); // status bar UpdateMsgPanel(); updateTitle(); // This ugly hack is to fix an option(left) toolbar update bug that seems to only affect // windows. See https://bugs.launchpad.net/kicad/+bug/1816492. For some reason, calling // wxWindow::Refresh() does not resolve the issue. Only a resize event seems to force the // toolbar to update correctly. #if defined( __WXMSW__ ) PostSizeEvent(); #endif } void SCH_EDIT_FRAME::UpdateNetHighlightStatus() { if( !GetHighlightedConnection().IsEmpty() ) { SetStatusText( wxString::Format( _( "Highlighted net: %s" ), UnescapeString( GetHighlightedConnection() ) ) ); } else { SetStatusText( wxT( "" ) ); } } void SCH_EDIT_FRAME::SetScreen( BASE_SCREEN* aScreen ) { if( m_toolManager ) m_toolManager->RunAction( EE_ACTIONS::clearSelection ); SCH_BASE_FRAME::SetScreen( aScreen ); GetCanvas()->DisplaySheet( static_cast( aScreen ) ); if( m_toolManager ) m_toolManager->ResetTools( TOOL_BASE::MODEL_RELOAD ); } const BOX2I SCH_EDIT_FRAME::GetDocumentExtents( bool aIncludeAllVisible ) const { BOX2I bBoxDoc; if( aIncludeAllVisible ) { // Get the whole page size and return that int sizeX = GetScreen()->GetPageSettings().GetWidthIU( schIUScale.IU_PER_MILS ); int sizeY = GetScreen()->GetPageSettings().GetHeightIU( schIUScale.IU_PER_MILS ); bBoxDoc = BOX2I( VECTOR2I( 0, 0 ), VECTOR2I( sizeX, sizeY ) ); } else { // Get current drawing-sheet in a form we can compare to an EDA_ITEM DS_PROXY_VIEW_ITEM* ds = SCH_BASE_FRAME::GetCanvas()->GetView()->GetDrawingSheet(); EDA_ITEM* dsAsItem = static_cast( ds ); // Calc the bounding box of all items on screen except the page border for( EDA_ITEM* item : GetScreen()->Items() ) { if( item != dsAsItem ) // Ignore the drawing-sheet itself bBoxDoc.Merge( item->GetBoundingBox() ); } } return bBoxDoc; } bool SCH_EDIT_FRAME::IsContentModified() const { return Schematic().GetSheets().IsModified(); } bool SCH_EDIT_FRAME::GetShowAllPins() const { EESCHEMA_SETTINGS* cfg = eeconfig(); return cfg && cfg->m_Appearance.show_hidden_pins; } void SCH_EDIT_FRAME::FocusOnItem( SCH_ITEM* aItem ) { static KIID lastBrightenedItemID( niluuid ); SCH_SHEET_LIST sheetList = Schematic().GetSheets(); SCH_SHEET_PATH dummy; SCH_ITEM* lastItem = sheetList.GetItem( lastBrightenedItemID, &dummy ); if( lastItem && lastItem != aItem ) { lastItem->ClearBrightened(); UpdateItem( lastItem ); lastBrightenedItemID = niluuid; } if( aItem ) { if( !aItem->IsBrightened() ) { aItem->SetBrightened(); UpdateItem( aItem ); lastBrightenedItemID = aItem->m_Uuid; } FocusOnLocation( aItem->GetFocusPosition() ); } } wxString SCH_EDIT_FRAME::GetCurrentFileName() const { return Schematic().GetFileName(); } SELECTION& SCH_EDIT_FRAME::GetCurrentSelection() { return m_toolManager->GetTool()->GetSelection(); } void SCH_EDIT_FRAME::onSize( wxSizeEvent& aEvent ) { if( IsShown() ) { // We only need this until the frame is done resizing and the final client size is // established. Unbind( wxEVT_SIZE, &SCH_EDIT_FRAME::onSize, this ); GetToolManager()->RunAction( ACTIONS::zoomFitScreen ); } // Skip() is called in the base class. EDA_DRAW_FRAME::OnSize( aEvent ); } void SCH_EDIT_FRAME::SaveSymbolToSchematic( const LIB_SYMBOL& aSymbol, const KIID& aSchematicSymbolUUID ) { SCH_SHEET_PATH principalPath; SCH_ITEM* item = Schematic().GetSheets().GetItem( aSchematicSymbolUUID, &principalPath ); SCH_SYMBOL* principalSymbol = dynamic_cast( item ); SCH_COMMIT commit( m_toolManager ); if( !principalSymbol ) return; wxString principalRef; if( principalSymbol->IsAnnotated( &principalPath ) ) principalRef = principalSymbol->GetRef( &principalPath, false ); std::vector< std::pair > allUnits; for( const SCH_SHEET_PATH& path : Schematic().GetSheets() ) { for( SCH_ITEM* candidate : path.LastScreen()->Items().OfType( SCH_SYMBOL_T ) ) { SCH_SYMBOL* candidateSymbol = static_cast( candidate ); if( candidateSymbol == principalSymbol || ( candidateSymbol->IsAnnotated( &path ) && candidateSymbol->GetRef( &path, false ) == principalRef ) ) { allUnits.emplace_back( candidateSymbol, path ); } } } for( auto& [ unit, path ] : allUnits ) { // This needs to be done before the LIB_SYMBOL is changed to prevent stale // library symbols in the schematic file. path.LastScreen()->Remove( unit ); if( !unit->IsNew() ) commit.Modify( unit, path.LastScreen() ); unit->SetLibSymbol( aSymbol.Flatten().release() ); unit->UpdateFields( &GetCurrentSheet(), true, /* update style */ true, /* update ref */ true, /* update other fields */ false, /* reset ref */ false /* reset other fields */ ); path.LastScreen()->Append( unit ); GetCanvas()->GetView()->Update( unit ); } if( !commit.Empty() ) commit.Push( _( "Save Symbol to Schematic" ) ); } void SCH_EDIT_FRAME::UpdateItem( EDA_ITEM* aItem, bool isAddOrDelete, bool aUpdateRtree ) { SCH_BASE_FRAME::UpdateItem( aItem, isAddOrDelete, aUpdateRtree ); if( SCH_ITEM* sch_item = dynamic_cast( aItem ) ) sch_item->ClearCaches(); } void SCH_EDIT_FRAME::DisplayCurrentSheet() { wxCHECK( m_toolManager, /* void */ ); m_toolManager->RunAction( ACTIONS::cancelInteractive ); m_toolManager->RunAction( EE_ACTIONS::clearSelection ); SCH_SCREEN* screen = GetCurrentSheet().LastScreen(); wxCHECK( screen, /* void */ ); m_toolManager->RunAction( EE_ACTIONS::clearSelection ); SCH_BASE_FRAME::SetScreen( screen ); m_toolManager->ResetTools( TOOL_BASE::MODEL_RELOAD ); // update the References GetCurrentSheet().UpdateAllScreenReferences(); SetSheetNumberAndCount(); RefreshOperatingPointDisplay(); EE_SELECTION_TOOL* selectionTool = m_toolManager->GetTool(); wxCHECK( selectionTool, /* void */ ); auto visit = [&]( EDA_ITEM* item ) { if( m_findReplaceDialog && !m_findReplaceData->findString.IsEmpty() && item->Matches( *m_findReplaceData, &GetCurrentSheet() ) ) { item->SetForceVisible( true ); selectionTool->BrightenItem( item ); } else if( item->IsBrightened() ) { item->SetForceVisible( false ); selectionTool->UnbrightenItem( item ); } }; for( SCH_ITEM* item : screen->Items() ) { visit( item ); item->RunOnChildren( [&]( SCH_ITEM* aChild ) { visit( aChild ); } ); } if( !screen->m_zoomInitialized ) { initScreenZoom(); } else { // Set zoom to last used in this screen GetCanvas()->GetView()->SetScale( GetScreen()->m_LastZoomLevel ); GetCanvas()->GetView()->SetCenter( GetScreen()->m_ScrollCenter ); } updateTitle(); HardRedraw(); // Ensure all items are redrawn (especially the drawing-sheet items) // Allow tools to re-add their VIEW_ITEMs after the last call to Clear in HardRedraw m_toolManager->ResetTools( TOOL_BASE::MODEL_RELOAD ); SCH_EDITOR_CONTROL* editTool = m_toolManager->GetTool(); wxCHECK( editTool, /* void */ ); TOOL_EVENT dummy; editTool->UpdateNetHighlighting( dummy ); m_hierarchy->UpdateHierarchySelection(); m_schematic->OnSchSheetChanged(); } DIALOG_BOOK_REPORTER* SCH_EDIT_FRAME::GetSymbolDiffDialog() { if( !m_diffSymbolDialog ) m_diffSymbolDialog = new DIALOG_BOOK_REPORTER( this, DIFF_SYMBOLS_DIALOG_NAME, _( "Diff Symbol with Library" ) ); return m_diffSymbolDialog; } void SCH_EDIT_FRAME::onCloseSymbolDiffDialog( wxCommandEvent& aEvent ) { if( m_diffSymbolDialog && aEvent.GetString() == DIFF_SYMBOLS_DIALOG_NAME ) { m_diffSymbolDialog->Destroy(); m_diffSymbolDialog = nullptr; } } DIALOG_ERC* SCH_EDIT_FRAME::GetErcDialog() { if( !m_ercDialog ) m_ercDialog = new DIALOG_ERC( this ); return m_ercDialog; } void SCH_EDIT_FRAME::onCloseErcDialog( wxCommandEvent& aEvent ) { if( m_ercDialog ) { m_ercDialog->Destroy(); m_ercDialog = nullptr; } } DIALOG_SYMBOL_FIELDS_TABLE* SCH_EDIT_FRAME::GetSymbolFieldsTableDialog() { if( !m_symbolFieldsTableDialog ) m_symbolFieldsTableDialog = new DIALOG_SYMBOL_FIELDS_TABLE( this ); return m_symbolFieldsTableDialog; } void SCH_EDIT_FRAME::onCloseSymbolFieldsTableDialog( wxCommandEvent& aEvent ) { if( m_symbolFieldsTableDialog ) { m_symbolFieldsTableDialog->Destroy(); m_symbolFieldsTableDialog = nullptr; } } void SCH_EDIT_FRAME::AddSchematicChangeListener( wxEvtHandler* aListener ) { auto it = std::find( m_schematicChangeListeners.begin(), m_schematicChangeListeners.end(), aListener ); // Don't add duplicate listeners. if( it == m_schematicChangeListeners.end() ) m_schematicChangeListeners.push_back( aListener ); } void SCH_EDIT_FRAME::RemoveSchematicChangeListener( wxEvtHandler* aListener ) { auto it = std::find( m_schematicChangeListeners.begin(), m_schematicChangeListeners.end(), aListener ); // Don't add duplicate listeners. if( it != m_schematicChangeListeners.end() ) m_schematicChangeListeners.erase( it ); } wxTreeCtrl* SCH_EDIT_FRAME::createHighlightedNetNavigator() { m_netNavigator = new wxTreeCtrl( this, wxID_ANY, wxPoint( 0, 0 ), FromDIP( wxSize( 160, 250 ) ), wxTR_DEFAULT_STYLE | wxNO_BORDER ); return m_netNavigator; } void SCH_EDIT_FRAME::SetHighlightedConnection( const wxString& aConnection, const NET_NAVIGATOR_ITEM_DATA* aSelection ) { bool refreshNetNavigator = aConnection != m_highlightedConn; m_highlightedConn = aConnection; if( refreshNetNavigator ) RefreshNetNavigator( aSelection ); } void SCH_EDIT_FRAME::unitsChangeRefresh() { if( m_netNavigator && !m_highlightedConn.IsEmpty() ) { NET_NAVIGATOR_ITEM_DATA itemData; wxTreeItemId selection = m_netNavigator->GetSelection(); bool refreshSelection = selection.IsOk() && ( selection != m_netNavigator->GetRootItem() ); if( refreshSelection ) { NET_NAVIGATOR_ITEM_DATA* tmp = dynamic_cast( m_netNavigator->GetItemData( selection ) ); wxCHECK( tmp, /* void */ ); itemData = *tmp; } m_netNavigator->DeleteAllItems(); RefreshNetNavigator( refreshSelection ? &itemData : nullptr ); } UpdateProperties(); }