mirror of
https://gitlab.com/kicad/code/kicad.git
synced 2025-02-18 22:39:45 +00:00
Recommendation is to avoid using the year nomenclature as this information is already encoded in the git repo. Avoids needing to repeatly update. Also updates AUTHORS.txt from current repo with contributor names
505 lines
14 KiB
C++
505 lines
14 KiB
C++
/*
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
*
|
|
* Copyright (C) 2019 CERN
|
|
* Copyright The 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 <wx/log.h>
|
|
#include <fmt/format.h>
|
|
|
|
#include <tool/tool_manager.h>
|
|
#include <tool/actions.h>
|
|
#include <view/view_controls.h>
|
|
#include <gal/graphics_abstraction_layer.h>
|
|
#include <confirm.h>
|
|
#include <bitmaps.h>
|
|
#include <status_popup.h>
|
|
#include <drawing_sheet/ds_draw_item.h>
|
|
#include <drawing_sheet/ds_data_item.h>
|
|
#include "pl_editor_frame.h"
|
|
#include "pl_point_editor.h"
|
|
#include "properties_frame.h"
|
|
#include "tools/pl_selection_tool.h"
|
|
|
|
// 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:
|
|
{
|
|
DS_DRAW_ITEM_LINE* line = static_cast<DS_DRAW_ITEM_LINE*>( aItem );
|
|
points->AddPoint( line->GetStart() );
|
|
points->AddPoint( line->GetEnd() );
|
|
break;
|
|
}
|
|
case WSG_RECT_T:
|
|
{
|
|
DS_DRAW_ITEM_RECT* rect = static_cast<DS_DRAW_ITEM_RECT*>( aItem );
|
|
VECTOR2I topLeft = rect->GetStart();
|
|
VECTOR2I botRight = rect->GetEnd();
|
|
|
|
if( topLeft.y > botRight.y )
|
|
std::swap( topLeft.y, botRight.y );
|
|
|
|
if( topLeft.x > botRight.x )
|
|
std::swap( topLeft.x, botRight.x );
|
|
|
|
points->AddPoint( topLeft );
|
|
points->AddPoint( VECTOR2I( botRight.x, topLeft.y ) );
|
|
points->AddPoint( VECTOR2I( topLeft.x, botRight.y ) );
|
|
points->AddPoint( botRight );
|
|
break;
|
|
}
|
|
default:
|
|
points.reset();
|
|
break;
|
|
}
|
|
|
|
return points;
|
|
}
|
|
|
|
private:
|
|
EDIT_POINTS_FACTORY() {};
|
|
};
|
|
|
|
|
|
PL_POINT_EDITOR::PL_POINT_EDITOR() :
|
|
TOOL_INTERACTIVE( "plEditor.PointEditor" ),
|
|
m_frame( nullptr ),
|
|
m_selectionTool( nullptr ),
|
|
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() );
|
|
else
|
|
point = m_editPoints->FindPoint( getViewControls()->GetCursorPosition(), getView() );
|
|
|
|
if( m_editedPoint != point )
|
|
setEditedPoint( point );
|
|
}
|
|
|
|
|
|
int PL_POINT_EDITOR::Main( const TOOL_EVENT& aEvent )
|
|
{
|
|
KIGFX::VIEW_CONTROLS* controls = getViewControls();
|
|
const PL_SELECTION& selection = m_selectionTool->GetSelection();
|
|
|
|
if( selection.Size() != 1 || !selection.Front()->IsType( { WSG_LINE_T, WSG_RECT_T } ) )
|
|
return 0;
|
|
|
|
EDA_ITEM* item = (EDA_ITEM*) selection.Front();
|
|
|
|
// Wait till drawing tool is done
|
|
if( item->IsNew() )
|
|
return 0;
|
|
|
|
Activate();
|
|
// Must be done after Activate() so that it gets set into the correct context
|
|
controls->ShowCursor( true );
|
|
|
|
m_editPoints = EDIT_POINTS_FACTORY::Make( item );
|
|
|
|
if( !m_editPoints )
|
|
return 0;
|
|
|
|
getView()->Add( m_editPoints.get() );
|
|
setEditedPoint( nullptr );
|
|
updateEditedPoint( aEvent );
|
|
bool inDrag = false;
|
|
bool modified = false;
|
|
|
|
// Main loop: keep receiving events
|
|
while( TOOL_EVENT* evt = Wait() )
|
|
{
|
|
if( !m_editPoints || evt->IsSelectionEvent() )
|
|
break;
|
|
|
|
if ( !inDrag )
|
|
updateEditedPoint( *evt );
|
|
|
|
if( evt->IsDrag( BUT_LEFT ) && m_editedPoint )
|
|
{
|
|
if( !inDrag )
|
|
{
|
|
try
|
|
{
|
|
m_frame->SaveCopyInUndoList();
|
|
}
|
|
catch( const fmt::format_error& exc )
|
|
{
|
|
wxLogWarning( wxS( "Exception \"%s\" serializing string ocurred." ),
|
|
exc.what() );
|
|
return 1;
|
|
}
|
|
|
|
controls->ForceCursorPosition( false );
|
|
inDrag = true;
|
|
modified = true;
|
|
}
|
|
|
|
m_editedPoint->SetPosition( controls->GetCursorPosition( !evt->DisableGridSnapping() ) );
|
|
|
|
updateItem();
|
|
updatePoints();
|
|
}
|
|
|
|
else if( inDrag && evt->IsMouseUp( BUT_LEFT ) )
|
|
{
|
|
controls->SetAutoPan( false );
|
|
inDrag = false;
|
|
}
|
|
|
|
else if( evt->IsCancelInteractive() || evt->IsActivate() )
|
|
{
|
|
if( inDrag ) // Restore the last change
|
|
{
|
|
m_frame->RollbackFromUndo();
|
|
inDrag = false;
|
|
modified = false;
|
|
}
|
|
else if( evt->IsCancelInteractive() )
|
|
{
|
|
break;
|
|
}
|
|
|
|
if( evt->IsActivate() && !evt->IsMoveTool() )
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
evt->SetPassEvent();
|
|
}
|
|
|
|
controls->SetAutoPan( inDrag );
|
|
controls->CaptureCursor( inDrag );
|
|
}
|
|
|
|
controls->SetAutoPan( false );
|
|
controls->CaptureCursor( false );
|
|
|
|
if( m_editPoints )
|
|
{
|
|
getView()->Remove( m_editPoints.get() );
|
|
|
|
if( modified )
|
|
m_frame->OnModify();
|
|
|
|
m_editPoints.reset();
|
|
m_frame->GetCanvas()->Refresh();
|
|
}
|
|
|
|
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;
|
|
|
|
DS_DATA_ITEM* dataItem = static_cast<DS_DRAW_ITEM_BASE*>( item )->GetPeer();
|
|
|
|
// The current item is perhaps not the main item if we have a set of repeated items.
|
|
// So we change the coordinate references in dataItem using move vectors of the start and
|
|
// end points that are the same for each repeated item.
|
|
|
|
switch( item->Type() )
|
|
{
|
|
case WSG_LINE_T:
|
|
{
|
|
DS_DRAW_ITEM_LINE* line = static_cast<DS_DRAW_ITEM_LINE*>( item );
|
|
|
|
VECTOR2I move_startpoint = m_editPoints->Point( LINE_START ).GetPosition() - line->GetStart();
|
|
VECTOR2I move_endpoint = m_editPoints->Point( LINE_END ).GetPosition() - line->GetEnd();
|
|
|
|
dataItem->MoveStartPointToIU( dataItem->GetStartPosIU() + move_startpoint );
|
|
dataItem->MoveEndPointToIU( dataItem->GetEndPosIU() + move_endpoint );
|
|
|
|
for( DS_DRAW_ITEM_BASE* draw_item : dataItem->GetDrawItems() )
|
|
{
|
|
DS_DRAW_ITEM_LINE* draw_line = static_cast<DS_DRAW_ITEM_LINE*>( draw_item );
|
|
|
|
draw_line->SetStart( draw_line->GetStart() + move_startpoint );
|
|
draw_line->SetEnd( draw_line->GetEnd() + move_endpoint );
|
|
getView()->Update( draw_item );
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case WSG_RECT_T:
|
|
{
|
|
DS_DRAW_ITEM_RECT* rect = static_cast<DS_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(), drawSheetIUScale.MilsToIU( 1 ),
|
|
drawSheetIUScale.MilsToIU( 1 ),
|
|
topLeft, topRight, botLeft, botRight );
|
|
|
|
VECTOR2I start_delta, end_delta;
|
|
|
|
if( rect->GetStart().y > rect->GetEnd().y )
|
|
{
|
|
start_delta.y = botRight.y - rect->GetStart().y;
|
|
end_delta.y = topLeft.y - rect->GetEnd().y;
|
|
}
|
|
else
|
|
{
|
|
start_delta.y = topLeft.y - rect->GetStart().y;
|
|
end_delta.y = botRight.y - rect->GetEnd().y;
|
|
}
|
|
|
|
if( rect->GetStart().x > rect->GetEnd().x )
|
|
{
|
|
start_delta.x = botRight.x - rect->GetStart().x;
|
|
end_delta.x = topLeft.x - rect->GetEnd().x;
|
|
}
|
|
else
|
|
{
|
|
start_delta.x = topLeft.x - rect->GetStart().x;
|
|
end_delta.x = botRight.x - rect->GetEnd().x;
|
|
}
|
|
|
|
dataItem->MoveStartPointToIU( dataItem->GetStartPosIU() + start_delta );
|
|
dataItem->MoveEndPointToIU( dataItem->GetEndPosIU() + end_delta );
|
|
|
|
for( DS_DRAW_ITEM_BASE* draw_item : dataItem->GetDrawItems() )
|
|
{
|
|
DS_DRAW_ITEM_RECT* draw_rect = static_cast<DS_DRAW_ITEM_RECT*>( draw_item );
|
|
|
|
draw_rect->SetStart( draw_rect->GetStart() + start_delta );
|
|
draw_rect->SetEnd( draw_rect->GetEnd() + end_delta );
|
|
getView()->Update( draw_item );
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
m_frame->SetMsgPanel( item );
|
|
|
|
// The Properties frame will be updated. Avoid flicker during update:
|
|
m_frame->GetPropertiesFrame()->Freeze();
|
|
m_frame->GetPropertiesFrame()->CopyPrmsFromItemToPanel( dataItem );
|
|
m_frame->GetPropertiesFrame()->Thaw();
|
|
}
|
|
|
|
|
|
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:
|
|
{
|
|
DS_DRAW_ITEM_LINE* line = static_cast<DS_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:
|
|
{
|
|
DS_DRAW_ITEM_RECT* rect = static_cast<DS_DRAW_ITEM_RECT*>( item );
|
|
VECTOR2I topLeft = rect->GetPosition();
|
|
VECTOR2I botRight = rect->GetEnd();
|
|
|
|
if( topLeft.y > botRight.y )
|
|
std::swap( topLeft.y, botRight.y );
|
|
|
|
if( topLeft.x > botRight.x )
|
|
std::swap( topLeft.x, botRight.x );
|
|
|
|
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 )
|
|
{
|
|
m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
|
|
controls->ForceCursorPosition( true, aPoint->GetPosition() );
|
|
controls->ShowCursor( true );
|
|
}
|
|
else
|
|
{
|
|
if( m_frame->ToolStackIsEmpty() )
|
|
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 );
|
|
Go( &PL_POINT_EDITOR::Main, ACTIONS::activatePointEditor.MakeEvent() );
|
|
Go( &PL_POINT_EDITOR::modifiedSelection, EVENTS::SelectedItemsModified );
|
|
}
|
|
|
|
|