2019-05-20 10:23:32 +00:00
|
|
|
/*
|
|
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
|
|
*
|
|
|
|
* Copyright (C) 2019 KiCad Developers, see AUTHORS.txt for contributors.
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, you may find one here:
|
|
|
|
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
|
|
|
* or you may search the http://www.gnu.org website for the version 2 license,
|
|
|
|
* or you may write to the Free Software Foundation, Inc.,
|
|
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <functional>
|
|
|
|
using namespace std::placeholders;
|
|
|
|
|
|
|
|
#include "pl_point_editor.h"
|
|
|
|
#include <tool/tool_manager.h>
|
|
|
|
#include <tool/actions.h>
|
|
|
|
#include <tools/pl_actions.h>
|
|
|
|
#include <tools/pl_selection_tool.h>
|
|
|
|
#include <view/view_controls.h>
|
|
|
|
#include <gal/graphics_abstraction_layer.h>
|
|
|
|
#include <geometry/seg.h>
|
|
|
|
#include <confirm.h>
|
|
|
|
|
|
|
|
#include <bitmaps.h>
|
|
|
|
#include <status_popup.h>
|
|
|
|
#include <pl_editor_frame.h>
|
|
|
|
#include <ws_draw_item.h>
|
|
|
|
#include <widgets/progress_reporter.h>
|
2019-05-25 19:39:42 +00:00
|
|
|
#include <pl_editor_id.h>
|
2019-05-20 10:23:32 +00:00
|
|
|
|
|
|
|
|
|
|
|
// Few constants to avoid using bare numbers for point indices
|
|
|
|
enum RECTANGLE_POINTS
|
|
|
|
{
|
|
|
|
RECT_TOPLEFT, RECT_TOPRIGHT, RECT_BOTLEFT, RECT_BOTRIGHT
|
|
|
|
};
|
|
|
|
|
|
|
|
enum LINE_POINTS
|
|
|
|
{
|
|
|
|
LINE_START, LINE_END
|
|
|
|
};
|
|
|
|
|
|
|
|
class EDIT_POINTS_FACTORY
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
static std::shared_ptr<EDIT_POINTS> Make( EDA_ITEM* aItem )
|
|
|
|
{
|
|
|
|
std::shared_ptr<EDIT_POINTS> points = std::make_shared<EDIT_POINTS>( aItem );
|
|
|
|
|
|
|
|
if( !aItem )
|
|
|
|
return points;
|
|
|
|
|
|
|
|
// Generate list of edit points based on the item type
|
|
|
|
switch( aItem->Type() )
|
|
|
|
{
|
|
|
|
case WSG_LINE_T:
|
|
|
|
{
|
|
|
|
WS_DRAW_ITEM_LINE* line = (WS_DRAW_ITEM_LINE*) aItem;
|
|
|
|
points->AddPoint( line->GetStart() );
|
|
|
|
points->AddPoint( line->GetEnd() );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case WSG_RECT_T:
|
|
|
|
{
|
|
|
|
WS_DRAW_ITEM_RECT* rect = (WS_DRAW_ITEM_RECT*) aItem;
|
|
|
|
wxPoint topLeft = rect->GetStart();
|
|
|
|
wxPoint botRight = rect->GetEnd();
|
|
|
|
|
|
|
|
points->AddPoint( (VECTOR2I) topLeft );
|
|
|
|
points->AddPoint( VECTOR2I( botRight.x, topLeft.y ) );
|
|
|
|
points->AddPoint( VECTOR2I( topLeft.x, botRight.y ) );
|
|
|
|
points->AddPoint( (VECTOR2I) botRight );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
points.reset();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return points;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
EDIT_POINTS_FACTORY() {};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
PL_POINT_EDITOR::PL_POINT_EDITOR() :
|
|
|
|
TOOL_INTERACTIVE( "plEditor.PointEditor" ),
|
|
|
|
m_editedPoint( nullptr )
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PL_POINT_EDITOR::Reset( RESET_REASON aReason )
|
|
|
|
{
|
|
|
|
if( aReason == MODEL_RELOAD )
|
|
|
|
{
|
|
|
|
// Init variables used by every drawing tool
|
|
|
|
m_frame = getEditFrame<PL_EDITOR_FRAME>();
|
|
|
|
}
|
|
|
|
|
|
|
|
m_editPoints.reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool PL_POINT_EDITOR::Init()
|
|
|
|
{
|
|
|
|
m_frame = getEditFrame<PL_EDITOR_FRAME>();
|
|
|
|
m_selectionTool = m_toolMgr->GetTool<PL_SELECTION_TOOL>();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PL_POINT_EDITOR::updateEditedPoint( const TOOL_EVENT& aEvent )
|
|
|
|
{
|
|
|
|
EDIT_POINT* point = m_editedPoint;
|
|
|
|
|
|
|
|
if( aEvent.IsMotion() )
|
|
|
|
{
|
|
|
|
point = m_editPoints->FindPoint( aEvent.Position(), getView() );
|
|
|
|
}
|
|
|
|
else if( aEvent.IsDrag( BUT_LEFT ) )
|
|
|
|
{
|
|
|
|
point = m_editPoints->FindPoint( aEvent.DragOrigin(), getView() );
|
|
|
|
}
|
|
|
|
|
|
|
|
if( m_editedPoint != point )
|
|
|
|
setEditedPoint( point );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int PL_POINT_EDITOR::Main( const TOOL_EVENT& aEvent )
|
|
|
|
{
|
|
|
|
static KICAD_T pointTypes[] = { WSG_LINE_T, WSG_RECT_T, EOT };
|
|
|
|
|
|
|
|
if( !m_selectionTool )
|
|
|
|
return 0;
|
|
|
|
|
2019-06-08 21:48:22 +00:00
|
|
|
const PL_SELECTION& selection = m_selectionTool->GetSelection();
|
2019-05-20 10:23:32 +00:00
|
|
|
|
|
|
|
if( selection.Size() != 1 || !selection.Front()->IsType( pointTypes ) )
|
|
|
|
return 0;
|
|
|
|
|
2019-05-24 11:39:30 +00:00
|
|
|
// Wait till drawing tool is done
|
|
|
|
if( selection.Front()->IsNew() )
|
|
|
|
return 0;
|
|
|
|
|
2019-05-20 10:23:32 +00:00
|
|
|
Activate();
|
|
|
|
|
|
|
|
KIGFX::VIEW_CONTROLS* controls = getViewControls();
|
|
|
|
KIGFX::VIEW* view = getView();
|
|
|
|
EDA_ITEM* item = (EDA_ITEM*) selection.Front();
|
|
|
|
|
|
|
|
controls->ShowCursor( true );
|
|
|
|
|
|
|
|
m_editPoints = EDIT_POINTS_FACTORY::Make( item );
|
|
|
|
|
|
|
|
if( !m_editPoints )
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
view->Add( m_editPoints.get() );
|
|
|
|
setEditedPoint( nullptr );
|
|
|
|
bool inDrag = false;
|
|
|
|
bool modified = false;
|
|
|
|
|
|
|
|
// Main loop: keep receiving events
|
2019-06-17 13:43:22 +00:00
|
|
|
while( TOOL_EVENT* evt = Wait() )
|
2019-05-20 10:23:32 +00:00
|
|
|
{
|
2019-06-17 13:43:22 +00:00
|
|
|
if( !m_editPoints || TOOL_EVT_UTILS::IsSelectionEvent( *evt ) )
|
2019-05-20 10:23:32 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
if ( !inDrag )
|
|
|
|
updateEditedPoint( *evt );
|
|
|
|
|
|
|
|
if( evt->IsDrag( BUT_LEFT ) && m_editedPoint )
|
|
|
|
{
|
|
|
|
if( !inDrag )
|
|
|
|
{
|
|
|
|
m_frame->SaveCopyInUndoList();
|
|
|
|
controls->ForceCursorPosition( false );
|
2019-06-15 00:29:42 +00:00
|
|
|
m_frame->PushTool( _( "Drag Point" ).ToStdString() );
|
2019-05-20 10:23:32 +00:00
|
|
|
inDrag = true;
|
|
|
|
modified = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_editedPoint->SetPosition( controls->GetCursorPosition( !evt->Modifier( MD_ALT ) ) );
|
|
|
|
|
|
|
|
updateItem();
|
|
|
|
updatePoints();
|
|
|
|
}
|
|
|
|
|
2019-06-17 13:43:22 +00:00
|
|
|
else if( inDrag && evt->IsMouseUp( BUT_LEFT ) )
|
2019-05-20 10:23:32 +00:00
|
|
|
{
|
|
|
|
controls->SetAutoPan( false );
|
2019-06-15 00:29:42 +00:00
|
|
|
m_frame->PopTool();
|
2019-05-20 10:23:32 +00:00
|
|
|
inDrag = false;
|
2019-06-16 00:06:43 +00:00
|
|
|
}
|
2019-05-20 10:23:32 +00:00
|
|
|
|
2019-06-16 00:06:43 +00:00
|
|
|
else if( evt->IsCancel() )
|
|
|
|
{
|
|
|
|
if( inDrag ) // Restore the last change
|
|
|
|
{
|
|
|
|
m_frame->PopTool();
|
|
|
|
m_frame->RollbackFromUndo();
|
|
|
|
inDrag = false;
|
|
|
|
modified = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ESC should clear selection along with edit points
|
|
|
|
m_toolMgr->RunAction( PL_ACTIONS::clearSelection, true );
|
|
|
|
|
|
|
|
break;
|
2019-05-20 10:23:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
else
|
2019-06-16 11:06:49 +00:00
|
|
|
evt->SetPassEvent();
|
2019-05-20 10:23:32 +00:00
|
|
|
|
|
|
|
controls->SetAutoPan( inDrag );
|
|
|
|
controls->CaptureCursor( inDrag );
|
|
|
|
}
|
|
|
|
|
|
|
|
controls->SetAutoPan( false );
|
|
|
|
controls->CaptureCursor( false );
|
|
|
|
|
|
|
|
if( m_editPoints )
|
|
|
|
{
|
|
|
|
view->Remove( m_editPoints.get() );
|
|
|
|
|
|
|
|
if( modified )
|
|
|
|
m_frame->OnModify();
|
|
|
|
|
|
|
|
m_editPoints.reset();
|
2019-06-13 17:28:55 +00:00
|
|
|
m_frame->GetCanvas()->Refresh();
|
2019-05-20 10:23:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void pinEditedCorner( int editedPointIndex, int minWidth, int minHeight, VECTOR2I& topLeft,
|
|
|
|
VECTOR2I& topRight, VECTOR2I& botLeft, VECTOR2I& botRight )
|
|
|
|
{
|
|
|
|
switch( editedPointIndex )
|
|
|
|
{
|
|
|
|
case RECT_TOPLEFT:
|
|
|
|
// pin edited point within opposite corner
|
|
|
|
topLeft.x = std::min( topLeft.x, botRight.x - minWidth );
|
|
|
|
topLeft.y = std::min( topLeft.y, botRight.y - minHeight );
|
|
|
|
|
|
|
|
// push edited point edges to adjacent corners
|
|
|
|
topRight.y = topLeft.y;
|
|
|
|
botLeft.x = topLeft.x;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case RECT_TOPRIGHT:
|
|
|
|
// pin edited point within opposite corner
|
|
|
|
topRight.x = std::max( topRight.x, botLeft.x + minWidth );
|
|
|
|
topRight.y = std::min( topRight.y, botLeft.y - minHeight );
|
|
|
|
|
|
|
|
// push edited point edges to adjacent corners
|
|
|
|
topLeft.y = topRight.y;
|
|
|
|
botRight.x = topRight.x;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case RECT_BOTLEFT:
|
|
|
|
// pin edited point within opposite corner
|
|
|
|
botLeft.x = std::min( botLeft.x, topRight.x - minWidth );
|
|
|
|
botLeft.y = std::max( botLeft.y, topRight.y + minHeight );
|
|
|
|
|
|
|
|
// push edited point edges to adjacent corners
|
|
|
|
botRight.y = botLeft.y;
|
|
|
|
topLeft.x = botLeft.x;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case RECT_BOTRIGHT:
|
|
|
|
// pin edited point within opposite corner
|
|
|
|
botRight.x = std::max( botRight.x, topLeft.x + minWidth );
|
|
|
|
botRight.y = std::max( botRight.y, topLeft.y + minHeight );
|
|
|
|
|
|
|
|
// push edited point edges to adjacent corners
|
|
|
|
botLeft.y = botRight.y;
|
|
|
|
topRight.x = botRight.x;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PL_POINT_EDITOR::updateItem() const
|
|
|
|
{
|
|
|
|
EDA_ITEM* item = m_editPoints->GetParent();
|
|
|
|
|
|
|
|
if( !item )
|
|
|
|
return;
|
|
|
|
|
|
|
|
switch( item->Type() )
|
|
|
|
{
|
|
|
|
case WSG_LINE_T:
|
|
|
|
{
|
|
|
|
WS_DRAW_ITEM_LINE* line = (WS_DRAW_ITEM_LINE*) item;
|
|
|
|
|
|
|
|
line->SetStart( (wxPoint) m_editPoints->Point( LINE_START ).GetPosition() );
|
|
|
|
line->SetEnd( (wxPoint) m_editPoints->Point( LINE_END ).GetPosition() );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case WSG_RECT_T:
|
|
|
|
{
|
|
|
|
WS_DRAW_ITEM_RECT* rect = (WS_DRAW_ITEM_RECT*) item;
|
|
|
|
VECTOR2I topLeft = m_editPoints->Point( RECT_TOPLEFT ).GetPosition();
|
|
|
|
VECTOR2I topRight = m_editPoints->Point( RECT_TOPRIGHT ).GetPosition();
|
|
|
|
VECTOR2I botLeft = m_editPoints->Point( RECT_BOTLEFT ).GetPosition();
|
|
|
|
VECTOR2I botRight = m_editPoints->Point( RECT_BOTRIGHT ).GetPosition();
|
|
|
|
|
|
|
|
pinEditedCorner( getEditedPointIndex(), Mils2iu( 1 ), Mils2iu( 1 ),
|
|
|
|
topLeft, topRight, botLeft, botRight );
|
|
|
|
|
|
|
|
rect->SetStart( (wxPoint) topLeft );
|
|
|
|
rect->SetEnd( (wxPoint) botRight );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
getView()->Update( item );
|
|
|
|
m_frame->SetMsgPanel( item );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PL_POINT_EDITOR::updatePoints()
|
|
|
|
{
|
|
|
|
if( !m_editPoints )
|
|
|
|
return;
|
|
|
|
|
|
|
|
EDA_ITEM* item = m_editPoints->GetParent();
|
|
|
|
|
|
|
|
if( !item )
|
|
|
|
return;
|
|
|
|
|
|
|
|
switch( item->Type() )
|
|
|
|
{
|
|
|
|
case WSG_LINE_T:
|
|
|
|
{
|
|
|
|
WS_DRAW_ITEM_LINE* line = (WS_DRAW_ITEM_LINE*) item;
|
|
|
|
|
|
|
|
m_editPoints->Point( LINE_START ).SetPosition( line->GetStart() );
|
|
|
|
m_editPoints->Point( LINE_END ).SetPosition( line->GetEnd() );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case WSG_RECT_T:
|
|
|
|
{
|
|
|
|
WS_DRAW_ITEM_RECT* rect = (WS_DRAW_ITEM_RECT*) item;
|
|
|
|
wxPoint topLeft = rect->GetPosition();
|
|
|
|
wxPoint botRight = rect->GetEnd();
|
|
|
|
|
|
|
|
m_editPoints->Point( RECT_TOPLEFT ).SetPosition( (VECTOR2I) topLeft );
|
|
|
|
m_editPoints->Point( RECT_TOPRIGHT ).SetPosition( VECTOR2I( botRight.x, topLeft.y ) );
|
|
|
|
m_editPoints->Point( RECT_BOTLEFT ).SetPosition( VECTOR2I( topLeft.x, botRight.y ) );
|
|
|
|
m_editPoints->Point( RECT_BOTRIGHT ).SetPosition( (VECTOR2I) botRight );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
getView()->Update( m_editPoints.get() );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PL_POINT_EDITOR::setEditedPoint( EDIT_POINT* aPoint )
|
|
|
|
{
|
|
|
|
KIGFX::VIEW_CONTROLS* controls = getViewControls();
|
|
|
|
|
|
|
|
if( aPoint )
|
|
|
|
{
|
|
|
|
controls->ForceCursorPosition( true, aPoint->GetPosition() );
|
|
|
|
controls->ShowCursor( true );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
controls->ShowCursor( false );
|
|
|
|
controls->ForceCursorPosition( false );
|
|
|
|
}
|
|
|
|
|
|
|
|
m_editedPoint = aPoint;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int PL_POINT_EDITOR::modifiedSelection( const TOOL_EVENT& aEvent )
|
|
|
|
{
|
|
|
|
updatePoints();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PL_POINT_EDITOR::setTransitions()
|
|
|
|
{
|
|
|
|
Go( &PL_POINT_EDITOR::Main, EVENTS::SelectedEvent );
|
2019-05-24 11:39:30 +00:00
|
|
|
Go( &PL_POINT_EDITOR::Main, ACTIONS::activatePointEditor.MakeEvent() );
|
2019-05-20 10:23:32 +00:00
|
|
|
Go( &PL_POINT_EDITOR::modifiedSelection, EVENTS::SelectedItemsModified );
|
|
|
|
}
|
|
|
|
|
|
|
|
|