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