2019-05-03 00:29:04 +02:00
|
|
|
/*++
|
|
|
|
Copyright (c) Microsoft Corporation
|
|
|
|
Licensed under the MIT license.
|
|
|
|
|
|
|
|
Module Name:
|
|
|
|
- UiaTextRange.hpp
|
|
|
|
|
|
|
|
Abstract:
|
|
|
|
- This module provides UI Automation access to the text of the console
|
|
|
|
window to support both automation tests and accessibility (screen
|
|
|
|
reading) applications.
|
|
|
|
|
|
|
|
Author(s):
|
|
|
|
- Austin Diviness (AustDi) 2017
|
|
|
|
--*/
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include "precomp.h"
|
|
|
|
|
|
|
|
#include "../inc/IConsoleWindow.hpp"
|
|
|
|
#include "../types/inc/viewport.hpp"
|
|
|
|
#include "../../buffer/out/cursor.h"
|
|
|
|
|
|
|
|
#include <deque>
|
|
|
|
#include <tuple>
|
|
|
|
|
|
|
|
#ifdef UNIT_TESTING
|
|
|
|
class UiaTextRangeTests;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// The UiaTextRange deals with several data structures that have
|
|
|
|
// similar semantics. In order to keep the information from these data
|
|
|
|
// structures separated, each structure has its own naming for a
|
|
|
|
// row.
|
|
|
|
//
|
|
|
|
// There is the generic Row, which does not know which data structure
|
|
|
|
// the row came from.
|
|
|
|
//
|
|
|
|
// There is the ViewportRow, which is a 0-indexed row value from the
|
|
|
|
// viewport. The top row of the viewport is at 0, rows below the top
|
|
|
|
// row increase in value and rows above the top row get increasingly
|
|
|
|
// negative.
|
|
|
|
//
|
|
|
|
// ScreenInfoRow is a row from the screen info data structure. They
|
|
|
|
// start at 0 at the top of screen info buffer. Their positions do not
|
|
|
|
// change but their associated row in the text buffer does change each
|
|
|
|
// time a new line is written.
|
|
|
|
//
|
|
|
|
// TextBufferRow is a row from the text buffer. It is not a ROW
|
|
|
|
// struct, but rather the index of a row. This is also 0-indexed. A
|
|
|
|
// TextBufferRow with a value of 0 does not necessarily refer to the
|
|
|
|
// top row of the console.
|
|
|
|
|
|
|
|
typedef int Row;
|
|
|
|
typedef int ViewportRow;
|
|
|
|
typedef unsigned int ScreenInfoRow;
|
|
|
|
typedef unsigned int TextBufferRow;
|
|
|
|
|
|
|
|
typedef unsigned long long IdType;
|
|
|
|
|
|
|
|
// A Column is a row agnostic value that refers to the column an
|
|
|
|
// endpoint is equivalent to. It is 0-indexed.
|
|
|
|
typedef unsigned int Column;
|
|
|
|
|
|
|
|
// an endpoint is a char location in the text buffer. endpoint 0 is
|
|
|
|
// the first char of the 0th row in the text buffer row array.
|
|
|
|
typedef unsigned int Endpoint;
|
|
|
|
|
2019-05-23 19:35:30 +02:00
|
|
|
constexpr IdType InvalidId = 0;
|
|
|
|
|
2019-05-03 00:29:04 +02:00
|
|
|
namespace Microsoft::Console::Interactivity::Win32
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
class UiaTextRange final : public ITextRangeProvider
|
|
|
|
{
|
|
|
|
private:
|
|
|
|
static IdType id;
|
|
|
|
|
|
|
|
protected:
|
|
|
|
// indicates which direction a movement operation
|
|
|
|
// is going
|
|
|
|
enum class MovementDirection
|
|
|
|
{
|
|
|
|
Forward,
|
|
|
|
Backward
|
|
|
|
};
|
|
|
|
|
|
|
|
// valid increment amounts for forward and
|
|
|
|
// backward movement
|
|
|
|
enum class MovementIncrement
|
|
|
|
{
|
|
|
|
Forward = 1,
|
|
|
|
Backward = -1
|
|
|
|
};
|
|
|
|
|
|
|
|
// common information used by the variety of
|
|
|
|
// movement operations
|
|
|
|
struct MoveState
|
|
|
|
{
|
|
|
|
// screen/column position of _start
|
|
|
|
ScreenInfoRow StartScreenInfoRow;
|
|
|
|
Column StartColumn;
|
|
|
|
// screen/column position of _end
|
|
|
|
ScreenInfoRow EndScreenInfoRow;
|
|
|
|
Column EndColumn;
|
|
|
|
// last row in the direction being moved
|
|
|
|
ScreenInfoRow LimitingRow;
|
|
|
|
// first column in the direction being moved
|
|
|
|
Column FirstColumnInRow;
|
|
|
|
// last column in the direction being moved
|
|
|
|
Column LastColumnInRow;
|
|
|
|
// increment amount
|
|
|
|
MovementIncrement Increment;
|
|
|
|
// direction moving
|
|
|
|
MovementDirection Direction;
|
|
|
|
|
|
|
|
MoveState(const UiaTextRange& range,
|
|
|
|
const MovementDirection direction);
|
|
|
|
|
|
|
|
private:
|
|
|
|
MoveState(const ScreenInfoRow startScreenInfoRow,
|
|
|
|
const Column startColumn,
|
|
|
|
const ScreenInfoRow endScreenInfoRow,
|
|
|
|
const Column endColumn,
|
|
|
|
const ScreenInfoRow limitingRow,
|
|
|
|
const Column firstColumnInRow,
|
|
|
|
const Column lastColumnInRow,
|
|
|
|
const MovementIncrement increment,
|
|
|
|
const MovementDirection direction);
|
|
|
|
|
|
|
|
#ifdef UNIT_TESTING
|
|
|
|
friend class ::UiaTextRangeTests;
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
static std::deque<UiaTextRange*> GetSelectionRanges(_In_ IRawElementProviderSimple* pProvider);
|
|
|
|
|
|
|
|
// degenerate range
|
|
|
|
static UiaTextRange* Create(_In_ IRawElementProviderSimple* const pProvider);
|
|
|
|
|
|
|
|
// degenerate range at cursor position
|
|
|
|
static UiaTextRange* Create(_In_ IRawElementProviderSimple* const pProvider,
|
|
|
|
const Cursor& cursor);
|
|
|
|
|
|
|
|
// specific endpoint range
|
|
|
|
static UiaTextRange* Create(_In_ IRawElementProviderSimple* const pProvider,
|
|
|
|
const Endpoint start,
|
|
|
|
const Endpoint end,
|
|
|
|
const bool degenerate);
|
|
|
|
|
|
|
|
// range from a UiaPoint
|
|
|
|
static UiaTextRange* Create(_In_ IRawElementProviderSimple* const pProvider,
|
|
|
|
const UiaPoint point);
|
|
|
|
|
|
|
|
~UiaTextRange();
|
|
|
|
|
|
|
|
|
|
|
|
const IdType GetId() const;
|
|
|
|
const Endpoint GetStart() const;
|
|
|
|
const Endpoint GetEnd() const;
|
|
|
|
const bool IsDegenerate() const;
|
|
|
|
|
|
|
|
// IUnknown methods
|
|
|
|
IFACEMETHODIMP_(ULONG) AddRef();
|
|
|
|
IFACEMETHODIMP_(ULONG) Release();
|
|
|
|
IFACEMETHODIMP QueryInterface(_In_ REFIID riid,
|
|
|
|
_COM_Outptr_result_maybenull_ void** ppInterface);
|
|
|
|
|
|
|
|
// ITextRangeProvider methods
|
|
|
|
IFACEMETHODIMP Clone(_Outptr_result_maybenull_ ITextRangeProvider** ppRetVal);
|
|
|
|
IFACEMETHODIMP Compare(_In_opt_ ITextRangeProvider* pRange, _Out_ BOOL* pRetVal);
|
|
|
|
IFACEMETHODIMP CompareEndpoints(_In_ TextPatternRangeEndpoint endpoint,
|
|
|
|
_In_ ITextRangeProvider* pTargetRange,
|
|
|
|
_In_ TextPatternRangeEndpoint targetEndpoint,
|
|
|
|
_Out_ int* pRetVal);
|
|
|
|
IFACEMETHODIMP ExpandToEnclosingUnit(_In_ TextUnit unit);
|
|
|
|
IFACEMETHODIMP FindAttribute(_In_ TEXTATTRIBUTEID textAttributeId,
|
|
|
|
_In_ VARIANT val,
|
|
|
|
_In_ BOOL searchBackward,
|
|
|
|
_Outptr_result_maybenull_ ITextRangeProvider** ppRetVal);
|
|
|
|
IFACEMETHODIMP FindText(_In_ BSTR text,
|
|
|
|
_In_ BOOL searchBackward,
|
|
|
|
_In_ BOOL ignoreCase,
|
|
|
|
_Outptr_result_maybenull_ ITextRangeProvider** ppRetVal);
|
|
|
|
IFACEMETHODIMP GetAttributeValue(_In_ TEXTATTRIBUTEID textAttributeId,
|
|
|
|
_Out_ VARIANT* pRetVal);
|
|
|
|
IFACEMETHODIMP GetBoundingRectangles(_Outptr_result_maybenull_ SAFEARRAY** ppRetVal);
|
|
|
|
IFACEMETHODIMP GetEnclosingElement(_Outptr_result_maybenull_ IRawElementProviderSimple** ppRetVal);
|
|
|
|
IFACEMETHODIMP GetText(_In_ int maxLength,
|
|
|
|
_Out_ BSTR* pRetVal);
|
|
|
|
IFACEMETHODIMP Move(_In_ TextUnit unit,
|
|
|
|
_In_ int count,
|
|
|
|
_Out_ int* pRetVal);
|
|
|
|
IFACEMETHODIMP MoveEndpointByUnit(_In_ TextPatternRangeEndpoint endpoint,
|
|
|
|
_In_ TextUnit unit,
|
|
|
|
_In_ int count,
|
|
|
|
_Out_ int* pRetVal);
|
|
|
|
IFACEMETHODIMP MoveEndpointByRange(_In_ TextPatternRangeEndpoint endpoint,
|
|
|
|
_In_ ITextRangeProvider* pTargetRange,
|
|
|
|
_In_ TextPatternRangeEndpoint targetEndpoint);
|
|
|
|
IFACEMETHODIMP Select();
|
|
|
|
IFACEMETHODIMP AddToSelection();
|
|
|
|
IFACEMETHODIMP RemoveFromSelection();
|
|
|
|
IFACEMETHODIMP ScrollIntoView(_In_ BOOL alignToTop);
|
|
|
|
IFACEMETHODIMP GetChildren(_Outptr_result_maybenull_ SAFEARRAY** ppRetVal);
|
|
|
|
|
|
|
|
protected:
|
|
|
|
#if _DEBUG
|
|
|
|
void _outputRowConversions();
|
|
|
|
void _outputObjectState();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
IRawElementProviderSimple* const _pProvider;
|
|
|
|
|
|
|
|
private:
|
|
|
|
// degenerate range
|
|
|
|
UiaTextRange(_In_ IRawElementProviderSimple* const pProvider);
|
|
|
|
|
|
|
|
// degenerate range at cursor position
|
|
|
|
UiaTextRange(_In_ IRawElementProviderSimple* const pProvider,
|
|
|
|
const Cursor& cursor);
|
|
|
|
|
|
|
|
// specific endpoint range
|
|
|
|
UiaTextRange(_In_ IRawElementProviderSimple* const pProvider,
|
|
|
|
const Endpoint start,
|
|
|
|
const Endpoint end,
|
|
|
|
const bool degenerate);
|
|
|
|
|
|
|
|
// range from a UiaPoint
|
|
|
|
UiaTextRange(_In_ IRawElementProviderSimple* const pProvider,
|
|
|
|
const UiaPoint point);
|
|
|
|
|
|
|
|
UiaTextRange(const UiaTextRange& a);
|
|
|
|
|
|
|
|
// used to debug objects passed back and forth
|
|
|
|
// between the provider and the client
|
|
|
|
IdType _id;
|
|
|
|
|
|
|
|
// Ref counter for COM object
|
|
|
|
ULONG _cRefs;
|
|
|
|
|
|
|
|
// measure units in the form [_start, _end]. _start
|
|
|
|
// may be a bigger number than _end if the range
|
|
|
|
// wraps around the end of the text buffer.
|
|
|
|
//
|
|
|
|
// In this scenario, _start <= _end
|
|
|
|
// 0 ............... N (text buffer line indices)
|
|
|
|
// s-----e (_start to _end)
|
|
|
|
//
|
|
|
|
// In this scenario, _start >= end
|
|
|
|
// 0 ............... N (text buffer line indices)
|
|
|
|
// ---e s----- (_start to _end)
|
|
|
|
//
|
|
|
|
Endpoint _start;
|
|
|
|
Endpoint _end;
|
|
|
|
|
|
|
|
// The msdn documentation (and hence this class) talks a bunch about a
|
|
|
|
// degenerate range. A range is degenerate if it contains
|
|
|
|
// no text (both the start and end endpoints are the same). Note that
|
|
|
|
// a degenerate range may have a position in the text. We indicate a
|
|
|
|
// degenerate range internally with a bool. If a range is degenerate
|
|
|
|
// then both endpoints will contain the same value.
|
|
|
|
bool _degenerate;
|
|
|
|
|
|
|
|
static const Microsoft::Console::Types::Viewport& _getViewport();
|
|
|
|
static HWND _getWindowHandle();
|
|
|
|
static IConsoleWindow* const _getIConsoleWindow();
|
|
|
|
static SCREEN_INFORMATION& _getScreenInfo();
|
|
|
|
static TextBuffer& _getTextBuffer();
|
|
|
|
static const COORD _getScreenBufferCoords();
|
|
|
|
|
|
|
|
static const unsigned int _getTotalRows();
|
|
|
|
static const unsigned int _getRowWidth();
|
|
|
|
|
|
|
|
static const unsigned int _getFirstScreenInfoRowIndex();
|
|
|
|
static const unsigned int _getLastScreenInfoRowIndex();
|
|
|
|
|
|
|
|
static const Column _getFirstColumnIndex();
|
|
|
|
static const Column _getLastColumnIndex();
|
|
|
|
|
|
|
|
const unsigned int _rowCountInRange() const;
|
|
|
|
|
|
|
|
static const TextBufferRow _endpointToTextBufferRow(const Endpoint endpoint);
|
|
|
|
static const ScreenInfoRow _textBufferRowToScreenInfoRow(const TextBufferRow row);
|
|
|
|
|
|
|
|
static const TextBufferRow _screenInfoRowToTextBufferRow(const ScreenInfoRow row);
|
|
|
|
static const Endpoint _textBufferRowToEndpoint(const TextBufferRow row);
|
|
|
|
|
|
|
|
static const ScreenInfoRow _endpointToScreenInfoRow(const Endpoint endpoint);
|
|
|
|
static const Endpoint _screenInfoRowToEndpoint(const ScreenInfoRow row);
|
|
|
|
|
|
|
|
static COORD _endpointToCoord(const Endpoint endpoint);
|
|
|
|
static Endpoint _coordToEndpoint(const COORD coord);
|
|
|
|
|
|
|
|
static const Column _endpointToColumn(const Endpoint endpoint);
|
|
|
|
|
|
|
|
static const Row _normalizeRow(const Row row);
|
|
|
|
|
|
|
|
static const ViewportRow _screenInfoRowToViewportRow(const ScreenInfoRow row);
|
|
|
|
static const ViewportRow _screenInfoRowToViewportRow(const ScreenInfoRow row,
|
|
|
|
const SMALL_RECT viewport);
|
|
|
|
|
|
|
|
static const bool _isScreenInfoRowInViewport(const ScreenInfoRow row);
|
|
|
|
static const bool _isScreenInfoRowInViewport(const ScreenInfoRow row,
|
|
|
|
const SMALL_RECT viewport);
|
|
|
|
|
|
|
|
static const unsigned int _getViewportHeight(const SMALL_RECT viewport);
|
|
|
|
static const unsigned int _getViewportWidth(const SMALL_RECT viewport);
|
|
|
|
|
|
|
|
void _addScreenInfoRowBoundaries(const ScreenInfoRow screenInfoRow,
|
|
|
|
_Inout_ std::vector<double>& coords) const;
|
|
|
|
|
|
|
|
static const int _compareScreenCoords(const ScreenInfoRow rowA,
|
|
|
|
const Column colA,
|
|
|
|
const ScreenInfoRow rowB,
|
|
|
|
const Column colB);
|
|
|
|
|
|
|
|
static std::pair<Endpoint, Endpoint> _moveByCharacter(const int moveCount,
|
|
|
|
const MoveState moveState,
|
|
|
|
_Out_ int* const pAmountMoved);
|
|
|
|
|
|
|
|
static std::pair<Endpoint, Endpoint> _moveByCharacterForward(const int moveCount,
|
|
|
|
const MoveState moveState,
|
|
|
|
_Out_ int* const pAmountMoved);
|
|
|
|
|
|
|
|
static std::pair<Endpoint, Endpoint> _moveByCharacterBackward(const int moveCount,
|
|
|
|
const MoveState moveState,
|
|
|
|
_Out_ int* const pAmountMoved);
|
|
|
|
|
|
|
|
static std::pair<Endpoint, Endpoint> _moveByLine(const int moveCount,
|
|
|
|
const MoveState moveState,
|
|
|
|
_Out_ int* const pAmountMoved);
|
|
|
|
|
|
|
|
static std::pair<Endpoint, Endpoint> _moveByDocument(const int moveCount,
|
|
|
|
const MoveState moveState,
|
|
|
|
_Out_ int* const pAmountMoved);
|
|
|
|
|
|
|
|
static std::tuple<Endpoint, Endpoint, bool>
|
|
|
|
_moveEndpointByUnitCharacter(const int moveCount,
|
|
|
|
const TextPatternRangeEndpoint endpoint,
|
|
|
|
const MoveState moveState,
|
|
|
|
_Out_ int* const pAmountMoved);
|
|
|
|
|
|
|
|
static std::tuple<Endpoint, Endpoint, bool>
|
|
|
|
_moveEndpointByUnitCharacterForward(const int moveCount,
|
|
|
|
const TextPatternRangeEndpoint endpoint,
|
|
|
|
const MoveState moveState,
|
|
|
|
_Out_ int* const pAmountMoved);
|
|
|
|
|
|
|
|
static std::tuple<Endpoint, Endpoint, bool>
|
|
|
|
_moveEndpointByUnitCharacterBackward(const int moveCount,
|
|
|
|
const TextPatternRangeEndpoint endpoint,
|
|
|
|
const MoveState moveState,
|
|
|
|
_Out_ int* const pAmountMoved);
|
|
|
|
|
|
|
|
static std::tuple<Endpoint, Endpoint, bool>
|
|
|
|
_moveEndpointByUnitLine(const int moveCount,
|
|
|
|
const TextPatternRangeEndpoint endpoint,
|
|
|
|
const MoveState moveState,
|
|
|
|
_Out_ int* const pAmountMoved);
|
|
|
|
|
|
|
|
static std::tuple<Endpoint, Endpoint, bool>
|
|
|
|
_moveEndpointByUnitDocument(const int moveCount,
|
|
|
|
const TextPatternRangeEndpoint endpoint,
|
|
|
|
const MoveState moveState,
|
|
|
|
_Out_ int* const pAmountMoved);
|
|
|
|
|
|
|
|
#ifdef UNIT_TESTING
|
|
|
|
friend class ::UiaTextRangeTests;
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
|
|
|
namespace UiaTextRangeTracing
|
|
|
|
{
|
|
|
|
|
|
|
|
enum class ApiCall
|
|
|
|
{
|
|
|
|
Constructor,
|
|
|
|
AddRef,
|
|
|
|
Release,
|
|
|
|
QueryInterface,
|
|
|
|
Clone,
|
|
|
|
Compare,
|
|
|
|
CompareEndpoints,
|
|
|
|
ExpandToEnclosingUnit,
|
|
|
|
FindAttribute,
|
|
|
|
FindText,
|
|
|
|
GetAttributeValue,
|
|
|
|
GetBoundingRectangles,
|
|
|
|
GetEnclosingElement,
|
|
|
|
GetText,
|
|
|
|
Move,
|
|
|
|
MoveEndpointByUnit,
|
|
|
|
MoveEndpointByRange,
|
|
|
|
Select,
|
|
|
|
AddToSelection,
|
|
|
|
RemoveFromSelection,
|
|
|
|
ScrollIntoView,
|
|
|
|
GetChildren
|
|
|
|
};
|
|
|
|
|
|
|
|
struct IApiMsg
|
|
|
|
{
|
|
|
|
};
|
|
|
|
|
|
|
|
struct ApiMsgConstructor : public IApiMsg
|
|
|
|
{
|
|
|
|
IdType Id;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct ApiMsgClone : public IApiMsg
|
|
|
|
{
|
|
|
|
IdType CloneId;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct ApiMsgCompare : public IApiMsg
|
|
|
|
{
|
|
|
|
IdType OtherId;
|
|
|
|
bool Equal;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct ApiMsgCompareEndpoints : public IApiMsg
|
|
|
|
{
|
|
|
|
IdType OtherId;
|
|
|
|
TextPatternRangeEndpoint Endpoint;
|
|
|
|
TextPatternRangeEndpoint TargetEndpoint;
|
|
|
|
int Result;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct ApiMsgExpandToEnclosingUnit : public IApiMsg
|
|
|
|
{
|
|
|
|
TextUnit Unit;
|
|
|
|
Endpoint OriginalStart;
|
|
|
|
Endpoint OriginalEnd;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct ApiMsgGetText : IApiMsg
|
|
|
|
{
|
|
|
|
const wchar_t* Text;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct ApiMsgMove : IApiMsg
|
|
|
|
{
|
|
|
|
Endpoint OriginalStart;
|
|
|
|
Endpoint OriginalEnd;
|
|
|
|
TextUnit Unit;
|
|
|
|
int RequestedCount;
|
|
|
|
int MovedCount;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct ApiMsgMoveEndpointByUnit : IApiMsg
|
|
|
|
{
|
|
|
|
Endpoint OriginalStart;
|
|
|
|
Endpoint OriginalEnd;
|
|
|
|
TextPatternRangeEndpoint Endpoint;
|
|
|
|
TextUnit Unit;
|
|
|
|
int RequestedCount;
|
|
|
|
int MovedCount;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct ApiMsgMoveEndpointByRange : IApiMsg
|
|
|
|
{
|
|
|
|
Endpoint OriginalStart;
|
|
|
|
Endpoint OriginalEnd;
|
|
|
|
TextPatternRangeEndpoint Endpoint;
|
|
|
|
TextPatternRangeEndpoint TargetEndpoint;
|
|
|
|
IdType OtherId;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct ApiMsgScrollIntoView : IApiMsg
|
|
|
|
{
|
|
|
|
bool AlignToTop;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|