tool_event.h 13.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
/*
 * 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
 */

#ifndef __TOOL_EVENT_H
#define __TOOL_EVENT_H

#include <cstdio>
#include <deque>

#include <math/vector2d.h>
32
#include <cassert>
33 34 35

#include <boost/optional.hpp>

36
class TOOL_ACTION;
37 38 39 40 41 42
class TOOL_MANAGER;

/**
 * Internal (GUI-independent) event definitions.
 * Enums are mostly self-explanatory.
 */
Maciej Suminski's avatar
Maciej Suminski committed
43
enum TOOL_EVENT_CATEGORY
44
{
Maciej Suminski's avatar
Maciej Suminski committed
45 46 47 48 49 50 51
    TC_NONE     = 0x00,
    TC_MOUSE    = 0x01,
    TC_KEYBOARD = 0x02,
    TC_COMMAND  = 0x04,
    TC_MESSAGE  = 0x08,
    TC_VIEW     = 0x10,
    TC_ANY      = 0xffffffff
52 53
};

Maciej Suminski's avatar
Maciej Suminski committed
54
enum TOOL_ACTIONS
55
{
56
    // UI input events
57 58 59 60 61 62 63 64 65 66 67 68 69
    TA_NONE                 = 0x0000,
    TA_MOUSE_CLICK          = 0x0001,
    TA_MOUSE_DBLCLICK       = 0x0002,
    TA_MOUSE_UP             = 0x0004,
    TA_MOUSE_DOWN           = 0x0008,
    TA_MOUSE_DRAG           = 0x0010,
    TA_MOUSE_MOTION         = 0x0020,
    TA_MOUSE_WHEEL          = 0x0040,
    TA_MOUSE                = 0x007f,

    TA_KEY_UP               = 0x0080,
    TA_KEY_DOWN             = 0x0100,
    TA_KEYBOARD             = TA_KEY_UP | TA_KEY_DOWN,
70 71

    // View related events
72 73 74 75 76 77 78
    TA_VIEW_REFRESH         = 0x0200,
    TA_VIEW_ZOOM            = 0x0400,
    TA_VIEW_PAN             = 0x0800,
    TA_VIEW_DIRTY           = 0x1000,
    TA_VIEW                 = 0x1e00,

    TA_CHANGE_LAYER         = 0x2000,
79

80 81
    // Tool cancel event. Issued automagically when the user hits escape or selects End Tool from
    // the context menu.
82
    TA_CANCEL_TOOL          = 0x4000,
83

84 85
    // Context menu update. Issued whenever context menu is open and the user hovers the mouse
    // over one of choices. Used in dynamic highligting in disambiguation menu
86
    TA_CONTEXT_MENU_UPDATE  = 0x8000,
87

88 89
    // Context menu choice. Sent if the user picked something from the context menu or
    // closed it without selecting anything.
90
    TA_CONTEXT_MENU_CHOICE  = 0x10000,
91

92
    // This event is sent *before* undo/redo command is performed.
93 94
    TA_UNDO_REDO            = 0x20000,

95
    // Tool action (allows to control tools)
96
    TA_ACTION               = 0x40000,
97

Maciej Suminski's avatar
Maciej Suminski committed
98
    TA_ANY = 0xffffffff
99 100
};

Maciej Suminski's avatar
Maciej Suminski committed
101
enum TOOL_MOUSE_BUTTONS
102
{
103 104 105 106 107 108
    BUT_NONE         = 0x0,
    BUT_LEFT         = 0x1,
    BUT_RIGHT        = 0x2,
    BUT_MIDDLE       = 0x4,
    BUT_BUTTON_MASK  = BUT_LEFT | BUT_RIGHT | BUT_MIDDLE,
    BUT_ANY          = 0xffffffff
109 110
};

Maciej Suminski's avatar
Maciej Suminski committed
111
enum TOOL_MODIFIERS
112
{
Maciej Suminski's avatar
Maciej Suminski committed
113 114 115 116
    MD_SHIFT        = 0x1000,
    MD_CTRL         = 0x2000,
    MD_ALT          = 0x4000,
    MD_MODIFIER_MASK = MD_SHIFT | MD_CTRL | MD_ALT,
117
};
118

119
/// Scope of tool actions
Maciej Suminski's avatar
Maciej Suminski committed
120
enum TOOL_ACTION_SCOPE
121 122 123 124 125 126 127
{
    AS_CONTEXT = 1,  ///> Action belongs to a particular tool (i.e. a part of a pop-up menu)
    AS_ACTIVE,       ///> All active tools
    AS_GLOBAL        ///> Global action (toolbar/main menu event, global shortcut)
};

/// Defines when a context menu is opened.
128
enum CONTEXT_MENU_TRIGGER
129
{
130 131 132
    CMENU_BUTTON = 0,   // On the right button
    CMENU_NOW,          // Right now (after TOOL_INTERACTIVE::SetContextMenu)
    CMENU_OFF           // Never
133 134
};

135
/**
136
 * Class TOOL_EVENT
137
 *
138 139 140 141
 * Generic, UI-independent tool event.
 */
class TOOL_EVENT
{
142
public:
143 144 145 146 147 148
    /**
     * Function Format()
     * Returns information about event in form of a human-readable string.
     *
     * @return Event information.
     */
149 150
    const std::string Format() const;

Maciej Suminski's avatar
Maciej Suminski committed
151 152
    TOOL_EVENT( TOOL_EVENT_CATEGORY aCategory = TC_NONE, TOOL_ACTIONS aAction = TA_NONE,
            TOOL_ACTION_SCOPE aScope = AS_GLOBAL ) :
153 154
        m_category( aCategory ),
        m_actions( aAction ),
155
        m_scope( aScope ),
156 157 158
        m_mouseButtons( 0 ),
        m_keyCode( 0 ),
        m_modifiers( 0 ) {}
159

Maciej Suminski's avatar
Maciej Suminski committed
160 161
    TOOL_EVENT( TOOL_EVENT_CATEGORY aCategory, TOOL_ACTIONS aAction, int aExtraParam,
            TOOL_ACTION_SCOPE aScope = AS_GLOBAL ) :
162
        m_category( aCategory ),
163 164
        m_actions( aAction ),
        m_scope( aScope )
165
    {
Maciej Suminski's avatar
Maciej Suminski committed
166
        if( aCategory == TC_MOUSE )
167
        {
168
            m_mouseButtons = aExtraParam & BUT_BUTTON_MASK;
169
        }
Maciej Suminski's avatar
Maciej Suminski committed
170
        else if( aCategory == TC_KEYBOARD )
171
        {
Maciej Suminski's avatar
Maciej Suminski committed
172
            m_keyCode = aExtraParam & ~MD_MODIFIER_MASK;         // Filter out modifiers
173
        }
Maciej Suminski's avatar
Maciej Suminski committed
174
        else if( aCategory == TC_COMMAND )
175
        {
176
            m_commandId = aExtraParam;
177 178
        }

Maciej Suminski's avatar
Maciej Suminski committed
179
        if( aCategory & ( TC_MOUSE | TC_KEYBOARD ) )
180
        {
Maciej Suminski's avatar
Maciej Suminski committed
181
            m_modifiers = aExtraParam & MD_MODIFIER_MASK;
182 183 184
        }
    }

Maciej Suminski's avatar
Maciej Suminski committed
185 186
    TOOL_EVENT( TOOL_EVENT_CATEGORY aCategory, TOOL_ACTIONS aAction,
            const std::string& aExtraParam, TOOL_ACTION_SCOPE aScope = AS_GLOBAL ) :
187 188
        m_category( aCategory ),
        m_actions( aAction ),
189
        m_scope( aScope ),
190
        m_mouseButtons( 0 )
191
    {
Maciej Suminski's avatar
Maciej Suminski committed
192
        if( aCategory == TC_COMMAND )
193 194
            m_commandStr = aExtraParam;
    }
195

196
    ///> Returns the category (eg. mouse/keyboard/action) of an event..
Maciej Suminski's avatar
Maciej Suminski committed
197
    TOOL_EVENT_CATEGORY Category() const
198 199 200 201
    {
        return m_category;
    }

202
    ///> Returns more specific information about the type of an event.
Maciej Suminski's avatar
Maciej Suminski committed
203
    TOOL_ACTIONS Action() const
204 205 206 207
    {
        return m_actions;
    }

208 209
    ///> Returns information about difference between current mouse cursor position and the place
    ///> where dragging has started.
210 211
    const VECTOR2D Delta() const
    {
Maciej Suminski's avatar
Maciej Suminski committed
212
        assert( m_category == TC_MOUSE );    // this should be used only with mouse events
213 214 215
        return m_mouseDelta;
    }

216
    ///> Returns mouse cursor position in world coordinates.
217 218
    const VECTOR2D& Position() const
    {
Maciej Suminski's avatar
Maciej Suminski committed
219
        assert( m_category == TC_MOUSE );    // this should be used only with mouse events
220 221 222
        return m_mousePos;
    }

223
    ///> Returns the point where dragging has started.
224 225
    const VECTOR2D& DragOrigin() const
    {
Maciej Suminski's avatar
Maciej Suminski committed
226
        assert( m_category == TC_MOUSE );    // this should be used only with mouse events
227 228 229
        return m_mouseDragOrigin;
    }

230
    ///> Returns information about mouse buttons state.
231 232
    int Buttons() const
    {
Maciej Suminski's avatar
Maciej Suminski committed
233
        assert( m_category == TC_MOUSE );    // this should be used only with mouse events
234 235 236
        return m_mouseButtons;
    }

237
    bool IsClick( int aButtonMask = BUT_ANY ) const
238
    {
Maciej Suminski's avatar
Maciej Suminski committed
239
        return ( m_actions == TA_MOUSE_CLICK )
240
               && ( ( m_mouseButtons & aButtonMask ) == aButtonMask );
241 242
    }

243 244 245 246 247 248
    bool IsDblClick( int aButtonMask = BUT_ANY ) const
    {
        return ( m_actions == TA_MOUSE_DBLCLICK )
               && ( ( m_mouseButtons & aButtonMask ) == aButtonMask );
    }

249
    bool IsDrag( int aButtonMask = BUT_ANY ) const
250
    {
Maciej Suminski's avatar
Maciej Suminski committed
251
        return ( m_actions == TA_MOUSE_DRAG ) && ( ( m_mouseButtons & aButtonMask ) == aButtonMask );
252 253
    }

254
    bool IsMouseUp( int aButtonMask = BUT_ANY ) const
255
    {
Maciej Suminski's avatar
Maciej Suminski committed
256
        return ( m_actions == TA_MOUSE_UP ) && ( ( m_mouseButtons & aButtonMask ) == aButtonMask );
257 258 259 260
    }

    bool IsMotion() const
    {
Maciej Suminski's avatar
Maciej Suminski committed
261
        return m_actions == TA_MOUSE_MOTION;
262 263 264 265
    }

    bool IsCancel() const
    {
Maciej Suminski's avatar
Maciej Suminski committed
266
        return m_actions == TA_CANCEL_TOOL;
267 268
    }

269
    ///> Returns information about key modifiers state (Ctrl, Alt, etc.)
Maciej Suminski's avatar
Maciej Suminski committed
270
    int Modifier( int aMask = MD_MODIFIER_MASK ) const
271
    {
272
        return m_modifiers & aMask;
273 274 275
    }

    int KeyCode() const
276
    {
277
        return m_keyCode;
278 279
    }

280 281
    bool IsKeyUp() const
    {
Maciej Suminski's avatar
Maciej Suminski committed
282
        return m_actions == TA_KEY_UP;
283 284 285 286
    }

    bool IsKeyDown() const
    {
Maciej Suminski's avatar
Maciej Suminski committed
287
        return m_actions == TA_KEY_DOWN;
288 289
    }

290
    void SetMouseDragOrigin( const VECTOR2D& aP )
291 292
    {
        m_mouseDragOrigin = aP;
293
     }
294 295 296 297 298 299 300 301 302 303 304

    void SetMousePosition( const VECTOR2D& aP )
    {
        m_mousePos = aP;
    }

    void SetMouseDelta( const VECTOR2D& aP )
    {
        m_mouseDelta = aP;
    }

305 306 307 308 309 310 311
    /**
     * Function Matches()
     * Tests whether two events match in terms of category & action or command.
     *
     * @param aEvent is the event to test against.
     * @return True if two events match, false otherwise.
     */
312 313
    bool Matches( const TOOL_EVENT& aEvent ) const
    {
Maciej Suminski's avatar
Maciej Suminski committed
314
        if( !( m_category & aEvent.m_category ) )
315 316
            return false;

Maciej Suminski's avatar
Maciej Suminski committed
317
        if( !( m_actions & aEvent.m_actions ) )
318 319
            return false;

Maciej Suminski's avatar
Maciej Suminski committed
320
        if( m_category == TC_COMMAND )
321 322
        {
            if( m_commandStr && aEvent.m_commandStr )
323 324
                return *m_commandStr == *aEvent.m_commandStr;

325
            if( m_commandId && aEvent.m_commandId )
326
                return *m_commandId == *aEvent.m_commandId;
327 328 329

            // Command-type event has to contain either id or string
            assert( false );
330 331 332 333 334
        }

        return true;
    }

335 336 337 338 339 340 341 342
    /**
     * Function IsAction()
     * Tests if the event contains an action issued upon activation of the given TOOL_ACTION.
     * @param aAction is the TOOL_ACTION to be checked against.
     * @return True if it matches the given TOOL_ACTION.
     */
    bool IsAction( const TOOL_ACTION* aAction ) const;

343 344 345 346 347 348 349 350
    boost::optional<int> GetCommandId()
    {
        return m_commandId;
    }

private:
    friend class TOOL_MANAGER;

Maciej Suminski's avatar
Maciej Suminski committed
351 352 353
    TOOL_EVENT_CATEGORY m_category;
    TOOL_ACTIONS m_actions;
    TOOL_ACTION_SCOPE m_scope;
354

355 356
    ///> Difference between mouse cursor position and
    ///> the point where dragging event has started
357
    VECTOR2D m_mouseDelta;
358 359

    ///> Current mouse cursor position
360
    VECTOR2D m_mousePos;
361 362

    ///> Point where dragging has started
363 364
    VECTOR2D m_mouseDragOrigin;

365
    ///> State of mouse buttons
366
    int m_mouseButtons;
367 368

    ///> Stores code of pressed/released key
369
    int m_keyCode;
370 371

    ///> State of key modifierts (Ctrl/Alt/etc.)
372
    int m_modifiers;
373

374 375
    boost::optional<int> m_commandId;
    boost::optional<std::string> m_commandStr;
376 377 378 379
};

typedef boost::optional<TOOL_EVENT> OPT_TOOL_EVENT;

380
/**
381
 * Class TOOL_EVENT_LIST
382
 *
383 384 385
 * A list of TOOL_EVENTs, with overloaded || operators allowing for
 * concatenating TOOL_EVENTs with little code.
 */
386 387
class TOOL_EVENT_LIST
{
388 389 390 391 392
public:
    typedef TOOL_EVENT value_type;
    typedef std::deque<TOOL_EVENT>::iterator iterator;
    typedef std::deque<TOOL_EVENT>::const_iterator const_iterator;

393
    ///> Default constructor. Creates an empty list.
Maciej Suminski's avatar
Maciej Suminski committed
394 395
    TOOL_EVENT_LIST()
    {}
396 397

    ///> Constructor for a list containing only one TOOL_EVENT.
398 399
    TOOL_EVENT_LIST( const TOOL_EVENT& aSingleEvent )
    {
400
        m_events.push_back( aSingleEvent );
401 402
    }

403 404 405 406 407 408
    /**
     * Function Format()
     * Returns information about event in form of a human-readable string.
     *
     * @return Event information.
     */
409 410
    const std::string Format() const;

Maciej Suminski's avatar
Maciej Suminski committed
411
    boost::optional<const TOOL_EVENT&> Matches( const TOOL_EVENT& aEvent ) const
412 413
    {
        for( const_iterator i = m_events.begin(); i != m_events.end(); ++i )
Maciej Suminski's avatar
Maciej Suminski committed
414
            if( i->Matches( aEvent ) )
415
                return *i;
416

417 418 419
        return boost::optional<const TOOL_EVENT&>();
    }

420 421 422 423 424
    /**
     * Function Add()
     * Adds a tool event to the list.
     * @param aEvent is the tool event to be addded.
     */
425 426 427 428 429 430 431 432 433 434 435 436 437 438 439
    void Add( const TOOL_EVENT& aEvent )
    {
        m_events.push_back( aEvent );
    }

    iterator begin()
    {
        return m_events.begin();
    }

    iterator end()
    {
        return m_events.end();
    }

440
    const_iterator cbegin() const
441 442 443 444
    {
        return m_events.begin();
    }

445
    const_iterator cend() const
446 447 448 449 450 451 452 453 454 455 456 457 458 459
    {
        return m_events.end();
    }

    int size() const
    {
        return m_events.size();
    }

    void clear()
    {
        m_events.clear();
    }

Maciej Suminski's avatar
Maciej Suminski committed
460
    TOOL_EVENT_LIST& operator=( const TOOL_EVENT_LIST& aEventList )
461 462 463
    {
        m_events.clear();

Maciej Suminski's avatar
Maciej Suminski committed
464 465
        for( std::deque<TOOL_EVENT>::const_iterator i = aEventList.m_events.begin();
             i != aEventList.m_events.end(); ++i )
466
        {
467
            m_events.push_back( *i );
468 469 470 471 472
        }

        return *this;
    }

Maciej Suminski's avatar
Maciej Suminski committed
473
    TOOL_EVENT_LIST& operator=( const TOOL_EVENT& aEvent )
474 475
    {
        m_events.clear();
Maciej Suminski's avatar
Maciej Suminski committed
476
        m_events.push_back( aEvent );
477 478 479
        return *this;
    }

Maciej Suminski's avatar
Maciej Suminski committed
480
    TOOL_EVENT_LIST& operator||( const TOOL_EVENT& aEvent )
481
    {
Maciej Suminski's avatar
Maciej Suminski committed
482
        Add( aEvent );
483 484 485
        return *this;
    }

Maciej Suminski's avatar
Maciej Suminski committed
486
    TOOL_EVENT_LIST& operator||( const TOOL_EVENT_LIST& aEvent )
487 488 489 490 491 492
    {
        return *this;
    }

private:
    std::deque<TOOL_EVENT> m_events;
493 494
};

Maciej Suminski's avatar
Maciej Suminski committed
495
inline const TOOL_EVENT_LIST operator||( const TOOL_EVENT& aEventA, const TOOL_EVENT& aEventB )
496
{
497
    TOOL_EVENT_LIST l;
498

Maciej Suminski's avatar
Maciej Suminski committed
499 500
    l.Add( aEventA );
    l.Add( aEventB );
501

502
    return l;
503 504
}

505

Maciej Suminski's avatar
Maciej Suminski committed
506 507
inline const TOOL_EVENT_LIST operator||( const TOOL_EVENT& aEvent,
                                         const TOOL_EVENT_LIST& aEventList )
508
{
Maciej Suminski's avatar
Maciej Suminski committed
509
    TOOL_EVENT_LIST l( aEventList );
510

Maciej Suminski's avatar
Maciej Suminski committed
511
    l.Add( aEvent );
512
    return l;
513 514 515
}

#endif