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

source code of qtbase/src/widgets/accessible/qaccessiblemenu.cpp