1 /****************************************************************************
2 **
3 ** Copyright (C) 2017 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the plugins of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include <QtGui/qtguiglobal.h>
41 #if QT_CONFIG(accessibility)
42 
43 #include "qwindowsuiautils.h"
44 #include "qwindowscontext.h"
45 #include "qwindowswindow.h"
46 
47 #include <QtGui/qwindow.h>
48 #include <QtGui/private/qhighdpiscaling_p.h>
49 #include <cmath>
50 
51 QT_BEGIN_NAMESPACE
52 
53 namespace QWindowsUiAutomation {
54 
55 // Returns the window containing the element (usually the top window),
windowForAccessible(const QAccessibleInterface * accessible)56 QWindow *windowForAccessible(const QAccessibleInterface *accessible)
57 {
58     QWindow *window = accessible->window();
59     if (!window) {
60         const QAccessibleInterface *acc = accessible;
61         const QAccessibleInterface *par = accessible->parent();
62         while (par && par->isValid() && !window) {
63             window = par->window();
64             acc = par;
65             par = par->parent();
66         }
67         if (!window) {
68             // Workaround for WebEngineView not knowing its parent.
69             const auto appWindows = QGuiApplication::topLevelWindows();
70             for (QWindow *w : appWindows) {
71                 if (QAccessibleInterface *root = w->accessibleRoot()) {
72                     int count = root->childCount();
73                     for (int i = 0; i < count; ++i) {
74                         if (root->child(i) == acc)
75                             return w;
76                     }
77                 }
78             }
79         }
80     }
81     return window;
82 }
83 
84 // Returns the native window handle associated with the element, if any.
85 // Usually it will be NULL, as Qt5 by default uses alien widgets with no native windows.
hwndForAccessible(const QAccessibleInterface * accessible)86 HWND hwndForAccessible(const QAccessibleInterface *accessible)
87 {
88     if (QWindow *window = accessible->window()) {
89         if (!accessible->parent() || (accessible->parent()->window() != window)) {
90             return QWindowsBaseWindow::handleOf(window);
91         }
92     }
93     return nullptr;
94 }
95 
clearVariant(VARIANT * variant)96 void clearVariant(VARIANT *variant)
97 {
98     variant->vt = VT_EMPTY;
99     variant->punkVal = nullptr;
100 }
101 
setVariantI4(int value,VARIANT * variant)102 void setVariantI4(int value, VARIANT *variant)
103 {
104     variant->vt = VT_I4;
105     variant->lVal = value;
106 }
107 
setVariantBool(bool value,VARIANT * variant)108 void setVariantBool(bool value, VARIANT *variant)
109 {
110     variant->vt = VT_BOOL;
111     variant->boolVal = value ? -1 : 0;
112 }
113 
setVariantDouble(double value,VARIANT * variant)114 void setVariantDouble(double value, VARIANT *variant)
115 {
116     variant->vt = VT_R8;
117     variant->dblVal = value;
118 }
119 
bStrFromQString(const QString & value)120 BSTR bStrFromQString(const QString &value)
121 {
122     return SysAllocString(reinterpret_cast<const wchar_t *>(value.utf16()));
123 }
124 
setVariantString(const QString & value,VARIANT * variant)125 void setVariantString(const QString &value, VARIANT *variant)
126 {
127     variant->vt = VT_BSTR;
128     variant->bstrVal = bStrFromQString(value);
129 }
130 
131 // Scales a rect to native coordinates, according to high dpi settings.
rectToNativeUiaRect(const QRect & rect,const QWindow * w,UiaRect * uiaRect)132 void rectToNativeUiaRect(const QRect &rect, const QWindow *w, UiaRect *uiaRect)
133 {
134     if (w && uiaRect) {
135         const qreal factor = QHighDpiScaling::factor(w);
136         uiaRect->left = qreal(rect.x()) * factor;
137         uiaRect->top = qreal(rect.y()) * factor;
138         uiaRect->width = qreal(rect.width()) * factor;
139         uiaRect->height = qreal(rect.height()) * factor;
140     }
141 }
142 
143 // Scales a point from native coordinates, according to high dpi settings.
nativeUiaPointToPoint(const UiaPoint & uiaPoint,const QWindow * w,QPoint * point)144 void nativeUiaPointToPoint(const UiaPoint &uiaPoint, const QWindow *w, QPoint *point)
145 {
146     if (w && point) {
147         const qreal factor = QHighDpiScaling::factor(w);
148         point->setX(int(std::lround(uiaPoint.x / factor)));
149         point->setY(int(std::lround(uiaPoint.y / factor)));
150     }
151 }
152 
153 // Maps an accessibility role ID to an UI Automation control type ID.
roleToControlTypeId(QAccessible::Role role)154 long roleToControlTypeId(QAccessible::Role role)
155 {
156     static const QHash<QAccessible::Role, long> mapping {
157         {QAccessible::TitleBar, UIA_TitleBarControlTypeId},
158         {QAccessible::MenuBar, UIA_MenuBarControlTypeId},
159         {QAccessible::ScrollBar, UIA_ScrollBarControlTypeId},
160         {QAccessible::Grip, UIA_ThumbControlTypeId},
161         {QAccessible::Sound, UIA_CustomControlTypeId},
162         {QAccessible::Cursor, UIA_CustomControlTypeId},
163         {QAccessible::Caret, UIA_CustomControlTypeId},
164         {QAccessible::AlertMessage, UIA_WindowControlTypeId},
165         {QAccessible::Window, UIA_WindowControlTypeId},
166         {QAccessible::Client, UIA_GroupControlTypeId},
167         {QAccessible::PopupMenu, UIA_MenuControlTypeId},
168         {QAccessible::MenuItem, UIA_MenuItemControlTypeId},
169         {QAccessible::ToolTip, UIA_ToolTipControlTypeId},
170         {QAccessible::Application, UIA_CustomControlTypeId},
171         {QAccessible::Document, UIA_DocumentControlTypeId},
172         {QAccessible::Pane, UIA_PaneControlTypeId},
173         {QAccessible::Chart, UIA_CustomControlTypeId},
174         {QAccessible::Dialog, UIA_WindowControlTypeId},
175         {QAccessible::Border, UIA_CustomControlTypeId},
176         {QAccessible::Grouping, UIA_GroupControlTypeId},
177         {QAccessible::Separator, UIA_SeparatorControlTypeId},
178         {QAccessible::ToolBar, UIA_ToolBarControlTypeId},
179         {QAccessible::StatusBar, UIA_StatusBarControlTypeId},
180         {QAccessible::Table, UIA_TableControlTypeId},
181         {QAccessible::ColumnHeader, UIA_HeaderControlTypeId},
182         {QAccessible::RowHeader, UIA_HeaderControlTypeId},
183         {QAccessible::Column, UIA_HeaderItemControlTypeId},
184         {QAccessible::Row, UIA_HeaderItemControlTypeId},
185         {QAccessible::Cell, UIA_DataItemControlTypeId},
186         {QAccessible::Link, UIA_HyperlinkControlTypeId},
187         {QAccessible::HelpBalloon, UIA_ToolTipControlTypeId},
188         {QAccessible::Assistant, UIA_CustomControlTypeId},
189         {QAccessible::List, UIA_ListControlTypeId},
190         {QAccessible::ListItem, UIA_ListItemControlTypeId},
191         {QAccessible::Tree, UIA_TreeControlTypeId},
192         {QAccessible::TreeItem, UIA_TreeItemControlTypeId},
193         {QAccessible::PageTab, UIA_TabItemControlTypeId},
194         {QAccessible::PropertyPage, UIA_CustomControlTypeId},
195         {QAccessible::Indicator, UIA_CustomControlTypeId},
196         {QAccessible::Graphic, UIA_ImageControlTypeId},
197         {QAccessible::StaticText, UIA_TextControlTypeId},
198         {QAccessible::EditableText, UIA_EditControlTypeId},
199         {QAccessible::Button, UIA_ButtonControlTypeId},
200         {QAccessible::CheckBox, UIA_CheckBoxControlTypeId},
201         {QAccessible::RadioButton, UIA_RadioButtonControlTypeId},
202         {QAccessible::ComboBox, UIA_ComboBoxControlTypeId},
203         {QAccessible::ProgressBar, UIA_ProgressBarControlTypeId},
204         {QAccessible::Dial, UIA_CustomControlTypeId},
205         {QAccessible::HotkeyField, UIA_CustomControlTypeId},
206         {QAccessible::Slider, UIA_SliderControlTypeId},
207         {QAccessible::SpinBox, UIA_SpinnerControlTypeId},
208         {QAccessible::Canvas, UIA_CustomControlTypeId},
209         {QAccessible::Animation, UIA_CustomControlTypeId},
210         {QAccessible::Equation, UIA_CustomControlTypeId},
211         {QAccessible::ButtonDropDown, UIA_ButtonControlTypeId},
212         {QAccessible::ButtonMenu, UIA_ButtonControlTypeId},
213         {QAccessible::ButtonDropGrid, UIA_ButtonControlTypeId},
214         {QAccessible::Whitespace, UIA_CustomControlTypeId},
215         {QAccessible::PageTabList, UIA_TabControlTypeId},
216         {QAccessible::Clock, UIA_CustomControlTypeId},
217         {QAccessible::Splitter, UIA_CustomControlTypeId},
218     };
219 
220     return mapping.value(role, UIA_CustomControlTypeId);
221 }
222 
223 // True if a character can be a separator for a text unit.
isTextUnitSeparator(TextUnit unit,const QChar & ch)224 bool isTextUnitSeparator(TextUnit unit, const QChar &ch)
225 {
226     return (((unit == TextUnit_Word) || (unit == TextUnit_Format)) && ch.isSpace())
227             || ((unit == TextUnit_Line) && (ch.toLatin1() == '\n'));
228 }
229 
230 } // namespace QWindowsUiAutomation
231 
232 
233 QT_END_NAMESPACE
234 
235 #endif // QT_CONFIG(accessibility)
236