summaryrefslogtreecommitdiffstats
path: root/src/gui
diff options
context:
space:
mode:
authorFrederik Gladhorn <frederik.gladhorn@digia.com>2013-01-03 17:18:40 +0100
committerThe Qt Project <gerrit-noreply@qt-project.org>2013-04-02 14:32:25 +0200
commitb2ec0da95641d9cec006fa9699e6d082ad35db0b (patch)
treec6ceef7e1527b98f3eda33b6e525d65ee1d810a6 /src/gui
parent8dfe1385b5f05b7242802a72897258a63af1ca1d (diff)
Cache QAccessibleInterfaces.
Since there already is a one-to-one relationship between QObject and QAccessibleInterface it makes little sense to create and destroy the interfaces on each call to queryAccessibleInterface. Add a cache and keep created interfaces around for the lifetime of the corresponding QObject. This changes the memory management rules: accessible interfaces must no longer be deleted. If you get an QAccessibleIntrface pointer that pointer will stay valid as long as the corresponding QObject is not deleted. This also re-enables accessibility for Mac. We limit the range of the IDs so that they are useable for Windows directly. That means we can get rid of the event cache there. This is based on: Iebf2f374916fc70a9dd29e95f45a6444b85f6cee Change-Id: I9fe6531812c0dbc5b41101ac05830a6dd75e13a3 Reviewed-by: Morten Johan Sørvig <morten.sorvig@digia.com>
Diffstat (limited to 'src/gui')
-rw-r--r--src/gui/accessible/accessible.pri2
-rw-r--r--src/gui/accessible/qaccessible.cpp106
-rw-r--r--src/gui/accessible/qaccessible.h20
-rw-r--r--src/gui/accessible/qaccessible2_p.h2
-rw-r--r--src/gui/accessible/qaccessiblecache.cpp116
-rw-r--r--src/gui/accessible/qaccessiblecache_p.h78
-rw-r--r--src/gui/accessible/qaccessibleobject.cpp6
7 files changed, 310 insertions, 20 deletions
diff --git a/src/gui/accessible/accessible.pri b/src/gui/accessible/accessible.pri
index 592607bce5..6fdb2d35b2 100644
--- a/src/gui/accessible/accessible.pri
+++ b/src/gui/accessible/accessible.pri
@@ -3,6 +3,7 @@
contains(QT_CONFIG, accessibility) {
HEADERS += \
accessible/qaccessible.h \
+ accessible/qaccessiblecache_p.h \
accessible/qaccessible2_p.h \
accessible/qaccessibleobject.h \
accessible/qaccessibleplugin.h \
@@ -10,6 +11,7 @@ contains(QT_CONFIG, accessibility) {
SOURCES += accessible/qaccessible.cpp \
accessible/qaccessible2.cpp \
+ accessible/qaccessiblecache.cpp \
accessible/qaccessibleobject.cpp \
accessible/qaccessibleplugin.cpp \
accessible/qplatformaccessibility.cpp
diff --git a/src/gui/accessible/qaccessible.cpp b/src/gui/accessible/qaccessible.cpp
index 3468ebc8a7..e007c9967e 100644
--- a/src/gui/accessible/qaccessible.cpp
+++ b/src/gui/accessible/qaccessible.cpp
@@ -41,6 +41,8 @@
#include "qaccessible.h"
+#include "qaccessible2_p.h"
+#include "qaccessiblecache_p.h"
#include "qaccessibleplugin.h"
#include "qaccessibleobject.h"
#include "qaccessiblebridge.h"
@@ -558,6 +560,8 @@ QAccessible::RootObjectHandler QAccessible::installRootObjectHandler(RootObjectH
return old;
}
+Q_GLOBAL_STATIC(QAccessibleCache, qAccessibleCache)
+
/*!
If a QAccessibleInterface implementation exists for the given \a object,
this function returns a pointer to the implementation; otherwise it
@@ -574,8 +578,7 @@ QAccessible::RootObjectHandler QAccessible::installRootObjectHandler(RootObjectH
function tries to find an implementation for the object's parent
class, using the above strategy.
- \warning The caller is responsible for deleting the returned
- interface after use.
+ All interfaces are managed by an internal cache and should not be deleted.
*/
QAccessibleInterface *QAccessible::queryAccessibleInterface(QObject *object)
{
@@ -583,6 +586,9 @@ QAccessibleInterface *QAccessible::queryAccessibleInterface(QObject *object)
if (!object)
return 0;
+ if (Id id = qAccessibleCache->objectToId.value(object))
+ return qAccessibleCache->interfaceForId(id);
+
// Create a QAccessibleInterface for the object class. Start by the most
// derived class and walk up the class hierarchy.
const QMetaObject *mo = object->metaObject();
@@ -592,8 +598,11 @@ QAccessibleInterface *QAccessible::queryAccessibleInterface(QObject *object)
// Check if the class has a InterfaceFactory installed.
for (int i = qAccessibleFactories()->count(); i > 0; --i) {
InterfaceFactory factory = qAccessibleFactories()->at(i - 1);
- if (QAccessibleInterface *iface = factory(cn, object))
+ if (QAccessibleInterface *iface = factory(cn, object)) {
+ qAccessibleCache->insert(object, iface);
+ Q_ASSERT(qAccessibleCache->objectToId.contains(object));
return iface;
+ }
}
#ifndef QT_NO_ACCESSIBILITY
#ifndef QT_NO_LIBRARY
@@ -610,22 +619,84 @@ QAccessibleInterface *QAccessible::queryAccessibleInterface(QObject *object)
// At this point the cache should contain a valid factory pointer or 0:
Q_ASSERT(qAccessiblePlugins()->contains(cn));
QAccessiblePlugin *factory = qAccessiblePlugins()->value(cn);
- if (factory)
- return factory->create(cn, object);
+ if (factory) {
+ QAccessibleInterface *result = factory->create(cn, object);
+ if (result) { // Need this condition because of QDesktopScreenWidget
+ qAccessibleCache->insert(object, result);
+ Q_ASSERT(qAccessibleCache->objectToId.contains(object));
+ }
+ return result;
+ }
#endif
#endif
mo = mo->superClass();
}
#ifndef QT_NO_ACCESSIBILITY
- if (object == qApp)
- return new QAccessibleApplication;
+ if (object == qApp) {
+ QAccessibleInterface *appInterface = new QAccessibleApplication;
+ qAccessibleCache->insert(object, appInterface);
+ Q_ASSERT(qAccessibleCache->objectToId.contains(qApp));
+ return appInterface;
+ }
#endif
return 0;
}
/*!
+ \internal
+ Required to ensure that manually created interfaces
+ are properly memory managed.
+
+ Must only be called exactly once per interface.
+ This is implicitly called when calling queryAccessibleInterface,
+ so it's only required when re-implementing for example
+ the child function and returning the child after new-ing
+ a QAccessibleInterface subclass.
+ */
+QAccessible::Id QAccessible::registerAccessibleInterface(QAccessibleInterface *iface)
+{
+ Q_ASSERT(iface);
+ return qAccessibleCache->insert(iface->object(), iface);
+}
+
+/*!
+ \internal
+ Removes the interface belonging to this id from the cache and
+ deletes it. The id becomes invalid an may be re-used by the
+ cache.
+*/
+void QAccessible::deleteAccessibleInterface(Id id)
+{
+ qAccessibleCache->deleteInterface(id);
+}
+
+/*!
+ \internal
+ Returns the unique ID for the accessibleInterface.
+*/
+QAccessible::Id QAccessible::uniqueId(QAccessibleInterface *iface)
+{
+ Id id = qAccessibleCache->idToInterface.key(iface);
+ if (!id)
+ id = registerAccessibleInterface(iface);
+ return id;
+}
+
+/*!
+ \internal
+ Returns the QAccessibleInterface belonging to the id.
+
+ Returns 0 if the id is invalid.
+*/
+QAccessibleInterface *QAccessible::accessibleInterface(Id id)
+{
+ return qAccessibleCache->idToInterface.value(id);
+}
+
+
+/*!
Returns true if an accessibility implementation has been requested
during the runtime of the application; otherwise returns false.
@@ -687,15 +758,23 @@ void QAccessible::setRootObject(QObject *object)
*/
void QAccessible::updateAccessibility(QAccessibleEvent *event)
{
- if (updateHandler) {
- updateHandler(event);
+ if (!isActive())
return;
+
+#ifndef QT_NO_ACCESSIBILITY
+ if (event->type() == QAccessible::TableModelChanged) {
+ Q_ASSERT(event->object());
+ if (QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(event->object())) {
+ if (iface->tableInterface())
+ iface->tableInterface()->modelChange(static_cast<QAccessibleTableModelChangeEvent*>(event));
+ }
}
- if (!isActive())
+ if (updateHandler) {
+ updateHandler(event);
return;
+ }
-#ifndef QT_NO_ACCESSIBILITY
if (QPlatformAccessibility *pfAccessibility = platformAccessibility())
pfAccessibility->notifyAccessibilityUpdate(event);
#endif
@@ -1028,6 +1107,10 @@ QColor QAccessibleInterface::backgroundColor() const
return QColor();
}
+QAccessibleInterface::~QAccessibleInterface()
+{
+}
+
/*!
\fn QAccessibleTextInterface *QAccessibleInterface::textInterface()
\internal
@@ -1362,7 +1445,6 @@ QAccessibleInterface *QAccessibleEvent::accessibleInterface() const
if (m_child >= 0) {
QAccessibleInterface *child = iface->child(m_child);
if (child) {
- delete iface;
iface = child;
} else {
qWarning() << "Cannot creat accessible child interface for object: " << m_object << " index: " << m_child;
diff --git a/src/gui/accessible/qaccessible.h b/src/gui/accessible/qaccessible.h
index 09e259c1d0..8447f34154 100644
--- a/src/gui/accessible/qaccessible.h
+++ b/src/gui/accessible/qaccessible.h
@@ -45,6 +45,7 @@
#ifndef QACCESSIBLE_H
#define QACCESSIBLE_H
+#include <QtCore/qdebug.h>
#include <QtCore/qglobal.h>
#include <QtCore/qobject.h>
#include <QtCore/qrect.h>
@@ -58,7 +59,6 @@
QT_BEGIN_NAMESPACE
-
class QAccessibleInterface;
class QAccessibleEvent;
class QWindow;
@@ -335,12 +335,20 @@ public:
typedef void(*UpdateHandler)(QAccessibleEvent *event);
typedef void(*RootObjectHandler)(QObject*);
+ typedef unsigned Id;
+
static void installFactory(InterfaceFactory);
static void removeFactory(InterfaceFactory);
static UpdateHandler installUpdateHandler(UpdateHandler);
static RootObjectHandler installRootObjectHandler(RootObjectHandler);
static QAccessibleInterface *queryAccessibleInterface(QObject *);
+ static Id uniqueId(QAccessibleInterface *iface);
+ static QAccessibleInterface *accessibleInterface(Id uniqueId);
+ static Id registerAccessibleInterface(QAccessibleInterface *iface);
+ static void deleteAccessibleInterface(Id uniqueId);
+
+
#if QT_DEPRECATED_SINCE(5, 0)
QT_DEPRECATED static inline void updateAccessibility(QObject *object, int child, Event reason);
#endif
@@ -360,6 +368,8 @@ private:
it is not supposed to be instantiated.
*/
QAccessible() {}
+
+ friend class QAccessibleCache;
};
Q_GUI_EXPORT bool operator==(const QAccessible::State &first, const QAccessible::State &second);
@@ -377,8 +387,10 @@ class QAccessibleTableCellInterface;
class Q_GUI_EXPORT QAccessibleInterface
{
+protected:
+ virtual ~QAccessibleInterface();
+
public:
- virtual ~QAccessibleInterface() {}
// check for valid pointers
virtual bool isValid() const = 0;
virtual QObject *object() const = 0;
@@ -431,7 +443,9 @@ public:
virtual void *interface_cast(QAccessible::InterfaceType)
{ return 0; }
-private:
+
+protected:
+ friend class QAccessibleCache;
};
class Q_GUI_EXPORT QAccessibleEvent
diff --git a/src/gui/accessible/qaccessible2_p.h b/src/gui/accessible/qaccessible2_p.h
index 169ca2b5e5..1a1eeea4ba 100644
--- a/src/gui/accessible/qaccessible2_p.h
+++ b/src/gui/accessible/qaccessible2_p.h
@@ -193,6 +193,8 @@ public:
// Unselects one column, leaving other selected columns selected (if any).
virtual bool unselectColumn(int column) = 0;
+ virtual void modelChange(QAccessibleTableModelChangeEvent *event) = 0;
+
protected:
friend class QAbstractItemView;
friend class QAbstractItemViewPrivate;
diff --git a/src/gui/accessible/qaccessiblecache.cpp b/src/gui/accessible/qaccessiblecache.cpp
new file mode 100644
index 0000000000..1b79c30b6c
--- /dev/null
+++ b/src/gui/accessible/qaccessiblecache.cpp
@@ -0,0 +1,116 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtGui module 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 "qaccessiblecache_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QAccessibleCache
+ \internal
+ \brief Maintains a cache of accessible interfaces.
+*/
+
+/*
+ The ID is always in the range [INT_MAX+1, UINT_MAX].
+ This makes it easy on windows to reserve the positive integer range
+ for the index of a child and not clash with the unique ids.
+*/
+QAccessible::Id QAccessibleCache::acquireId() const
+{
+ static const QAccessible::Id FirstId = QAccessible::Id(INT_MAX) + 1;
+ static QAccessible::Id lastUsedId = FirstId;
+
+ while (idToInterface.contains(lastUsedId)) {
+ if (lastUsedId == UINT_MAX) // (wrap back when when we reach UINT_MAX)
+ lastUsedId = FirstId;
+ else
+ ++lastUsedId;
+ }
+
+ return lastUsedId;
+}
+
+QAccessibleInterface *QAccessibleCache::interfaceForId(QAccessible::Id id) const
+{
+ return idToInterface.value(id);
+}
+
+QAccessible::Id QAccessibleCache::insert(QObject *object, QAccessibleInterface *iface) const
+{
+ Q_ASSERT(iface);
+ Q_UNUSED(object)
+
+ // object might be 0
+ Q_ASSERT(!objectToId.contains(object));
+ Q_ASSERT_X(!idToInterface.values().contains(iface), "", "Accessible interface inserted into cache twice!");
+
+ QAccessible::Id id = acquireId();
+ QObject *obj = iface->object();
+ Q_ASSERT(object == obj);
+ if (obj) {
+ objectToId.insert(obj, id);
+ connect(obj, SIGNAL(destroyed(QObject *)), this, SLOT(objectDestroyed(QObject *)));
+ }
+ idToInterface.insert(id, iface);
+ return id;
+}
+
+void QAccessibleCache::objectDestroyed(QObject* obj)
+{
+ QAccessible::Id id = objectToId.value(obj);
+ if (id) {
+ Q_ASSERT_X(idToInterface.contains(id), "", "QObject with accessible interface deleted, where interface not in cache!");
+ deleteInterface(id, obj);
+ }
+}
+
+void QAccessibleCache::deleteInterface(QAccessible::Id id, QObject *obj)
+{
+ QAccessibleInterface *iface = idToInterface.take(id);
+ if (!obj)
+ obj = iface->object();
+ if (obj)
+ objectToId.remove(obj);
+ delete iface;
+}
+
+QT_END_NAMESPACE
diff --git a/src/gui/accessible/qaccessiblecache_p.h b/src/gui/accessible/qaccessiblecache_p.h
new file mode 100644
index 0000000000..32f9c443ba
--- /dev/null
+++ b/src/gui/accessible/qaccessiblecache_p.h
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtGui module 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$
+**
+****************************************************************************/
+
+#ifndef QACCESSIBLECACHE_P
+#define QACCESSIBLECACHE_P
+
+#include <QtCore/qglobal.h>
+#include <QtCore/qobject.h>
+#include <QtCore/qhash.h>
+
+#include "qaccessible.h"
+
+QT_BEGIN_NAMESPACE
+
+
+class QAccessibleCache :public QObject
+{
+ Q_OBJECT
+
+public:
+ QAccessibleInterface *interfaceForId(QAccessible::Id id) const;
+ QAccessible::Id insert(QObject *object, QAccessibleInterface *iface) const;
+ void deleteInterface(QAccessible::Id id, QObject *obj = 0);
+
+private Q_SLOTS:
+ void objectDestroyed(QObject *obj);
+
+private:
+ QAccessible::Id acquireId() const;
+
+ mutable QHash<QAccessible::Id, QAccessibleInterface *> idToInterface;
+ mutable QHash<QObject *, QAccessible::Id> objectToId;
+
+ friend class QAccessible;
+ friend class QAccessibleInterface;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/gui/accessible/qaccessibleobject.cpp b/src/gui/accessible/qaccessibleobject.cpp
index c1ef71e1fa..ccbfd36b70 100644
--- a/src/gui/accessible/qaccessibleobject.cpp
+++ b/src/gui/accessible/qaccessibleobject.cpp
@@ -132,11 +132,8 @@ QAccessibleInterface *QAccessibleObject::childAt(int x, int y) const
for (int i = 0; i < childCount(); ++i) {
QAccessibleInterface *childIface = child(i);
Q_ASSERT(childIface);
- if (childIface->rect().contains(x,y)) {
+ if (childIface->rect().contains(x,y))
return childIface;
- } else {
- delete childIface;
- }
}
return 0;
}
@@ -176,7 +173,6 @@ static QObjectList topLevelObjects()
if (QAccessibleInterface *root = w->accessibleRoot()) {
if (root->object())
list.append(root->object());
- delete root;
}
}
}