diff --git a/include/eda_base_frame.h b/include/eda_base_frame.h index 9e538fb4c9..b894a1a6d4 100644 --- a/include/eda_base_frame.h +++ b/include/eda_base_frame.h @@ -278,6 +278,8 @@ public: void PrintMsg( const wxString& text ); + WX_INFOBAR* GetInfoBar() { return m_infoBar; } + /** * Returns the settings object used in SaveSettings(), and is overloaded in * KICAD_MANAGER_FRAME diff --git a/include/render_settings.h b/include/render_settings.h index 2d7537aa85..e5fa3efe29 100644 --- a/include/render_settings.h +++ b/include/render_settings.h @@ -77,7 +77,7 @@ public: * Returns the set of currently active layers. * @return The set of currently active layers. */ - const std::set GetActiveLayers() + const std::set GetActiveLayers() const { return m_activeLayers; } diff --git a/libs/kimath/include/geometry/shape_poly_set.h b/libs/kimath/include/geometry/shape_poly_set.h index 1e282cc8a5..a0c081fc5e 100644 --- a/libs/kimath/include/geometry/shape_poly_set.h +++ b/libs/kimath/include/geometry/shape_poly_set.h @@ -831,6 +831,12 @@ class SHAPE_POLY_SET : public SHAPE return IterateSegments( aOutline, aOutline, true ); } + ///> Returns an iterator object, for the aOutline-th outline in the set (with holes) + CONST_SEGMENT_ITERATOR CIterateSegmentsWithHoles() const + { + return CIterateSegments( 0, OutlineCount() - 1, true ); + } + ///> Returns an iterator object, for the aOutline-th outline in the set (with holes) CONST_SEGMENT_ITERATOR CIterateSegmentsWithHoles( int aOutline ) const { @@ -1026,7 +1032,7 @@ class SHAPE_POLY_SET : public SHAPE * @return bool - true if there is a collision, false in any other case. */ bool CollideVertex( const VECTOR2I& aPoint, VERTEX_INDEX& aClosestVertex, - int aClearance = 0 ); + int aClearance = 0 ) const; /** * Function CollideEdge @@ -1039,7 +1045,7 @@ class SHAPE_POLY_SET : public SHAPE * @return bool - true if there is a collision, false in any other case. */ bool CollideEdge( const VECTOR2I& aPoint, VERTEX_INDEX& aClosestVertex, - int aClearance = 0 ); + int aClearance = 0 ) const; /** * Constructs BBoxCaches for Contains(), below. These caches MUST be built before a diff --git a/libs/kimath/src/geometry/shape_poly_set.cpp b/libs/kimath/src/geometry/shape_poly_set.cpp index 1e59064ca9..6e51a0cf6b 100644 --- a/libs/kimath/src/geometry/shape_poly_set.cpp +++ b/libs/kimath/src/geometry/shape_poly_set.cpp @@ -1243,9 +1243,9 @@ bool SHAPE_POLY_SET::Collide( const SEG& aSeg, int aClearance ) const if( polySet.Contains( aSeg.A ) ) return true; - for( SEGMENT_ITERATOR it = ( (SHAPE_POLY_SET*) this )->IterateSegmentsWithHoles(); it; it++ ) + for( CONST_SEGMENT_ITERATOR it = CIterateSegmentsWithHoles(); it; it++ ) { - SEG polygonEdge = *it; + const SEG polygonEdge = *it; if( polygonEdge.Intersect( aSeg, true ) ) return true; @@ -1359,7 +1359,8 @@ void SHAPE_POLY_SET::Append( const VECTOR2I& aP, int aOutline, int aHole ) bool SHAPE_POLY_SET::CollideVertex( const VECTOR2I& aPoint, - SHAPE_POLY_SET::VERTEX_INDEX& aClosestVertex, int aClearance ) + SHAPE_POLY_SET::VERTEX_INDEX& aClosestVertex, + int aClearance ) const { // Shows whether there was a collision bool collision = false; @@ -1371,7 +1372,7 @@ bool SHAPE_POLY_SET::CollideVertex( const VECTOR2I& aPoint, // Convert clearance to double for precission when comparing distances clearance = aClearance; - for( ITERATOR iterator = IterateWithHoles(); iterator; iterator++ ) + for( CONST_ITERATOR iterator = CIterateWithHoles(); iterator; iterator++ ) { // Get the difference vector between current vertex and aPoint delta = *iterator - aPoint; @@ -1397,16 +1398,15 @@ bool SHAPE_POLY_SET::CollideVertex( const VECTOR2I& aPoint, bool SHAPE_POLY_SET::CollideEdge( const VECTOR2I& aPoint, - SHAPE_POLY_SET::VERTEX_INDEX& aClosestVertex, int aClearance ) + SHAPE_POLY_SET::VERTEX_INDEX& aClosestVertex, + int aClearance ) const { // Shows whether there was a collision bool collision = false; - SEGMENT_ITERATOR iterator; - - for( iterator = IterateSegmentsWithHoles(); iterator; iterator++ ) + for( CONST_SEGMENT_ITERATOR iterator = CIterateSegmentsWithHoles(); iterator; iterator++ ) { - SEG currentSegment = *iterator; + const SEG currentSegment = *iterator; int distance = currentSegment.Distance( aPoint ); // Check for collisions diff --git a/pcbnew/board_items_to_polygon_shape_transform.cpp b/pcbnew/board_items_to_polygon_shape_transform.cpp index ab37e9d749..722f599128 100644 --- a/pcbnew/board_items_to_polygon_shape_transform.cpp +++ b/pcbnew/board_items_to_polygon_shape_transform.cpp @@ -367,7 +367,10 @@ void DRAWSEGMENT::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerB switch( m_Shape ) { case S_CIRCLE: - TransformRingToPolygon( aCornerBuffer, GetCenter(), GetRadius(), aError, width ); + if( width == 0 ) + TransformCircleToPolygon( aCornerBuffer, GetCenter(), GetRadius() + width / 2, aError ); + else + TransformRingToPolygon( aCornerBuffer, GetCenter(), GetRadius(), aError, width ); break; case S_RECT: @@ -375,13 +378,17 @@ void DRAWSEGMENT::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerB std::vector pts; GetRectCorners( &pts ); - aCornerBuffer.NewOutline(); - - for( const wxPoint& pt : pts ) - aCornerBuffer.Append( pt ); - - if( width != 0 ) // Add thick outlines + if( width == 0 ) { + aCornerBuffer.NewOutline(); + + for( const wxPoint& pt : pts ) + aCornerBuffer.Append( pt ); + } + + if( width > 0 ) + { + // Add in segments TransformSegmentToPolygon( aCornerBuffer, pts[0], pts[1], aError, width ); TransformSegmentToPolygon( aCornerBuffer, pts[1], pts[2], aError, width ); TransformSegmentToPolygon( aCornerBuffer, pts[2], pts[3], aError, width ); @@ -420,27 +427,15 @@ void DRAWSEGMENT::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerB point += offset; } - // If the polygon is not filled, treat it as a closed set of lines - if( !IsPolygonFilled() ) + if( IsPolygonFilled() || width == 0 ) { - for( size_t ii = 1; ii < poly.size(); ii++ ) - TransformOvalToPolygon( aCornerBuffer, poly[ii-1], poly[ii], width, aError ); + aCornerBuffer.NewOutline(); - TransformOvalToPolygon( aCornerBuffer, poly.back(), poly.front(), width, aError ); - break; + for( wxPoint& point : poly ) + aCornerBuffer.Append( point.x, point.y ); } - // Generate polygons for the outline + clearance - // This code is compatible with a polygon with holes linked to external outline - // by overlapping segments. - - // Insert the initial polygon: - aCornerBuffer.NewOutline(); - - for( wxPoint& point : poly ) - aCornerBuffer.Append( point.x, point.y ); - - if( width != 0 ) // Add thick outlines + if( width > 0 ) { wxPoint pt1( poly[ poly.size() - 1] ); diff --git a/pcbnew/class_drawsegment.cpp b/pcbnew/class_drawsegment.cpp index a44c2def8e..5b4f46b330 100644 --- a/pcbnew/class_drawsegment.cpp +++ b/pcbnew/class_drawsegment.cpp @@ -586,6 +586,26 @@ bool DRAWSEGMENT::HitTest( const wxPoint& aPosition, int aAccuracy ) const switch( m_Shape ) { case S_CIRCLE: + { + int radius = GetRadius(); + int dist = KiROUND( EuclideanNorm( aPosition - GetCenter() ) ); + + if( m_Width == 0 ) + { + // Filled circle hit-test + if( dist <= radius + maxdist ) + return true; + } + + if( m_Width > 0 ) + { + // Ring hit-test + if( abs( radius - dist ) <= maxdist ) + return true; + } + } + break; + case S_ARC: { wxPoint relPos = aPosition - GetCenter(); @@ -594,9 +614,6 @@ bool DRAWSEGMENT::HitTest( const wxPoint& aPosition, int aAccuracy ) const if( abs( radius - dist ) <= maxdist ) { - if( m_Shape == S_CIRCLE ) - return true; - // For arcs, the test point angle must be >= arc angle start // and <= arc angle end // However angle values > 360 deg are not easy to handle @@ -649,12 +666,27 @@ bool DRAWSEGMENT::HitTest( const wxPoint& aPosition, int aAccuracy ) const std::vector pts; GetRectCorners( &pts ); - if( TestSegmentHit( aPosition, pts[0], pts[1], maxdist ) - || TestSegmentHit( aPosition, pts[1], pts[2], maxdist ) - || TestSegmentHit( aPosition, pts[2], pts[3], maxdist ) - || TestSegmentHit( aPosition, pts[3], pts[0], maxdist ) ) + if( m_Width == 0 ) { - return true; + SHAPE_POLY_SET poly; + poly.NewOutline(); + + for( const wxPoint& pt : pts ) + poly.Append( pt ); + + if( poly.Collide( VECTOR2I( aPosition ), maxdist ) ) + return true; + } + + if( m_Width > 0 ) + { + if( TestSegmentHit( aPosition, pts[0], pts[1], maxdist ) + || TestSegmentHit( aPosition, pts[1], pts[2], maxdist ) + || TestSegmentHit( aPosition, pts[2], pts[3], maxdist ) + || TestSegmentHit( aPosition, pts[3], pts[0], maxdist ) ) + { + return true; + } } } break; @@ -663,10 +695,8 @@ bool DRAWSEGMENT::HitTest( const wxPoint& aPosition, int aAccuracy ) const { if( !IsPolygonFilled() ) { - SHAPE_POLY_SET::VERTEX_INDEX i; - auto poly = m_Poly; //todo: Fix CollideEdge to be const - return poly.CollideEdge( VECTOR2I( aPosition ), i, - std::max( maxdist, Millimeter2iu( 0.25 ) ) ); + SHAPE_POLY_SET::VERTEX_INDEX dummy; + return m_Poly.CollideEdge( VECTOR2I( aPosition ), dummy, maxdist ); } else return m_Poly.Collide( VECTOR2I( aPosition ), maxdist ); diff --git a/pcbnew/class_pad.h b/pcbnew/class_pad.h index 5612a6a386..c16c9901c2 100644 --- a/pcbnew/class_pad.h +++ b/pcbnew/class_pad.h @@ -291,7 +291,7 @@ public: * Add to the basic shape list */ void AddPrimitives( const std::vector>& aPrimitivesList ); - + void AddPrimitive( DRAWSEGMENT* aPrimitive ); /** * Function SetOrientation diff --git a/pcbnew/dialogs/dialog_pad_properties.cpp b/pcbnew/dialogs/dialog_pad_properties.cpp index 4e148d7152..97a6950479 100644 --- a/pcbnew/dialogs/dialog_pad_properties.cpp +++ b/pcbnew/dialogs/dialog_pad_properties.cpp @@ -42,7 +42,7 @@ #include #include #include -#include +#include #include // for pad property feature management @@ -106,10 +106,10 @@ void PCB_BASE_FRAME::InstallPadOptionsFrame( D_PAD* aPad ) // to set the default pad setup if( aPad ) { - FOOTPRINT_EDITOR_TOOLS* fpTools = m_toolManager->GetTool(); + PAD_TOOL* padTools = m_toolManager->GetTool(); - if( fpTools ) - fpTools->SetLastPadName( aPad->GetName() ); + if( padTools ) + padTools->SetLastPadName( aPad->GetName() ); } } } diff --git a/pcbnew/footprint_editor_utils.cpp b/pcbnew/footprint_editor_utils.cpp index d3cb45d223..457f81e8f1 100644 --- a/pcbnew/footprint_editor_utils.cpp +++ b/pcbnew/footprint_editor_utils.cpp @@ -26,13 +26,9 @@ #include #include #include -#include -#include #include #include #include -#include -#include #include #include #include @@ -40,28 +36,18 @@ #include #include #include -#include -#include #include #include -#include -#include -#include #include #include -#include #include -#include #include - #include #include #include #include #include -#include #include -#include using namespace std::placeholders; diff --git a/pcbnew/pad_custom_shape_functions.cpp b/pcbnew/pad_custom_shape_functions.cpp index cbbface32d..4149d52f40 100644 --- a/pcbnew/pad_custom_shape_functions.cpp +++ b/pcbnew/pad_custom_shape_functions.cpp @@ -163,6 +163,14 @@ void D_PAD::AddPrimitives( const std::vector>& aPri } +void D_PAD::AddPrimitive( DRAWSEGMENT* aPrimitive ) +{ + m_editPrimitives.emplace_back( aPrimitive ); + + m_shapesDirty = true; +} + + // clear the basic shapes list and associated data void D_PAD::DeletePrimitivesList() { @@ -190,9 +198,7 @@ void D_PAD::addPadPrimitivesToPolygon( SHAPE_POLY_SET* aMergedPolygon, int aErro converter.GetPoly( poly, lineWidth ); for( unsigned ii = 1; ii < poly.size(); ii++ ) - { TransformSegmentToPolygon( polyset, poly[ ii - 1 ], poly[ ii ], aError, lineWidth ); - } break; } @@ -210,14 +216,14 @@ void D_PAD::addPadPrimitivesToPolygon( SHAPE_POLY_SET* aMergedPolygon, int aErro break; } - case S_CIRCLE: // ring or circle + case S_CIRCLE: { - if( primitive->GetWidth() ) // ring + if( lineWidth ) // Ring { TransformRingToPolygon( polyset, primitive->GetStart(), primitive->GetRadius(), aError, lineWidth ); } - else // Filled circle + else // Filled circle { TransformCircleToPolygon( polyset, primitive->GetStart(), primitive->GetRadius(), aError ); @@ -226,23 +232,39 @@ void D_PAD::addPadPrimitivesToPolygon( SHAPE_POLY_SET* aMergedPolygon, int aErro } case S_RECT: - case S_POLYGON: // polygon + { + wxPoint corners[4]; + + corners[0] = primitive->GetStart(); + corners[1] = wxPoint( primitive->GetEnd().x, primitive->GetStart().y ); + corners[2] = primitive->GetEnd(); + corners[3] = wxPoint( primitive->GetStart().x, primitive->GetEnd().y ); + + if( lineWidth ) // Rect boundary + { + TransformSegmentToPolygon( polyset, corners[0], corners[1], aError, lineWidth ); + TransformSegmentToPolygon( polyset, corners[1], corners[2], aError, lineWidth ); + TransformSegmentToPolygon( polyset, corners[2], corners[3], aError, lineWidth ); + TransformSegmentToPolygon( polyset, corners[3], corners[0], aError, lineWidth ); + } + else // Filled rect + { + // Insert the polygon: + polyset.NewOutline(); + + for( const wxPoint& corner : corners ) + polyset.Append( corner ); + } + } + break; + + case S_POLYGON: { SHAPE_POLY_SET poly; poly.NewOutline(); - if( primitive->GetShape() == S_RECT ) - { - poly.Append( primitive->GetStart() ); - poly.Append( primitive->GetEnd().x, primitive->GetStart().y ); - poly.Append( primitive->GetEnd() ); - poly.Append( primitive->GetStart().x, primitive->GetEnd().y ); - } - else - { - for( const VECTOR2I& pt : primitive->GetPolyShape().Outline( 0 ).CPoints() ) - poly.Append( pt ); - } + for( const VECTOR2I& pt : primitive->GetPolyShape().Outline( 0 ).CPoints() ) + poly.Append( pt ); if( primitive->GetWidth() > 0 ) { diff --git a/pcbnew/tools/edit_tool.cpp b/pcbnew/tools/edit_tool.cpp index eb0afc8a0c..80af314571 100644 --- a/pcbnew/tools/edit_tool.cpp +++ b/pcbnew/tools/edit_tool.cpp @@ -25,14 +25,12 @@ */ #include -#include #include #include #include #include #include #include -#include #include #include #include @@ -43,7 +41,7 @@ #include #include #include -#include +#include #include #include #include @@ -210,7 +208,7 @@ bool EDIT_TOOL::Init() // Footprint actions menu.AddSeparator(); - menu.AddItem( PCB_ACTIONS::editFootprintInFpEditor, singleModuleCondition ); + menu.AddItem( PCB_ACTIONS::editFpInFpEditor, singleModuleCondition ); menu.AddItem( PCB_ACTIONS::updateFootprint, singleModuleCondition ); menu.AddItem( PCB_ACTIONS::changeFootprint, singleModuleCondition ); @@ -1200,10 +1198,10 @@ int EDIT_TOOL::Duplicate( const TOOL_EVENT& aEvent ) if( increment && item->Type() == PCB_PAD_T && PAD_NAMING::PadCanHaveName( *static_cast( dupe_item ) ) ) { - FOOTPRINT_EDITOR_TOOLS* modEdit = m_toolMgr->GetTool(); - wxString padName = modEdit->GetLastPadName(); + PAD_TOOL* padTool = m_toolMgr->GetTool(); + wxString padName = padTool->GetLastPadName(); padName = editModule->GetNextPadName( padName ); - modEdit->SetLastPadName( padName ); + padTool->SetLastPadName( padName ); static_cast( dupe_item )->SetName( padName ); } } @@ -1341,34 +1339,6 @@ bool EDIT_TOOL::updateModificationPoint( PCBNEW_SELECTION& aSelection ) } -int EDIT_TOOL::EditFpInFpEditor( const TOOL_EVENT& aEvent ) -{ - const auto& selection = m_selectionTool->RequestSelection( FootprintFilter ); - - if( selection.Empty() ) - return 0; - - MODULE* mod = selection.FirstOfKind(); - - if( !mod ) - return 0; - - PCB_BASE_EDIT_FRAME* editFrame = getEditFrame(); - - auto editor = (FOOTPRINT_EDIT_FRAME*) editFrame->Kiway().Player( FRAME_FOOTPRINT_EDITOR, true ); - - editor->Load_Module_From_BOARD( mod ); - - editor->Show( true ); - editor->Raise(); // Iconize( false ); - - if( selection.IsHover() ) - m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true ); - - return 0; -} - - bool EDIT_TOOL::pickCopyReferencePoint( VECTOR2I& aReferencePoint ) { std::string tool = "pcbnew.InteractiveEdit.selectReferencePoint"; @@ -1492,8 +1462,6 @@ void EDIT_TOOL::setTransitions() Go( &EDIT_TOOL::Mirror, PCB_ACTIONS::mirror.MakeEvent() ); Go( &EDIT_TOOL::ChangeTrackWidth, PCB_ACTIONS::changeTrackWidth.MakeEvent() ); - Go( &EDIT_TOOL::EditFpInFpEditor, PCB_ACTIONS::editFootprintInFpEditor.MakeEvent() ); - Go( &EDIT_TOOL::copyToClipboard, ACTIONS::copy.MakeEvent() ); Go( &EDIT_TOOL::cutToClipboard, ACTIONS::cut.MakeEvent() ); } diff --git a/pcbnew/tools/edit_tool.h b/pcbnew/tools/edit_tool.h index 4e9e44f308..2255d4daf8 100644 --- a/pcbnew/tools/edit_tool.h +++ b/pcbnew/tools/edit_tool.h @@ -176,8 +176,6 @@ private: ///> selected items. bool updateModificationPoint( PCBNEW_SELECTION& aSelection ); - int EditFpInFpEditor( const TOOL_EVENT& aEvent ); - bool invokeInlineRouter( int aDragMode ); bool isInteractiveDragEnabled() const; diff --git a/pcbnew/tools/footprint_editor_tools.cpp b/pcbnew/tools/footprint_editor_tools.cpp index ae3a983dfb..450a12df9d 100644 --- a/pcbnew/tools/footprint_editor_tools.cpp +++ b/pcbnew/tools/footprint_editor_tools.cpp @@ -24,21 +24,16 @@ */ #include "footprint_editor_tools.h" -#include #include "kicad_clipboard.h" -#include "selection_tool.h" -#include #include #include #include -#include #include #include #include #include #include #include -#include #include #include #include @@ -59,9 +54,6 @@ FOOTPRINT_EDITOR_TOOLS::~FOOTPRINT_EDITOR_TOOLS() void FOOTPRINT_EDITOR_TOOLS::Reset( RESET_REASON aReason ) { m_frame = getEditFrame(); - - if( aReason == MODEL_RELOAD ) - m_lastPadName = wxT( "1" ); } @@ -316,322 +308,6 @@ int FOOTPRINT_EDITOR_TOOLS::DefaultPadProperties( const TOOL_EVENT& aEvent ) } -int FOOTPRINT_EDITOR_TOOLS::PlacePad( const TOOL_EVENT& aEvent ) -{ - if( !m_frame->GetBoard()->GetFirstModule() ) - return 0; - - struct PAD_PLACER : public INTERACTIVE_PLACER_BASE - { - PAD_PLACER( FOOTPRINT_EDITOR_TOOLS* aFPEditTools ) - { - m_fpEditTools = aFPEditTools; - } - - virtual ~PAD_PLACER() - { - } - - std::unique_ptr CreateItem() override - { - D_PAD* pad = new D_PAD( m_board->GetFirstModule() ); - - pad->ImportSettingsFrom( m_frame->GetDesignSettings().m_Pad_Master ); - - if( PAD_NAMING::PadCanHaveName( *pad ) ) - { - wxString padName = m_fpEditTools->GetLastPadName(); - padName = m_board->GetFirstModule()->GetNextPadName( padName ); - pad->SetName( padName ); - m_fpEditTools->SetLastPadName( padName ); - } - - return std::unique_ptr( pad ); - } - - bool PlaceItem( BOARD_ITEM *aItem, BOARD_COMMIT& aCommit ) override - { - D_PAD* pad = dynamic_cast( aItem ); - - if( pad ) - { - m_frame->GetDesignSettings().m_Pad_Master.ImportSettingsFrom( *pad ); - pad->SetLocalCoord(); - aCommit.Add( aItem ); - return true; - } - - return false; - } - - FOOTPRINT_EDITOR_TOOLS* m_fpEditTools; - }; - - PAD_PLACER placer( this ); - - doInteractiveItemPlacement( aEvent.GetCommandStr().get(), &placer, _( "Place pad" ), - IPO_REPEAT | IPO_SINGLE_CLICK | IPO_ROTATE | IPO_FLIP ); - - return 0; -} - - -int FOOTPRINT_EDITOR_TOOLS::ExplodePadToShapes( const TOOL_EVENT& aEvent ) -{ - PCBNEW_SELECTION& selection = m_toolMgr->GetTool()->GetSelection(); - BOARD_COMMIT commit( m_frame ); - - if( selection.Size() != 1 ) - return 0; - - if( selection[0]->Type() != PCB_PAD_T ) - return 0; - - auto pad = static_cast( selection[0] ); - - if( pad->GetShape() != PAD_SHAPE_CUSTOM ) - return 0; - - commit.Modify( pad ); - - wxPoint anchor = pad->GetPosition(); - - for( const std::shared_ptr& primitive : pad->GetPrimitives() ) - { - EDGE_MODULE* ds = new EDGE_MODULE( board()->GetFirstModule() ); - - ds->SetShape( primitive->GetShape() ); - ds->SetWidth( primitive->GetWidth() ); - ds->SetStart( primitive->GetStart() ); - ds->SetEnd( primitive->GetEnd() ); - ds->SetBezControl1( primitive->GetBezControl1() ); - ds->SetBezControl2( primitive->GetBezControl2() ); - ds->SetAngle( primitive->GetAngle() ); - ds->SetPolyShape( primitive->GetPolyShape() ); - ds->SetLocalCoord(); - - // Fix an arbitray draw layer for this EDGE_MODULE - ds->SetLayer( Dwgs_User ); //pad->GetLayer() ); - ds->Move( anchor ); - ds->Rotate( anchor, pad->GetOrientation() ); - - commit.Add( ds ); - } - - pad->SetShape( pad->GetAnchorPadShape() ); - // Cleanup the pad primitives data, because the initial pad was a custom - // shaped pad, and it contains primitives, that does not exist in non custom pads, - // and can create issues later: - if( pad->GetShape() != PAD_SHAPE_CUSTOM ) // should be always the case - { - pad->DeletePrimitivesList(); - } - - commit.Push( _("Explode pad to shapes") ); - - m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true ); - - return 0; -} - - -int FOOTPRINT_EDITOR_TOOLS::CreatePadFromShapes( const TOOL_EVENT& aEvent ) -{ - PCBNEW_SELECTION& selection = m_toolMgr->GetTool()->GetSelection(); - - std::unique_ptr pad( new D_PAD( board()->GetFirstModule() ) ); - D_PAD *refPad = nullptr; - bool multipleRefPadsFound = false; - bool illegalItemsFound = false; - - std::vector> shapes; - - BOARD_COMMIT commit( m_frame ); - - for( EDA_ITEM* item : selection ) - { - switch( item->Type() ) - { - case PCB_PAD_T: - { - if( refPad ) - multipleRefPadsFound = true; - - refPad = static_cast( item ); - break; - } - - case PCB_MODULE_EDGE_T: - { - EDGE_MODULE* em = static_cast( item ); - DRAWSEGMENT* ds = new DRAWSEGMENT; - - ds->SetShape( em->GetShape() ); - ds->SetWidth( em->GetWidth() ); - ds->SetStart( em->GetStart() ); - ds->SetEnd( em->GetEnd() ); - ds->SetBezControl1( em->GetBezControl1() ); - ds->SetBezControl2( em->GetBezControl2() ); - ds->SetAngle( em->GetAngle() ); - ds->SetPolyShape( em->GetPolyShape() ); - shapes.emplace_back( ds ); - - break; - } - - default: - { - illegalItemsFound = true; - break; - } - } - } - - if( refPad && selection.Size() == 1 ) - { - // don't convert a pad into itself... - return 0; - } - - if( multipleRefPadsFound ) - { - DisplayErrorMessage( m_frame, _( "Cannot convert items to a custom-shaped pad:\n" - "selection contains more than one reference pad." ) ); - return 0; - } - - if( illegalItemsFound ) - { - DisplayErrorMessage( m_frame, _( "Cannot convert items to a custom-shaped pad:\n" - "selection contains unsupported items.\n" - "Only graphical lines, circles, arcs and polygons " - "are allowed." ) ); - return 0; - } - - double deltaAngle = 0.0; - - if( refPad && refPad->GetShape() == PAD_SHAPE_CUSTOM ) - { - // it's already a pad anchor - } - else if( refPad ) - { - pad.reset( static_cast( refPad->Clone() ) ); - - if( refPad->GetShape() == PAD_SHAPE_RECT ) - { - pad->SetAnchorPadShape( PAD_SHAPE_RECT ); - deltaAngle = 0.0; - } - else if( refPad->GetShape() == PAD_SHAPE_CIRCLE ) - { - pad->SetAnchorPadShape( PAD_SHAPE_CIRCLE ); - deltaAngle = 0.0; - } - else - { - // Create a new minimally-sized circular anchor and convert existing pad - // to a polygon primitive - pad->SetAnchorPadShape( PAD_SHAPE_CIRCLE ); - int r = refPad->GetDrillSize().x + Millimeter2iu( 0.2 ); - pad->SetSize( wxSize( r, r ) ); - pad->SetOffset( wxPoint( 0, 0 ) ); - - SHAPE_POLY_SET existingOutline; - int maxError = board()->GetDesignSettings().m_MaxError; - refPad->TransformShapeWithClearanceToPolygon( existingOutline, 0, maxError ); - - DRAWSEGMENT* shape = new DRAWSEGMENT; - shape->SetShape( S_POLYGON ); - shape->SetPolyShape( existingOutline ); - - shapes.emplace_back( shape ); - - deltaAngle = refPad->GetOrientation(); - pad->SetOrientation( 0.0 ); - } - } - else - { - // Create a default pad anchor: - pad->SetAnchorPadShape( PAD_SHAPE_CIRCLE ); - pad->SetAttribute( PAD_ATTRIB_SMD ); - pad->SetLayerSet( D_PAD::SMDMask() ); - int radius = Millimeter2iu( 0.2 ); - pad->SetSize( wxSize( radius, radius ) ); - pad->SetOrientation( 0 ); - - if( PAD_NAMING::PadCanHaveName( *pad ) ) - { - wxString padName = GetLastPadName(); - padName = board()->GetFirstModule()->GetNextPadName( padName ); - pad->SetName( padName ); - SetLastPadName( padName ); - } - } - - pad->SetShape ( PAD_SHAPE_CUSTOM ); - - OPT anchor; - VECTOR2I tmp; - - if( refPad ) - { - anchor = VECTOR2I( pad->GetPosition() ); - } - else if( pad->GetBestAnchorPosition( tmp ) ) - { - anchor = tmp; - } - - if( !anchor ) - { - DisplayErrorMessage( m_frame, _( "Cannot convert items to a custom-shaped pad:\n" - "unable to determine the anchor point position.\n" - "Consider adding a small anchor pad to the selection " - "and try again.") ); - return 0; - } - - - // relocate the shapes, they are relative to the anchor pad position - for( std::shared_ptr& shape : shapes ) - { - shape->Move( wxPoint( -anchor->x, -anchor->y ) ); - shape->Rotate( wxPoint( 0, 0 ), -deltaAngle ); - } - - pad->SetPosition( wxPoint( anchor->x, anchor->y ) ); - pad->Rotate( wxPoint( anchor->x, anchor->y ), deltaAngle ); - pad->AddPrimitives( shapes ); - pad->ClearFlags(); - - SHAPE_POLY_SET mergedPolygon; - pad->MergePrimitivesAsPolygon( &mergedPolygon ); - - if( mergedPolygon.OutlineCount() > 1 ) - { - DisplayErrorMessage( m_frame, _( "Cannot convert items to a custom-shaped pad:\n" - "selected items do not form a single solid shape.") ); - return 0; - } - - D_PAD* padPtr = pad.release(); - - commit.Add( padPtr ); - - for ( EDA_ITEM* item : selection ) - commit.Remove( item ); - - m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true ); - commit.Push(_("Create Pad from Selected Shapes") ); - m_toolMgr->RunAction( PCB_ACTIONS::selectItem, true, padPtr ); - - return 0; -} - - int FOOTPRINT_EDITOR_TOOLS::CleanupGraphics( const TOOL_EVENT& aEvent ) { FOOTPRINT_EDIT_FRAME* editFrame = getEditFrame(); @@ -669,8 +345,4 @@ void FOOTPRINT_EDITOR_TOOLS::setTransitions() Go( &FOOTPRINT_EDITOR_TOOLS::ToggleFootprintTree, PCB_ACTIONS::toggleFootprintTree.MakeEvent() ); Go( &FOOTPRINT_EDITOR_TOOLS::Properties, PCB_ACTIONS::footprintProperties.MakeEvent() ); Go( &FOOTPRINT_EDITOR_TOOLS::DefaultPadProperties, PCB_ACTIONS::defaultPadProperties.MakeEvent() ); - - Go( &FOOTPRINT_EDITOR_TOOLS::PlacePad, PCB_ACTIONS::placePad.MakeEvent() ); - Go( &FOOTPRINT_EDITOR_TOOLS::CreatePadFromShapes, PCB_ACTIONS::createPadFromShapes.MakeEvent() ); - Go( &FOOTPRINT_EDITOR_TOOLS::ExplodePadToShapes, PCB_ACTIONS::explodePadToShapes.MakeEvent() ); } diff --git a/pcbnew/tools/footprint_editor_tools.h b/pcbnew/tools/footprint_editor_tools.h index a097c5aaf0..12ab1c3b16 100644 --- a/pcbnew/tools/footprint_editor_tools.h +++ b/pcbnew/tools/footprint_editor_tools.h @@ -74,47 +74,15 @@ public: */ int DefaultPadProperties( const TOOL_EVENT& aEvent ); - /** - * Function PlacePad() - * Places a pad in module editor. - */ - int PlacePad( const TOOL_EVENT& aEvent ); - - /** - * Function CreateArray - * - * Creates an array of objects using settings from a dialog - */ - int CreateArray( TOOL_EVENT& aEvent ); - - /** - * Function CreatePadFromShapes() - * - * Creates a custom-shaped pad from a set of selected graphical shapes - */ - int CreatePadFromShapes( const TOOL_EVENT& aEvent ); - - /** - * Function ExplodePadToShapes() - * - * Breaks apart a complex-shaped part into a set of graphical shapes - */ - int ExplodePadToShapes( const TOOL_EVENT& aEvent ); - - wxString GetLastPadName() const { return m_lastPadName; } - void SetLastPadName( const wxString& aPadName ) { m_lastPadName = aPadName; } - private: ///> Sets up handlers for various events. void setTransitions() override; private: - FOOTPRINT_EDIT_FRAME* m_frame; - - wxString m_lastPadName; + FOOTPRINT_EDIT_FRAME* m_frame; // A private clipboard for cut/copy/past of an entire footprint - std::unique_ptr m_copiedModule; + std::unique_ptr m_copiedModule; }; #endif diff --git a/pcbnew/tools/pad_tool.cpp b/pcbnew/tools/pad_tool.cpp index cb4bb6c0c6..bfc200ee9f 100644 --- a/pcbnew/tools/pad_tool.cpp +++ b/pcbnew/tools/pad_tool.cpp @@ -30,19 +30,21 @@ #include #include #include +#include #include #include #include #include #include #include -#include #include - +#include +#include PAD_TOOL::PAD_TOOL() : PCB_TOOL_BASE( "pcbnew.PadTool" ), - m_padCopied( false ) + m_padCopied( false ), + m_editPad( niluuid ) {} @@ -52,7 +54,11 @@ PAD_TOOL::~PAD_TOOL() void PAD_TOOL::Reset( RESET_REASON aReason ) { + if( aReason == MODEL_RELOAD ) + m_lastPadName = wxT( "1" ); + m_padCopied = false; + m_editPad = niluuid; } @@ -69,18 +75,29 @@ bool PAD_TOOL::Init() SELECTION_CONDITION singlePadSel = SELECTION_CONDITIONS::Count( 1 ) && SELECTION_CONDITIONS::OnlyType( PCB_PAD_T ); + auto explodeCondition = [&]( const SELECTION& aSel ) + { + return m_editPad == niluuid + && aSel.Size() == 1 && aSel[0]->Type() == PCB_PAD_T; + }; + + auto recombineCondition = [&]( const SELECTION& aSel ) + { + return m_editPad != niluuid; + }; + menu.AddSeparator( 400 ); if( m_editModules ) { - menu.AddItem( PCB_ACTIONS::enumeratePads, SELECTION_CONDITIONS::ShowAlways, 400 ); - menu.AddItem( PCB_ACTIONS::createPadFromShapes, SELECTION_CONDITIONS::NotEmpty, 400 ); - menu.AddItem( PCB_ACTIONS::explodePadToShapes, singlePadSel, 400 ); + menu.AddItem( PCB_ACTIONS::enumeratePads, SELECTION_CONDITIONS::ShowAlways, 400 ); + menu.AddItem( PCB_ACTIONS::recombinePad, recombineCondition, 400 ); + menu.AddItem( PCB_ACTIONS::explodePad, explodeCondition, 400 ); } - menu.AddItem( PCB_ACTIONS::copyPadSettings, singlePadSel, 400 ); - menu.AddItem( PCB_ACTIONS::applyPadSettings, padSel, 400 ); - menu.AddItem( PCB_ACTIONS::pushPadSettings, singlePadSel, 400 ); + menu.AddItem( PCB_ACTIONS::copyPadSettings, singlePadSel, 400 ); + menu.AddItem( PCB_ACTIONS::applyPadSettings, padSel, 400 ); + menu.AddItem( PCB_ACTIONS::pushPadSettings, singlePadSel, 400 ); } return true; @@ -263,7 +280,6 @@ int PAD_TOOL::EnumeratePads( const TOOL_EVENT& aEvent ) m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true ); getViewControls()->ShowCursor( true ); - FOOTPRINT_EDITOR_TOOLS* fpTools = m_toolMgr->GetTool(); KIGFX::VIEW* view = m_toolMgr->GetView(); VECTOR2I oldCursorPos; // store the previous mouse cursor position, during mouse drag std::list selectedPads; @@ -352,7 +368,7 @@ int PAD_TOOL::EnumeratePads( const TOOL_EVENT& aEvent ) wxString newName = wxString::Format( wxT( "%s%d" ), padPrefix, newval ); oldNames[newName] = { newval, pad->GetName() }; pad->SetName( newName ); - fpTools->SetLastPadName( newName ); + SetLastPadName( newName ); pad->SetSelected(); getView()->Update( pad ); @@ -375,7 +391,7 @@ int PAD_TOOL::EnumeratePads( const TOOL_EVENT& aEvent ) { storedPadNumbers.push_back( it->second.first ); pad->SetName( it->second.second ); - fpTools->SetLastPadName( it->second.second ); + SetLastPadName( it->second.second ); oldNames.erase( it ); int newval = storedPadNumbers.front(); @@ -422,11 +438,273 @@ int PAD_TOOL::EnumeratePads( const TOOL_EVENT& aEvent ) } +int PAD_TOOL::PlacePad( const TOOL_EVENT& aEvent ) +{ + if( !board()->GetFirstModule() ) + return 0; + + struct PAD_PLACER : public INTERACTIVE_PLACER_BASE + { + PAD_PLACER( PAD_TOOL* aPadTool ) + { + m_padTool = aPadTool; + } + + virtual ~PAD_PLACER() + { + } + + std::unique_ptr CreateItem() override + { + D_PAD* pad = new D_PAD( m_board->GetFirstModule() ); + + pad->ImportSettingsFrom( m_frame->GetDesignSettings().m_Pad_Master ); + + if( PAD_NAMING::PadCanHaveName( *pad ) ) + { + wxString padName = m_padTool->GetLastPadName(); + padName = m_board->GetFirstModule()->GetNextPadName( padName ); + pad->SetName( padName ); + m_padTool->SetLastPadName( padName ); + } + + return std::unique_ptr( pad ); + } + + bool PlaceItem( BOARD_ITEM *aItem, BOARD_COMMIT& aCommit ) override + { + D_PAD* pad = dynamic_cast( aItem ); + + if( pad ) + { + m_frame->GetDesignSettings().m_Pad_Master.ImportSettingsFrom( *pad ); + pad->SetLocalCoord(); + aCommit.Add( aItem ); + return true; + } + + return false; + } + + PAD_TOOL* m_padTool; + }; + + PAD_PLACER placer( this ); + + doInteractiveItemPlacement( aEvent.GetCommandStr().get(), &placer, _( "Place pad" ), + IPO_REPEAT | IPO_SINGLE_CLICK | IPO_ROTATE | IPO_FLIP ); + + return 0; +} + + +int PAD_TOOL::EditPad( const TOOL_EVENT& aEvent ) +{ + PCB_DISPLAY_OPTIONS opts = frame()->GetDisplayOptions(); + WX_INFOBAR* infoBar = frame()->GetInfoBar(); + PCBNEW_SELECTION& selection = m_toolMgr->GetTool()->GetSelection(); + wxString msg; + + if( m_editPad != niluuid ) + { + D_PAD* pad = dynamic_cast( frame()->GetItem( m_editPad ) ); + + if( pad ) + recombinePad( pad ); + + m_editPad = niluuid; + } + else if( selection.Size() == 1 && selection[0]->Type() == PCB_PAD_T ) + { + D_PAD* pad = static_cast( selection[0] ); + PCB_LAYER_ID layer = explodePad( pad ); + + m_wasHighContrast = opts.m_ContrastModeDisplay; + frame()->SetActiveLayer( layer ); + + if( !opts.m_ContrastModeDisplay ) + m_toolMgr->RunAction( ACTIONS::highContrastMode, false ); + + if( PCB_ACTIONS::explodePad.GetHotKey() == PCB_ACTIONS::recombinePad.GetHotKey() ) + msg.Printf( _( "Pad Edit Mode. Press %s again to exit." ), + KeyNameFromKeyCode( PCB_ACTIONS::recombinePad.GetHotKey() ) ); + else + msg.Printf( _( "Pad Edit Mode. Press %s to exit." ), + KeyNameFromKeyCode( PCB_ACTIONS::recombinePad.GetHotKey() ) ); + + infoBar->RemoveAllButtons(); + infoBar->ShowMessage( msg, wxICON_INFORMATION ); + + m_editPad = pad->m_Uuid; + } + + if( m_editPad == niluuid ) + { + if( m_wasHighContrast != opts.m_ContrastModeDisplay ) + m_toolMgr->RunAction( ACTIONS::highContrastMode, false ); + + infoBar->Dismiss(); + } + + return 0; +} + + +PCB_LAYER_ID PAD_TOOL::explodePad( D_PAD* aPad ) +{ + PCB_LAYER_ID layer; + BOARD_COMMIT commit( frame() ); + + if( aPad->IsOnLayer( F_Cu ) ) + layer = F_Cu; + else if( aPad->IsOnLayer( B_Cu ) ) + layer = B_Cu; + else + layer = *aPad->GetLayerSet().UIOrder(); + + if( aPad->GetShape() == PAD_SHAPE_CUSTOM ) + { + commit.Modify( aPad ); + + for( const std::shared_ptr& primitive : aPad->GetPrimitives() ) + { + EDGE_MODULE* ds = new EDGE_MODULE( board()->GetFirstModule() ); + + ds->SetShape( primitive->GetShape() ); + ds->SetWidth( primitive->GetWidth() ); + ds->SetStart( primitive->GetStart() ); + ds->SetEnd( primitive->GetEnd() ); + ds->SetBezControl1( primitive->GetBezControl1() ); + ds->SetBezControl2( primitive->GetBezControl2() ); + ds->SetAngle( primitive->GetAngle() ); + ds->SetPolyShape( primitive->GetPolyShape() ); + ds->SetLocalCoord(); + ds->Move( aPad->GetPosition() ); + ds->Rotate( aPad->GetPosition(), aPad->GetOrientation() ); + + ds->SetLayer( layer ); + + commit.Add( ds ); + } + + aPad->SetShape( aPad->GetAnchorPadShape() ); + aPad->DeletePrimitivesList(); + m_editPad = aPad->m_Uuid; + } + + commit.Push( _("Edit pad shapes") ); + m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true ); + return layer; +} + + +void PAD_TOOL::recombinePad( D_PAD* aPad ) +{ + auto findNext = [&]( PCB_LAYER_ID aLayer ) -> EDGE_MODULE* + { + SHAPE_POLY_SET padPoly; + aPad->TransformShapeWithClearanceToPolygon( padPoly, 0 ); + + for( BOARD_ITEM* item : board()->GetFirstModule()->GraphicalItems() ) + { + DRAWSEGMENT* draw = dynamic_cast( item ); + + if( !draw || ( draw->GetEditFlags() & STRUCT_DELETED ) ) + continue; + + if( draw->GetLayer() != aLayer ) + continue; + + SHAPE_POLY_SET drawPoly; + draw->TransformShapeWithClearanceToPolygon( drawPoly, 0 ); + drawPoly.BooleanIntersection( padPoly, SHAPE_POLY_SET::PM_FAST ); + + if( !drawPoly.IsEmpty() ) + return (EDGE_MODULE*) item; + } + + return nullptr; + }; + + BOARD_COMMIT commit( frame() ); + PCB_LAYER_ID layer; + + if( aPad->IsOnLayer( F_Cu ) ) + layer = F_Cu; + else if( aPad->IsOnLayer( B_Cu ) ) + layer = B_Cu; + else + layer = *aPad->GetLayerSet().UIOrder(); + + while( EDGE_MODULE* edge = findNext( layer ) ) + { + commit.Modify( aPad ); + + // We've found an intersecting item. First convert the pad to a custom-shape + // pad (if it isn't already) + // + if( aPad->GetShape() == PAD_SHAPE_RECT || aPad->GetShape() == PAD_SHAPE_CIRCLE ) + { + aPad->SetAnchorPadShape( aPad->GetShape() ); + } + else if( aPad->GetShape() != PAD_SHAPE_CUSTOM ) + { + // Create a new minimally-sized circular anchor and convert existing pad + // to a polygon primitive + SHAPE_POLY_SET existingOutline; + int maxError = board()->GetDesignSettings().m_MaxError; + aPad->TransformShapeWithClearanceToPolygon( existingOutline, 0, maxError ); + + aPad->SetAnchorPadShape( PAD_SHAPE_CIRCLE ); + int r = aPad->GetDrillSize().x + Millimeter2iu( 0.2 ); + aPad->SetSize( wxSize( r, r ) ); + aPad->SetOffset( wxPoint( 0, 0 ) ); + + DRAWSEGMENT* shape = new DRAWSEGMENT; + shape->SetShape( S_POLYGON ); + shape->SetPolyShape( existingOutline ); + shape->Move( - aPad->GetPosition() ); + shape->Rotate( wxPoint( 0, 0 ), - aPad->GetOrientation() ); + + aPad->AddPrimitive( shape ); + } + + aPad->SetShape( PAD_SHAPE_CUSTOM ); + + // Now add the new shape to the primitives list + // + DRAWSEGMENT* ds = new DRAWSEGMENT; + + ds->SetShape( edge->GetShape() ); + ds->SetWidth( edge->GetWidth() ); + ds->SetStart( edge->GetStart() ); + ds->SetEnd( edge->GetEnd() ); + ds->SetBezControl1( edge->GetBezControl1() ); + ds->SetBezControl2( edge->GetBezControl2() ); + ds->SetAngle( edge->GetAngle() ); + ds->SetPolyShape( edge->GetPolyShape() ); + + ds->Move( - aPad->GetPosition() ); + ds->Rotate( wxPoint( 0, 0 ), - aPad->GetOrientation() ); + aPad->AddPrimitive( ds ); + + edge->SetFlags( STRUCT_DELETED ); + commit.Remove( edge ); + } + + commit.Push(_("Recombine pads") ); +} + + void PAD_TOOL::setTransitions() { - Go( &PAD_TOOL::pastePadProperties, PCB_ACTIONS::applyPadSettings.MakeEvent() ); - Go( &PAD_TOOL::copyPadSettings, PCB_ACTIONS::copyPadSettings.MakeEvent() ); - Go( &PAD_TOOL::pushPadSettings, PCB_ACTIONS::pushPadSettings.MakeEvent() ); + Go( &PAD_TOOL::pastePadProperties, PCB_ACTIONS::applyPadSettings.MakeEvent() ); + Go( &PAD_TOOL::copyPadSettings, PCB_ACTIONS::copyPadSettings.MakeEvent() ); + Go( &PAD_TOOL::pushPadSettings, PCB_ACTIONS::pushPadSettings.MakeEvent() ); - Go( &PAD_TOOL::EnumeratePads, PCB_ACTIONS::enumeratePads.MakeEvent() ); + Go( &PAD_TOOL::PlacePad, PCB_ACTIONS::placePad.MakeEvent() ); + Go( &PAD_TOOL::EnumeratePads, PCB_ACTIONS::enumeratePads.MakeEvent() ); + + Go( &PAD_TOOL::EditPad, PCB_ACTIONS::explodePad.MakeEvent() ); + Go( &PAD_TOOL::EditPad, PCB_ACTIONS::recombinePad.MakeEvent() ); } diff --git a/pcbnew/tools/pad_tool.h b/pcbnew/tools/pad_tool.h index 82d2e40a77..4741188ffb 100644 --- a/pcbnew/tools/pad_tool.h +++ b/pcbnew/tools/pad_tool.h @@ -52,10 +52,31 @@ public: */ int EnumeratePads( const TOOL_EVENT& aEvent ); + /** + * Function PlacePad() + * Places a pad in module editor. + */ + int PlacePad( const TOOL_EVENT& aEvent ); + + /** + * Function CreatePadFromShapes() + * + * Creates a custom-shaped pad from a set of selected graphical shapes + */ + int CreatePadFromShapes( const TOOL_EVENT& aEvent ); + + /** + * Enters/exits WYSIWYG pad shape editing + */ + int EditPad( const TOOL_EVENT& aEvent ); + + wxString GetLastPadName() const { return m_lastPadName; } + void SetLastPadName( const wxString& aPadName ) { m_lastPadName = aPadName; } + +private: ///> Bind handlers to corresponding TOOL_ACTIONs void setTransitions() override; -private: ///> Determine if there are any footprints on the board bool haveFootprints(); @@ -68,8 +89,15 @@ private: ///> Push pad settings from a pad to other pads on board or module int pushPadSettings( const TOOL_EVENT& aEvent ); - ///> Flag to indicate there are valid settings stored in the Master Pad object - bool m_padCopied; + PCB_LAYER_ID explodePad( D_PAD* aPad ); + void recombinePad( D_PAD* aPad ); + +private: + wxString m_lastPadName; + bool m_padCopied; // Indicates there are valid settings in the Master Pad object + + bool m_wasHighContrast; + KIID m_editPad; }; #endif // __PAD_TOOL_H diff --git a/pcbnew/tools/pcb_actions.cpp b/pcbnew/tools/pcb_actions.cpp index 46da7d08a0..77b60e79b1 100644 --- a/pcbnew/tools/pcb_actions.cpp +++ b/pcbnew/tools/pcb_actions.cpp @@ -179,7 +179,7 @@ TOOL_ACTION PCB_ACTIONS::runDRC( "pcbnew.DRCTool.runDRC", // EDIT_TOOL // -TOOL_ACTION PCB_ACTIONS::editFootprintInFpEditor( "pcbnew.InteractiveEdit.EditFpInFpEditor", +TOOL_ACTION PCB_ACTIONS::editFpInFpEditor( "pcbnew.EditorControl.EditFpInFpEditor", AS_GLOBAL, MD_CTRL + 'E', LEGACY_HK_NAME( "Edit with Footprint Editor" ), _( "Open in Footprint Editor" ), @@ -333,36 +333,6 @@ TOOL_ACTION PCB_ACTIONS::footprintProperties( "pcbnew.ModuleEditor.footprintProp _( "Footprint Properties..." ), "", module_options_xpm ); -TOOL_ACTION PCB_ACTIONS::placePad( "pcbnew.ModuleEditor.placePad", - AS_GLOBAL, 0, "", - _( "Add Pad" ), _( "Add a pad" ), - pad_xpm, AF_ACTIVATE ); - -TOOL_ACTION PCB_ACTIONS::createPadFromShapes( "pcbnew.ModuleEditor.createPadFromShapes", - AS_CONTEXT, 0, "", - _( "Create Pad from Selected Shapes" ), - _( "Creates a custom-shaped pads from a set of selected shapes" ), - primitives_to_custom_pad_xpm ); - -TOOL_ACTION PCB_ACTIONS::explodePadToShapes( "pcbnew.ModuleEditor.explodePadToShapes", - AS_CONTEXT, 0, "", - _( "Explode Pad to Graphic Shapes" ), - _( "Converts a custom-shaped pads to a set of graphical shapes" ), - custom_pad_to_primitives_xpm ); - -TOOL_ACTION PCB_ACTIONS::defaultPadProperties( "pcbnew.ModuleEditor.defaultPadProperties", - AS_GLOBAL, 0, "", - _( "Default Pad Properties..." ), _( "Edit the pad properties used when creating new pads" ), - options_pad_xpm ); - - -// SHAPE_EDITOR_TOOLS -// -TOOL_ACTION PCB_ACTIONS::shapeProperties( "pcbnew.ShapeEditor.shapeProperties", - AS_GLOBAL, 0, "", - _( "Shape Properties..." ), "", - options_pad_xpm ); - // GLOBAL_EDIT_TOOL // @@ -475,6 +445,30 @@ TOOL_ACTION PCB_ACTIONS::enumeratePads( "pcbnew.PadTool.enumeratePads", _( "Renumber pads by clicking on them in the desired order" ), pad_enumerate_xpm, AF_ACTIVATE ); +TOOL_ACTION PCB_ACTIONS::placePad( "pcbnew.PadTool.placePad", + AS_GLOBAL, 0, "", + _( "Add Pad" ), _( "Add a pad" ), + pad_xpm, AF_ACTIVATE ); + +TOOL_ACTION PCB_ACTIONS::explodePad( "pcbnew.PadTool.explodePad", + AS_GLOBAL, + MD_CTRL + 'E', "", + _( "Edit Pad as Graphic Shapes" ), + _( "Ungroups a custom-shaped pad for editing as individual graphic shapes" ), + custom_pad_to_primitives_xpm ); + +TOOL_ACTION PCB_ACTIONS::recombinePad( "pcbnew.PadTool.recombinePad", + AS_GLOBAL, + MD_CTRL + 'E', "", + _( "Finish Pad Edit" ), + _( "Regroups all touching graphic shapes into the edited pad" ), + custom_pad_to_primitives_xpm ); + +TOOL_ACTION PCB_ACTIONS::defaultPadProperties( "pcbnew.PadTool.defaultPadProperties", + AS_GLOBAL, 0, "", + _( "Default Pad Properties..." ), _( "Edit the pad properties used when creating new pads" ), + options_pad_xpm ); + // PCB_EDITOR_CONTROL // diff --git a/pcbnew/tools/pcb_actions.h b/pcbnew/tools/pcb_actions.h index d9d6ce421b..ab557e54e1 100644 --- a/pcbnew/tools/pcb_actions.h +++ b/pcbnew/tools/pcb_actions.h @@ -319,7 +319,7 @@ public: static TOOL_ACTION listNets; static TOOL_ACTION runDRC; - static TOOL_ACTION editFootprintInFpEditor; + static TOOL_ACTION editFpInFpEditor; static TOOL_ACTION showLayersManager; static TOOL_ACTION showMicrowaveToolbar; static TOOL_ACTION showPythonConsole; @@ -350,13 +350,12 @@ public: static TOOL_ACTION footprintProperties; static TOOL_ACTION defaultPadProperties; - static TOOL_ACTION shapeProperties; /// Activation of the drawing tool (placing a PAD) static TOOL_ACTION placePad; - static TOOL_ACTION createPadFromShapes; - static TOOL_ACTION explodePadToShapes; + static TOOL_ACTION explodePad; + static TOOL_ACTION recombinePad; /// Tool for quick pad enumeration static TOOL_ACTION enumeratePads; diff --git a/pcbnew/tools/pcb_editor_control.cpp b/pcbnew/tools/pcb_editor_control.cpp index f584acc305..6c6a4ce6f9 100644 --- a/pcbnew/tools/pcb_editor_control.cpp +++ b/pcbnew/tools/pcb_editor_control.cpp @@ -27,6 +27,7 @@ #include "pcb_actions.h" #include "pcbnew_picker_tool.h" #include "selection_tool.h" +#include "edit_tool.h" #include #include #include @@ -57,6 +58,7 @@ #include #include #include +#include using namespace std::placeholders; @@ -1077,8 +1079,8 @@ int PCB_EDITOR_CONTROL::ZoneMerge( const TOOL_EVENT& aEvent ) int PCB_EDITOR_CONTROL::ZoneDuplicate( const TOOL_EVENT& aEvent ) { - auto selTool = m_toolMgr->GetTool(); - const auto& selection = selTool->GetSelection(); + SELECTION_TOOL* selTool = m_toolMgr->GetTool(); + const SELECTION& selection = selTool->GetSelection(); // because this pops up the zone editor, it would be confusing to handle multiple zones, // so just handle single selections containing exactly one zone @@ -1126,6 +1128,35 @@ int PCB_EDITOR_CONTROL::ZoneDuplicate( const TOOL_EVENT& aEvent ) } +int PCB_EDITOR_CONTROL::EditFpInFpEditor( const TOOL_EVENT& aEvent ) +{ + SELECTION_TOOL* selTool = m_toolMgr->GetTool(); + const SELECTION& selection = selTool->RequestSelection( EDIT_TOOL::FootprintFilter ); + + if( selection.Empty() ) + return 0; + + MODULE* mod = selection.FirstOfKind(); + + if( !mod ) + return 0; + + PCB_BASE_EDIT_FRAME* editFrame = getEditFrame(); + + auto editor = (FOOTPRINT_EDIT_FRAME*) editFrame->Kiway().Player( FRAME_FOOTPRINT_EDITOR, true ); + + editor->Load_Module_From_BOARD( mod ); + + editor->Show( true ); + editor->Raise(); // Iconize( false ); + + if( selection.IsHover() ) + m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true ); + + return 0; +} + + void PCB_EDITOR_CONTROL::DoSetDrillOrigin( KIGFX::VIEW* aView, PCB_BASE_FRAME* aFrame, BOARD_ITEM* originViewItem, const VECTOR2D& aPosition ) { @@ -1203,6 +1234,8 @@ void PCB_EDITOR_CONTROL::setTransitions() Go( &PCB_EDITOR_CONTROL::PlaceModule, PCB_ACTIONS::placeModule.MakeEvent() ); Go( &PCB_EDITOR_CONTROL::DrillOrigin, PCB_ACTIONS::drillOrigin.MakeEvent() ); + Go( &PCB_EDITOR_CONTROL::EditFpInFpEditor, PCB_ACTIONS::editFpInFpEditor.MakeEvent() ); + // Other Go( &PCB_EDITOR_CONTROL::ToggleLockSelected, PCB_ACTIONS::toggleLock.MakeEvent() ); Go( &PCB_EDITOR_CONTROL::LockSelected, PCB_ACTIONS::lock.MakeEvent() ); diff --git a/pcbnew/tools/pcb_editor_control.h b/pcbnew/tools/pcb_editor_control.h index 1f8dbe13e3..b0548bb12f 100644 --- a/pcbnew/tools/pcb_editor_control.h +++ b/pcbnew/tools/pcb_editor_control.h @@ -86,6 +86,8 @@ public: ///> Duplicates a zone onto a layer (prompts for new layer) int ZoneDuplicate( const TOOL_EVENT& aEvent ); + int EditFpInFpEditor( const TOOL_EVENT& aEvent ); + /** * Function PlaceTarget() * Allows user to place a layer alignment target. diff --git a/pcbnew/tools/selection_tool.cpp b/pcbnew/tools/selection_tool.cpp index 9f28c7c324..199c07b107 100644 --- a/pcbnew/tools/selection_tool.cpp +++ b/pcbnew/tools/selection_tool.cpp @@ -463,8 +463,6 @@ bool SELECTION_TOOL::selectPoint( const VECTOR2I& aWhere, bool aOnDrag, { if( m_selection.GetSize() > 0 ) { - // Don't fire an event now as it will end up redundant if we fire a SelectedEvent - // or an UnselectedEvent. ClearSelection( true /*quiet mode*/ ); anySubtracted = true; } @@ -1525,23 +1523,20 @@ BOARD_ITEM* SELECTION_TOOL::pickSmallestComponent( GENERAL_COLLECTOR* aCollector bool SELECTION_TOOL::Selectable( const BOARD_ITEM* aItem, bool checkVisibilityOnly ) const { - // Is high contrast mode enabled? - bool highContrast = getView()->GetPainter()->GetSettings()->GetHighContrast(); + const RENDER_SETTINGS* settings = getView()->GetPainter()->GetSettings(); - int layers[KIGFX::VIEW::VIEW_MAX_LAYERS], layers_count; - - // Filter out items that do not belong to active layers - std::set activeLayers = getView()->GetPainter()->GetSettings()->GetActiveLayers(); - - aItem->ViewGetLayers( layers, layers_count ); - - if( highContrast ) + if( settings->GetHighContrast() ) { + int itemLayers[KIGFX::VIEW::VIEW_MAX_LAYERS], layers_count; + std::set activeLayers = settings->GetActiveLayers(); + + aItem->ViewGetLayers( itemLayers, layers_count ); + bool onActive = false; // Is the item on any of active layers? for( int i = 0; i < layers_count; ++i ) { - if( activeLayers.count( layers[i] ) > 0 ) // Item is on at least one of the active layers + if( activeLayers.count( itemLayers[i] ) > 0 ) // Item is on at least one of the active layers { onActive = true; break; @@ -1665,7 +1660,7 @@ bool SELECTION_TOOL::Selectable( const BOARD_ITEM* aItem, bool checkVisibilityOn // pick up items under an (unlocked) module without also moving the module's sub-parts. if( !m_editModules && !checkVisibilityOnly ) { - if( m_multiple && !highContrast ) + if( m_multiple && !settings->GetHighContrast() ) return false; }