/**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the plugins of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qaccessiblemenu.h" #include #include #include #include #ifndef QT_NO_ACCESSIBILITY QT_BEGIN_NAMESPACE #ifndef QT_NO_MENU QString Q_GUI_EXPORT qt_accStripAmp(const QString &text); QString Q_GUI_EXPORT qt_accHotKey(const QString &text); QAccessibleMenu::QAccessibleMenu(QWidget *w) : QAccessibleWidgetEx(w) { Q_ASSERT(menu()); } QMenu *QAccessibleMenu::menu() const { return qobject_cast(object()); } int QAccessibleMenu::childCount() const { return menu()->actions().count(); } QRect QAccessibleMenu::rect(int child) const { if (!child || child > childCount()) return QAccessibleWidgetEx::rect(child); QRect r = menu()->actionGeometry(menu()->actions()[child - 1]); QPoint tlp = menu()->mapToGlobal(QPoint(0,0)); return QRect(tlp.x() + r.x(), tlp.y() + r.y(), r.width(), r.height()); } int QAccessibleMenu::childAt(int x, int y) const { QAction *act = menu()->actionAt(menu()->mapFromGlobal(QPoint(x,y))); if(act && act->isSeparator()) act = 0; return menu()->actions().indexOf(act) + 1; } QString QAccessibleMenu::text(Text t, int child) const { QString tx = QAccessibleWidgetEx::text(t, child); if (!child && tx.size()) return tx; switch (t) { case Name: if (!child) return menu()->windowTitle(); return qt_accStripAmp(menu()->actions().at(child-1)->text()); case Help: return child ? menu()->actions().at(child-1)->whatsThis() : tx; #ifndef QT_NO_SHORTCUT case Accelerator: return child ? static_cast(menu()->actions().at(child-1)->shortcut()) : tx; #endif default: break; } return tx; } QAccessible::Role QAccessibleMenu::role(int child) const { if (!child) return PopupMenu; QAction *action = menu()->actions()[child-1]; if (action && action->isSeparator()) return Separator; return MenuItem; } QAccessible::State QAccessibleMenu::state(int child) const { State s = QAccessibleWidgetEx::state(child); if (!child) return s; QAction *action = menu()->actions()[child-1]; if (!action) return s; if (menu()->style()->styleHint(QStyle::SH_Menu_MouseTracking)) s |= HotTracked; if (action->isSeparator() || !action->isEnabled()) s |= Unavailable; if (action->isChecked()) s |= Checked; if (menu()->activeAction() == action) s |= Focused; return s; } QString QAccessibleMenu::actionText(int action, QAccessible::Text text, int child) const { if (action == QAccessible::DefaultAction && child && text == QAccessible::Name) { QAction *a = menu()->actions().value(child-1, 0); if (!a || a->isSeparator()) return QString(); if (a->menu()) { if (a->menu()->isVisible()) return QMenu::tr("Close"); return QMenu::tr("Open"); } return QMenu::tr("Execute"); } return QAccessibleWidgetEx::actionText(action, text, child); } bool QAccessibleMenu::doAction(int act, int child, const QVariantList &) { if (!child || act != QAccessible::DefaultAction) return false; QAction *action = menu()->actions().value(child-1, 0); if (!action || !action->isEnabled()) return false; if (action->menu() && action->menu()->isVisible()) action->menu()->hide(); else menu()->setActiveAction(action); return true; } int QAccessibleMenu::navigate(RelationFlag relation, int entry, QAccessibleInterface **target) const { int ret = -1; if (entry < 0) { *target = 0; return ret; } if (relation == Self || entry == 0) { *target = new QAccessibleMenu(menu()); return 0; } switch (relation) { case Child: if (entry <= childCount()) { *target = new QAccessibleMenuItem(menu(), menu()->actions().at( entry - 1 )); ret = 0; } break; case Ancestor: { QAccessibleInterface *iface; QWidget *parent = menu()->parentWidget(); if (qobject_cast(parent) || qobject_cast(parent)) { iface = new QAccessibleMenuItem(parent, menu()->menuAction()); if (entry == 1) { *target = iface; ret = 0; } else { ret = iface->navigate(Ancestor, entry - 1, target); delete iface; } } else { return QAccessibleWidgetEx::navigate(relation, entry, target); } break;} default: return QAccessibleWidgetEx::navigate(relation, entry, target); } if (ret == -1) *target = 0; return ret; } int QAccessibleMenu::indexOfChild( const QAccessibleInterface *child ) const { int index = -1; Role r = child->role(0); if ((r == MenuItem || r == Separator) && menu()) { index = menu()->actions().indexOf(qobject_cast(child->object())); if (index != -1) ++index; } return index; } #ifndef QT_NO_MENUBAR QAccessibleMenuBar::QAccessibleMenuBar(QWidget *w) : QAccessibleWidgetEx(w) { Q_ASSERT(menuBar()); } QMenuBar *QAccessibleMenuBar::menuBar() const { return qobject_cast(object()); } int QAccessibleMenuBar::childCount() const { return menuBar()->actions().count(); } QRect QAccessibleMenuBar::rect(int child) const { if (!child) return QAccessibleWidgetEx::rect(child); QRect r = menuBar()->actionGeometry(menuBar()->actions()[child - 1]); QPoint tlp = menuBar()->mapToGlobal(QPoint(0,0)); return QRect(tlp.x() + r.x(), tlp.y() + r.y(), r.width(), r.height()); } int QAccessibleMenuBar::childAt(int x, int y) const { for (int i = childCount(); i >= 0; --i) { if (rect(i).contains(x,y)) return i; } return -1; } int QAccessibleMenuBar::navigate(RelationFlag relation, int entry, QAccessibleInterface **target) const { int ret = -1; if (entry < 0) { *target = 0; return ret; } if (relation == Self || entry == 0) { *target = new QAccessibleMenuBar(menuBar()); return 0; } switch (relation) { case Child: if (entry <= childCount()) { *target = new QAccessibleMenuItem(menuBar(), menuBar()->actions().at( entry - 1 )); ret = 0; } break; default: return QAccessibleWidgetEx::navigate(relation, entry, target); } if (ret == -1) *target = 0; return ret; } int QAccessibleMenuBar::indexOfChild( const QAccessibleInterface *child ) const { int index = -1; Role r = child->role(0); if ((r == MenuItem || r == Separator) && menuBar()) { index = menuBar()->actions().indexOf(qobject_cast(child->object())); if (index != -1) ++index; } return index; } QString QAccessibleMenuBar::text(Text t, int child) const { QString str; if (child) { if (QAction *action = menuBar()->actions().value(child - 1, 0)) { switch (t) { case Name: return qt_accStripAmp(action->text()); case Accelerator: str = qt_accHotKey(action->text()); break; default: break; } } } if (str.isEmpty()) str = QAccessibleWidgetEx::text(t, child); return str; } QAccessible::Role QAccessibleMenuBar::role(int child) const { if (!child) return MenuBar; QAction *action = menuBar()->actions()[child-1]; if (action && action->isSeparator()) return Separator; return MenuItem; } QAccessible::State QAccessibleMenuBar::state(int child) const { State s = QAccessibleWidgetEx::state(child); if (!child) return s; QAction *action = menuBar()->actions().value(child-1, 0); if (!action) return s; if (menuBar()->style()->styleHint(QStyle::SH_Menu_MouseTracking)) s |= HotTracked; if (action->isSeparator() || !action->isEnabled()) s |= Unavailable; if (menuBar()->activeAction() == action) s |= Focused; return s; } QString QAccessibleMenuBar::actionText(int action, QAccessible::Text text, int child) const { if (action == QAccessible::DefaultAction && child && text == QAccessible::Name) { QAction *a = menuBar()->actions().value(child-1, 0); if (!a || a->isSeparator()) return QString(); if (a->menu()) { if (a->menu()->isVisible()) return QMenu::tr("Close"); return QMenu::tr("Open"); } return QMenu::tr("Execute"); } return QAccessibleWidgetEx::actionText(action, text, child); } bool QAccessibleMenuBar::doAction(int act, int child, const QVariantList &) { if (act != !child) return false; QAction *action = menuBar()->actions().value(child-1, 0); if (!action || !action->isEnabled()) return false; if (action->menu() && action->menu()->isVisible()) action->menu()->hide(); else menuBar()->setActiveAction(action); return true; } #endif // QT_NO_MENUBAR QAccessibleMenuItem::QAccessibleMenuItem(QWidget *owner, QAction *action) : m_action(action), m_owner(owner) { } QAccessibleMenuItem::~QAccessibleMenuItem() {} int QAccessibleMenuItem::childAt(int x, int y ) const { for (int i = childCount(); i >= 0; --i) { if (rect(i).contains(x,y)) return i; } return -1; } int QAccessibleMenuItem::childCount() const { return m_action->menu() ? 1 : 0; } QString QAccessibleMenuItem::actionText(int action, Text text, int child ) const { if (text == Name && child == 0) { switch (action) { case Press: case DefaultAction: if (m_action->menu()) return QMenu::tr("Open"); return QMenu::tr("Execute"); break; default: break; } } return QString(); } bool QAccessibleMenuItem::doAction(int action, int child, const QVariantList & /*params = QVariantList()*/ ) { if ((child) || ((action != DefaultAction) && (action != Press))) return false; // if the action has a menu, expand/hide it if (m_action->menu()) { if (m_action->menu()->isVisible()) { m_action->menu()->hide(); return true; } else { if (QMenuBar *bar = qobject_cast(owner())) { bar->setActiveAction(m_action); return true; } else if (QMenu *menu = qobject_cast(owner())){ menu->setActiveAction(m_action); return true; } } } // no menu m_action->trigger(); return true; } // action interface int QAccessibleMenuItem::actionCount() { return 1; } void QAccessibleMenuItem::doAction(int actionIndex) { if (actionIndex) return; doAction(DefaultAction, 0); } int QAccessibleMenuItem::indexOfChild( const QAccessibleInterface * child ) const { if (child->role(0) == PopupMenu && child->object() == m_action->menu()) return 1; return -1; } bool QAccessibleMenuItem::isValid() const { return m_action ? true : false; } int QAccessibleMenuItem::navigate(RelationFlag relation, int entry, QAccessibleInterface ** target ) const { int ret = -1; if (entry < 0) { *target = 0; return ret; } if (relation == Self || entry == 0) { *target = new QAccessibleMenuItem(owner(), action()); return 0; } switch (relation) { case Child: if (entry <= childCount()) { *target = new QAccessibleMenu(action()->menu()); ret = 0; } break; case Ancestor:{ QWidget *parent = owner(); QAccessibleInterface *ancestor = parent ? QAccessible::queryAccessibleInterface(parent) : 0; if (ancestor) { if (entry == 1) { *target = ancestor; ret = 0; } else { ret = ancestor->navigate(Ancestor, entry - 1, target); delete ancestor; } } break;} case Up: case Down:{ QAccessibleInterface *parent = 0; int ent = navigate(Ancestor, 1, &parent); if (ent == 0) { int index = parent->indexOfChild(this); if (index != -1) { index += (relation == Down ? +1 : -1); ret = parent->navigate(Child, index, target); } } delete parent; break;} case Sibling: { QAccessibleInterface *parent = 0; int ent = navigate(Ancestor, 1, &parent); if (ent == 0) { ret = parent->navigate(Child, entry, target); } delete parent; break;} default: break; } if (ret == -1) *target = 0; return ret; } QObject *QAccessibleMenuItem::object() const { return m_action; } QRect QAccessibleMenuItem::rect (int child ) const { QRect rect; if (child == 0) { QWidget *own = owner(); #ifndef QT_NO_MENUBAR if (QMenuBar *menuBar = qobject_cast(own)) { rect = menuBar->actionGeometry(m_action); QPoint globalPos = menuBar->mapToGlobal(QPoint(0,0)); rect = rect.translated(globalPos); } else #endif // QT_NO_MENUBAR if (QMenu *menu = qobject_cast(own)) { rect = menu->actionGeometry(m_action); QPoint globalPos = menu->mapToGlobal(QPoint(0,0)); rect = rect.translated(globalPos); } } else if (child == 1) { QMenu *menu = m_action->menu(); if (menu) { rect = menu->rect(); QPoint globalPos = menu->mapToGlobal(QPoint(0,0)); rect = rect.translated(globalPos); } } return rect; } QAccessible::Relation QAccessibleMenuItem::relationTo ( int child, const QAccessibleInterface * other, int otherChild ) const { if (other->object() == owner()) { return Child; } Q_UNUSED(child) Q_UNUSED(other) Q_UNUSED(otherChild) // ### return Unrelated; } QAccessible::Role QAccessibleMenuItem::role(int /*child*/ ) const { return m_action->isSeparator() ? Separator :MenuItem; } void QAccessibleMenuItem::setText ( Text /*t*/, int /*child*/, const QString & /*text */) { } QAccessible::State QAccessibleMenuItem::state(int child ) const { QAccessible::State s = Unavailable; if (child == 0) { s = Normal; QWidget *own = owner(); if (own && (own->testAttribute(Qt::WA_WState_Visible) == false || m_action->isVisible() == false)) { s |= Invisible; } if (QMenu *menu = qobject_cast(own)) { if (menu->activeAction() == m_action) s |= Focused; #ifndef QT_NO_MENUBAR } else if (QMenuBar *menuBar = qobject_cast(own)) { if (menuBar->activeAction() == m_action) s |= Focused; #endif } if (own && own->style()->styleHint(QStyle::SH_Menu_MouseTracking)) s |= HotTracked; if (m_action->isSeparator() || !m_action->isEnabled()) s |= Unavailable; if (m_action->isChecked()) s |= Checked; } else if (child == 1) { QMenu *menu = m_action->menu(); if (menu) { QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(menu); s = iface->state(0); delete iface; } } return s | HasInvokeExtension;; } QString QAccessibleMenuItem::text ( Text t, int child ) const { QString str; switch (t) { case Name: if (child == 0) { str = m_action->text(); } else if (child == 1) { QMenu *m = m_action->menu(); if (m) str = m->title(); } str = qt_accStripAmp(str); break; case Accelerator: if (child == 0) { #ifndef QT_NO_SHORTCUT QKeySequence key = m_action->shortcut(); if (!key.isEmpty()) { str = key.toString(); } else #endif { str = qt_accHotKey(m_action->text()); } } break; default: break; } return str; } // action interface int QAccessibleMenuItem::userActionCount ( int /*child*/ ) const { return 0; } QAction *QAccessibleMenuItem::action() const { return m_action; } QString QAccessibleMenuItem::description(int) { return text(QAccessible::Description, 0); } QString QAccessibleMenuItem::name(int) { return actionText(DefaultAction, QAccessible::Name, 0); } QString QAccessibleMenuItem::localizedName(int) { return text(QAccessible::Name, 0); } QStringList QAccessibleMenuItem::keyBindings(int) { QStringList keys; #ifndef QT_NO_SHORTCUT QKeySequence key = m_action->shortcut(); if (!key.isEmpty()) { keys.append(key.toString()); } #endif return keys; } QVariant QAccessibleMenuItem::invokeMethodEx(Method, int, const QVariantList &) { return QVariant(); } QWidget *QAccessibleMenuItem::owner() const { return m_owner; } #endif // QT_NO_MENU QT_END_NAMESPACE #endif // QT_NO_ACCESSIBILITY