KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pl_selection_tool.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2019 CERN
5 * Copyright (C) 2019-2022 KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, you may find one here:
19 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20 * or you may search the http://www.gnu.org website for the version 2 license,
21 * or you may write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 */
24
25#include <advanced_config.h>
26#include <view/view.h>
27#include <view/view_controls.h>
29#include <tool/tool_event.h>
30#include <tool/tool_manager.h>
31#include <tool/selection.h>
34#include <tools/pl_actions.h>
38#include <collector.h>
39#include <math/util.h> // for KiROUND
40
41#include "pl_editor_frame.h"
42
43
44#define HITTEST_THRESHOLD_PIXELS 3
45
46
48 SELECTION_TOOL( "plEditor.InteractiveSelection" ),
49 m_frame( nullptr )
50{
51}
52
53
55{
56 m_frame = getEditFrame<PL_EDITOR_FRAME>();
57
58 auto& menu = m_menu.GetMenu();
59
60 menu.AddSeparator( 200 );
65
66 menu.AddSeparator( 1000 );
68
69 m_disambiguateTimer.SetOwner( this );
70 Connect( wxEVT_TIMER, wxTimerEventHandler( PL_SELECTION_TOOL::onDisambiguationExpire ), nullptr, this );
71
72 return true;
73}
74
75
77{
78 if( aReason == MODEL_RELOAD )
79 m_frame = getEditFrame<PL_EDITOR_FRAME>();
80}
81
82
84{
85 // Main loop: keep receiving events
86 while( TOOL_EVENT* evt = Wait() )
87 {
88 // on left click, a selection is made, depending on modifiers ALT, SHIFT, CTRL:
89 setModifiersState( evt->Modifier( MD_SHIFT ), evt->Modifier( MD_CTRL ),
90 evt->Modifier( MD_ALT ) );
91
92 if( evt->IsMouseDown( BUT_LEFT ) )
93 {
94 // Avoid triggering when running under other tools
96
97 if( m_frame->ToolStackIsEmpty() && pt_tool && !pt_tool->HasPoint() )
98 {
101 }
102 }
103 // Single click? Select single object
104 else if( evt->IsClick( BUT_LEFT ) )
105 {
106 // If the timer has stopped, then we have already run the disambiguate routine
107 // and we don't want to register an extra click here
108 if( !m_disambiguateTimer.IsRunning() )
109 {
110 evt->SetPassEvent();
111 continue;
112 }
113
114 m_disambiguateTimer.Stop();
115 SelectPoint( evt->Position() );
116 }
117
118 // right click? if there is any object - show the context menu
119 else if( evt->IsClick( BUT_RIGHT ) )
120 {
121 m_disambiguateTimer.Stop();
122 bool selectionCancelled = false;
123
124 if( m_selection.Empty() )
125 {
126 SelectPoint( evt->Position(), &selectionCancelled );
127 m_selection.SetIsHover( true );
128 }
129
130 // Show selection before opening menu
132
133 if( !selectionCancelled )
135 }
136
137 // double click? Display the properties window
138 else if( evt->IsDblClick( BUT_LEFT ) )
139 {
140 // No double-click actions currently defined
141 }
142
143 // drag with LMB? Select multiple objects (or at least draw a selection box) or drag them
144 else if( evt->IsDrag( BUT_LEFT ) )
145 {
146 m_disambiguateTimer.Stop();
147
148 if( hasModifier() || m_selection.Empty() )
149 {
151 }
152 else
153 {
154 // Check if dragging has started within any of selected items bounding box
155 if( selectionContains( evt->Position() ) )
156 {
157 // Yes -> run the move tool and wait till it finishes
158 m_toolMgr->RunAction( "plEditor.InteractiveMove.move" );
159 }
160 else
161 {
162 // No -> clear the selection list
164 }
165 }
166 }
167
168 // Middle double click? Do zoom to fit or zoom to objects
169 else if( evt->IsDblClick( BUT_MIDDLE ) )
170 {
172 }
173
174 else if( evt->IsCancelInteractive() )
175 {
176 m_disambiguateTimer.Stop();
178 }
179
180 else if( evt->Action() == TA_UNDO_REDO_PRE )
181 {
183 }
184
185 else
186 evt->SetPassEvent();
187
188
190 {
191 if( !hasModifier()
192 && !m_selection.Empty()
193 && m_frame->GetDragAction() == MOUSE_DRAG_ACTION::DRAG_SELECTED
194 && evt->HasPosition()
195 && selectionContains( evt->Position() ) )
196 {
197 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::MOVING );
198 }
199 else
200 {
201 if( m_additive )
202 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ADD );
203 else if( m_subtractive )
204 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::SUBTRACT );
205 else if( m_exclusive_or )
206 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::XOR );
207 else
208 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
209 }
210 }
211 }
212
213 return 0;
214}
215
216
218{
219 wxMouseState keyboardState = wxGetMouseState();
220
221 setModifiersState( keyboardState.ShiftDown(), keyboardState.ControlDown(),
222 keyboardState.AltDown() );
223
224 m_skip_heuristics = true;
226 m_skip_heuristics = false;
227
228 return 0;
229}
230
231
233{
234 return m_selection;
235}
236
237
238void PL_SELECTION_TOOL::SelectPoint( const VECTOR2I& aWhere, bool* aSelectionCancelledFlag )
239{
240 int threshold = KiROUND( getView()->ToWorld( HITTEST_THRESHOLD_PIXELS ) );
241
242 // locate items.
243 COLLECTOR collector;
244
245 for( DS_DATA_ITEM* dataItem : DS_DATA_MODEL::GetTheInstance().GetItems() )
246 {
247 for( DS_DRAW_ITEM_BASE* drawItem : dataItem->GetDrawItems() )
248 {
249 if( drawItem->HitTest( aWhere, threshold ) )
250 collector.Append( drawItem );
251 }
252 }
253
255
256 // Apply some ugly heuristics to avoid disambiguation menus whenever possible
257 if( collector.GetCount() > 1 && !m_skip_heuristics )
258 guessSelectionCandidates( collector, aWhere );
259
260 // If still more than one item we're going to have to ask the user.
261 if( collector.GetCount() > 1 )
262 {
263 doSelectionMenu( &collector );
264
265 if( collector.m_MenuCancelled )
266 {
267 if( aSelectionCancelledFlag )
268 *aSelectionCancelledFlag = true;
269
270 return;
271 }
272 }
273
274 bool anyAdded = false;
275 bool anySubtracted = false;
276
277
279 {
280 if( collector.GetCount() == 0 )
281 anySubtracted = true;
282
284 }
285
286 if( collector.GetCount() > 0 )
287 {
288 for( int i = 0; i < collector.GetCount(); ++i )
289 {
290 if( m_subtractive || ( m_exclusive_or && collector[i]->IsSelected() ) )
291 {
292 unselect( collector[i] );
293 anySubtracted = true;
294 }
295 else
296 {
297 select( collector[i] );
298 anyAdded = true;
299 }
300 }
301 }
302
303 if( anyAdded )
305
306 if( anySubtracted )
308}
309
310
312{
313 // There are certain conditions that can be handled automatically.
314
315 // Prefer an exact hit to a sloppy one
316 for( int i = 0; collector.GetCount() == 2 && i < 2; ++i )
317 {
318 EDA_ITEM* item = collector[ i ];
319 EDA_ITEM* other = collector[ ( i + 1 ) % 2 ];
320
321 if( item->HitTest( aPos, 0 ) && !other->HitTest( aPos, 0 ) )
322 collector.Transfer( other );
323 }
324}
325
326
328{
329 // If nothing is selected do a hover selection
330 if( m_selection.Empty() )
331 {
332 VECTOR2D cursorPos = getViewControls()->GetCursorPosition( true );
333
335 SelectPoint( cursorPos );
336 m_selection.SetIsHover( true );
337 }
338
339 return m_selection;
340}
341
342
344{
345 bool cancelled = false; // Was the tool cancelled while it was running?
346 m_multiple = true; // Multiple selection mode is active
347 KIGFX::VIEW* view = getView();
348
350 view->Add( &area );
351
352 while( TOOL_EVENT* evt = Wait() )
353 {
354 int width = area.GetEnd().x - area.GetOrigin().x;
355
356 /* Selection mode depends on direction of drag-selection:
357 * Left > Right : Select objects that are fully enclosed by selection
358 * Right > Left : Select objects that are crossed by selection
359 */
360 bool windowSelection = width >= 0 ? true : false;
361
362 m_frame->GetCanvas()->SetCurrentCursor( windowSelection ? KICURSOR::SELECT_WINDOW
363 : KICURSOR::SELECT_LASSO );
364
365 if( evt->IsCancelInteractive() || evt->IsActivate() )
366 {
367 cancelled = true;
368 break;
369 }
370
371 if( evt->IsDrag( BUT_LEFT ) )
372 {
375
376 // Start drawing a selection box
377 area.SetOrigin( evt->DragOrigin() );
378 area.SetEnd( evt->Position() );
381 area.SetExclusiveOr( false );
382
383 view->SetVisible( &area, true );
384 view->Update( &area );
385 getViewControls()->SetAutoPan( true );
386 }
387
388 if( evt->IsMouseUp( BUT_LEFT ) )
389 {
390 getViewControls()->SetAutoPan( false );
391
392 // End drawing the selection box
393 view->SetVisible( &area, false );
394
395 int height = area.GetEnd().y - area.GetOrigin().y;
396
397 bool anyAdded = false;
398 bool anySubtracted = false;
399
400 // Construct a BOX2I to determine EDA_ITEM selection
401 BOX2I selectionRect( area.GetOrigin(), VECTOR2I( width, height ) );
402
403 selectionRect.Normalize();
404
405 for( DS_DATA_ITEM* dataItem : DS_DATA_MODEL::GetTheInstance().GetItems() )
406 {
407 for( DS_DRAW_ITEM_BASE* item : dataItem->GetDrawItems() )
408 {
409 if( item->HitTest( selectionRect, windowSelection ) )
410 {
411 if( m_subtractive || ( m_exclusive_or && item->IsSelected() ) )
412 {
413 unselect( item );
414 anySubtracted = true;
415 }
416 else
417 {
418 select( item );
419 anyAdded = true;
420 }
421 }
422 }
423 }
424
425 // Inform other potentially interested tools
426 if( anyAdded )
428
429 if( anySubtracted )
431
432 break; // Stop waiting for events
433 }
434 }
435
436 getViewControls()->SetAutoPan( false );
437
438 // Stop drawing the selection box
439 view->Remove( &area );
440 m_multiple = false; // Multiple selection mode is inactive
441
442 if( !cancelled )
444
445 return cancelled;
446}
447
448
450{
452 return 0;
453}
454
455
457{
459
460 for( DS_DATA_ITEM* dataItem : DS_DATA_MODEL::GetTheInstance().GetItems() )
461 {
462 for( DS_DRAW_ITEM_BASE* item : dataItem->GetDrawItems() )
463 {
464 if( item->IsSelected() )
465 select( item );
466 }
467 }
468}
469
470
472{
473 if( m_selection.Empty() )
474 return;
475
476 while( m_selection.GetSize() )
478
480
481 m_selection.SetIsHover( false );
483
484 // Inform other potentially interested tools
486}
487
488
490{
491 highlight( aItem, SELECTED, &m_selection );
492}
493
494
496{
497 unhighlight( aItem, SELECTED, &m_selection );
498}
499
500
501void PL_SELECTION_TOOL::highlight( EDA_ITEM* aItem, int aMode, SELECTION* aGroup )
502{
503 if( aMode == SELECTED )
504 aItem->SetSelected();
505 else if( aMode == BRIGHTENED )
506 aItem->SetBrightened();
507
508 if( aGroup )
509 aGroup->Add( aItem );
510
511 getView()->Update( aItem );
512}
513
514
515void PL_SELECTION_TOOL::unhighlight( EDA_ITEM* aItem, int aMode, SELECTION* aGroup )
516{
517 if( aMode == SELECTED )
518 aItem->ClearSelected();
519 else if( aMode == BRIGHTENED )
520 aItem->ClearBrightened();
521
522 if( aGroup )
523 aGroup->Remove( aItem );
524
525 getView()->Update( aItem );
526}
527
528
530{
531 const unsigned GRIP_MARGIN = 20;
532 VECTOR2I margin = getView()->ToWorld( VECTOR2I( GRIP_MARGIN, GRIP_MARGIN ), false );
533
534 // Check if the point is located within any of the currently selected items bounding boxes
535 for( EDA_ITEM* item : m_selection )
536 {
537 BOX2I itemBox = item->ViewBBox();
538 itemBox.Inflate( margin.x, margin.y ); // Give some margin for gripping an item
539
540 if( itemBox.Contains( aPoint ) )
541 return true;
542 }
543
544 return false;
545}
546
547
549{
551
554
560
562}
static TOOL_ACTION updateMenu
Definition: actions.h:181
static TOOL_ACTION zoomFitScreen
Definition: actions.h:101
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
BOX2< Vec > & Normalize()
Ensure that the height and width are positive.
Definition: box2.h:120
bool Contains(const Vec &aPoint) const
Definition: box2.h:142
BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition: box2.h:507
An abstract class that will find and hold all the objects according to an inspection done by the Insp...
Definition: collector.h:48
void Transfer(int aIndex)
Move the item at aIndex (first position is 0) to the backup list.
Definition: collector.h:151
bool m_MenuCancelled
Definition: collector.h:237
int GetCount() const
Return the number of objects in the list.
Definition: collector.h:81
void Append(EDA_ITEM *item)
Add an item to the end of the list.
Definition: collector.h:99
void AddSeparator(int aOrder=ANY_ORDER)
Add a separator to the menu.
Drawing sheet structure type definitions.
Definition: ds_data_item.h:96
static DS_DATA_MODEL & GetTheInstance()
static function: returns the instance of DS_DATA_MODEL used in the application
Base class to handle basic graphic items.
Definition: ds_draw_item.h:59
void AddStandardSubMenus(TOOL_MENU &aMenu)
Construct a "basic" menu for a tool, containing only items that apply to all tools (e....
void ForceRefresh()
Force a redraw.
void SetCurrentCursor(KICURSOR aCursor)
Set the current cursor shape for this panel.
A base class for most all the KiCad significant classes used in schematics and boards.
Definition: eda_item.h:85
void ClearSelected()
Definition: eda_item.h:118
void SetSelected()
Definition: eda_item.h:115
void ClearBrightened()
Definition: eda_item.h:119
void SetBrightened()
Definition: eda_item.h:116
virtual bool HitTest(const VECTOR2I &aPosition, int aAccuracy=0) const
Test if aPosition is inside or on the boundary of this item.
Definition: eda_item.h:212
static const TOOL_EVENT DisambiguatePoint
Used for hotkey feedback.
Definition: actions.h:251
static const TOOL_EVENT ClearedEvent
Definition: actions.h:236
static const TOOL_EVENT SelectedEvent
Definition: actions.h:234
static const TOOL_EVENT UnselectedEvent
Definition: actions.h:235
Represent a selection area (currently a rectangle) in a VIEW, drawn corner-to-corner between two poin...
void SetSubtractive(bool aSubtractive)
void SetAdditive(bool aAdditive)
void SetOrigin(const VECTOR2I &aOrigin)
void SetExclusiveOr(bool aExclusiveOr)
void SetEnd(const VECTOR2I &aEnd)
Set the current end of the rectangle (the corner that moves with the cursor.
VECTOR2D GetCursorPosition() const
Return the current cursor position in world coordinates.
virtual void SetAutoPan(bool aEnabled)
Turn on/off auto panning (this feature is used when there is a tool active (eg.
Hold a (potentially large) number of VIEW_ITEMs and renders them on a graphics device provided by the...
Definition: view.h:68
virtual void Add(VIEW_ITEM *aItem, int aDrawPriority=-1)
Add a VIEW_ITEM to the view.
Definition: view.cpp:313
virtual void Remove(VIEW_ITEM *aItem)
Remove a VIEW_ITEM from the view.
Definition: view.cpp:350
virtual void Update(const VIEW_ITEM *aItem, int aUpdateFlags) const
For dynamic VIEWs, inform the associated VIEW that the graphical representation of this item has chan...
Definition: view.cpp:1619
VECTOR2D ToWorld(const VECTOR2D &aCoord, bool aAbsolute=true) const
Converts a screen space point/vector to a point/vector in world space coordinates.
Definition: view.cpp:444
void SetVisible(VIEW_ITEM *aItem, bool aIsVisible=true)
Set the item visibility.
Definition: view.cpp:1546
static TOOL_ACTION clearSelection
Clear the current selection.
Definition: pl_actions.h:43
static TOOL_ACTION placeImage
Definition: pl_actions.h:59
static TOOL_ACTION selectionActivate
Activation of the selection tool.
Definition: pl_actions.h:40
static TOOL_ACTION selectionMenu
Run a selection menu to select from a list of items.
Definition: pl_actions.h:54
static TOOL_ACTION drawRectangle
Definition: pl_actions.h:60
static TOOL_ACTION removeItemFromSel
Definition: pl_actions.h:47
static TOOL_ACTION addItemsToSel
Select a list of items (specified as the event parameter)
Definition: pl_actions.h:50
static TOOL_ACTION removeItemsFromSel
Definition: pl_actions.h:51
static TOOL_ACTION addItemToSel
Select an item (specified as the event parameter).
Definition: pl_actions.h:46
static TOOL_ACTION placeText
Definition: pl_actions.h:58
static TOOL_ACTION drawLine
Definition: pl_actions.h:61
PL_DRAW_PANEL_GAL * GetCanvas() const override
Return a pointer to GAL-based canvas of given EDA draw frame.
Tool that displays edit points allowing to modify items by dragging the points.
bool HasPoint()
Indicate the cursor is over an edit point.
void select(EDA_ITEM *aItem) override
Takes necessary action mark an item as selected.
void setTransitions() override
This method is meant to be overridden in order to specify handlers for events.
bool Init() override
Init() is called once upon a registration of the tool.
PL_SELECTION m_selection
int disambiguateCursor(const TOOL_EVENT &aEvent)
Handle disambiguation actions including displaying the menu.
void SelectPoint(const VECTOR2I &aWhere, bool *aSelectionCancelledFlag=nullptr)
Select an item pointed by the parameter aWhere.
void guessSelectionCandidates(COLLECTOR &collector, const VECTOR2I &aWhere)
Apply heuristics to try and determine a single object when multiple are found under the cursor.
bool selectionContains(const VECTOR2I &aPoint) const
Set up handlers for various events.
void unselect(EDA_ITEM *aItem) override
Take necessary action mark an item as unselected.
bool selectMultiple()
Handle drawing a selection box that allows one to select many items at the same time.
PL_SELECTION & GetSelection()
Return the set of currently selected items.
PL_SELECTION & RequestSelection()
Return either an existing selection (filtered), or the selection at the current cursor if the existin...
void highlight(EDA_ITEM *aItem, int aHighlightMode, SELECTION *aGroup=nullptr) override
Highlight the item visually.
void unhighlight(EDA_ITEM *aItem, int aHighlightMode, SELECTION *aGroup=nullptr) override
Unhighlight the item visually.
PL_EDITOR_FRAME * m_frame
int Main(const TOOL_EVENT &aEvent)
The main loop.
void RebuildSelection()
Rebuild the selection from the flags in the view items.
void Reset(RESET_REASON aReason) override
Bring the tool to a known, initial state.
static bool Empty(const SELECTION &aSelection)
Test if there are no items selected.
int RemoveItemFromSel(const TOOL_EVENT &aEvent)
bool doSelectionMenu(COLLECTOR *aCollector)
wxTimer m_disambiguateTimer
int AddItemsToSel(const TOOL_EVENT &aEvent)
int AddItemToSel(const TOOL_EVENT &aEvent)
int UpdateMenu(const TOOL_EVENT &aEvent)
Update a menu's state based on the current selection.
void setModifiersState(bool aShiftState, bool aCtrlState, bool aAltState)
Set the configuration of m_additive, m_subtractive, m_exclusive_or, m_skip_heuristics from the state ...
VECTOR2I m_originalCursor
int SelectionMenu(const TOOL_EVENT &aEvent)
Show a popup menu to trim the COLLECTOR passed as aEvent's parameter down to a single item.
int RemoveItemsFromSel(const TOOL_EVENT &aEvent)
bool hasModifier()
True if a selection modifier is enabled, false otherwise.
void onDisambiguationExpire(wxTimerEvent &aEvent)
Start the process to show our disambiguation menu once the user has kept the mouse down for the minim...
virtual void Add(EDA_ITEM *aItem)
Definition: selection.cpp:42
void SetIsHover(bool aIsHover)
Definition: selection.h:78
virtual void Remove(EDA_ITEM *aItem)
Definition: selection.cpp:60
virtual unsigned int GetSize() const override
Return the number of stored items.
Definition: selection.h:99
EDA_ITEM * Front() const
Definition: selection.h:208
virtual void Clear() override
Remove all the stored items from the group.
Definition: selection.h:92
void ClearReferencePoint()
Definition: selection.h:265
bool Empty() const
Checks if there is anything selected.
Definition: selection.h:109
MOUSE_DRAG_ACTION GetDragAction() const
Indicates whether a drag should draw a selection rectangle or drag selected (or unselected) objects.
Definition: tools_holder.h:144
bool ToolStackIsEmpty()
Definition: tools_holder.h:125
KIGFX::VIEW_CONTROLS * getViewControls() const
Return the instance of VIEW_CONTROLS object used in the application.
Definition: tool_base.cpp:42
TOOL_MANAGER * m_toolMgr
Definition: tool_base.h:216
KIGFX::VIEW * getView() const
Returns the instance of #VIEW object used in the application.
Definition: tool_base.cpp:36
RESET_REASON
Determine the reason of reset for a tool.
Definition: tool_base.h:78
@ MODEL_RELOAD
Model changes (the sheet for a schematic)
Definition: tool_base.h:80
Generic, UI-independent tool event.
Definition: tool_event.h:167
void Go(int(T::*aStateFunc)(const TOOL_EVENT &), const TOOL_EVENT_LIST &aConditions=TOOL_EVENT(TC_ANY, TA_ANY))
Define which state (aStateFunc) to go when a certain event arrives (aConditions).
TOOL_MENU m_menu
The functions below are not yet implemented - their interface may change.
TOOL_EVENT * Wait(const TOOL_EVENT_LIST &aEventList=TOOL_EVENT(TC_ANY, TA_ANY))
Suspend execution of the tool until an event specified in aEventList arrives.
bool ProcessEvent(const TOOL_EVENT &aEvent)
Propagate an event to tools that requested events of matching type(s).
bool RunAction(const std::string &aActionName, T aParam)
Run the specified action immediately, pausing the current action to run the new one.
Definition: tool_manager.h:145
VECTOR2D GetMousePosition() const
CONDITIONAL_MENU & GetMenu()
Definition: tool_menu.cpp:44
void ShowContextMenu(SELECTION &aSelection)
Helper function to set and immediately show a CONDITIONAL_MENU in concert with the given SELECTION.
Definition: tool_menu.cpp:57
#define BRIGHTENED
item is drawn with a bright contour
#define SELECTED
Item was manually selected by the user.
#define HITTEST_THRESHOLD_PIXELS
int m_DisambiguationMenuDelay
The number of milliseconds to wait in a click before showing a disambiguation menu.
@ TA_UNDO_REDO_PRE
Definition: tool_event.h:105
@ MD_ALT
Definition: tool_event.h:144
@ MD_CTRL
Definition: tool_event.h:143
@ MD_SHIFT
Definition: tool_event.h:142
@ BUT_MIDDLE
Definition: tool_event.h:133
@ BUT_LEFT
Definition: tool_event.h:131
@ BUT_RIGHT
Definition: tool_event.h:132
constexpr ret_type KiROUND(fp_type v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: util.h:85
VECTOR2< int > VECTOR2I
Definition: vector2d.h:588