WYSISYG custom pad editor.

Fixes https://gitlab.com/kicad/code/kicad/issues/2153

Fixes https://gitlab.com/kicad/code/kicad/issues/2305
This commit is contained in:
Jeff Young 2020-06-27 12:57:40 +01:00
parent 129c16cf7d
commit da2b7071b4
21 changed files with 532 additions and 556 deletions

View File

@ -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

View File

@ -77,7 +77,7 @@ public:
* Returns the set of currently active layers.
* @return The set of currently active layers.
*/
const std::set<unsigned int> GetActiveLayers()
const std::set<unsigned int> GetActiveLayers() const
{
return m_activeLayers;
}

View File

@ -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

View File

@ -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

View File

@ -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<wxPoint> 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] );

View File

@ -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<wxPoint> 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 );

View File

@ -291,7 +291,7 @@ public:
* Add to the basic shape list
*/
void AddPrimitives( const std::vector<std::shared_ptr<DRAWSEGMENT>>& aPrimitivesList );
void AddPrimitive( DRAWSEGMENT* aPrimitive );
/**
* Function SetOrientation

View File

@ -42,7 +42,7 @@
#include <view/view_controls.h>
#include <widgets/net_selector.h>
#include <tool/tool_manager.h>
#include <tools/footprint_editor_tools.h>
#include <tools/pad_tool.h>
#include <advanced_config.h> // 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<FOOTPRINT_EDITOR_TOOLS>();
PAD_TOOL* padTools = m_toolManager->GetTool<PAD_TOOL>();
if( fpTools )
fpTools->SetLastPadName( aPad->GetName() );
if( padTools )
padTools->SetLastPadName( aPad->GetName() );
}
}
}

View File

@ -26,13 +26,9 @@
#include <class_board.h>
#include <class_edge_mod.h>
#include <class_module.h>
#include <collectors.h>
#include <config_params.h>
#include <confirm.h>
#include <dialog_create_array.h>
#include <dialog_edit_footprint_for_fp_editor.h>
#include <dialog_move_exact.h>
#include <fctsys.h>
#include <footprint_edit_frame.h>
#include <footprint_tree_pane.h>
#include <footprint_viewer_frame.h>
@ -40,28 +36,18 @@
#include <fp_lib_table.h>
#include <functional>
#include <gestfich.h>
#include <invoke_pcb_dialog.h>
#include <kiface_i.h>
#include <kiway.h>
#include <kiway_express.h>
#include <macros.h>
#include <menus_helpers.h>
#include <pcb_draw_panel_gal.h>
#include <pcb_layer_box_selector.h>
#include <pcb_layer_widget.h>
#include <pcbnew.h>
#include <pcbnew_id.h>
#include <pcbnew_settings.h>
#include <ratsnest/ratsnest_data.h>
#include <pgm_base.h>
#include <settings/color_settings.h>
#include <tool/tool_manager.h>
#include <tools/pcb_actions.h>
#include <trigo.h>
#include <view/view.h>
#include <widgets/lib_tree.h>
#include <wildcards_and_files_ext.h>
using namespace std::placeholders;

View File

@ -163,6 +163,14 @@ void D_PAD::AddPrimitives( const std::vector<std::shared_ptr<DRAWSEGMENT>>& 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 )
{

View File

@ -25,14 +25,12 @@
*/
#include <limits>
#include <class_board.h>
#include <class_module.h>
#include <class_edge_mod.h>
#include <collectors.h>
#include <pcb_edit_frame.h>
#include <ws_proxy_view_item.h>
#include <kiway.h>
#include <footprint_edit_frame.h>
#include <array_creator.h>
#include <pcbnew_settings.h>
#include <status_popup.h>
@ -43,7 +41,7 @@
#include <tools/pcbnew_picker_tool.h>
#include <tools/tool_event_utils.h>
#include <tools/grid_helper.h>
#include <tools/footprint_editor_tools.h>
#include <tools/pad_tool.h>
#include <pad_naming.h>
#include <view/view_controls.h>
#include <connectivity/connectivity_data.h>
@ -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<D_PAD*>( dupe_item ) ) )
{
FOOTPRINT_EDITOR_TOOLS* modEdit = m_toolMgr->GetTool<FOOTPRINT_EDITOR_TOOLS>();
wxString padName = modEdit->GetLastPadName();
PAD_TOOL* padTool = m_toolMgr->GetTool<PAD_TOOL>();
wxString padName = padTool->GetLastPadName();
padName = editModule->GetNextPadName( padName );
modEdit->SetLastPadName( padName );
padTool->SetLastPadName( padName );
static_cast<D_PAD*>( 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<MODULE>();
if( !mod )
return 0;
PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
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() );
}

View File

@ -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;

View File

@ -24,21 +24,16 @@
*/
#include "footprint_editor_tools.h"
#include <pad_naming.h>
#include "kicad_clipboard.h"
#include "selection_tool.h"
#include <core/optional.h>
#include <tool/tool_manager.h>
#include <tools/pcb_actions.h>
#include <view/view_controls.h>
#include <pcb_painter.h>
#include <footprint_edit_frame.h>
#include <pcbnew_id.h>
#include <confirm.h>
#include <bitmaps.h>
#include <class_module.h>
#include <class_edge_mod.h>
#include <board_commit.h>
#include <project.h>
#include <fp_lib_table.h>
#include <dialogs/dialog_cleanup_graphics.h>
@ -59,9 +54,6 @@ FOOTPRINT_EDITOR_TOOLS::~FOOTPRINT_EDITOR_TOOLS()
void FOOTPRINT_EDITOR_TOOLS::Reset( RESET_REASON aReason )
{
m_frame = getEditFrame<FOOTPRINT_EDIT_FRAME>();
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<BOARD_ITEM> 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<BOARD_ITEM>( pad );
}
bool PlaceItem( BOARD_ITEM *aItem, BOARD_COMMIT& aCommit ) override
{
D_PAD* pad = dynamic_cast<D_PAD*>( 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<SELECTION_TOOL>()->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<D_PAD*>( selection[0] );
if( pad->GetShape() != PAD_SHAPE_CUSTOM )
return 0;
commit.Modify( pad );
wxPoint anchor = pad->GetPosition();
for( const std::shared_ptr<DRAWSEGMENT>& 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<SELECTION_TOOL>()->GetSelection();
std::unique_ptr<D_PAD> pad( new D_PAD( board()->GetFirstModule() ) );
D_PAD *refPad = nullptr;
bool multipleRefPadsFound = false;
bool illegalItemsFound = false;
std::vector<std::shared_ptr<DRAWSEGMENT>> 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<D_PAD*>( item );
break;
}
case PCB_MODULE_EDGE_T:
{
EDGE_MODULE* em = static_cast<EDGE_MODULE*>( 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<D_PAD*>( 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<VECTOR2I> 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<DRAWSEGMENT>& 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<FOOTPRINT_EDIT_FRAME>();
@ -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() );
}

View File

@ -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<MODULE> m_copiedModule;
std::unique_ptr<MODULE> m_copiedModule;
};
#endif

View File

@ -30,19 +30,21 @@
#include <bitmaps.h>
#include <class_board_item.h>
#include <class_module.h>
#include <class_edge_mod.h>
#include <board_commit.h>
#include <dialogs/dialog_push_pad_properties.h>
#include <tools/pcb_actions.h>
#include <tools/selection_tool.h>
#include <tools/pcb_selection_conditions.h>
#include <tools/edit_tool.h>
#include <tools/footprint_editor_tools.h>
#include <dialogs/dialog_enum_pads.h>
#include <pad_naming.h>
#include <widgets/infobar.h>
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<FOOTPRINT_EDITOR_TOOLS>();
KIGFX::VIEW* view = m_toolMgr->GetView();
VECTOR2I oldCursorPos; // store the previous mouse cursor position, during mouse drag
std::list<D_PAD*> 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<BOARD_ITEM> 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<BOARD_ITEM>( pad );
}
bool PlaceItem( BOARD_ITEM *aItem, BOARD_COMMIT& aCommit ) override
{
D_PAD* pad = dynamic_cast<D_PAD*>( 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<SELECTION_TOOL>()->GetSelection();
wxString msg;
if( m_editPad != niluuid )
{
D_PAD* pad = dynamic_cast<D_PAD*>( 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<D_PAD*>( 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<DRAWSEGMENT>& 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<DRAWSEGMENT*>( 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() );
}

View File

@ -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

View File

@ -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
//

View File

@ -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;

View File

@ -27,6 +27,7 @@
#include "pcb_actions.h"
#include "pcbnew_picker_tool.h"
#include "selection_tool.h"
#include "edit_tool.h"
#include <bitmaps.h>
#include <board_commit.h>
#include <class_board.h>
@ -57,6 +58,7 @@
#include <view/view_group.h>
#include <wildcards_and_files_ext.h>
#include <ws_proxy_undo_item.h>
#include <footprint_edit_frame.h>
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<SELECTION_TOOL>();
const auto& selection = selTool->GetSelection();
SELECTION_TOOL* selTool = m_toolMgr->GetTool<SELECTION_TOOL>();
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<SELECTION_TOOL>();
const SELECTION& selection = selTool->RequestSelection( EDIT_TOOL::FootprintFilter );
if( selection.Empty() )
return 0;
MODULE* mod = selection.FirstOfKind<MODULE>();
if( !mod )
return 0;
PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
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() );

View File

@ -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.

View File

@ -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<unsigned int> 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<unsigned int> 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;
}