diff --git a/common/tool/tool_manager.cpp b/common/tool/tool_manager.cpp index 817378440b..3540a8b7bd 100644 --- a/common/tool/tool_manager.cpp +++ b/common/tool/tool_manager.cpp @@ -561,7 +561,7 @@ void TOOL_MANAGER::ResetTools( TOOL_BASE::RESET_REASON aReason ) void TOOL_MANAGER::InitTools() { - for( TOOL_VEC::iterator it = m_toolOrder.begin(); it != m_toolOrder.end(); /* iter inside */ ) + for( auto it = m_toolOrder.begin(); it != m_toolOrder.end(); /* iter inside */ ) { TOOL_BASE* tool = *it; wxASSERT( m_toolState.count( tool ) ); diff --git a/include/commit.h b/include/commit.h index bf61eb925c..80eef7af13 100644 --- a/include/commit.h +++ b/include/commit.h @@ -133,7 +133,7 @@ public: ///< Execute the changes. virtual void Push( const wxString& aMessage = wxT( "A commit" ), bool aCreateUndoEntry = true, bool aSetDirtyBit = true, - bool aUpdateConnectivity = true ) = 0; + bool aUpdateConnectivity = true, bool aZoneFillOp = false ) = 0; ///< Revert the commit by restoring the modified items state. virtual void Revert() = 0; diff --git a/include/tool/tool_manager.h b/include/tool/tool_manager.h index 1ff1c6763b..26ead0e7d5 100644 --- a/include/tool/tool_manager.h +++ b/include/tool/tool_manager.h @@ -66,7 +66,6 @@ public: typedef std::map NAME_STATE_MAP; typedef std::map ID_STATE_MAP; typedef std::list ID_LIST; - typedef std::vector TOOL_VEC; /** * Generates a unique ID from for a tool with given name. @@ -226,6 +225,11 @@ public: return nullptr; } + /* + * Return all registered tools. + */ + std::vector Tools() { return m_toolOrder; } + /** * Deactivate the currently active tool. */ @@ -531,7 +535,7 @@ private: void setActiveState( TOOL_STATE* aState ); ///< List of tools in the order they were registered - TOOL_VEC m_toolOrder; + std::vector m_toolOrder; ///< Index of registered tools current states, associated by tools' objects. TOOL_STATE_MAP m_toolState; diff --git a/pcbnew/board_commit.cpp b/pcbnew/board_commit.cpp index b3b8bdb853..b02a04a23d 100644 --- a/pcbnew/board_commit.cpp +++ b/pcbnew/board_commit.cpp @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2016 CERN - * Copyright (C) 2020-2021 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2020-2022 KiCad Developers, see AUTHORS.txt for contributors. * @author Tomasz Wlostowski * * This program is free software; you can redistribute it and/or @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -42,6 +43,7 @@ using namespace std::placeholders; BOARD_COMMIT::BOARD_COMMIT( TOOL_MANAGER* aToolMgr ) : m_toolMgr( aToolMgr ), m_isFootprintEditor( false ), + m_isBoardEditor( false ), m_resolveNetConflicts( false ) { } @@ -52,6 +54,7 @@ BOARD_COMMIT::BOARD_COMMIT( PCB_TOOL_BASE* aTool ) : { m_toolMgr = aTool->GetManager(); m_isFootprintEditor = aTool->IsFootprintEditor(); + m_isBoardEditor = aTool->IsBoardEditor(); } @@ -60,6 +63,7 @@ BOARD_COMMIT::BOARD_COMMIT( EDA_DRAW_FRAME* aFrame ) : { m_toolMgr = aFrame->GetToolManager(); m_isFootprintEditor = aFrame->IsType( FRAME_FOOTPRINT_EDITOR ); + m_isBoardEditor = aFrame->IsType( FRAME_PCB_EDITOR ); } @@ -96,18 +100,59 @@ COMMIT& BOARD_COMMIT::Stage( const PICKED_ITEMS_LIST& aItems, UNDO_REDO aModFlag } +void BOARD_COMMIT::dirtyIntersectingZones( BOARD_ITEM* item ) +{ + if( item->Type() == PCB_FOOTPRINT_T ) + { + static_cast( item )->RunOnChildren( + [&]( BOARD_ITEM* child ) + { + dirtyIntersectingZones( child ); + } ); + } + else if( item->Type() == PCB_GROUP_T ) + { + static_cast( item )->RunOnChildren( + [&]( BOARD_ITEM* child ) + { + dirtyIntersectingZones( child ); + } ); + } + else + { + ZONE_FILLER_TOOL* zoneFillerTool = m_toolMgr->GetTool(); + BOARD* board = static_cast( m_toolMgr->GetModel() ); + EDA_RECT bbox = item->GetBoundingBox(); + LSET layers = item->GetLayerSet(); + + for( ZONE* zone : board->Zones() ) + { + if( zone->GetIsRuleArea() ) + continue; + + if( ( zone->GetLayerSet() & layers ).any() + && zone->GetCachedBoundingBox().Intersects( bbox ) ) + { + zoneFillerTool->DirtyZone( zone ); + } + } + } +} + + void BOARD_COMMIT::Push( const wxString& aMessage, bool aCreateUndoEntry, bool aSetDirtyBit, - bool aUpdateConnectivity ) + bool aUpdateConnectivity, bool aZoneFillOp ) { // Objects potentially interested in changes: PICKED_ITEMS_LIST undoList; KIGFX::VIEW* view = m_toolMgr->GetView(); - BOARD* board = (BOARD*) m_toolMgr->GetModel(); + BOARD* board = static_cast( m_toolMgr->GetModel() ); PCB_BASE_FRAME* frame = dynamic_cast( m_toolMgr->GetToolHolder() ); std::set savedModules; PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool(); bool itemsDeselected = false; bool solderMaskDirty = false; + bool autofillZones = false; std::vector bulkAddedItems; std::vector bulkRemovedItems; @@ -116,6 +161,14 @@ void BOARD_COMMIT::Push( const wxString& aMessage, bool aCreateUndoEntry, bool a if( Empty() ) return; + if( m_isBoardEditor && !aZoneFillOp && frame->GetPcbNewSettings()->m_AutoRefillZones ) + { + autofillZones = true; + + for( ZONE* zone : board->Zones() ) + zone->CacheBoundingBox(); + } + for( COMMIT_LINE& ent : m_changes ) { int changeType = ent.m_type & CHT_TYPE; @@ -205,6 +258,9 @@ void BOARD_COMMIT::Push( const wxString& aMessage, bool aCreateUndoEntry, bool a } } + if( autofillZones ) + dirtyIntersectingZones( boardItem ); + if( view && boardItem->Type() != PCB_NETINFO_T ) view->Add( boardItem ); @@ -224,6 +280,9 @@ void BOARD_COMMIT::Push( const wxString& aMessage, bool aCreateUndoEntry, bool a itemsDeselected = true; } + if( autofillZones ) + dirtyIntersectingZones( boardItem ); + switch( boardItem->Type() ) { // Footprint items @@ -334,7 +393,7 @@ void BOARD_COMMIT::Push( const wxString& aMessage, bool aCreateUndoEntry, bool a bulkRemovedItems.push_back( boardItem ); break; - default: // other types do not need to (or should not) be handled + default: // other types do not need to (or should not) be handled wxASSERT( false ); break; } @@ -362,6 +421,12 @@ void BOARD_COMMIT::Push( const wxString& aMessage, bool aCreateUndoEntry, bool a connectivity->Update( boardItem ); } + if( autofillZones ) + { + dirtyIntersectingZones( static_cast( ent.m_copy )); // before + dirtyIntersectingZones( boardItem ); // after + } + if( view ) { view->Update( boardItem ); @@ -400,7 +465,7 @@ void BOARD_COMMIT::Push( const wxString& aMessage, bool aCreateUndoEntry, bool a if( itemsChanged.size() > 0 ) board->OnItemsChanged( itemsChanged ); - if( !m_isFootprintEditor ) + if( m_isBoardEditor ) { size_t num_changes = m_changes.size(); @@ -415,42 +480,38 @@ void BOARD_COMMIT::Push( const wxString& aMessage, bool aCreateUndoEntry, bool a connectivity->ClearDynamicRatsnest(); } - if( frame && solderMaskDirty ) + if( solderMaskDirty ) frame->HideSolderMask(); - if( frame ) - frame->GetCanvas()->RedrawRatsnest(); + frame->GetCanvas()->RedrawRatsnest(); - if( m_changes.size() > num_changes ) + // Log undo items for any connectivity changes + for( size_t i = num_changes; i < m_changes.size(); ++i ) { - for( size_t i = num_changes; i < m_changes.size(); ++i ) + COMMIT_LINE& ent = m_changes[i]; + + wxASSERT( ( ent.m_type & CHT_TYPE ) == CHT_MODIFY ); + + BOARD_ITEM* boardItem = static_cast( ent.m_item ); + + if( aCreateUndoEntry ) { - COMMIT_LINE& ent = m_changes[i]; - - // This should only be modifications from the connectivity algo - wxASSERT( ( ent.m_type & CHT_TYPE ) == CHT_MODIFY ); - - BOARD_ITEM* boardItem = static_cast( ent.m_item ); - - if( aCreateUndoEntry ) - { - ITEM_PICKER itemWrapper( nullptr, boardItem, UNDO_REDO::CHANGED ); - wxASSERT( ent.m_copy ); - itemWrapper.SetLink( ent.m_copy ); - undoList.PushItem( itemWrapper ); - } - else - { - delete ent.m_copy; - } - - if( view ) - view->Update( boardItem ); + ITEM_PICKER itemWrapper( nullptr, boardItem, UNDO_REDO::CHANGED ); + wxASSERT( ent.m_copy ); + itemWrapper.SetLink( ent.m_copy ); + undoList.PushItem( itemWrapper ); } + else + { + delete ent.m_copy; + } + + if( view ) + view->Update( boardItem ); } } - if( !m_isFootprintEditor && aCreateUndoEntry && frame ) + if( m_isBoardEditor && aCreateUndoEntry ) frame->SaveCopyInUndoList( undoList, UNDO_REDO::UNSPECIFIED ); m_toolMgr->PostEvent( { TC_MESSAGE, TA_MODEL_CHANGE, AS_GLOBAL } ); @@ -458,6 +519,9 @@ void BOARD_COMMIT::Push( const wxString& aMessage, bool aCreateUndoEntry, bool a if( itemsDeselected ) m_toolMgr->PostEvent( EVENTS::UnselectedEvent ); + if( autofillZones ) + m_toolMgr->RunAction( PCB_ACTIONS::zoneFillDirty ); + if( frame ) { if( aSetDirtyBit ) diff --git a/pcbnew/board_commit.h b/pcbnew/board_commit.h index a453db26e7..b1042afddc 100644 --- a/pcbnew/board_commit.h +++ b/pcbnew/board_commit.h @@ -2,6 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2016 CERN + * Copyright (C) 2016-2022 KiCad Developers, see AUTHORS.txt for contributors. * @author Tomasz Wlostowski * * This program is free software; you can redistribute it and/or @@ -22,8 +23,8 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ -#ifndef __BOARD_COMMIT_H -#define __BOARD_COMMIT_H +#ifndef BOARD_COMMIT_H +#define BOARD_COMMIT_H #include @@ -45,7 +46,7 @@ public: virtual void Push( const wxString& aMessage = wxT( "A commit" ), bool aCreateUndoEntry = true, bool aSetDirtyBit = true, - bool aUpdateConnectivity = true ) override; + bool aUpdateConnectivity = true, bool aZoneFillOp = false ) override; virtual void Revert() override; COMMIT& Stage( EDA_ITEM* aItem, CHANGE_TYPE aChangeType ) override; @@ -66,10 +67,13 @@ public: private: virtual EDA_ITEM* parentObject( EDA_ITEM* aItem ) const override; + void dirtyIntersectingZones( BOARD_ITEM* item ); + private: - TOOL_MANAGER* m_toolMgr; - bool m_isFootprintEditor; - bool m_resolveNetConflicts; + TOOL_MANAGER* m_toolMgr; + bool m_isFootprintEditor; + bool m_isBoardEditor; + bool m_resolveNetConflicts; }; #endif diff --git a/pcbnew/dialogs/panel_edit_options_base.cpp b/pcbnew/dialogs/panel_edit_options_base.cpp index 571926cba3..fc24bb0bba 100644 --- a/pcbnew/dialogs/panel_edit_options_base.cpp +++ b/pcbnew/dialogs/panel_edit_options_base.cpp @@ -363,7 +363,7 @@ PANEL_EDIT_OPTIONS_BASE::PANEL_EDIT_OPTIONS_BASE( wxWindow* parent, wxWindowID i sbSizerPcbGraphics = new wxStaticBoxSizer( new wxStaticBox( pcbPage, wxID_ANY, _("Graphics Editing") ), wxVERTICAL ); m_cbPcbGraphic45Mode = new wxCheckBox( sbSizerPcbGraphics->GetStaticBox(), wxID_ANY, _("Limit actions to 45 degrees from start"), wxDefaultPosition, wxDefaultSize, 0 ); - sbSizerPcbGraphics->Add( m_cbPcbGraphic45Mode, 0, wxALL, 5 ); + sbSizerPcbGraphics->Add( m_cbPcbGraphic45Mode, 0, wxBOTTOM|wxRIGHT|wxLEFT, 5 ); pcbOptionsSizer->Add( sbSizerPcbGraphics, 0, wxEXPAND|wxTOP, 5 ); @@ -377,11 +377,11 @@ PANEL_EDIT_OPTIONS_BASE::PANEL_EDIT_OPTIONS_BASE( wxWindow* parent, wxWindowID i sbSizer4->Add( m_showPageLimits, 0, wxBOTTOM|wxRIGHT|wxLEFT, 5 ); - m_autoRefillZones = new wxCheckBox( sbSizer4->GetStaticBox(), wxID_ANY, _("Refill zones after Zone Properties dialog"), wxDefaultPosition, wxDefaultSize, 0 ); + m_autoRefillZones = new wxCheckBox( sbSizer4->GetStaticBox(), wxID_ANY, _("Auto-refill zones"), wxDefaultPosition, wxDefaultSize, 0 ); m_autoRefillZones->SetValue(true); m_autoRefillZones->SetToolTip( _("If checked, zones will be re-filled after editing the properties of the zone using the Zone Properties dialog") ); - sbSizer4->Add( m_autoRefillZones, 0, wxALL, 5 ); + sbSizer4->Add( m_autoRefillZones, 0, wxBOTTOM|wxRIGHT|wxLEFT, 5 ); pcbOptionsSizer->Add( sbSizer4, 1, wxEXPAND|wxTOP, 5 ); diff --git a/pcbnew/dialogs/panel_edit_options_base.fbp b/pcbnew/dialogs/panel_edit_options_base.fbp index f87be3b6bc..f4c98ccca6 100644 --- a/pcbnew/dialogs/panel_edit_options_base.fbp +++ b/pcbnew/dialogs/panel_edit_options_base.fbp @@ -3313,7 +3313,7 @@ none 5 - wxALL + wxBOTTOM|wxRIGHT|wxLEFT 0 1 @@ -3455,7 +3455,7 @@ 5 - wxALL + wxBOTTOM|wxRIGHT|wxLEFT 0 1 @@ -3486,7 +3486,7 @@ 0 0 wxID_ANY - Refill zones after Zone Properties dialog + Auto-refill zones 0 diff --git a/pcbnew/edit_zone_helpers.cpp b/pcbnew/edit_zone_helpers.cpp index f0092edf36..182a24fe05 100644 --- a/pcbnew/edit_zone_helpers.cpp +++ b/pcbnew/edit_zone_helpers.cpp @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2012 Jean-Pierre Charras, jean-pierre.charras@ujf-grenoble.fr - * Copyright (C) 1992-2012 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 1992-2022 KiCad Developers, see AUTHORS.txt for contributors. * * Some code comes from FreePCB. * @@ -33,8 +33,6 @@ #include #include #include -#include -#include void PCB_EDIT_FRAME::Edit_Zone_Params( ZONE* aZone ) @@ -103,43 +101,8 @@ void PCB_EDIT_FRAME::Edit_Zone_Params( ZONE* aZone ) UpdateCopyOfZonesList( pickedList, deletedList, GetBoard() ); - // refill zones with the new properties applied - std::vector zones_to_refill; - - for( unsigned i = 0; i < pickedList.GetCount(); ++i ) - { - ZONE* zone = dyn_cast( pickedList.GetPickedItem( i ) ); - - if( zone == nullptr ) - { - wxASSERT_MSG( false, wxT( "Expected a zone after zone properties edit" ) ); - continue; - } - - // aZone won't be filled if the layer set was modified, but it needs to be updated - if( zone->IsFilled() || zone == aZone ) - zones_to_refill.push_back( zone ); - } - commit.Stage( pickedList ); - // Only auto-refill zones here if in user preferences - if( Settings().m_AutoRefillZones ) - { - if( zones_to_refill.size() ) - { - ZONE_FILLER filler( GetBoard(), &commit ); - wxString title = wxString::Format( _( "Refill %d Zones" ), - (int) zones_to_refill.size() ); - - std::unique_ptr reporter; - reporter = std::make_unique( this, title, 4 ); - filler.SetProgressReporter( reporter.get() ); - - (void) filler.Fill( zones_to_refill ); - } - } - commit.Push( _( "Modify zone properties" ), true, true, false ); GetBoard()->GetConnectivity()->Build( GetBoard() ); diff --git a/pcbnew/footprint_edit_frame.cpp b/pcbnew/footprint_edit_frame.cpp index 8f55c4324a..3275880797 100644 --- a/pcbnew/footprint_edit_frame.cpp +++ b/pcbnew/footprint_edit_frame.cpp @@ -1020,16 +1020,11 @@ void FOOTPRINT_EDIT_FRAME::setupTools() m_toolManager->RegisterTool( new CONVERT_TOOL ); m_toolManager->RegisterTool( new SCRIPTING_TOOL ); - m_toolManager->GetTool()->SetIsFootprintEditor( true ); - m_toolManager->GetTool()->SetIsFootprintEditor( true ); - m_toolManager->GetTool()->SetIsFootprintEditor( true ); - m_toolManager->GetTool()->SetIsFootprintEditor( true ); - m_toolManager->GetTool()->SetIsFootprintEditor( true ); - m_toolManager->GetTool()->SetIsFootprintEditor( true ); - m_toolManager->GetTool()->SetIsFootprintEditor( true ); - m_toolManager->GetTool()->SetIsFootprintEditor( true ); - m_toolManager->GetTool()->SetIsFootprintEditor( true ); - m_toolManager->GetTool()->SetIsFootprintEditor( true ); + for( TOOL_BASE* tool : m_toolManager->Tools() ) + { + if( PCB_TOOL_BASE* pcbTool = dynamic_cast( tool ) ) + pcbTool->SetIsFootprintEditor( true ); + } m_toolManager->GetTool()->SetFootprintFrame( true ); m_toolManager->InitTools(); diff --git a/pcbnew/pcb_edit_frame.cpp b/pcbnew/pcb_edit_frame.cpp index d3dc1a7e23..c33cce18d6 100644 --- a/pcbnew/pcb_edit_frame.cpp +++ b/pcbnew/pcb_edit_frame.cpp @@ -568,6 +568,12 @@ void PCB_EDIT_FRAME::setupTools() m_toolManager->RegisterTool( new SCRIPTING_TOOL ); m_toolManager->InitTools(); + for( TOOL_BASE* tool : m_toolManager->Tools() ) + { + if( PCB_TOOL_BASE* pcbTool = dynamic_cast( tool ) ) + pcbTool->SetIsBoardEditor( true ); + } + // Run the selection tool, it is supposed to be always active m_toolManager->InvokeTool( "pcbnew.InteractiveSelection" ); } diff --git a/pcbnew/tools/pcb_actions.cpp b/pcbnew/tools/pcb_actions.cpp index bdb25ef60a..fff17ef782 100644 --- a/pcbnew/tools/pcb_actions.cpp +++ b/pcbnew/tools/pcb_actions.cpp @@ -1300,6 +1300,9 @@ TOOL_ACTION PCB_ACTIONS::zoneFillAll( "pcbnew.ZoneFiller.zoneFillAll", _( "Fill All Zones" ), _( "Update copper fill of all zones" ), BITMAPS::fill_zone ); +TOOL_ACTION PCB_ACTIONS::zoneFillDirty( "pcbnew.ZoneFiller.zoneFillDirty", + AS_CONTEXT ); + TOOL_ACTION PCB_ACTIONS::zoneUnfill( "pcbnew.ZoneFiller.zoneUnfill", AS_GLOBAL, 0, "", _( "Unfill Zone" ), _( "Remove copper fill from selected zone(s)" ), diff --git a/pcbnew/tools/pcb_actions.h b/pcbnew/tools/pcb_actions.h index 7d168b4072..a9d2ee4632 100644 --- a/pcbnew/tools/pcb_actions.h +++ b/pcbnew/tools/pcb_actions.h @@ -309,6 +309,7 @@ public: // Zone actions static TOOL_ACTION zoneFill; static TOOL_ACTION zoneFillAll; + static TOOL_ACTION zoneFillDirty; static TOOL_ACTION zoneUnfill; static TOOL_ACTION zoneUnfillAll; static TOOL_ACTION zoneMerge; diff --git a/pcbnew/tools/pcb_tool_base.h b/pcbnew/tools/pcb_tool_base.h index 8326a2297e..864831ca57 100644 --- a/pcbnew/tools/pcb_tool_base.h +++ b/pcbnew/tools/pcb_tool_base.h @@ -76,7 +76,8 @@ public: * Creates a tool with given id & name. The name must be unique. */ PCB_TOOL_BASE( TOOL_ID aId, const std::string& aName ) : TOOL_INTERACTIVE ( aId, aName ), - m_isFootprintEditor( false ) + m_isFootprintEditor( false ), + m_isBoardEditor( false ) {}; /** @@ -85,7 +86,8 @@ public: * Creates a tool with given name. The name must be unique. */ PCB_TOOL_BASE( const std::string& aName ) : TOOL_INTERACTIVE ( aName ), - m_isFootprintEditor( false ) + m_isFootprintEditor( false ), + m_isBoardEditor( false ) {}; virtual ~PCB_TOOL_BASE() {}; @@ -103,6 +105,9 @@ public: void SetIsFootprintEditor( bool aEnabled ) { m_isFootprintEditor = aEnabled; } bool IsFootprintEditor() const { return m_isFootprintEditor; } + void SetIsBoardEditor( bool aEnabled ) { m_isBoardEditor = aEnabled; } + bool IsBoardEditor() const { return m_isBoardEditor; } + /** * Should the tool use its 45° mode option? * @return True if set to use 45° @@ -179,6 +184,7 @@ protected: protected: bool m_isFootprintEditor; + bool m_isBoardEditor; }; #endif diff --git a/pcbnew/tools/zone_filler_tool.cpp b/pcbnew/tools/zone_filler_tool.cpp index 8dd3b2639d..39d3ab6d00 100644 --- a/pcbnew/tools/zone_filler_tool.cpp +++ b/pcbnew/tools/zone_filler_tool.cpp @@ -27,6 +27,8 @@ #include #include #include +#include +#include #include #include #include @@ -66,7 +68,7 @@ void ZONE_FILLER_TOOL::CheckAllZones( wxWindow* aCaller, PROGRESS_REPORTER* aRep std::vector toFill; for( ZONE* zone : board()->Zones() ) - toFill.push_back(zone); + toFill.push_back( zone ); BOARD_COMMIT commit( this ); std::unique_ptr reporter; @@ -84,7 +86,7 @@ void ZONE_FILLER_TOOL::CheckAllZones( wxWindow* aCaller, PROGRESS_REPORTER* aRep if( filler.Fill( toFill, true, aCaller ) ) { - commit.Push( _( "Fill Zone(s)" ), true, true, false ); + commit.Push( _( "Fill Zone(s)" ), true, true, false, true ); getEditFrame()->m_ZoneFillsDirty = false; } else @@ -159,7 +161,7 @@ void ZONE_FILLER_TOOL::FillAllZones( wxWindow* aCaller, PROGRESS_REPORTER* aRepo { filler.GetProgressReporter()->AdvancePhase(); - commit.Push( _( "Fill Zone(s)" ), true, true, false ); + commit.Push( _( "Fill Zone(s)" ), true, true, false, true ); frame->m_ZoneFillsDirty = false; } else @@ -181,6 +183,105 @@ void ZONE_FILLER_TOOL::FillAllZones( wxWindow* aCaller, PROGRESS_REPORTER* aRepo } +int ZONE_FILLER_TOOL::ZoneFillDirty( const TOOL_EVENT& aEvent ) +{ + PCB_EDIT_FRAME* frame = getEditFrame(); + std::vector toFill; + + for( ZONE* zone : board()->Zones() ) + { + if( m_dirtyZoneIDs.count( zone->m_Uuid ) ) + toFill.push_back( zone ); + } + + if( toFill.empty() ) + return 0; + + if( m_fillInProgress ) + return 0; + + m_fillInProgress = true; + + m_dirtyZoneIDs.clear(); + + board()->IncrementTimeStamp(); // Clear caches + + BOARD_COMMIT commit( this ); + std::unique_ptr reporter; + ZONE_FILLER filler( board(), &commit ); + int pts = 0; + + if( !board()->GetDesignSettings().m_DRCEngine->RulesValid() ) + { + WX_INFOBAR* infobar = frame->GetInfoBar(); + wxHyperlinkCtrl* button = new wxHyperlinkCtrl( infobar, wxID_ANY, _( "Show DRC rules" ), + wxEmptyString ); + + button->Bind( wxEVT_COMMAND_HYPERLINK, + std::function( + [frame]( wxHyperlinkEvent& aEvent ) + { + frame->ShowBoardSetupDialog( _( "Rules" ) ); + } ) ); + + infobar->RemoveAllButtons(); + infobar->AddButton( button ); + + infobar->ShowMessageFor( _( "Zone fills may be inaccurate. DRC rules contain errors." ), + 10000, wxICON_WARNING ); + } + + for( ZONE* zone : toFill ) + { + for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq() ) + pts += zone->GetFilledPolysList( layer )->FullPointCount(); + + if( pts > 1000 ) + { + wxString title = wxString::Format( _( "Refill %d Zones" ), (int) toFill.size() ); + + reporter = std::make_unique( frame, title, 5 ); + filler.SetProgressReporter( reporter.get() ); + break; + } + } + + if( filler.Fill( toFill ) ) + commit.Push( _( "Auto-fill Zone(s)" ), false, false, false, true ); + else + commit.Revert(); + + board()->GetConnectivity()->Build( board(), reporter.get() ); + + if( filler.IsDebug() ) + frame->UpdateUserInterface(); + + canvas()->Refresh(); + m_fillInProgress = false; + + for( ZONE* zone : toFill ) + { + int outlines = 0; + + for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq() ) + outlines += zone->GetFilledPolysList( layer )->OutlineCount(); + + if( outlines == 0 ) + { + // TODO: why does this trash memory? + // frame->ShowInfoBarWarning( _( "Zone has no connections." ) ); + break; + } + } + + // wxWidgets has keyboard focus issues after the progress reporter. Re-setting the focus + // here doesn't work, so we delay it to an idle event. + canvas()->Bind( wxEVT_IDLE, &ZONE_FILLER_TOOL::singleShotRefocus, this ); + + return 0; +} + + int ZONE_FILLER_TOOL::ZoneFill( const TOOL_EVENT& aEvent ) { if( m_fillInProgress ) @@ -216,7 +317,7 @@ int ZONE_FILLER_TOOL::ZoneFill( const TOOL_EVENT& aEvent ) if( filler.Fill( toFill ) ) { reporter->AdvancePhase(); - commit.Push( _( "Fill Zone(s)" ), true, true, false ); + commit.Push( _( "Fill Zone(s)" ), true, true, false, true ); } else { @@ -253,7 +354,7 @@ int ZONE_FILLER_TOOL::ZoneUnfill( const TOOL_EVENT& aEvent ) zone->UnFill(); } - commit.Push( _( "Unfill Zone" ) ); + commit.Push( _( "Unfill Zone" ), true, true, true, true ); canvas()->Refresh(); return 0; @@ -271,7 +372,7 @@ int ZONE_FILLER_TOOL::ZoneUnfillAll( const TOOL_EVENT& aEvent ) zone->UnFill(); } - commit.Push( _( "Unfill All Zones" ) ); + commit.Push( _( "Unfill All Zones" ), true, true, true, true ); canvas()->Refresh(); return 0; @@ -281,8 +382,9 @@ int ZONE_FILLER_TOOL::ZoneUnfillAll( const TOOL_EVENT& aEvent ) void ZONE_FILLER_TOOL::setTransitions() { // Zone actions - Go( &ZONE_FILLER_TOOL::ZoneFill, PCB_ACTIONS::zoneFill.MakeEvent() ); - Go( &ZONE_FILLER_TOOL::ZoneFillAll, PCB_ACTIONS::zoneFillAll.MakeEvent() ); - Go( &ZONE_FILLER_TOOL::ZoneUnfill, PCB_ACTIONS::zoneUnfill.MakeEvent() ); - Go( &ZONE_FILLER_TOOL::ZoneUnfillAll, PCB_ACTIONS::zoneUnfillAll.MakeEvent() ); + Go( &ZONE_FILLER_TOOL::ZoneFill, PCB_ACTIONS::zoneFill.MakeEvent() ); + Go( &ZONE_FILLER_TOOL::ZoneFillAll, PCB_ACTIONS::zoneFillAll.MakeEvent() ); + Go( &ZONE_FILLER_TOOL::ZoneFillDirty, PCB_ACTIONS::zoneFillDirty.MakeEvent() ); + Go( &ZONE_FILLER_TOOL::ZoneUnfill, PCB_ACTIONS::zoneUnfill.MakeEvent() ); + Go( &ZONE_FILLER_TOOL::ZoneUnfillAll, PCB_ACTIONS::zoneUnfillAll.MakeEvent() ); } diff --git a/pcbnew/tools/zone_filler_tool.h b/pcbnew/tools/zone_filler_tool.h index 0a70295b40..10ea6186d8 100644 --- a/pcbnew/tools/zone_filler_tool.h +++ b/pcbnew/tools/zone_filler_tool.h @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2014 CERN - * Copyright (C) 2021 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2021-2022 KiCad Developers, see AUTHORS.txt for contributors. * * @author Maciej Suminski * @@ -28,6 +28,7 @@ #define ZONE_FILLER_TOOL_H #include +#include class PCB_EDIT_FRAME; @@ -52,11 +53,17 @@ public: int ZoneFill( const TOOL_EVENT& aEvent ); int ZoneFillAll( const TOOL_EVENT& aEvent ); + int ZoneFillDirty( const TOOL_EVENT& aEvent ); int ZoneUnfill( const TOOL_EVENT& aEvent ); int ZoneUnfillAll( const TOOL_EVENT& aEvent ); bool IsBusy() { return m_fillInProgress; } + void DirtyZone( ZONE* aZone ) + { + m_dirtyZoneIDs.insert( aZone->m_Uuid ); + } + private: ///< Refocus on an idle event (used after the Progress Reporter messes up the focus). void singleShotRefocus( wxIdleEvent& ); @@ -66,6 +73,8 @@ private: private: bool m_fillInProgress; + + std::set m_dirtyZoneIDs; }; #endif diff --git a/qa/qa_utils/mocks.cpp b/qa/qa_utils/mocks.cpp index 8e74d655f3..08d9fa29bc 100644 --- a/qa/qa_utils/mocks.cpp +++ b/qa/qa_utils/mocks.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -619,6 +620,28 @@ bool PCB_TOOL_BASE::Is45Limited() const } +ZONE_FILLER_TOOL::ZONE_FILLER_TOOL() : + PCB_TOOL_BASE( "pcbnew.ZoneFiller" ), + m_fillInProgress( false ) +{ +} + + +ZONE_FILLER_TOOL::~ZONE_FILLER_TOOL() +{ +} + + +void ZONE_FILLER_TOOL::Reset( RESET_REASON aReason ) +{ +} + + +void ZONE_FILLER_TOOL::setTransitions() +{ +} + + PCBNEW_SETTINGS::DISPLAY_OPTIONS& PCB_TOOL_BASE::displayOptions() const { static PCBNEW_SETTINGS::DISPLAY_OPTIONS disp;