tool_event.h 13 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
    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,

67 68
    TA_KEY_PRESSED          = 0x0080,
    TA_KEYBOARD             = TA_KEY_PRESSED,
69 70

    // View related events
71 72 73 74 75
    TA_VIEW_REFRESH         = 0x0100,
    TA_VIEW_ZOOM            = 0x0200,
    TA_VIEW_PAN             = 0x0400,
    TA_VIEW_DIRTY           = 0x0800,
    TA_VIEW                 = 0x0f00,
76

77
    TA_CHANGE_LAYER         = 0x1000,
78

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

83 84
    // 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
85
    TA_CONTEXT_MENU_UPDATE  = 0x4000,
86

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

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

94
    // Tool action (allows to control tools)
95
    TA_ACTION               = 0x20000,
96

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

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

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

118
/// Scope of tool actions
Maciej Suminski's avatar
Maciej Suminski committed
119
enum TOOL_ACTION_SCOPE
120 121 122 123 124 125 126
{
    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.
127
enum CONTEXT_MENU_TRIGGER
128
{
129 130 131
    CMENU_BUTTON = 0,   // On the right button
    CMENU_NOW,          // Right now (after TOOL_INTERACTIVE::SetContextMenu)
    CMENU_OFF           // Never
132 133
};

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

279
    bool IsKeyPressed() const
280
    {
281
        return m_actions == TA_KEY_PRESSED;
282 283
    }

284
    void SetMouseDragOrigin( const VECTOR2D& aP )
285 286
    {
        m_mouseDragOrigin = aP;
287
     }
288 289 290 291 292 293 294 295 296 297 298

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

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

299 300 301 302 303 304 305
    /**
     * 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.
     */
306 307
    bool Matches( const TOOL_EVENT& aEvent ) const
    {
Maciej Suminski's avatar
Maciej Suminski committed
308
        if( !( m_category & aEvent.m_category ) )
309 310
            return false;

Maciej Suminski's avatar
Maciej Suminski committed
311
        if( !( m_actions & aEvent.m_actions ) )
312 313
            return false;

314
        if( m_category == TC_COMMAND || m_category == TC_MESSAGE )
315 316
        {
            if( m_commandStr && aEvent.m_commandStr )
317 318
                return *m_commandStr == *aEvent.m_commandStr;

319
            if( m_commandId && aEvent.m_commandId )
320
                return *m_commandId == *aEvent.m_commandId;
321 322 323

            // Command-type event has to contain either id or string
            assert( false );
324 325 326 327 328
        }

        return true;
    }

329 330 331 332 333 334 335 336
    /**
     * 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;

337 338 339 340 341 342 343 344
    boost::optional<int> GetCommandId()
    {
        return m_commandId;
    }

private:
    friend class TOOL_MANAGER;

Maciej Suminski's avatar
Maciej Suminski committed
345 346 347
    TOOL_EVENT_CATEGORY m_category;
    TOOL_ACTIONS m_actions;
    TOOL_ACTION_SCOPE m_scope;
348

349 350
    ///> Difference between mouse cursor position and
    ///> the point where dragging event has started
351
    VECTOR2D m_mouseDelta;
352 353

    ///> Current mouse cursor position
354
    VECTOR2D m_mousePos;
355 356

    ///> Point where dragging has started
357 358
    VECTOR2D m_mouseDragOrigin;

359
    ///> State of mouse buttons
360
    int m_mouseButtons;
361 362

    ///> Stores code of pressed/released key
363
    int m_keyCode;
364 365

    ///> State of key modifierts (Ctrl/Alt/etc.)
366
    int m_modifiers;
367

368 369
    boost::optional<int> m_commandId;
    boost::optional<std::string> m_commandStr;
370 371 372 373
};

typedef boost::optional<TOOL_EVENT> OPT_TOOL_EVENT;

374
/**
375
 * Class TOOL_EVENT_LIST
376
 *
377 378 379
 * A list of TOOL_EVENTs, with overloaded || operators allowing for
 * concatenating TOOL_EVENTs with little code.
 */
380 381
class TOOL_EVENT_LIST
{
382 383 384 385 386
public:
    typedef TOOL_EVENT value_type;
    typedef std::deque<TOOL_EVENT>::iterator iterator;
    typedef std::deque<TOOL_EVENT>::const_iterator const_iterator;

387
    ///> Default constructor. Creates an empty list.
Maciej Suminski's avatar
Maciej Suminski committed
388 389
    TOOL_EVENT_LIST()
    {}
390 391

    ///> Constructor for a list containing only one TOOL_EVENT.
392 393
    TOOL_EVENT_LIST( const TOOL_EVENT& aSingleEvent )
    {
394
        m_events.push_back( aSingleEvent );
395 396
    }

397 398 399 400 401 402
    /**
     * Function Format()
     * Returns information about event in form of a human-readable string.
     *
     * @return Event information.
     */
403 404
    const std::string Format() const;

Maciej Suminski's avatar
Maciej Suminski committed
405
    boost::optional<const TOOL_EVENT&> Matches( const TOOL_EVENT& aEvent ) const
406 407
    {
        for( const_iterator i = m_events.begin(); i != m_events.end(); ++i )
Maciej Suminski's avatar
Maciej Suminski committed
408
            if( i->Matches( aEvent ) )
409
                return *i;
410

411 412 413
        return boost::optional<const TOOL_EVENT&>();
    }

414 415 416 417 418
    /**
     * Function Add()
     * Adds a tool event to the list.
     * @param aEvent is the tool event to be addded.
     */
419 420 421 422 423 424 425 426 427 428 429 430 431 432 433
    void Add( const TOOL_EVENT& aEvent )
    {
        m_events.push_back( aEvent );
    }

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

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

434
    const_iterator cbegin() const
435 436 437 438
    {
        return m_events.begin();
    }

439
    const_iterator cend() const
440 441 442 443 444 445 446 447 448 449 450 451 452 453
    {
        return m_events.end();
    }

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

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

Maciej Suminski's avatar
Maciej Suminski committed
454
    TOOL_EVENT_LIST& operator=( const TOOL_EVENT_LIST& aEventList )
455 456 457
    {
        m_events.clear();

Maciej Suminski's avatar
Maciej Suminski committed
458 459
        for( std::deque<TOOL_EVENT>::const_iterator i = aEventList.m_events.begin();
             i != aEventList.m_events.end(); ++i )
460
        {
461
            m_events.push_back( *i );
462 463 464 465 466
        }

        return *this;
    }

Maciej Suminski's avatar
Maciej Suminski committed
467
    TOOL_EVENT_LIST& operator=( const TOOL_EVENT& aEvent )
468 469
    {
        m_events.clear();
Maciej Suminski's avatar
Maciej Suminski committed
470
        m_events.push_back( aEvent );
471 472 473
        return *this;
    }

Maciej Suminski's avatar
Maciej Suminski committed
474
    TOOL_EVENT_LIST& operator||( const TOOL_EVENT& aEvent )
475
    {
Maciej Suminski's avatar
Maciej Suminski committed
476
        Add( aEvent );
477 478 479
        return *this;
    }

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

private:
    std::deque<TOOL_EVENT> m_events;
487 488
};

Maciej Suminski's avatar
Maciej Suminski committed
489
inline const TOOL_EVENT_LIST operator||( const TOOL_EVENT& aEventA, const TOOL_EVENT& aEventB )
490
{
491
    TOOL_EVENT_LIST l;
492

Maciej Suminski's avatar
Maciej Suminski committed
493 494
    l.Add( aEventA );
    l.Add( aEventB );
495

496
    return l;
497 498
}

499

Maciej Suminski's avatar
Maciej Suminski committed
500 501
inline const TOOL_EVENT_LIST operator||( const TOOL_EVENT& aEvent,
                                         const TOOL_EVENT_LIST& aEventList )
502
{
Maciej Suminski's avatar
Maciej Suminski committed
503
    TOOL_EVENT_LIST l( aEventList );
504

Maciej Suminski's avatar
Maciej Suminski committed
505
    l.Add( aEvent );
506
    return l;
507 508 509
}

#endif