terminal/src/host/selection.cpp

757 lines
28 KiB
C++

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.h"
#include "_output.h"
#include "stream.h"
#include "scrolling.hpp"
#include "../interactivity/inc/ServiceLocator.hpp"
using namespace Microsoft::Console::Interactivity;
using namespace Microsoft::Console::Types;
Selection::Selection() :
_fSelectionVisible(false),
_ulSavedCursorSize(0),
_fSavedCursorVisible(false),
_savedCursorColor(INVALID_COLOR),
_savedCursorType(CursorType::Legacy),
_dwSelectionFlags(0),
_fLineSelection(true),
_fUseAlternateSelection(false),
_allowMouseDragSelection{ true }
{
ZeroMemory((void*)&_srSelectionRect, sizeof(_srSelectionRect));
ZeroMemory((void*)&_coordSelectionAnchor, sizeof(_coordSelectionAnchor));
ZeroMemory((void*)&_coordSavedCursorPosition, sizeof(_coordSavedCursorPosition));
}
Selection& Selection::Instance()
{
static std::unique_ptr<Selection> _instance{ new Selection() };
return *_instance;
}
// Routine Description:
// - Determines the line-by-line selection rectangles based on global selection state.
// Arguments:
// - selectionRect - The selection rectangle outlining the region to be selected
// - selectionAnchor - The corner of the selection rectangle that selection started from
// - lineSelection - True to process in line mode. False to process in block mode.
// Return Value:
// - Returns a vector where each SMALL_RECT is one Row worth of the area to be selected.
// - Returns empty vector if no rows are selected.
// - Throws exceptions for out of memory issues
std::vector<SMALL_RECT> Selection::s_GetSelectionRects(const SMALL_RECT& selectionRect,
const COORD selectionAnchor,
const bool lineSelection)
{
std::vector<SMALL_RECT> selectionAreas;
const auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
const auto& screenInfo = gci.GetActiveOutputBuffer();
// if the anchor (start of select) was in the top right or bottom left of the box,
// we need to remove rectangular overlap in the middle.
// e.g.
// For selections with the anchor in the top left (A) or bottom right (B),
// it is valid to maintain the inner rectangle (+) as part of the selection
// A+++++++================
// ==============++++++++B
// + and = are valid highlights in this scenario.
// For selections with the anchor in in the top right (A) or bottom left (B),
// we must remove a portion of the first/last line that lies within the rectangle (+)
// +++++++A=================
// ==============B+++++++
// Only = is valid for highlight in this scenario.
// This is only needed for line selection. Box selection doesn't need to account for this.
bool removeRectPortion = false;
if (lineSelection)
{
const auto selectionStart = selectionAnchor;
// only if top and bottom aren't the same line... we need the whole rectangle if we're on the same line.
// e.g. A++++++++++++++B
// All the + are valid select points.
if (selectionRect.Top != selectionRect.Bottom)
{
if ((selectionStart.X == selectionRect.Right && selectionStart.Y == selectionRect.Top) ||
(selectionStart.X == selectionRect.Left && selectionStart.Y == selectionRect.Bottom))
{
removeRectPortion = true;
}
}
}
// for each row within the selection rectangle
for (short i = selectionRect.Top; i <= selectionRect.Bottom; i++)
{
// create a rectangle representing the highlight on one row
SMALL_RECT highlightRow;
highlightRow.Top = i;
highlightRow.Bottom = i;
highlightRow.Left = selectionRect.Left;
highlightRow.Right = selectionRect.Right;
// compensate for line selection by extending one or both ends of the rectangle to the edge
if (lineSelection)
{
// if not the first row, pad the left selection to the buffer edge
if (i != selectionRect.Top)
{
highlightRow.Left = 0;
}
// if not the last row, pad the right selection to the buffer edge
if (i != selectionRect.Bottom)
{
highlightRow.Right = screenInfo.GetBufferSize().RightInclusive();
}
// if we've determined we're in a scenario where we must remove the inner rectangle from the lines...
if (removeRectPortion)
{
if (i == selectionRect.Top)
{
// from the top row, move the left edge of the highlight line to the right edge of the rectangle
highlightRow.Left = selectionRect.Right;
}
else if (i == selectionRect.Bottom)
{
// from the bottom row, move the right edge of the highlight line to the left edge of the rectangle
highlightRow.Right = selectionRect.Left;
}
}
}
// compensate for double width characters by calling double-width measuring/limiting function
const COORD targetPoint{ highlightRow.Left, highlightRow.Top };
const SHORT stringLength = highlightRow.Right - highlightRow.Left + 1;
highlightRow = s_BisectSelection(stringLength, targetPoint, screenInfo, highlightRow);
selectionAreas.emplace_back(highlightRow);
}
return selectionAreas;
}
// Routine Description:
// - Determines the line-by-line selection rectangles based on global selection state.
// Arguments:
// - <none> - Uses internal state to know what area is selected already.
// Return Value:
// - Returns a vector where each SMALL_RECT is one Row worth of the area to be selected.
// - Returns empty vector if no rows are selected.
// - Throws exceptions for out of memory issues
std::vector<SMALL_RECT> Selection::GetSelectionRects() const
{
if (!_fSelectionVisible)
{
return std::vector<SMALL_RECT>();
}
return s_GetSelectionRects(_srSelectionRect, _coordSelectionAnchor, IsLineSelection());
}
// Routine Description:
// - This routine checks to ensure that clipboard selection isn't trying to cut a double byte character in half.
// It will adjust the SmallRect rectangle size to ensure this.
// Arguments:
// - sStringLength - The length of the string we're attempting to clip.
// - coordTargetPoint - The row/column position within the text buffer that we're about to try to clip.
// - screenInfo - Screen information structure containing relevant text and dimension information.
// - rect - The region of the text that we want to clip, and then adjusted to the region that should be
// clipped without splicing double-width characters.
// Return Value:
// - the clipped region
SMALL_RECT Selection::s_BisectSelection(const short sStringLength,
const COORD coordTargetPoint,
const SCREEN_INFORMATION& screenInfo,
const SMALL_RECT rect)
{
SMALL_RECT outRect = rect;
try
{
auto iter = screenInfo.GetCellDataAt(coordTargetPoint);
if (iter->DbcsAttr().IsTrailing())
{
if (coordTargetPoint.X == 0)
{
outRect.Left++;
}
else
{
outRect.Left--;
}
}
// Check end position of strings
if (coordTargetPoint.X + sStringLength < screenInfo.GetBufferSize().Width())
{
iter += sStringLength;
if (iter->DbcsAttr().IsTrailing())
{
outRect.Right++;
}
}
else
{
if (coordTargetPoint.Y + 1 < screenInfo.GetBufferSize().Height())
{
const auto nextLineIter = screenInfo.GetCellDataAt({ 0, coordTargetPoint.Y + 1 });
if (nextLineIter->DbcsAttr().IsTrailing())
{
outRect.Right--;
}
}
}
}
CATCH_LOG();
return outRect;
}
// Routine Description:
// - Shows the selection area in the window if one is available and not already showing.
// Arguments:
// <none>
// Return Value:
// <none>
void Selection::ShowSelection()
{
_SetSelectionVisibility(true);
}
// Routine Description:
// - Hides the selection area in the window if one is available and already showing.
// Arguments:
// <none>
// Return Value:
// <none>
void Selection::HideSelection()
{
_SetSelectionVisibility(false);
}
// Routine Description:
// - Changes the visibility of the selection area on the screen.
// - Used to turn the selection area on or off.
// Arguments:
// - fMakeVisible - If TRUE, we're turning the selection ON.
// - If FALSE, we're turning the selection OFF.
// Return Value:
// <none>
void Selection::_SetSelectionVisibility(const bool fMakeVisible)
{
if (IsInSelectingState() && IsAreaSelected())
{
if (fMakeVisible == _fSelectionVisible)
{
return;
}
_fSelectionVisible = fMakeVisible;
_PaintSelection();
}
LOG_IF_FAILED(ServiceLocator::LocateConsoleWindow()->SignalUia(UIA_Text_TextSelectionChangedEventId));
}
// Routine Description:
// - Inverts the selected region on the current screen buffer.
// - Reads the selected area, selection mode, and active screen buffer
// from the global properties and dispatches a GDI invert on the selected text area.
// Arguments:
// - <none>
// Return Value:
// - <none>
void Selection::_PaintSelection() const
{
if (ServiceLocator::LocateGlobals().pRender != nullptr)
{
ServiceLocator::LocateGlobals().pRender->TriggerSelection();
}
}
// Routine Description:
// - Starts the selection with the given initial position
// Arguments:
// - coordBufferPos - Position in which user started a selection
// Return Value:
// - <none>
void Selection::InitializeMouseSelection(const COORD coordBufferPos)
{
Scrolling::s_ClearScroll();
// set flags
_SetSelectingState(true);
_dwSelectionFlags = CONSOLE_MOUSE_SELECTION | CONSOLE_SELECTION_NOT_EMPTY;
// store anchor and rectangle of selection
_coordSelectionAnchor = coordBufferPos;
// since we've started with just a point, the rectangle is 1x1 on the point given
_srSelectionRect.Left = coordBufferPos.X;
_srSelectionRect.Right = coordBufferPos.X;
_srSelectionRect.Top = coordBufferPos.Y;
_srSelectionRect.Bottom = coordBufferPos.Y;
// Check for ALT-Mouse Down "use alternate selection"
// If in box mode, use line mode. If in line mode, use box mode.
CheckAndSetAlternateSelection();
// set window title to mouse selection mode
IConsoleWindow* const pWindow = ServiceLocator::LocateConsoleWindow();
if (pWindow != nullptr)
{
pWindow->UpdateWindowText();
LOG_IF_FAILED(pWindow->SignalUia(UIA_Text_TextSelectionChangedEventId));
}
// Fire off an event to let accessibility apps know the selection has changed.
ServiceLocator::LocateAccessibilityNotifier()->NotifyConsoleCaretEvent(IAccessibilityNotifier::ConsoleCaretEventFlags::CaretSelection, PACKCOORD(coordBufferPos));
}
// Routine Description:
// - Modifies both ends of the current selection.
// - Intended for use with functions that help auto-complete a selection area (e.g. double clicking)
// Arguments:
// - coordSelectionStart - Replaces the selection anchor, a.k.a. where the selection started from originally.
// - coordSelectionEnd - The linear final position or opposite corner of the anchor to represent the complete selection area.
// Return Value:
// - <none>
void Selection::AdjustSelection(const COORD coordSelectionStart, const COORD coordSelectionEnd)
{
// modify the anchor and then just use extend to adjust the other portion of the selection rectangle
_coordSelectionAnchor = coordSelectionStart;
ExtendSelection(coordSelectionEnd);
_allowMouseDragSelection = false;
}
// Routine Description:
// - Extends the selection out to the given position from the initial anchor point.
// This means that a coordinate farther away will make the rectangle larger and a closer one will shrink it.
// Arguments:
// - coordBufferPos - Position to extend/contract the current selection up to.
// Return Value:
// - <none>
void Selection::ExtendSelection(_In_ COORD coordBufferPos)
{
CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
SCREEN_INFORMATION& screenInfo = gci.GetActiveOutputBuffer();
_allowMouseDragSelection = true;
// ensure position is within buffer bounds. Not less than 0 and not greater than the screen buffer size.
try
{
screenInfo.GetTerminalBufferSize().Clamp(coordBufferPos);
}
CATCH_LOG_RETURN();
if (!IsAreaSelected())
{
// we should only be extending a selection that has no area yet if we're coming from mark mode.
// if not, just return.
if (IsMouseInitiatedSelection())
{
return;
}
// scroll if necessary to make cursor visible.
screenInfo.MakeCursorVisible(coordBufferPos, false);
_dwSelectionFlags |= CONSOLE_SELECTION_NOT_EMPTY;
_srSelectionRect.Left = _srSelectionRect.Right = _coordSelectionAnchor.X;
_srSelectionRect.Top = _srSelectionRect.Bottom = _coordSelectionAnchor.Y;
ShowSelection();
}
else
{
// scroll if necessary to make cursor visible.
screenInfo.MakeCursorVisible(coordBufferPos, false);
}
// remember previous selection rect
SMALL_RECT srNewSelection = _srSelectionRect;
// update selection rect
// this adjusts the rectangle dimensions based on which way the move was requested
// in respect to the original selection position (the anchor)
if (coordBufferPos.X <= _coordSelectionAnchor.X)
{
srNewSelection.Left = coordBufferPos.X;
srNewSelection.Right = _coordSelectionAnchor.X;
}
else if (coordBufferPos.X > _coordSelectionAnchor.X)
{
srNewSelection.Right = coordBufferPos.X;
srNewSelection.Left = _coordSelectionAnchor.X;
}
if (coordBufferPos.Y <= _coordSelectionAnchor.Y)
{
srNewSelection.Top = coordBufferPos.Y;
srNewSelection.Bottom = _coordSelectionAnchor.Y;
}
else if (coordBufferPos.Y > _coordSelectionAnchor.Y)
{
srNewSelection.Bottom = coordBufferPos.Y;
srNewSelection.Top = _coordSelectionAnchor.Y;
}
// call special update method to modify the displayed selection in-place
// NOTE: Using HideSelection, editing the rectangle, then ShowSelection will cause flicker.
//_PaintUpdateSelection(&srNewSelection);
_srSelectionRect = srNewSelection;
_PaintSelection();
// Fire off an event to let accessibility apps know the selection has changed.
ServiceLocator::LocateAccessibilityNotifier()->NotifyConsoleCaretEvent(IAccessibilityNotifier::ConsoleCaretEventFlags::CaretSelection, PACKCOORD(coordBufferPos));
LOG_IF_FAILED(ServiceLocator::LocateConsoleWindow()->SignalUia(UIA_Text_TextSelectionChangedEventId));
}
// Routine Description:
// - Cancels any mouse selection state to return to normal mode.
// Arguments:
// - <none> (Uses global state)
// Return Value:
// - <none>
void Selection::_CancelMouseSelection()
{
CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
SCREEN_INFORMATION& ScreenInfo = gci.GetActiveOutputBuffer();
// invert old select rect. if we're selecting by mouse, we
// always have a selection rect.
HideSelection();
// turn off selection flag
_SetSelectingState(false);
IConsoleWindow* const pWindow = ServiceLocator::LocateConsoleWindow();
if (pWindow != nullptr)
{
pWindow->UpdateWindowText();
}
// Mark the cursor position as changed so we'll fire off a win event.
ScreenInfo.GetTextBuffer().GetCursor().SetHasMoved(true);
}
// Routine Description:
// - Cancels any mark mode selection state to return to normal mode.
// Arguments:
// - <none>
// Return Value:
// - <none>
void Selection::_CancelMarkSelection()
{
CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
SCREEN_INFORMATION& ScreenInfo = gci.GetActiveOutputBuffer();
// Hide existing selection, if we have one.
if (IsAreaSelected())
{
HideSelection();
}
// Turn off selection flag.
_SetSelectingState(false);
IConsoleWindow* const pWindow = ServiceLocator::LocateConsoleWindow();
if (pWindow != nullptr)
{
pWindow->UpdateWindowText();
}
// restore text cursor
_RestoreDataToCursor(ScreenInfo.GetTextBuffer().GetCursor());
}
// Routine Description:
// - If a selection exists, clears it and restores the state.
// Will also unblock a blocked write if one exists.
// Arguments:
// - <none> (Uses global state)
// Return Value:
// - <none>
void Selection::ClearSelection()
{
ClearSelection(false);
}
// Routine Description:
// - If a selection exists, clears it and restores the state.
// - Will only unblock a write if not starting a new selection.
// Arguments:
// - fStartingNewSelection - If we're going to start another selection right away, we'll keep the write blocked.
// Return Value:
// - <none>
void Selection::ClearSelection(const bool fStartingNewSelection)
{
if (IsInSelectingState())
{
if (IsMouseInitiatedSelection())
{
_CancelMouseSelection();
}
else
{
_CancelMarkSelection();
}
LOG_IF_FAILED(ServiceLocator::LocateConsoleWindow()->SignalUia(UIA_Text_TextSelectionChangedEventId));
_dwSelectionFlags = 0;
// If we were using alternate selection, cancel it here before starting a new area.
_fUseAlternateSelection = false;
// Only unblock if we're not immediately starting a new selection. Otherwise stay blocked.
if (!fStartingNewSelection)
{
UnblockWriteConsole(CONSOLE_SELECTING);
}
}
}
// Routine Description:
// - Colors all text in the given rectangle with the color attribute provided.
// - This does not validate whether there is a valid selection right now or not.
// It is assumed to already be in a proper selecting state and the given rectangle should be highlighted with the given color unconditionally.
// Arguments:
// - psrRect - Rectangular area to fill with color
// - attr - The color attributes to apply
void Selection::ColorSelection(const SMALL_RECT& srRect, const TextAttribute attr)
{
CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
// Read selection rectangle, assumed already clipped to buffer.
SCREEN_INFORMATION& screenInfo = gci.GetActiveOutputBuffer();
COORD coordTargetSize;
coordTargetSize.X = CalcWindowSizeX(srRect);
coordTargetSize.Y = CalcWindowSizeY(srRect);
COORD coordTarget;
coordTarget.X = srRect.Left;
coordTarget.Y = srRect.Top;
// Now color the selection a line at a time.
for (; (coordTarget.Y < srRect.Top + coordTargetSize.Y); ++coordTarget.Y)
{
const size_t cchWrite = gsl::narrow<size_t>(coordTargetSize.X);
try
{
screenInfo.Write(OutputCellIterator(attr, cchWrite), coordTarget);
}
CATCH_LOG();
}
}
// Routine Description:
// - Given two points in the buffer space, color the selection between the two with the given attribute.
// - This will create an internal selection rectangle covering the two points, assume a line selection,
// and use the first point as the anchor for the selection (as if the mouse click started at that point)
// Arguments:
// - coordSelectionStart - Anchor point (start of selection) for the region to be colored
// - coordSelectionEnd - Other point referencing the rectangle inscribing the selection area
// - attr - Color to apply to region.
void Selection::ColorSelection(const COORD coordSelectionStart, const COORD coordSelectionEnd, const TextAttribute attr)
{
// Make a rectangle for the region as if it were selected by a mouse.
// We will use the first one as the "anchor" to represent where the mouse went down.
SMALL_RECT srSelection;
srSelection.Top = std::min(coordSelectionStart.Y, coordSelectionEnd.Y);
srSelection.Bottom = std::max(coordSelectionStart.Y, coordSelectionEnd.Y);
srSelection.Left = std::min(coordSelectionStart.X, coordSelectionEnd.X);
srSelection.Right = std::max(coordSelectionStart.X, coordSelectionEnd.X);
// Extract row-by-row selection rectangles for the selection area.
try
{
const auto rectangles = s_GetSelectionRects(srSelection, coordSelectionStart, true);
for (const auto& rect : rectangles)
{
ColorSelection(rect, attr);
}
}
CATCH_LOG();
}
// Routine Description:
// - Enters mark mode selection. Prepares the cursor to move around to select a region and sets up state variables.
// Arguments:
// - <none>
// Return Value:
// - <none>
void Selection::InitializeMarkSelection()
{
CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
// clear any existing selection.
ClearSelection(true);
Scrolling::s_ClearScroll();
// set flags
_SetSelectingState(true);
_dwSelectionFlags = 0;
// save old cursor position and make console cursor into selection cursor.
SCREEN_INFORMATION& screenInfo = gci.GetActiveOutputBuffer();
const auto& cursor = screenInfo.GetTextBuffer().GetCursor();
_SaveCursorData(cursor);
screenInfo.SetCursorInformation(100, TRUE);
const COORD coordPosition = cursor.GetPosition();
LOG_IF_FAILED(screenInfo.SetCursorPosition(coordPosition, true));
// set the cursor position as the anchor position
// it will get updated as the cursor moves for mark mode,
// but it serves to prepare us for the inevitable start of the selection with Shift+Arrow Key
_coordSelectionAnchor = coordPosition;
// set frame title text
IConsoleWindow* const pWindow = ServiceLocator::LocateConsoleWindow();
if (pWindow != nullptr)
{
pWindow->UpdateWindowText();
LOG_IF_FAILED(pWindow->SignalUia(UIA_Text_TextSelectionChangedEventId));
}
}
// Routine Description:
// - Resets the current selection and selects a new region from the start to end coordinates
// Arguments:
// - coordStart - Position to start selection area from
// - coordEnd - Position to select up to
// Return Value:
// - <none>
void Selection::SelectNewRegion(const COORD coordStart, const COORD coordEnd)
{
// clear existing selection if applicable
ClearSelection();
// initialize selection
InitializeMouseSelection(coordStart);
ShowSelection();
// extend selection
ExtendSelection(coordEnd);
}
// Routine Description:
// - Creates a new selection region of "all" available text.
// The meaning of "all" can vary. If we have input text, then "all" is just the input text.
// If we have no input text, "all" is the entire valid screen buffer (output text and the prompt)
// Arguments:
// - <none> (Uses global state)
// Return Value:
// - <none>
void Selection::SelectAll()
{
CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
// save the old window position
SCREEN_INFORMATION& screenInfo = gci.GetActiveOutputBuffer();
COORD coordWindowOrigin = screenInfo.GetViewport().Origin();
// Get existing selection rectangle parameters
const bool fOldSelectionExisted = IsAreaSelected();
const SMALL_RECT srOldSelection = _srSelectionRect;
const COORD coordOldAnchor = _coordSelectionAnchor;
// Attempt to get the boundaries of the current input line.
COORD coordInputStart;
COORD coordInputEnd;
const bool fHasInputArea = s_GetInputLineBoundaries(&coordInputStart, &coordInputEnd);
// These variables will be used to specify the new selection area when we're done
COORD coordNewSelStart;
COORD coordNewSelEnd;
// Now evaluate conditions and attempt to assign a new selection area.
if (!fHasInputArea)
{
// If there's no input area, just select the entire valid text region.
GetValidAreaBoundaries(&coordNewSelStart, &coordNewSelEnd);
}
else
{
if (!fOldSelectionExisted)
{
// Temporary workaround until MSFT: 614579 is completed.
const auto bufferSize = screenInfo.GetBufferSize();
COORD coordOneAfterEnd = coordInputEnd;
bufferSize.IncrementInBounds(coordOneAfterEnd);
if (s_IsWithinBoundaries(screenInfo.GetTextBuffer().GetCursor().GetPosition(), coordInputStart, coordInputEnd))
{
// If there was no previous selection and the cursor is within the input line, select the input line only
coordNewSelStart = coordInputStart;
coordNewSelEnd = coordInputEnd;
}
else if (s_IsWithinBoundaries(screenInfo.GetTextBuffer().GetCursor().GetPosition(), coordOneAfterEnd, coordOneAfterEnd))
{
// Temporary workaround until MSFT: 614579 is completed.
// Select only the input line if the cursor is one after the final position of the input line.
coordNewSelStart = coordInputStart;
coordNewSelEnd = coordInputEnd;
}
else
{
// otherwise if the cursor is elsewhere, select everything
GetValidAreaBoundaries(&coordNewSelStart, &coordNewSelEnd);
}
}
else
{
// This is the complex case. We had an existing selection and we have an input area.
// To figure this out, we need the anchor (the point where the selection starts) and its opposite corner
COORD coordOldAnchorOpposite = Utils::s_GetOppositeCorner(srOldSelection, coordOldAnchor);
// Check if both anchor and opposite corner fall within the input line
const bool fIsOldSelWithinInput =
s_IsWithinBoundaries(coordOldAnchor, coordInputStart, coordInputEnd) &&
s_IsWithinBoundaries(coordOldAnchorOpposite, coordInputStart, coordInputEnd);
// Check if both anchor and opposite corner are exactly the bounds of the input line
const bool fAllInputSelected =
((Utils::s_CompareCoords(coordInputStart, coordOldAnchor) == 0 && Utils::s_CompareCoords(coordInputEnd, coordOldAnchorOpposite) == 0) ||
(Utils::s_CompareCoords(coordInputStart, coordOldAnchorOpposite) == 0 && Utils::s_CompareCoords(coordInputEnd, coordOldAnchor) == 0));
if (fIsOldSelWithinInput && !fAllInputSelected)
{
// If it's within the input area and the whole input is not selected, then select just the input
coordNewSelStart = coordInputStart;
coordNewSelEnd = coordInputEnd;
}
else
{
// Otherwise just select the whole valid area
GetValidAreaBoundaries(&coordNewSelStart, &coordNewSelEnd);
}
}
}
// If we're in box selection, adjust end coordinate to end of line and start coordinate to start of line
// or it won't be selecting all the text.
if (!IsLineSelection())
{
coordNewSelStart.X = 0;
coordNewSelEnd.X = screenInfo.GetBufferSize().RightInclusive();
}
SelectNewRegion(coordNewSelStart, coordNewSelEnd);
// restore the old window position
LOG_IF_FAILED(screenInfo.SetViewportOrigin(true, coordWindowOrigin, true));
}