1/****************************************************************************
2**
3** Copyright (C) 2016 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 "qaccessiblemenu_p.h"
41
42#if QT_CONFIG(menu)
43#include <qmenu.h>
44#endif
45#if QT_CONFIG(menubar)
46#include <qmenubar.h>
47#endif
48#include <qstyle.h>
49#include <private/qwidget_p.h>
50
51#ifndef QT_NO_ACCESSIBILITY
52
53QT_BEGIN_NAMESPACE
54
55#if QT_CONFIG(menu)
56
57QString qt_accStripAmp(const QString &text);
58QString qt_accHotKey(const QString &text);
59
60QAccessibleInterface *getOrCreateMenu(QWidget *menu, QAction *action)
61{
62 QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(action);
63 if (!iface) {
64 iface = new QAccessibleMenuItem(menu, action);
65 QAccessible::registerAccessibleInterface(iface);
66 }
67 return iface;
68}
69
70QAccessibleMenu::QAccessibleMenu(QWidget *w)
71: QAccessibleWidget(w)
72{
73 Q_ASSERT(menu());
74}
75
76QMenu *QAccessibleMenu::menu() const
77{
78 return qobject_cast<QMenu*>(object());
79}
80
81int QAccessibleMenu::childCount() const
82{
83 return menu()->actions().count();
84}
85
86QAccessibleInterface *QAccessibleMenu::childAt(int x, int y) const
87{
88 QAction *act = menu()->actionAt(menu()->mapFromGlobal(QPoint(x,y)));
89 if(act && act->isSeparator())
90 act = nullptr;
91 return act ? getOrCreateMenu(menu(), act) : nullptr;
92}
93
94QString QAccessibleMenu::text(QAccessible::Text t) const
95{
96 QString tx = QAccessibleWidget::text(t);
97 if (!tx.isEmpty())
98 return tx;
99
100 if (t == QAccessible::Name)
101 return menu()->windowTitle();
102 return tx;
103}
104
105QAccessible::Role QAccessibleMenu::role() const
106{
107 return QAccessible::PopupMenu;
108}
109
110QAccessibleInterface *QAccessibleMenu::child(int index) const
111{
112 if (index < childCount())
113 return getOrCreateMenu(menu(), menu()->actions().at(index));
114 return nullptr;
115}
116
117QAccessibleInterface *QAccessibleMenu::parent() const
118{
119 if (QAction *menuAction = menu()->menuAction()) {
120 QList<QObject *> parentCandidates;
121 const QList<QObject *> associatedObjects = menuAction->associatedObjects();
122 parentCandidates.reserve(associatedObjects.size() + 1);
123 parentCandidates << menu()->parentWidget() << associatedObjects;
124 for (QObject *object : qAsConst(parentCandidates)) {
125 if (qobject_cast<QMenu*>(object)
126#if QT_CONFIG(menubar)
127 || qobject_cast<QMenuBar*>(object)
128#endif
129 ) {
130 QWidget *widget = static_cast<QWidget*>(object);
131 if (widget->actions().indexOf(menuAction) != -1)
132 return getOrCreateMenu(widget, menuAction);
133 }
134 }
135 }
136 return QAccessibleWidget::parent();
137}
138
139int QAccessibleMenu::indexOfChild( const QAccessibleInterface *child) const
140{
141 QAccessible::Role r = child->role();
142 if ((r == QAccessible::MenuItem || r == QAccessible::Separator) && menu()) {
143 return menu()->actions().indexOf(qobject_cast<QAction*>(child->object()));
144 }
145 return -1;
146}
147
148#if QT_CONFIG(menubar)
149QAccessibleMenuBar::QAccessibleMenuBar(QWidget *w)
150 : QAccessibleWidget(w, QAccessible::MenuBar)
151{
152 Q_ASSERT(menuBar());
153}
154
155QMenuBar *QAccessibleMenuBar::menuBar() const
156{
157 return qobject_cast<QMenuBar*>(object());
158}
159
160int QAccessibleMenuBar::childCount() const
161{
162 return menuBar()->actions().count();
163}
164
165QAccessibleInterface *QAccessibleMenuBar::child(int index) const
166{
167 if (index < childCount()) {
168 return getOrCreateMenu(menuBar(), menuBar()->actions().at(index));
169 }
170 return nullptr;
171}
172
173int QAccessibleMenuBar::indexOfChild(const QAccessibleInterface *child) const
174{
175 QAccessible::Role r = child->role();
176 if ((r == QAccessible::MenuItem || r == QAccessible::Separator) && menuBar()) {
177 return menuBar()->actions().indexOf(qobject_cast<QAction*>(child->object()));
178 }
179 return -1;
180}
181
182#endif // QT_CONFIG(menubar)
183
184QAccessibleMenuItem::QAccessibleMenuItem(QWidget *owner, QAction *action)
185: m_action(action), m_owner(owner)
186{
187}
188
189QAccessibleMenuItem::~QAccessibleMenuItem()
190{}
191
192QAccessibleInterface *QAccessibleMenuItem::childAt(int x, int y ) const
193{
194 for (int i = childCount() - 1; i >= 0; --i) {
195 QAccessibleInterface *childInterface = child(i);
196 if (childInterface->rect().contains(x,y)) {
197 return childInterface;
198 }
199 }
200 return nullptr;
201}
202
203int QAccessibleMenuItem::childCount() const
204{
205 return m_action->menu() ? 1 : 0;
206}
207
208int QAccessibleMenuItem::indexOfChild(const QAccessibleInterface * child) const
209{
210 if (child && child->role() == QAccessible::PopupMenu && child->object() == m_action->menu())
211 return 0;
212 return -1;
213}
214
215bool QAccessibleMenuItem::isValid() const
216{
217 return m_action && m_owner;
218}
219
220QAccessibleInterface *QAccessibleMenuItem::parent() const
221{
222 return QAccessible::queryAccessibleInterface(owner());
223}
224
225QAccessibleInterface *QAccessibleMenuItem::child(int index) const
226{
227 if (index == 0 && action()->menu())
228 return QAccessible::queryAccessibleInterface(action()->menu());
229 return nullptr;
230}
231
232void *QAccessibleMenuItem::interface_cast(QAccessible::InterfaceType t)
233{
234 if (t == QAccessible::ActionInterface)
235 return static_cast<QAccessibleActionInterface*>(this);
236 return nullptr;
237}
238
239QObject *QAccessibleMenuItem::object() const
240{
241 return m_action;
242}
243
244/*! \reimp */
245QWindow *QAccessibleMenuItem::window() const
246{
247 return m_owner.isNull()
248 ? nullptr
249 : qt_widget_private(m_owner.data())->windowHandle(QWidgetPrivate::WindowHandleMode::Closest);
250}
251
252QRect QAccessibleMenuItem::rect() const
253{
254 QRect rect;
255 QWidget *own = owner();
256#if QT_CONFIG(menubar)
257 if (QMenuBar *menuBar = qobject_cast<QMenuBar*>(own)) {
258 rect = menuBar->actionGeometry(m_action);
259 QPoint globalPos = menuBar->mapToGlobal(QPoint(0,0));
260 rect = rect.translated(globalPos);
261 } else
262#endif // QT_CONFIG(menubar)
263 if (QMenu *menu = qobject_cast<QMenu*>(own)) {
264 rect = menu->actionGeometry(m_action);
265 QPoint globalPos = menu->mapToGlobal(QPoint(0,0));
266 rect = rect.translated(globalPos);
267 }
268 return rect;
269}
270
271QAccessible::Role QAccessibleMenuItem::role() const
272{
273 return m_action->isSeparator() ? QAccessible::Separator : QAccessible::MenuItem;
274}
275
276void QAccessibleMenuItem::setText(QAccessible::Text /*t*/, const QString & /*text */)
277{
278}
279
280QAccessible::State QAccessibleMenuItem::state() const
281{
282 QAccessible::State s;
283 QWidget *own = owner();
284
285 if (own && (own->testAttribute(Qt::WA_WState_Visible) == false || m_action->isVisible() == false)) {
286 s.invisible = true;
287 }
288
289 if (QMenu *menu = qobject_cast<QMenu*>(own)) {
290 if (menu->activeAction() == m_action)
291 s.focused = true;
292#if QT_CONFIG(menubar)
293 } else if (QMenuBar *menuBar = qobject_cast<QMenuBar*>(own)) {
294 if (menuBar->activeAction() == m_action)
295 s.focused = true;
296#endif
297 }
298 if (own && own->style()->styleHint(QStyle::SH_Menu_MouseTracking))
299 s.hotTracked = true;
300 if (m_action->isSeparator() || !m_action->isEnabled())
301 s.disabled = true;
302 if (m_action->isChecked())
303 s.checked = true;
304 if (m_action->isCheckable())
305 s.checkable = true;
306
307 return s;
308}
309
310QString QAccessibleMenuItem::text(QAccessible::Text t) const
311{
312 QString str;
313 switch (t) {
314 case QAccessible::Name:
315 str = qt_accStripAmp(m_action->text());
316 break;
317 case QAccessible::Accelerator: {
318#ifndef QT_NO_SHORTCUT
319 QKeySequence key = m_action->shortcut();
320 if (!key.isEmpty()) {
321 str = key.toString();
322 } else
323#endif
324 {
325 str = qt_accHotKey(m_action->text());
326 }
327 break;
328 }
329 default:
330 break;
331 }
332 return str;
333}
334
335QStringList QAccessibleMenuItem::actionNames() const
336{
337 QStringList actions;
338 if (!m_action || m_action->isSeparator())
339 return actions;
340
341 if (m_action->menu()) {
342 actions << showMenuAction();
343 } else {
344 actions << pressAction();
345 }
346 return actions;
347}
348
349void QAccessibleMenuItem::doAction(const QString &actionName)
350{
351 if (!m_action->isEnabled())
352 return;
353
354 if (actionName == pressAction()) {
355 m_action->trigger();
356 } else if (actionName == showMenuAction()) {
357#if QT_CONFIG(menubar)
358 if (QMenuBar *bar = qobject_cast<QMenuBar*>(owner())) {
359 if (m_action->menu() && m_action->menu()->isVisible()) {
360 m_action->menu()->hide();
361 } else {
362 bar->setActiveAction(m_action);
363 }
364 } else
365#endif
366 if (QMenu *menu = qobject_cast<QMenu*>(owner())){
367 if (m_action->menu() && m_action->menu()->isVisible()) {
368 m_action->menu()->hide();
369 } else {
370 menu->setActiveAction(m_action);
371 }
372 }
373 }
374}
375
376QStringList QAccessibleMenuItem::keyBindingsForAction(const QString &) const
377{
378 return QStringList();
379}
380
381
382QAction *QAccessibleMenuItem::action() const
383{
384 return m_action;
385}
386
387QWidget *QAccessibleMenuItem::owner() const
388{
389 return m_owner;
390}
391
392#endif // QT_CONFIG(menu)
393
394QT_END_NAMESPACE
395
396#endif // QT_NO_ACCESSIBILITY
397
398