pcbnew: Enforce maximum movement in Move Exact

This checks the movement against a maximum value that would place the
selection outside of the valid area for the footprint or board.

Fixes: lp:1833478
* https://bugs.launchpad.net/kicad/+bug/1833478
This commit is contained in:
Seth Hillbrand 2019-06-20 06:28:51 -07:00
parent f0ac61a8fd
commit 845833e8fd
9 changed files with 64 additions and 12 deletions

View File

@ -191,7 +191,7 @@ bool FOOTPRINT_EDIT_FRAME::HandleBlockEnd( wxDC* DC )
double rotation; double rotation;
ROTATION_ANCHOR rotationAnchor = ROTATE_AROUND_SEL_CENTER; ROTATION_ANCHOR rotationAnchor = ROTATE_AROUND_SEL_CENTER;
DIALOG_MOVE_EXACT dialog( this, translation, rotation, rotationAnchor ); DIALOG_MOVE_EXACT dialog( this, translation, rotation, rotationAnchor, GetScreen()->m_BlockLocate );
if( dialog.ShowModal() == wxID_OK ) if( dialog.ShowModal() == wxID_OK )
{ {

View File

@ -33,11 +33,13 @@ DIALOG_MOVE_EXACT::MOVE_EXACT_OPTIONS DIALOG_MOVE_EXACT::m_options;
DIALOG_MOVE_EXACT::DIALOG_MOVE_EXACT( PCB_BASE_FRAME *aParent, wxPoint& aTranslate, DIALOG_MOVE_EXACT::DIALOG_MOVE_EXACT( PCB_BASE_FRAME *aParent, wxPoint& aTranslate,
double& aRotate, ROTATION_ANCHOR& aAnchor ) : double& aRotate, ROTATION_ANCHOR& aAnchor,
const EDA_RECT& aBbox ) :
DIALOG_MOVE_EXACT_BASE( aParent ), DIALOG_MOVE_EXACT_BASE( aParent ),
m_translation( aTranslate ), m_translation( aTranslate ),
m_rotation( aRotate ), m_rotation( aRotate ),
m_rotationAnchor( aAnchor ), m_rotationAnchor( aAnchor ),
m_bbox( aBbox ),
m_moveX( aParent, m_xLabel, m_xEntry, m_xUnit ), m_moveX( aParent, m_xLabel, m_xEntry, m_xUnit ),
m_moveY( aParent, m_yLabel, m_yEntry, m_yUnit ), m_moveY( aParent, m_yLabel, m_yEntry, m_yUnit ),
m_rotate( aParent, m_rotLabel, m_rotEntry, m_rotUnit ) m_rotate( aParent, m_rotLabel, m_rotEntry, m_rotUnit )
@ -238,3 +240,37 @@ void DIALOG_MOVE_EXACT::OnTextFocusLost( wxFocusEvent& event )
event.Skip(); event.Skip();
} }
void DIALOG_MOVE_EXACT::OnTextChanged( wxCommandEvent& event )
{
int delta_x = m_moveX.GetValue();
int delta_y = m_moveY.GetValue();
int max_border = std::numeric_limits<int>::max() * 0.7071;
if( m_bbox.GetLeft() + delta_x < -max_border ||
m_bbox.GetRight() + delta_x > max_border ||
m_bbox.GetTop() + delta_y < -max_border ||
m_bbox.GetBottom() + delta_y > max_border )
{
const wxString invalid_length = _( "Invalid movement values. Movement would place selection "
"outside of the maximum board area." );
m_xEntry->SetToolTip( invalid_length );
m_xEntry->SetForegroundColour( *wxRED );
m_yEntry->SetToolTip( invalid_length );
m_yEntry->SetForegroundColour( *wxRED );
m_stdButtons->GetAffirmativeButton()->Disable();
}
else
{
m_xEntry->SetToolTip( "" );
m_xEntry->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOWTEXT ) );
m_yEntry->SetToolTip( "" );
m_yEntry->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOWTEXT ) );
m_stdButtons->GetAffirmativeButton()->Enable();
event.Skip();
}
}

View File

@ -49,6 +49,7 @@ private:
wxPoint& m_translation; wxPoint& m_translation;
double& m_rotation; double& m_rotation;
ROTATION_ANCHOR& m_rotationAnchor; ROTATION_ANCHOR& m_rotationAnchor;
const EDA_RECT& m_bbox;
UNIT_BINDER m_moveX; UNIT_BINDER m_moveX;
UNIT_BINDER m_moveY; UNIT_BINDER m_moveY;
@ -59,7 +60,8 @@ private:
public: public:
// Constructor and destructor // Constructor and destructor
DIALOG_MOVE_EXACT(PCB_BASE_FRAME *aParent, wxPoint& aTranslate, DIALOG_MOVE_EXACT(PCB_BASE_FRAME *aParent, wxPoint& aTranslate,
double& aRotate, ROTATION_ANCHOR& aAnchor ); double& aRotate, ROTATION_ANCHOR& aAnchor,
const EDA_RECT& aBbox);
~DIALOG_MOVE_EXACT() { }; ~DIALOG_MOVE_EXACT() { };
private: private:
@ -71,6 +73,7 @@ private:
void OnPolarChanged( wxCommandEvent& event ) override; void OnPolarChanged( wxCommandEvent& event ) override;
void OnClear( wxCommandEvent& event ) override; void OnClear( wxCommandEvent& event ) override;
void OnTextChanged( wxCommandEvent& event ) override;
// Automatically called when clicking on the OK button // Automatically called when clicking on the OK button
bool TransferDataFromWindow() override; bool TransferDataFromWindow() override;

View File

@ -1,5 +1,5 @@
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version Nov 10 2018) // C++ code generated with wxFormBuilder (version Apr 23 2019)
// http://www.wxformbuilder.org/ // http://www.wxformbuilder.org/
// //
// PLEASE DO *NOT* EDIT THIS FILE! // PLEASE DO *NOT* EDIT THIS FILE!
@ -103,8 +103,10 @@ DIALOG_MOVE_EXACT_BASE::DIALOG_MOVE_EXACT_BASE( wxWindow* parent, wxWindowID id,
// Connect Events // Connect Events
m_xEntry->Connect( wxEVT_KILL_FOCUS, wxFocusEventHandler( DIALOG_MOVE_EXACT_BASE::OnTextFocusLost ), NULL, this ); m_xEntry->Connect( wxEVT_KILL_FOCUS, wxFocusEventHandler( DIALOG_MOVE_EXACT_BASE::OnTextFocusLost ), NULL, this );
m_xEntry->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_MOVE_EXACT_BASE::OnTextChanged ), NULL, this );
m_clearX->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_MOVE_EXACT_BASE::OnClear ), NULL, this ); m_clearX->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_MOVE_EXACT_BASE::OnClear ), NULL, this );
m_yEntry->Connect( wxEVT_KILL_FOCUS, wxFocusEventHandler( DIALOG_MOVE_EXACT_BASE::OnTextFocusLost ), NULL, this ); m_yEntry->Connect( wxEVT_KILL_FOCUS, wxFocusEventHandler( DIALOG_MOVE_EXACT_BASE::OnTextFocusLost ), NULL, this );
m_yEntry->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_MOVE_EXACT_BASE::OnTextChanged ), NULL, this );
m_clearY->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_MOVE_EXACT_BASE::OnClear ), NULL, this ); m_clearY->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_MOVE_EXACT_BASE::OnClear ), NULL, this );
m_rotEntry->Connect( wxEVT_KILL_FOCUS, wxFocusEventHandler( DIALOG_MOVE_EXACT_BASE::OnTextFocusLost ), NULL, this ); m_rotEntry->Connect( wxEVT_KILL_FOCUS, wxFocusEventHandler( DIALOG_MOVE_EXACT_BASE::OnTextFocusLost ), NULL, this );
m_clearRot->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_MOVE_EXACT_BASE::OnClear ), NULL, this ); m_clearRot->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_MOVE_EXACT_BASE::OnClear ), NULL, this );
@ -115,8 +117,10 @@ DIALOG_MOVE_EXACT_BASE::~DIALOG_MOVE_EXACT_BASE()
{ {
// Disconnect Events // Disconnect Events
m_xEntry->Disconnect( wxEVT_KILL_FOCUS, wxFocusEventHandler( DIALOG_MOVE_EXACT_BASE::OnTextFocusLost ), NULL, this ); m_xEntry->Disconnect( wxEVT_KILL_FOCUS, wxFocusEventHandler( DIALOG_MOVE_EXACT_BASE::OnTextFocusLost ), NULL, this );
m_xEntry->Disconnect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_MOVE_EXACT_BASE::OnTextChanged ), NULL, this );
m_clearX->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_MOVE_EXACT_BASE::OnClear ), NULL, this ); m_clearX->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_MOVE_EXACT_BASE::OnClear ), NULL, this );
m_yEntry->Disconnect( wxEVT_KILL_FOCUS, wxFocusEventHandler( DIALOG_MOVE_EXACT_BASE::OnTextFocusLost ), NULL, this ); m_yEntry->Disconnect( wxEVT_KILL_FOCUS, wxFocusEventHandler( DIALOG_MOVE_EXACT_BASE::OnTextFocusLost ), NULL, this );
m_yEntry->Disconnect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_MOVE_EXACT_BASE::OnTextChanged ), NULL, this );
m_clearY->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_MOVE_EXACT_BASE::OnClear ), NULL, this ); m_clearY->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_MOVE_EXACT_BASE::OnClear ), NULL, this );
m_rotEntry->Disconnect( wxEVT_KILL_FOCUS, wxFocusEventHandler( DIALOG_MOVE_EXACT_BASE::OnTextFocusLost ), NULL, this ); m_rotEntry->Disconnect( wxEVT_KILL_FOCUS, wxFocusEventHandler( DIALOG_MOVE_EXACT_BASE::OnTextFocusLost ), NULL, this );
m_clearRot->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_MOVE_EXACT_BASE::OnClear ), NULL, this ); m_clearRot->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_MOVE_EXACT_BASE::OnClear ), NULL, this );

View File

@ -14,6 +14,7 @@
<property name="file">dialog_move_exact_base</property> <property name="file">dialog_move_exact_base</property>
<property name="first_id">1000</property> <property name="first_id">1000</property>
<property name="help_provider">none</property> <property name="help_provider">none</property>
<property name="image_path_wrapper_function_name"></property>
<property name="indent_with_spaces"></property> <property name="indent_with_spaces"></property>
<property name="internationalize">1</property> <property name="internationalize">1</property>
<property name="name">DIALOG_MOVE_EXACT_BASE</property> <property name="name">DIALOG_MOVE_EXACT_BASE</property>
@ -25,6 +26,7 @@
<property name="skip_php_events">1</property> <property name="skip_php_events">1</property>
<property name="skip_python_events">1</property> <property name="skip_python_events">1</property>
<property name="ui_table">UI</property> <property name="ui_table">UI</property>
<property name="use_array_enum">0</property>
<property name="use_enum">0</property> <property name="use_enum">0</property>
<property name="use_microsoft_bom">0</property> <property name="use_microsoft_bom">0</property>
<object class="Dialog" expanded="1"> <object class="Dialog" expanded="1">
@ -198,6 +200,7 @@
<property name="window_name"></property> <property name="window_name"></property>
<property name="window_style"></property> <property name="window_style"></property>
<event name="OnKillFocus">OnTextFocusLost</event> <event name="OnKillFocus">OnTextFocusLost</event>
<event name="OnText">OnTextChanged</event>
</object> </object>
</object> </object>
<object class="sizeritem" expanded="0"> <object class="sizeritem" expanded="0">
@ -458,6 +461,7 @@
<property name="window_name"></property> <property name="window_name"></property>
<property name="window_style"></property> <property name="window_style"></property>
<event name="OnKillFocus">OnTextFocusLost</event> <event name="OnKillFocus">OnTextFocusLost</event>
<event name="OnText">OnTextChanged</event>
</object> </object>
</object> </object>
<object class="sizeritem" expanded="0"> <object class="sizeritem" expanded="0">

View File

@ -1,5 +1,5 @@
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version Nov 10 2018) // C++ code generated with wxFormBuilder (version Apr 23 2019)
// http://www.wxformbuilder.org/ // http://www.wxformbuilder.org/
// //
// PLEASE DO *NOT* EDIT THIS FILE! // PLEASE DO *NOT* EDIT THIS FILE!
@ -61,6 +61,7 @@ class DIALOG_MOVE_EXACT_BASE : public DIALOG_SHIM
// Virtual event handlers, overide them in your derived class // Virtual event handlers, overide them in your derived class
virtual void OnTextFocusLost( wxFocusEvent& event ) { event.Skip(); } virtual void OnTextFocusLost( wxFocusEvent& event ) { event.Skip(); }
virtual void OnTextChanged( wxCommandEvent& event ) { event.Skip(); }
virtual void OnClear( wxCommandEvent& event ) { event.Skip(); } virtual void OnClear( wxCommandEvent& event ) { event.Skip(); }
virtual void OnPolarChanged( wxCommandEvent& event ) { event.Skip(); } virtual void OnPolarChanged( wxCommandEvent& event ) { event.Skip(); }

View File

@ -1521,13 +1521,14 @@ void PCB_EDIT_FRAME::moveExact()
wxPoint translation; wxPoint translation;
double rotation; double rotation;
ROTATION_ANCHOR rotationAnchor = ROTATE_AROUND_ITEM_ANCHOR; ROTATION_ANCHOR rotationAnchor = ROTATE_AROUND_ITEM_ANCHOR;
BOARD_ITEM* item = GetScreen()->GetCurItem();
DIALOG_MOVE_EXACT dialog( this, translation, rotation, rotationAnchor ); DIALOG_MOVE_EXACT dialog( this, translation, rotation, rotationAnchor, item->GetBoundingBox() );
int ret = dialog.ShowModal(); int ret = dialog.ShowModal();
if( ret == wxID_OK ) if( ret == wxID_OK )
{ {
if( BOARD_ITEM* item = GetScreen()->GetCurItem() ) if( item )
{ {
// When a pad is modified, the full footprint is saved // When a pad is modified, the full footprint is saved
BOARD_ITEM* itemToSave = item; BOARD_ITEM* itemToSave = item;

View File

@ -844,16 +844,16 @@ void FOOTPRINT_EDIT_FRAME::moveExact()
wxPoint translation; wxPoint translation;
double rotation; double rotation;
ROTATION_ANCHOR rotationAnchor = ROTATE_AROUND_ITEM_ANCHOR; ROTATION_ANCHOR rotationAnchor = ROTATE_AROUND_ITEM_ANCHOR;
BOARD_ITEM* item = GetScreen()->GetCurItem();
DIALOG_MOVE_EXACT dialog( this, translation, rotation, rotationAnchor ); DIALOG_MOVE_EXACT dialog( this, translation, rotation, rotationAnchor,
item->GetBoundingBox() );
int ret = dialog.ShowModal(); int ret = dialog.ShowModal();
if( ret == wxID_OK ) if( ret == wxID_OK )
{ {
SaveCopyInUndoList( GetBoard()->m_Modules, UR_CHANGED ); SaveCopyInUndoList( GetBoard()->m_Modules, UR_CHANGED );
BOARD_ITEM* item = GetScreen()->GetCurItem();
item->Move( translation ); item->Move( translation );
switch( rotationAnchor ) switch( rotationAnchor )
@ -903,7 +903,7 @@ void FOOTPRINT_EDIT_FRAME::Transform( MODULE* module, int transform )
double rotation; double rotation;
ROTATION_ANCHOR rotationAnchor = ROTATE_AROUND_ITEM_ANCHOR; ROTATION_ANCHOR rotationAnchor = ROTATE_AROUND_ITEM_ANCHOR;
DIALOG_MOVE_EXACT dialog( this, translation, rotation, rotationAnchor ); DIALOG_MOVE_EXACT dialog( this, translation, rotation, rotationAnchor, module->GetBoundingBox() );
if( dialog.ShowModal() == wxID_OK ) if( dialog.ShowModal() == wxID_OK )
{ {

View File

@ -1022,7 +1022,10 @@ int EDIT_TOOL::MoveExact( const TOOL_EVENT& aEvent )
ROTATION_ANCHOR rotationAnchor = selection.Size() > 1 ? ROTATE_AROUND_SEL_CENTER ROTATION_ANCHOR rotationAnchor = selection.Size() > 1 ? ROTATE_AROUND_SEL_CENTER
: ROTATE_AROUND_ITEM_ANCHOR; : ROTATE_AROUND_ITEM_ANCHOR;
DIALOG_MOVE_EXACT dialog( editFrame, translation, rotation, rotationAnchor ); // TODO: Implement a visible bounding border at the edge
auto sel_box = selection.GetBoundingBox();
DIALOG_MOVE_EXACT dialog( editFrame, translation, rotation, rotationAnchor, sel_box );
int ret = dialog.ShowModal(); int ret = dialog.ShowModal();
if( ret == wxID_OK ) if( ret == wxID_OK )