2013-08-06 08:30:09 +00:00
|
|
|
/*
|
|
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
|
|
*
|
|
|
|
* Copyright (C) 2013 CERN
|
|
|
|
* @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
|
|
|
|
*
|
|
|
|
* 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
|
|
|
|
*/
|
|
|
|
|
2013-08-02 14:46:53 +00:00
|
|
|
#include <wxPcbStruct.h>
|
|
|
|
#include <wxBasePcbFrame.h>
|
|
|
|
|
|
|
|
#include <tool/tool_manager.h>
|
|
|
|
#include <tool/tool_dispatcher.h>
|
|
|
|
#include <view/view.h>
|
2013-09-24 14:12:02 +00:00
|
|
|
#include <view/wx_view_controls.h>
|
2013-08-02 14:46:53 +00:00
|
|
|
|
|
|
|
#include <class_drawpanel_gal.h>
|
|
|
|
|
|
|
|
#include <pcbnew_id.h>
|
|
|
|
|
|
|
|
#include <boost/optional.hpp>
|
|
|
|
#include <boost/foreach.hpp>
|
|
|
|
|
|
|
|
using boost::optional;
|
|
|
|
|
2013-09-27 16:51:21 +00:00
|
|
|
///> Stores information about a mouse button state
|
2013-10-14 18:40:36 +00:00
|
|
|
struct TOOL_DISPATCHER::BUTTON_STATE
|
2013-08-02 14:46:53 +00:00
|
|
|
{
|
2013-10-14 18:40:36 +00:00
|
|
|
BUTTON_STATE( TOOL_MOUSE_BUTTONS aButton, const wxEventType& aDownEvent,
|
2013-10-14 11:43:57 +00:00
|
|
|
const wxEventType& aUpEvent ) :
|
|
|
|
button( aButton ),
|
|
|
|
downEvent( aDownEvent ),
|
|
|
|
upEvent( aUpEvent )
|
|
|
|
{};
|
|
|
|
|
|
|
|
///> Flag indicating that dragging is active for the given button.
|
|
|
|
bool dragging;
|
|
|
|
|
|
|
|
///> Flag indicating that the given button is pressed.
|
|
|
|
bool pressed;
|
|
|
|
|
|
|
|
///> Point where dragging has started (in world coordinates).
|
|
|
|
VECTOR2D dragOrigin;
|
|
|
|
|
|
|
|
///> Point where click event has occurred.
|
|
|
|
VECTOR2D downPosition;
|
|
|
|
|
|
|
|
///> Difference between drag origin point and current mouse position (expressed as distance in
|
|
|
|
///> pixels).
|
|
|
|
double dragMaxDelta;
|
|
|
|
|
|
|
|
///> Determines the mouse button for which information are stored.
|
2013-10-14 18:40:36 +00:00
|
|
|
TOOL_MOUSE_BUTTONS button;
|
2013-10-14 11:43:57 +00:00
|
|
|
|
|
|
|
///> The type of wxEvent that determines mouse button press.
|
|
|
|
wxEventType downEvent;
|
|
|
|
|
|
|
|
///> The type of wxEvent that determines mouse button release.
|
|
|
|
wxEventType upEvent;
|
|
|
|
|
|
|
|
///> Time stamp for the last mouse button press event.
|
|
|
|
wxLongLong downTimestamp;
|
|
|
|
|
|
|
|
///> Restores initial state.
|
|
|
|
void Reset()
|
|
|
|
{
|
|
|
|
dragging = false;
|
|
|
|
pressed = false;
|
|
|
|
}
|
2013-08-02 14:46:53 +00:00
|
|
|
};
|
|
|
|
|
2013-08-06 08:30:09 +00:00
|
|
|
|
2013-08-21 15:37:27 +00:00
|
|
|
TOOL_DISPATCHER::TOOL_DISPATCHER( TOOL_MANAGER* aToolMgr, PCB_BASE_FRAME* aEditFrame ) :
|
2013-10-14 11:43:57 +00:00
|
|
|
m_toolMgr( aToolMgr ), m_editFrame( aEditFrame )
|
2013-08-06 08:30:09 +00:00
|
|
|
{
|
2013-10-15 08:41:00 +00:00
|
|
|
m_buttons.push_back( new BUTTON_STATE( BUT_LEFT, wxEVT_LEFT_DOWN, wxEVT_LEFT_UP ) );
|
|
|
|
m_buttons.push_back( new BUTTON_STATE( BUT_RIGHT, wxEVT_RIGHT_DOWN, wxEVT_RIGHT_UP ) );
|
|
|
|
m_buttons.push_back( new BUTTON_STATE( BUT_MIDDLE, wxEVT_MIDDLE_DOWN, wxEVT_MIDDLE_UP ) );
|
2013-08-06 08:30:09 +00:00
|
|
|
|
|
|
|
ResetState();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-08-02 14:46:53 +00:00
|
|
|
TOOL_DISPATCHER::~TOOL_DISPATCHER()
|
|
|
|
{
|
2013-10-14 18:40:36 +00:00
|
|
|
BOOST_FOREACH( BUTTON_STATE* st, m_buttons )
|
2013-10-14 11:43:57 +00:00
|
|
|
delete st;
|
2013-08-02 14:46:53 +00:00
|
|
|
}
|
|
|
|
|
2013-08-06 08:30:09 +00:00
|
|
|
|
2013-08-02 14:46:53 +00:00
|
|
|
void TOOL_DISPATCHER::ResetState()
|
|
|
|
{
|
2013-10-14 18:40:36 +00:00
|
|
|
BOOST_FOREACH( BUTTON_STATE* st, m_buttons )
|
2013-10-14 11:43:57 +00:00
|
|
|
st->Reset();
|
2013-08-02 14:46:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-10-14 14:13:35 +00:00
|
|
|
KIGFX::VIEW* TOOL_DISPATCHER::getView()
|
2013-08-02 14:46:53 +00:00
|
|
|
{
|
2013-10-14 11:43:57 +00:00
|
|
|
return m_editFrame->GetGalCanvas()->GetView();
|
2013-08-02 14:46:53 +00:00
|
|
|
}
|
|
|
|
|
2013-08-06 08:30:09 +00:00
|
|
|
|
2013-08-21 15:37:27 +00:00
|
|
|
bool TOOL_DISPATCHER::handleMouseButton( wxEvent& aEvent, int aIndex, bool aMotion )
|
2013-08-02 14:46:53 +00:00
|
|
|
{
|
2013-10-14 18:40:36 +00:00
|
|
|
BUTTON_STATE* st = m_buttons[aIndex];
|
2013-10-14 11:43:57 +00:00
|
|
|
wxEventType type = aEvent.GetEventType();
|
|
|
|
optional<TOOL_EVENT> evt;
|
|
|
|
bool isClick = false;
|
|
|
|
|
|
|
|
bool up = type == st->upEvent;
|
|
|
|
bool down = type == st->downEvent;
|
|
|
|
|
|
|
|
int mods = decodeModifiers<wxMouseEvent>( static_cast<wxMouseEvent*>( &aEvent ) );
|
|
|
|
int args = st->button | mods;
|
|
|
|
|
|
|
|
if( down ) // Handle mouse button press
|
|
|
|
{
|
|
|
|
st->downTimestamp = wxGetLocalTimeMillis();
|
|
|
|
st->dragOrigin = m_lastMousePos;
|
|
|
|
st->downPosition = m_lastMousePos;
|
|
|
|
st->dragMaxDelta = 0;
|
|
|
|
st->pressed = true;
|
2013-10-14 18:40:36 +00:00
|
|
|
evt = TOOL_EVENT( TC_MOUSE, TA_MOUSE_DOWN, args );
|
2013-10-14 11:43:57 +00:00
|
|
|
}
|
2013-10-14 14:13:35 +00:00
|
|
|
else if( up ) // Handle mouse button release
|
2013-10-14 11:43:57 +00:00
|
|
|
{
|
|
|
|
st->pressed = false;
|
|
|
|
|
|
|
|
if( st->dragging )
|
|
|
|
{
|
|
|
|
wxLongLong t = wxGetLocalTimeMillis();
|
|
|
|
|
|
|
|
// Determine if it was just a single click or beginning of dragging
|
|
|
|
if( t - st->downTimestamp < DragTimeThreshold &&
|
|
|
|
st->dragMaxDelta < DragDistanceThreshold )
|
|
|
|
isClick = true;
|
|
|
|
else
|
2013-10-14 18:40:36 +00:00
|
|
|
evt = TOOL_EVENT( TC_MOUSE, TA_MOUSE_UP, args );
|
2013-10-14 11:43:57 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
isClick = true;
|
|
|
|
|
|
|
|
if( isClick )
|
2013-10-14 18:40:36 +00:00
|
|
|
evt = TOOL_EVENT( TC_MOUSE, TA_MOUSE_CLICK, args );
|
2013-10-14 11:43:57 +00:00
|
|
|
|
|
|
|
st->dragging = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( st->pressed && aMotion )
|
|
|
|
{
|
|
|
|
st->dragging = true;
|
2013-10-14 14:13:35 +00:00
|
|
|
double dragPixelDistance =
|
|
|
|
getView()->ToScreen( m_lastMousePos - st->dragOrigin, false ).EuclideanNorm();
|
2013-10-14 11:43:57 +00:00
|
|
|
st->dragMaxDelta = std::max( st->dragMaxDelta, dragPixelDistance );
|
|
|
|
|
|
|
|
wxLongLong t = wxGetLocalTimeMillis();
|
|
|
|
|
|
|
|
if( t - st->downTimestamp > DragTimeThreshold || st->dragMaxDelta > DragDistanceThreshold )
|
|
|
|
{
|
2013-10-14 18:40:36 +00:00
|
|
|
evt = TOOL_EVENT( TC_MOUSE, TA_MOUSE_DRAG, args );
|
2013-10-14 11:43:57 +00:00
|
|
|
evt->SetMouseDragOrigin( st->dragOrigin );
|
|
|
|
evt->SetMouseDelta( m_lastMousePos - st->dragOrigin );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if( evt )
|
|
|
|
{
|
|
|
|
evt->SetMousePosition( isClick ? st->downPosition : m_lastMousePos );
|
|
|
|
m_toolMgr->ProcessEvent( *evt );
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
2013-08-02 14:46:53 +00:00
|
|
|
}
|
|
|
|
|
2013-08-06 08:30:09 +00:00
|
|
|
|
2013-08-21 15:37:27 +00:00
|
|
|
void TOOL_DISPATCHER::DispatchWxEvent( wxEvent& aEvent )
|
2013-08-02 14:46:53 +00:00
|
|
|
{
|
2013-10-14 11:43:57 +00:00
|
|
|
bool motion = false, buttonEvents = false;
|
|
|
|
optional<TOOL_EVENT> evt;
|
|
|
|
|
|
|
|
int type = aEvent.GetEventType();
|
|
|
|
|
|
|
|
// Mouse handling
|
|
|
|
if( type == wxEVT_MOTION || type == wxEVT_MOUSEWHEEL ||
|
2013-10-14 14:13:35 +00:00
|
|
|
type == wxEVT_LEFT_DOWN || type == wxEVT_LEFT_UP ||
|
|
|
|
type == wxEVT_MIDDLE_DOWN || type == wxEVT_MIDDLE_UP ||
|
|
|
|
type == wxEVT_RIGHT_DOWN || type == wxEVT_RIGHT_UP ||
|
|
|
|
// Event issued whem mouse retains position in screen coordinates,
|
|
|
|
// but changes in world coordinates (eg. autopanning)
|
|
|
|
type == KIGFX::WX_VIEW_CONTROLS::EVT_REFRESH_MOUSE )
|
2013-10-14 11:43:57 +00:00
|
|
|
{
|
2013-09-16 14:46:02 +00:00
|
|
|
VECTOR2D screenPos = m_toolMgr->GetViewControls()->GetCursorPosition();
|
|
|
|
VECTOR2D pos = getView()->ToWorld( screenPos );
|
2013-09-27 14:23:43 +00:00
|
|
|
|
2013-10-14 14:13:35 +00:00
|
|
|
if( pos != m_lastMousePos || type == KIGFX::WX_VIEW_CONTROLS::EVT_REFRESH_MOUSE )
|
2013-10-14 11:43:57 +00:00
|
|
|
{
|
|
|
|
motion = true;
|
|
|
|
m_lastMousePos = pos;
|
|
|
|
}
|
2013-08-02 14:46:53 +00:00
|
|
|
|
2013-10-14 11:43:57 +00:00
|
|
|
for( unsigned int i = 0; i < m_buttons.size(); i++ )
|
|
|
|
buttonEvents |= handleMouseButton( aEvent, i, motion );
|
2013-08-02 14:46:53 +00:00
|
|
|
|
2013-10-14 11:43:57 +00:00
|
|
|
if( !buttonEvents && motion )
|
|
|
|
{
|
2013-10-14 18:40:36 +00:00
|
|
|
evt = TOOL_EVENT( TC_MOUSE, TA_MOUSE_MOTION );
|
2013-10-14 11:43:57 +00:00
|
|
|
evt->SetMousePosition( pos );
|
|
|
|
}
|
|
|
|
}
|
2013-08-02 14:46:53 +00:00
|
|
|
|
2013-10-14 11:43:57 +00:00
|
|
|
// Keyboard handling
|
|
|
|
else if( type == wxEVT_KEY_UP || type == wxEVT_KEY_DOWN )
|
2013-08-21 15:37:27 +00:00
|
|
|
{
|
|
|
|
wxKeyEvent* ke = static_cast<wxKeyEvent*>( &aEvent );
|
|
|
|
int key = ke->GetKeyCode();
|
2013-09-29 12:25:04 +00:00
|
|
|
int mods = decodeModifiers<wxKeyEvent>( ke );
|
2013-08-21 15:37:27 +00:00
|
|
|
|
|
|
|
if( type == wxEVT_KEY_UP )
|
2013-08-22 13:07:34 +00:00
|
|
|
{
|
2013-10-14 14:13:35 +00:00
|
|
|
if( key == WXK_ESCAPE ) // ESC is the special key for cancelling tools
|
2013-10-14 18:40:36 +00:00
|
|
|
evt = TOOL_EVENT( TC_COMMAND, TA_CANCEL_TOOL );
|
2013-08-22 13:07:34 +00:00
|
|
|
else
|
2013-10-14 18:40:36 +00:00
|
|
|
evt = TOOL_EVENT( TC_KEYBOARD, TA_KEY_UP, key | mods );
|
2013-08-22 13:07:34 +00:00
|
|
|
}
|
2013-08-21 15:37:27 +00:00
|
|
|
else
|
2013-08-22 13:07:34 +00:00
|
|
|
{
|
2013-10-14 18:40:36 +00:00
|
|
|
evt = TOOL_EVENT( TC_KEYBOARD, TA_KEY_DOWN, key | mods );
|
2013-08-22 13:07:34 +00:00
|
|
|
}
|
2013-08-21 15:37:27 +00:00
|
|
|
}
|
|
|
|
|
2013-10-14 11:43:57 +00:00
|
|
|
if( evt )
|
|
|
|
m_toolMgr->ProcessEvent( *evt );
|
2013-08-02 14:46:53 +00:00
|
|
|
|
2013-10-14 11:43:57 +00:00
|
|
|
// pass the event to the GUI, it might still be interested in it
|
|
|
|
aEvent.Skip();
|
2013-08-02 14:46:53 +00:00
|
|
|
}
|
|
|
|
|
2013-08-06 08:30:09 +00:00
|
|
|
|
2013-09-27 14:23:43 +00:00
|
|
|
void TOOL_DISPATCHER::DispatchWxCommand( const wxCommandEvent& aEvent )
|
2013-08-02 14:46:53 +00:00
|
|
|
{
|
2013-10-14 11:43:57 +00:00
|
|
|
bool activateTool = false;
|
|
|
|
std::string toolName;
|
|
|
|
|
|
|
|
// fixme: use TOOL_ACTIONs here
|
|
|
|
switch( aEvent.GetId() )
|
|
|
|
{
|
2013-10-14 14:13:35 +00:00
|
|
|
case ID_PNS_ROUTER_TOOL:
|
|
|
|
toolName = "pcbnew.InteractiveRouter";
|
|
|
|
activateTool = true;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ID_SELECTION_TOOL:
|
|
|
|
toolName = "pcbnew.InteractiveSelection";
|
|
|
|
activateTool = true;
|
|
|
|
break;
|
2013-10-14 11:43:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// do nothing if the legacy view is active
|
|
|
|
if( activateTool && m_editFrame->IsGalCanvasActive() )
|
|
|
|
m_toolMgr->InvokeTool( toolName );
|
2013-08-06 08:30:09 +00:00
|
|
|
}
|