qt5base-lts/util/accessibilityinspector/accessibilityscenemanager.cpp
Morten Sorvig cff475f339 Move a11y inspector from tools to util.
Change-Id: Ifc032c511aea72a8f7a4ec62d304e89718f712db
Reviewed-by: Morten Johan Sørvig <morten.sorvig@nokia.com>
2011-10-20 02:31:45 +02:00

488 lines
15 KiB
C++

/****************************************************************************
**
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the tools applications of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** 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, Nokia gives you certain additional
** rights. These rights are described in the Nokia 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.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "accessibilityscenemanager.h"
AccessibilitySceneManager::AccessibilitySceneManager()
{
m_window = 0;
m_view = 0;
m_scene = 0;
m_rootItem = 0;
m_optionsWidget = 0;
m_selectedObject = 0;
}
void AccessibilitySceneManager::populateAccessibilityScene()
{
m_scene->clear();
m_graphicsItems.clear();
QAccessibleInterface * rootInterface = m_window->accessibleRoot();
if (!rootInterface)
return;
populateAccessibilityScene(rootInterface, 0, m_scene);
}
void AccessibilitySceneManager::updateAccessibilitySceneItemFlags()
{
qDebug() << "update";
foreach (QObject *object, m_graphicsItems.keys()) {
if (!object)
continue;
QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(object);
if (!interface)
continue;
updateItemFlags(m_graphicsItems.value(object), interface);
delete interface;
}
}
void AccessibilitySceneManager::populateAccessibilityTreeScene()
{
m_treeScene->clear();
QAccessibleInterface * rootInterface = m_window->accessibleRoot();
if (!rootInterface)
return;
populateAccessibilityTreeScene(rootInterface, 0);
}
void AccessibilitySceneManager::handleUpdate(QObject *object, QAccessible::Event reason)
{
QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(object);
if (!interface)
return;
QString name = interface->text(QAccessible::Name, 0);
if (reason == QAccessible::ObjectCreated) {
// qDebug() << "ObjectCreated" << object << name;
populateAccessibilityScene(interface, 0, m_scene);
}
QGraphicsRectItem *item = m_graphicsItems.value(object);
if (!item) {
// qDebug() << "populateAccessibilityScene failed for" << object;
return;
}
if (reason == QAccessible::LocationChanged) {
//if (name.startsWith("List"))
qDebug() << "locationChange" << object << name << interface->rect(0);
updateItem(item, interface);
for (int i = 0; i < interface->childCount(); ++i) {
QAccessibleInterface *child = 0;
int ret = interface->navigate(QAccessible::Child, i + 1, &child);
if (ret == 0 && child) {
updateItem(m_graphicsItems.value(child->object()), child);
delete child;
}
}
delete interface;
} else if (reason == QAccessible::ObjectDestroyed) {
// qDebug() << "ObjectDestroyed" << object << name;
delete m_graphicsItems.value(object);
m_graphicsItems.remove(object);
m_animatedObjects.remove(object);
if (object == m_selectedObject) {
m_selectedObject = 0;
}
} else if (reason == QAccessible::ObjectHide) {
// qDebug() << "ObjectCreated Hide" << object;
updateItemFlags(item, interface);
} else if (reason == QAccessible::ObjectShow) {
// qDebug() << "ObjectCreated Show" << object;
updateItemFlags(item, interface);
} else if (reason == QAccessible::ScrollingStart) {
qDebug() << "ObjectCreated ScrollingStart" << object;
QAccessibleInterface *child = 0;
for (int i = 0; i < interface->childCount(); ++i) {
int ret = interface->navigate(QAccessible::Child, i + 1, &child);
if (ret == 0 && child) {
m_animatedObjects.insert(child->object());
delete child;
}
}
} else if (reason == QAccessible::ScrollingEnd) {
// qDebug() << "ObjectCreated ScrollingEnd" << object;
foreach (QObject *object, m_animatedObjects) {
updateItem(m_graphicsItems.value(object), interface);
}
delete interface;
m_animatedObjects.clear();
} else {
qDebug() << "other update" << object;
}
}
void AccessibilitySceneManager::setSelected(QObject *object)
{
m_scene->update(); // scedule update
// clear existing selection
if (m_selectedObject) {
QObject *previousSelectedObject = m_selectedObject;
m_selectedObject = 0;
updateItem(previousSelectedObject);
}
m_selectedObject = object;
updateItem(object);
populateAccessibilityTreeScene();
}
void AccessibilitySceneManager::changeScale(int)
{
// No QGraphicsView::setScale :(
//m_view->scale(scale / 10.0, scale / 10.0);
//if (m_rootItem)
// m_view->ensureVisible(m_rootItem);
}
void AccessibilitySceneManager::updateItems(QObject *root)
{
QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(root);
if (!interface)
return;
updateItem(m_graphicsItems.value(root), interface);
for (int i = 0; i < interface->childCount(); ++i) {
QAccessibleInterface *child = interface->child(i);
updateItems(child->object());
delete child;
}
delete interface;
}
void AccessibilitySceneManager::updateItem(QObject *object)
{
if (!object)
return;
QAccessibleInterface *interface = QAccessible::queryAccessibleInterface(object);
if (!interface)
return;
updateItem(m_graphicsItems.value(object), interface);
delete interface;
}
void AccessibilitySceneManager::updateItem(QGraphicsRectItem *item, QAccessibleInterface *interface)
{
if (!item)
return;
QRect rect = interface->rect(0);
item->setPos(rect.topLeft());
item->setRect(QRect(QPoint(0,0), rect.size()));
updateItemFlags(item, interface);
}
void AccessibilitySceneManager::updateItemFlags(QGraphicsRectItem *item, QAccessibleInterface *interface)
{
// qDebug() << "udpateItemFlags" << interface << interface->object();
bool shouldShow = true;
if (m_optionsWidget->hideInvisibleItems()) {
if (isHidden(interface)) {
shouldShow = false;
}
}
if (m_optionsWidget->hideOffscreenItems()) {
if (interface->state(0) & QAccessible::Offscreen) {
shouldShow = false;
}
}
if (m_optionsWidget->hidePaneItems()) {
if (interface->role(0) & QAccessible::Pane) {
shouldShow = false;
}
}
item->setVisible(shouldShow);
if (interface->object() == m_selectedObject)
item->setBrush(QColor(Qt::yellow));
else
item->setBrush(QColor(Qt::white));
m_view->update();
}
QGraphicsRectItem * AccessibilitySceneManager::processInterface(QAccessibleInterface * interface, int child, QGraphicsScene *scene)
{
// Process this interface
QGraphicsRectItem * item = new QGraphicsRectItem();
scene->addItem(item);
if (!m_rootItem)
m_rootItem = item;
QString name = interface->text(QAccessibleInterface::Name, child);
QString description; // = interface->text(QAccessibleInterface::Description, child);
QString role = translateRole(interface->role(child));
int childCount = interface->childCount();
/* qDebug() << "name:" << name << "local pos" <<
interface->rect(0) << "description" << description << "childCount" << childCount;
*/
updateItem(item, interface);
QGraphicsSimpleTextItem * textItem = new QGraphicsSimpleTextItem();
textItem->setParentItem(item);
textItem->setPos(QPoint(5, 5));
QString text;
text.append("Name: " + name + " ");
if (!description.isEmpty())
text.append("Description: " + description + " ");
text.append("Role: " + role + " ");
if (childCount > 0)
text.append("ChildCount: " + QString::number(childCount) + " ");
textItem->setText(text);
QFont font;
font.setPointSize(10);
// font.setPointSize(14);
textItem->setFont(font);
return item;
}
void AccessibilitySceneManager::populateAccessibilityScene(QAccessibleInterface * interface, int child, QGraphicsScene *scene)
{
if (!interface)
return;
QGraphicsRectItem *item = processInterface(interface, child, scene);
QObject *object = interface->object();
if (object) {
m_graphicsItems.insert(object, item);
}
// Possibly process children
if (child != 0)
return;
for (int i = 0; i < interface->childCount(); ++i) {
QAccessibleInterface *child = interface->child(i);
updateItems(child->object());
populateAccessibilityScene(child, 0, scene);
delete child;
}
}
AccessibilitySceneManager::TreeItem AccessibilitySceneManager::computeLevels(QAccessibleInterface * interface, int level)
{
if (interface == 0)
return TreeItem();
TreeItem currentLevel;
int usedChildren = 0;
for (int i = 0; i < interface->childCount(); ++i) {
QAccessibleInterface *child = interface->child(i);
if (child != 0) {
++usedChildren;
TreeItem childLevel = computeLevels(child, level + 1);
currentLevel.children.append(childLevel);
currentLevel.width += childLevel.width + m_treeItemHorizontalPadding;
delete child;
}
}
// leaf node case
if (usedChildren == 0) {
currentLevel.width = m_treeItemWidth + m_treeItemHorizontalPadding;
}
// capture information:
currentLevel.name = interface->text(QAccessible::Name, 0);
//currentLevel.description += interface->text(QAccessible::DebugDescription, 0);
currentLevel.role = translateRole(interface->role(0));
currentLevel.rect = interface->rect(0);
currentLevel.state = interface->state(0);
currentLevel.object = interface->object();
return currentLevel;
}
void AccessibilitySceneManager::populateAccessibilityTreeScene(QAccessibleInterface * interface, int child)
{
if (!interface)
return;
// set some layout metrics:
m_treeItemWidth = 90;
m_treeItemHorizontalPadding = 10;
m_treeItemHeight = 60;
m_treeItemVerticalPadding = 30;
// We want to draw the accessibility hiearchy as a vertical
// tree, growing from the root node at the top.
// First, figure out the number of levels and the width of each level:
m_rootTreeItem = computeLevels(interface, 0);
// create graphics items for each tree item
addGraphicsItems(m_rootTreeItem, 0, 0);
}
void AccessibilitySceneManager::addGraphicsItems(AccessibilitySceneManager::TreeItem item, int row, int xPos)
{
//qDebug() << "add graphics item" << row << item.name << item.role << xPos << item.width << item.children.count();
int yPos = row * (m_treeItemHeight + m_treeItemVerticalPadding);
// Process this interface
QGraphicsRectItem * graphicsItem = new QGraphicsRectItem();
graphicsItem->setPos(xPos, yPos);
graphicsItem->setRect(0, 0, m_treeItemWidth, m_treeItemHeight);
graphicsItem->setFlag(QGraphicsItem::ItemClipsChildrenToShape);
if (item.object == m_selectedObject)
graphicsItem->setBrush(QColor(Qt::yellow));
else
graphicsItem->setBrush(QColor(Qt::white));
if (item.state & QAccessible::Invisible) {
QPen linePen;
linePen.setStyle(Qt::DashLine);
graphicsItem->setPen(linePen);
}
m_treeScene->addItem(graphicsItem);
QGraphicsTextItem * textItem = new QGraphicsTextItem();
textItem->setParentItem(graphicsItem);
textItem->setPos(QPoint(0, 0));
QFont font;
font.setPointSize(8);
textItem->setFont(font);
QString text;
text += item.name + "\n";
text += item.role + "\n";
text += item.description.split(" ", QString::SkipEmptyParts).join("\n") + "\n";
text += "P:" + QString::number(item.rect.x()) + " " + QString::number(item.rect.y()) + " ";
text += "S:" + QString::number(item.rect.width()) + " " + QString::number(item.rect.height()) + "\n";
textItem->setPlainText(text);
// recurse to children
int childIndex = 0;
int childCount = item.children.count();
int segmentSize = item.width / qMax(1, childCount);
int segmentCenterOffset = segmentSize / 2;
int segmentsStart = xPos - (item.width / 2);
foreach (TreeItem child, item.children) {
// spread the children out, covering the width, centered on xPos
int segmentPosition = segmentsStart + (segmentSize * childIndex) + segmentCenterOffset;
addGraphicsItems(child, row + 1, segmentPosition);
++childIndex;
}
// add lines from parents to kids
int boxBottom = yPos + m_treeItemHeight;
int boxMiddleX = xPos + m_treeItemWidth / 2;
int yBottomMiddle = boxBottom + m_treeItemVerticalPadding / 2;
int boxTop = yPos;
int yTopMiddle = boxTop - m_treeItemVerticalPadding / 2;
if (row > 0) {
QGraphicsLineItem *childVerticalStem = new QGraphicsLineItem();
childVerticalStem->setLine(boxMiddleX, yTopMiddle, boxMiddleX, boxTop);
m_treeScene->addItem(childVerticalStem);
}
if (childCount > 0) {
QGraphicsLineItem *parentVerticalStem = new QGraphicsLineItem();
parentVerticalStem->setLine(boxMiddleX, boxBottom, boxMiddleX, yBottomMiddle);
m_treeScene->addItem(parentVerticalStem);
}
if (childCount > 1) {
QGraphicsLineItem *horizontalStem = new QGraphicsLineItem();
// match the end points with the horizontal lines
int lineStartX = segmentsStart + segmentCenterOffset + m_treeItemWidth / 2;
int lineStopX = segmentsStart + segmentSize * (childCount -1) + segmentCenterOffset + m_treeItemWidth / 2;
horizontalStem->setLine(lineStartX, yBottomMiddle, lineStopX , yBottomMiddle);
m_treeScene->addItem(horizontalStem);
}
}
bool AccessibilitySceneManager::isHidden(QAccessibleInterface *interface)
{
QAccessibleInterface *current = interface;
while (current) {
if (current->state(0) & QAccessible::Invisible) {
return true;
}
QAccessibleInterface *parent = current->parent();
if (current != interface)
delete current;
current = parent;
}
return false;
}