/****************************************************************************
**
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the plugins of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** 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, 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.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "qaccessiblemenu.h"

#include <qmenu.h>
#include <qmenubar.h>
#include <QtGui/QAction>
#include <qstyle.h>

#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<QMenu*>(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 (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<QString>(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<QMenu*>(parent) || qobject_cast<QMenuBar*>(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<QAction*>(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<QMenuBar*>(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<QAction*>(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:
            return QMenu::tr("Execute");
            break;
        default:
            break;
        }
    }
    return QString();
}

bool QAccessibleMenuItem::doAction(int action, int child, const QVariantList & /*params = QVariantList()*/ )
{
    if ((action == Press || action == DefaultAction) && child == 0) {
        m_action->trigger();
        return true;
    }
    return false;
}

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<QMenuBar*>(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<QMenu*>(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->testAttribute(Qt::WA_WState_Visible) == false || m_action->isVisible() == false) {
            s |= Invisible;
        }

        if (QMenu *menu = qobject_cast<QMenu*>(own)) {
            if (menu->activeAction() == m_action)
                s |= Focused;
#ifndef QT_NO_MENUBAR
        } else if (QMenuBar *menuBar = qobject_cast<QMenuBar*>(own)) {
            if (menuBar->activeAction() == m_action)
                s |= Focused;
#endif
        }
        if (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;
}

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;
}

int QAccessibleMenuItem::userActionCount ( int /*child*/ ) const
{
    return 0;
}


QAction *QAccessibleMenuItem::action() const
{
    return m_action;
}

QWidget *QAccessibleMenuItem::owner() const
{
    return m_owner;
}

#endif // QT_NO_MENU

QT_END_NAMESPACE

#endif // QT_NO_ACCESSIBILITY

