-
Notifications
You must be signed in to change notification settings - Fork 9.3k
Expand file tree
/
Copy pathselectionState.cpp
More file actions
304 lines (274 loc) · 10.4 KB
/
selectionState.cpp
File metadata and controls
304 lines (274 loc) · 10.4 KB
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.h"
#include "../interactivity/inc/ServiceLocator.hpp"
#include "../types/inc/viewport.hpp"
using namespace Microsoft::Console::Types;
using namespace Microsoft::Console::Interactivity;
// Routine Description:
// - Determines whether the console is in a selecting state
// Arguments:
// - <none> (gets global state)
// Return Value:
// - True if the console is in a selecting state. False otherwise.
bool Selection::IsInSelectingState() const
{
const auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
return WI_IsFlagSet(gci.Flags, CONSOLE_SELECTING);
}
// Routine Description:
// - Helps set the global selecting state.
// Arguments:
// - fSelectingOn - Set true to set the global flag on. False to turn the global flag off.
// Return Value:
// - <none>
void Selection::_SetSelectingState(const bool fSelectingOn)
{
auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
WI_UpdateFlag(gci.Flags, CONSOLE_SELECTING, fSelectingOn);
}
// Routine Description:
// - Determines whether the console should do selections with the mouse
// a.k.a. "Quick Edit" mode
// Arguments:
// - <none> (gets global state)
// Return Value:
// - True if quick edit mode is enabled. False otherwise.
bool Selection::IsInQuickEditMode() const
{
const auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
return WI_IsFlagSet(gci.Flags, CONSOLE_QUICK_EDIT_MODE);
}
// Routine Description:
// - Determines whether we are performing a line selection right now
// Arguments:
// - <none>
// Return Value:
// - True if the selection is to be treated line by line. False if it is to be a block.
bool Selection::IsLineSelection() const
{
// if line selection is on and alternate is off -OR-
// if line selection is off and alternate is on...
return (_d->fLineSelection != _d->fUseAlternateSelection);
}
// Routine Description:
// - Assures that the alternate selection flag is flipped in line with the requested format.
// If true, we'll align to ensure line selection is used. If false, we'll make sure box selection is used.
// Arguments:
// - fAlignToLineSelect - whether or not to use line selection
// Return Value:
// - <none>
void Selection::_AlignAlternateSelection(const bool fAlignToLineSelect)
{
if (fAlignToLineSelect)
{
// states are opposite when in line selection.
// e.g. Line = true, Alternate = false.
// and Line = false, Alternate = true.
_d.write()->fUseAlternateSelection = !_d->fLineSelection;
}
else
{
// states are the same when in box selection.
// e.g. Line = true, Alternate = true.
// and Line = false, Alternate = false.
_d.write()->fUseAlternateSelection = _d->fLineSelection;
}
}
// Routine Description:
// - Determines whether the selection area is empty.
// Arguments:
// - <none>
// Return Value:
// - True if the selection variables contain valid selection data. False otherwise.
bool Selection::IsAreaSelected() const
{
return WI_IsFlagSet(_d->dwSelectionFlags, CONSOLE_SELECTION_NOT_EMPTY);
}
// Routine Description:
// - Determines whether mark mode specifically started this selection.
// Arguments:
// - <none>
// Return Value:
// - True if the selection was started as mark mode. False otherwise.
bool Selection::IsKeyboardMarkSelection() const
{
return WI_IsFlagClear(_d->dwSelectionFlags, CONSOLE_MOUSE_SELECTION);
}
// Routine Description:
// - Determines whether a mouse event was responsible for initiating this selection.
// - This primarily refers to mouse drag in QuickEdit mode.
// - However, it refers to any non-mark-mode selection, whether or not the mouse actually started it.
// Arguments:
// - <none>
// Return Value:
// - True if the selection is mouse-initiated. False otherwise.
bool Selection::IsMouseInitiatedSelection() const
{
return WI_IsFlagSet(_d->dwSelectionFlags, CONSOLE_MOUSE_SELECTION);
}
// Routine Description:
// - Determines whether the mouse button is currently being held down
// to extend or otherwise manipulate the selection area.
// Arguments:
// - <none>
// Return Value:
// - True if the mouse button is currently down. False otherwise.
bool Selection::IsMouseButtonDown() const
{
return WI_IsFlagSet(_d->dwSelectionFlags, CONSOLE_MOUSE_DOWN);
}
void Selection::MouseDown()
{
WI_SetFlag(_d.write()->dwSelectionFlags, CONSOLE_MOUSE_DOWN);
// We must capture the mouse on button down to ensure we receive messages if
// it comes back up outside the window.
const auto pWindow = ServiceLocator::LocateConsoleWindow();
if (pWindow != nullptr)
{
pWindow->CaptureMouse();
}
}
void Selection::MouseUp()
{
WI_ClearFlag(_d.write()->dwSelectionFlags, CONSOLE_MOUSE_DOWN);
const auto pWindow = ServiceLocator::LocateConsoleWindow();
if (pWindow != nullptr)
{
pWindow->ReleaseMouse();
}
}
// Routine Description:
// - Saves the current cursor position data so it can be manipulated during selection.
// Arguments:
// - textBuffer - text buffer to set cursor data
// Return Value:
// - <none>
void Selection::_SaveCursorData(const Cursor& cursor) noexcept
{
auto d{ _d.write() };
d->coordSavedCursorPosition = cursor.GetPosition();
d->ulSavedCursorSize = cursor.GetSize();
d->fSavedCursorVisible = cursor.IsVisible();
d->savedCursorType = cursor.GetType();
}
// Routine Description:
// - Restores the cursor position data that was captured when selection was started.
// Arguments:
// - <none> (Restores global state)
// Return Value:
// - <none>
void Selection::_RestoreDataToCursor(Cursor& cursor) noexcept
{
cursor.SetSize(_d->ulSavedCursorSize);
cursor.SetIsVisible(_d->fSavedCursorVisible);
cursor.SetType(_d->savedCursorType);
cursor.SetPosition(_d->coordSavedCursorPosition);
}
// Routine Description:
// - Gets the current selection anchor position
// Arguments:
// - none
// Return Value:
// - current selection anchor
til::point Selection::GetSelectionAnchor() const noexcept
{
return _d->coordSelectionAnchor;
}
// Routine Description:
// - Gets the current selection begin and end (inclusive) anchor positions. The
// first anchor is at the top left, and the second is at the bottom right
// corner of the selection area.
// Return Value:
// - The current selection anchors
std::pair<til::point, til::point> Selection::GetSelectionAnchors() const noexcept
{
if (!_d->fSelectionVisible)
{
// return anchors that represent an empty selection
return { { 0, 0 }, { -1, -1 } };
}
auto startSelectionAnchor = _d->coordSelectionAnchor;
// _coordSelectionAnchor is at one of the corners of _srSelectionRects
// endSelectionAnchor is at the exact opposite corner
til::point endSelectionAnchor;
endSelectionAnchor.x = (_d->coordSelectionAnchor.x == _d->srSelectionRect.left) ? _d->srSelectionRect.right : _d->srSelectionRect.left;
endSelectionAnchor.y = (_d->coordSelectionAnchor.y == _d->srSelectionRect.top) ? _d->srSelectionRect.bottom : _d->srSelectionRect.top;
// GH #18106: Conhost and Terminal share most of the selection code.
// Both now store the selection data as a half-open range [start, end),
// where "end" is the bottom-right-most point.
// Conhost operates as an inclusive range, so we need to adjust the "end" endpoint by incrementing it by one.
const auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
const auto& bufferSize = gci.GetActiveOutputBuffer().GetTextBuffer().GetSize();
if (IsLineSelection())
{
// General comparison for line selection.
bufferSize.IncrementInExclusiveBounds(startSelectionAnchor <= endSelectionAnchor ? endSelectionAnchor : startSelectionAnchor);
}
else
{
// Compare x-values when we're in block selection!
bufferSize.IncrementInExclusiveBounds(startSelectionAnchor.x <= endSelectionAnchor.x ? endSelectionAnchor : startSelectionAnchor);
}
if (startSelectionAnchor > endSelectionAnchor)
{
return { endSelectionAnchor, startSelectionAnchor };
}
else
{
return { startSelectionAnchor, endSelectionAnchor };
}
}
// Routine Description:
// - Gets the current selection rectangle
// Arguments:
// - none
// Return Value:
// - The rectangle to fill with selection data.
til::inclusive_rect Selection::GetSelectionRectangle() const noexcept
{
return _d->srSelectionRect;
}
// Routine Description:
// - Gets the publicly facing set of selection flags.
// Strips out any internal flags in use.
// Arguments:
// - none
// Return Value:
// - The public selection flags
DWORD Selection::GetPublicSelectionFlags() const noexcept
{
// CONSOLE_SELECTION_VALID is the union (binary OR) of all externally valid flags in wincon.h
return (_d->dwSelectionFlags & CONSOLE_SELECTION_VALID);
}
// Routine Description:
// - Sets the line selection status.
// If true, we'll use line selection. If false, we'll use traditional box selection.
// Arguments:
// - fLineSelectionOn - whether or not to use line selection
// Return Value:
// - <none>
void Selection::SetLineSelection(const bool fLineSelectionOn)
{
if (_d->fLineSelection != fLineSelectionOn)
{
// Ensure any existing selections are cleared so the draw state is updated appropriately.
ClearSelection();
_d.write()->fLineSelection = fLineSelectionOn;
}
}
// Routine Description:
// - checks if the selection can be changed by a mouse drag.
// - this is to allow double-click selection and click-mouse-drag selection to play nice together instead of
// the click-mouse-drag selection overwriting the double-click selection in case the user moves the mouse
// while double-clicking.
// Arguments:
// - mousePosition - current mouse position
// Return Value:
// - true if the selection can be changed by a mouse drag
bool Selection::ShouldAllowMouseDragSelection(const til::point mousePosition) const noexcept
{
const auto viewport = Viewport::FromInclusive(_d->srSelectionRect);
const auto selectionContainsMouse = viewport.IsInBounds(mousePosition);
return _d->allowMouseDragSelection || !selectionContainsMouse;
}