/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Designer of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** 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 The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "qdesigner_menubar_p.h"
#include "qdesigner_menu_p.h"
#include "qdesigner_command_p.h"
#include "qdesigner_propertycommand_p.h"
#include "actionrepository_p.h"
#include "actionprovider_p.h"
#include "actioneditor_p.h"
#include "qdesigner_utils_p.h"
#include "promotiontaskmenu_p.h"
#include "qdesigner_objectinspector_p.h"

#include <QtDesigner/abstractformwindow.h>
#include <QtDesigner/abstractformeditor.h>
#include <QtDesigner/abstractwidgetfactory.h>
#include <QtDesigner/qextensionmanager.h>

#include <QtCore/qmimedata.h>

#include <QtCore/qdebug.h>

#include <QtWidgets/qapplication.h>
#include <QtGui/qdrag.h>
#include <QtWidgets/qlineedit.h>
#include <QtGui/qpainter.h>
#include <QtGui/qevent.h>

Q_DECLARE_METATYPE(QAction*)

QT_BEGIN_NAMESPACE

typedef QList<QAction *> ActionList;

using namespace qdesigner_internal;

namespace qdesigner_internal
{

/////////////////////////////////////////////////////////////////////////////////////////////////////////
SpecialMenuAction::SpecialMenuAction(QObject *parent)
    : QAction(parent)
{
}

SpecialMenuAction::~SpecialMenuAction() = default;

} // namespace qdesigner_internal


/////////////////////////////////////////////////////////////////////////////////////////////////////////
QDesignerMenuBar::QDesignerMenuBar(QWidget *parent)  :
    QMenuBar(parent),
    m_addMenu(new SpecialMenuAction(this)),
    m_currentIndex(0),
    m_interactive(true),
    m_editor(new QLineEdit(this)),
    m_dragging(false),
    m_lastMenuActionIndex( -1),
    m_promotionTaskMenu(new PromotionTaskMenu(this, PromotionTaskMenu::ModeSingleWidget, this))
{
    setContextMenuPolicy(Qt::DefaultContextMenu);

    setAcceptDrops(true); // ### fake
    // Fake property: Keep the menu bar editable in the form even if a native menu bar is used.
    setNativeMenuBar(false);

    m_addMenu->setText(tr("Type Here"));
    addAction(m_addMenu);

    QFont italic;
    italic.setItalic(true);
    m_addMenu->setFont(italic);

    m_editor->setObjectName(QStringLiteral("__qt__passive_editor"));
    m_editor->hide();
    m_editor->installEventFilter(this);
    installEventFilter(this);
}

QDesignerMenuBar::~QDesignerMenuBar() = default;

void QDesignerMenuBar::paintEvent(QPaintEvent *event)
{
    QMenuBar::paintEvent(event);

    QPainter p(this);

    const auto &actionList = actions();
    for (QAction *a : actionList) {
        if (qobject_cast<SpecialMenuAction*>(a)) {
            const QRect g = actionGeometry(a);
            QLinearGradient lg(g.left(), g.top(), g.left(), g.bottom());
            lg.setColorAt(0.0, Qt::transparent);
            lg.setColorAt(0.7, QColor(0, 0, 0, 32));
            lg.setColorAt(1.0, Qt::transparent);

            p.fillRect(g, lg);
        }
    }

    QAction *action = currentAction();

    if (m_dragging || !action)
        return;

    if (hasFocus()) {
        const QRect g = actionGeometry(action);
        QDesignerMenu::drawSelection(&p, g.adjusted(1, 1, -1, -1));
    } else if (action->menu() && action->menu()->isVisible()) {
        const QRect g = actionGeometry(action);
        p.drawRect(g.adjusted(1, 1, -1, -1));
    }
}

bool QDesignerMenuBar::handleEvent(QWidget *widget, QEvent *event)
{
    if (!formWindow())
        return false;

    if (event->type() == QEvent::FocusIn || event->type() == QEvent::FocusOut)
        update();

    switch (event->type()) {
        default: break;

        case QEvent::MouseButtonDblClick:
            return handleMouseDoubleClickEvent(widget, static_cast<QMouseEvent*>(event));
        case QEvent::MouseButtonPress:
            return handleMousePressEvent(widget, static_cast<QMouseEvent*>(event));
        case QEvent::MouseButtonRelease:
            return handleMouseReleaseEvent(widget, static_cast<QMouseEvent*>(event));
        case QEvent::MouseMove:
            return handleMouseMoveEvent(widget, static_cast<QMouseEvent*>(event));
        case QEvent::ContextMenu:
            return handleContextMenuEvent(widget, static_cast<QContextMenuEvent*>(event));
        case QEvent::KeyPress:
            return handleKeyPressEvent(widget, static_cast<QKeyEvent*>(event));
        case QEvent::FocusIn:
        case QEvent::FocusOut:
            return widget != m_editor;
    }

    return true;
}

bool QDesignerMenuBar::handleMouseDoubleClickEvent(QWidget *, QMouseEvent *event)
{
    if (!rect().contains(event->pos()))
        return true;

    if ((event->buttons() & Qt::LeftButton) != Qt::LeftButton)
        return true;

    event->accept();

    m_startPosition = QPoint();

    m_currentIndex = actionIndexAt(this, event->pos(), Qt::Horizontal);
    if (m_currentIndex != -1) {
        showLineEdit();
    }

    return true;
}

bool QDesignerMenuBar::handleKeyPressEvent(QWidget *, QKeyEvent *e)
{
    if (m_editor->isHidden()) { // In navigation mode
        switch (e->key()) {

        case Qt::Key_Delete:
            if (m_currentIndex == -1 || m_currentIndex >= realActionCount())
                break;
            hideMenu();
            deleteMenu();
            break;

        case Qt::Key_Left:
            e->accept();
            moveLeft(e->modifiers() & Qt::ControlModifier);
            return true;

        case Qt::Key_Right:
            e->accept();
            moveRight(e->modifiers() & Qt::ControlModifier);
            return true; // no update

        case Qt::Key_Up:
            e->accept();
            moveUp();
            return true;

        case Qt::Key_Down:
            e->accept();
            moveDown();
            return true;

        case Qt::Key_PageUp:
            m_currentIndex = 0;
            break;

        case Qt::Key_PageDown:
            m_currentIndex = actions().count() - 1;
            break;

        case Qt::Key_Enter:
        case Qt::Key_Return:
            e->accept();
            enterEditMode();
            return true; // no update

        case Qt::Key_Alt:
        case Qt::Key_Shift:
        case Qt::Key_Control:
        case Qt::Key_Escape:
            e->ignore();
            setFocus(); // FIXME: this is because some other widget get the focus when CTRL is pressed
            return true; // no update

        default:
            if (!e->text().isEmpty() && e->text().at(0).toLatin1() >= 32) {
                showLineEdit();
                QApplication::sendEvent(m_editor, e);
                e->accept();
            } else {
                e->ignore();
            }
            return true;
        }
    } else { // In edit mode
        switch (e->key()) {
        default:
            return false;

        case Qt::Key_Control:
            e->ignore();
            return true;

        case Qt::Key_Enter:
        case Qt::Key_Return:
            if (!m_editor->text().isEmpty()) {
                leaveEditMode(ForceAccept);
                if (m_lastFocusWidget)
                    m_lastFocusWidget->setFocus();

                m_editor->hide();
                showMenu();
                break;
            }
            Q_FALLTHROUGH();

        case Qt::Key_Escape:
            update();
            setFocus();
            break;
        }
    }

    e->accept();
    update();

    return true;
}

void QDesignerMenuBar::startDrag(const QPoint &pos)
{
    const int index = findAction(pos);
    if (m_currentIndex == -1 || index >= realActionCount())
        return;

    QAction *action = safeActionAt(index);

    QDesignerFormWindowInterface *fw = formWindow();
    RemoveActionFromCommand *cmd = new RemoveActionFromCommand(fw);
    cmd->init(this, action, actions().at(index + 1));
    fw->commandHistory()->push(cmd);

    adjustSize();

    hideMenu(index);

    QDrag *drag = new QDrag(this);
    drag->setPixmap(ActionRepositoryMimeData::actionDragPixmap(action));
    drag->setMimeData(new ActionRepositoryMimeData(action, Qt::MoveAction));

    const int old_index = m_currentIndex;
    m_currentIndex = -1;

    if (drag->start(Qt::MoveAction) == Qt::IgnoreAction) {
        InsertActionIntoCommand *cmd = new InsertActionIntoCommand(fw);
        cmd->init(this, action, safeActionAt(index));
        fw->commandHistory()->push(cmd);

        m_currentIndex = old_index;
        adjustSize();
    }
}

bool QDesignerMenuBar::handleMousePressEvent(QWidget *, QMouseEvent *event)
{
    m_startPosition = QPoint();
    event->accept();

    if (event->button() != Qt::LeftButton)
        return true;

    m_startPosition = event->pos();
    const int newIndex = actionIndexAt(this, m_startPosition, Qt::Horizontal);
    const bool changed = newIndex != m_currentIndex;
    m_currentIndex =  newIndex;
    updateCurrentAction(changed);

    return true;
}

bool QDesignerMenuBar::handleMouseReleaseEvent(QWidget *, QMouseEvent *event)
{
    m_startPosition = QPoint();

    if (event->button() != Qt::LeftButton)
        return true;

    event->accept();
    m_currentIndex = actionIndexAt(this, event->pos(), Qt::Horizontal);
    if (!m_editor->isVisible() && m_currentIndex != -1 && m_currentIndex < realActionCount())
        showMenu();

    return true;
}

bool QDesignerMenuBar::handleMouseMoveEvent(QWidget *, QMouseEvent *event)
{
    if ((event->buttons() & Qt::LeftButton) != Qt::LeftButton)
        return true;

    if (m_startPosition.isNull())
        return true;

    const QPoint pos = mapFromGlobal(event->globalPos());

    if ((pos - m_startPosition).manhattanLength() < qApp->startDragDistance())
        return true;

    const int index =  actionIndexAt(this, m_startPosition, Qt::Horizontal);
    if (index < actions().count()) {
        hideMenu(index);
        update();
    }

    startDrag(m_startPosition);
    m_startPosition = QPoint();

    return true;
}

ActionList QDesignerMenuBar::contextMenuActions()
{
    ActionList rc;
    if (QAction *action = safeActionAt(m_currentIndex)) {
        if (!qobject_cast<SpecialMenuAction*>(action)) {
            QVariant itemData;
            itemData.setValue(action);

            QAction *remove_action = new QAction(tr("Remove Menu '%1'").arg(action->menu()->objectName()), 0);
            remove_action->setData(itemData);
            connect(remove_action, &QAction::triggered, this, &QDesignerMenuBar::deleteMenu);
            rc.push_back(remove_action);
            QAction *sep = new QAction(0);
            sep->setSeparator(true);
            rc.push_back(sep);
        }
    }

    m_promotionTaskMenu->addActions(formWindow(), PromotionTaskMenu::TrailingSeparator, rc);

    QAction *remove_menubar = new QAction(tr("Remove Menu Bar"), 0);
    connect(remove_menubar, &QAction::triggered, this, &QDesignerMenuBar::slotRemoveMenuBar);
    rc.push_back(remove_menubar);
    return rc;
}

bool QDesignerMenuBar::handleContextMenuEvent(QWidget *, QContextMenuEvent *event)
{
    event->accept();

    m_currentIndex = actionIndexAt(this, mapFromGlobal(event->globalPos()), Qt::Horizontal);

    update();

    QMenu menu;
    const ActionList al = contextMenuActions();
    const ActionList::const_iterator acend = al.constEnd();
    for (ActionList::const_iterator it =  al.constBegin(); it != acend; ++it)
        menu.addAction(*it);
    menu.exec(event->globalPos());
    return true;
}

void QDesignerMenuBar::slotRemoveMenuBar()
{
    Q_ASSERT(formWindow() != 0);

    QDesignerFormWindowInterface *fw = formWindow();

    DeleteMenuBarCommand *cmd = new DeleteMenuBarCommand(fw);
    cmd->init(this);
    fw->commandHistory()->push(cmd);
}

void QDesignerMenuBar::focusOutEvent(QFocusEvent *event)
{
    QMenuBar::focusOutEvent(event);
}

void QDesignerMenuBar::enterEditMode()
{
    if (m_currentIndex >= 0 && m_currentIndex <= realActionCount()) {
        showLineEdit();
    }
}

void QDesignerMenuBar::leaveEditMode(LeaveEditMode mode)
{
    m_editor->releaseKeyboard();

    if (mode == Default)
        return;

    if (m_editor->text().isEmpty())
        return;

    QAction *action = 0;

    QDesignerFormWindowInterface *fw = formWindow();
    Q_ASSERT(fw);

    if (m_currentIndex >= 0 && m_currentIndex < realActionCount()) {
        action = safeActionAt(m_currentIndex);
        fw->beginCommand(QApplication::translate("Command", "Change Title"));
    } else {
        fw->beginCommand(QApplication::translate("Command", "Insert Menu"));
        const QString niceObjectName = ActionEditor::actionTextToName(m_editor->text(), QStringLiteral("menu"));
        QMenu *menu = qobject_cast<QMenu*>(fw->core()->widgetFactory()->createWidget(QStringLiteral("QMenu"), this));
        fw->core()->widgetFactory()->initialize(menu);
        menu->setObjectName(niceObjectName);
        menu->setTitle(tr("Menu"));
        fw->ensureUniqueObjectName(menu);
        action = menu->menuAction();
        AddMenuActionCommand *cmd = new AddMenuActionCommand(fw);
        cmd->init(action, m_addMenu, this, this);
        fw->commandHistory()->push(cmd);
    }

    SetPropertyCommand *cmd = new SetPropertyCommand(fw);
    cmd->init(action, QStringLiteral("text"), m_editor->text());
    fw->commandHistory()->push(cmd);
    fw->endCommand();
}

void QDesignerMenuBar::showLineEdit()
{
    QAction *action = 0;

    if (m_currentIndex >= 0 && m_currentIndex < realActionCount())
        action = safeActionAt(m_currentIndex);
    else
        action = m_addMenu;

    if (action->isSeparator())
        return;

    // hideMenu();

    m_lastFocusWidget = qApp->focusWidget();

    // open edit field for item name
    const QString text = action != m_addMenu ? action->text() : QString();

    m_editor->setText(text);
    m_editor->selectAll();
    m_editor->setGeometry(actionGeometry(action));
    m_editor->show();
    qApp->setActiveWindow(m_editor);
    m_editor->setFocus();
    m_editor->grabKeyboard();
}

bool QDesignerMenuBar::eventFilter(QObject *object, QEvent *event)
{
    if (object != this && object != m_editor)
        return false;

    if (!m_editor->isHidden() && object == m_editor && event->type() == QEvent::FocusOut) {
        leaveEditMode(Default);
        m_editor->hide();
        update();
        return true;
    }

    bool dispatch = true;

    switch (event->type()) {
        default: break;

        case QEvent::KeyPress:
        case QEvent::KeyRelease:
        case QEvent::ContextMenu:
        case QEvent::MouseMove:
        case QEvent::MouseButtonPress:
        case QEvent::MouseButtonRelease:
        case QEvent::MouseButtonDblClick:
            dispatch = (object != m_editor);
            Q_FALLTHROUGH(); // no break

        case QEvent::Enter:
        case QEvent::Leave:
        case QEvent::FocusIn:
        case QEvent::FocusOut:
        {
            QWidget *widget = qobject_cast<QWidget*>(object);

            if (dispatch && widget && (widget == this || isAncestorOf(widget)))
                return handleEvent(widget, event);
        } break;

        case QEvent::Shortcut:
            event->accept();
            return true;
    }

    return false;
};

int QDesignerMenuBar::findAction(const QPoint &pos) const
{
    const int index = actionIndexAt(this, pos, Qt::Horizontal);
    if (index == -1)
        return realActionCount();

    return index;
}

void QDesignerMenuBar::adjustIndicator(const QPoint &pos)
{
    const int index = findAction(pos);
    QAction *action = safeActionAt(index);
    Q_ASSERT(action != 0);

    if (pos != QPoint(-1, -1)) {
        QDesignerMenu *m = qobject_cast<QDesignerMenu*>(action->menu());
        if (!m || m->parentMenu()) {
            m_currentIndex = index;
            showMenu(index);
        }
    }

    if (QDesignerActionProviderExtension *a = actionProvider()) {
        a->adjustIndicator(pos);
    }
}

QDesignerMenuBar::ActionDragCheck QDesignerMenuBar::checkAction(QAction *action) const
{
    // action belongs to another form
    if (!action || !Utils::isObjectAncestorOf(formWindow()->mainContainer(), action))
        return NoActionDrag;

    if (!action->menu())
        return ActionDragOnSubMenu; // simple action only on sub menus

    QDesignerMenu *m = qobject_cast<QDesignerMenu*>(action->menu());
    if (m && m->parentMenu())
        return ActionDragOnSubMenu; // it looks like a submenu

    if (actions().contains(action))
        return ActionDragOnSubMenu; // we already have the action in the menubar

    return AcceptActionDrag;
}

void QDesignerMenuBar::dragEnterEvent(QDragEnterEvent *event)
{
    const ActionRepositoryMimeData *d = qobject_cast<const ActionRepositoryMimeData*>(event->mimeData());
    if (!d || d->actionList().empty()) {
        event->ignore();
        return;
    }

    QAction *action = d->actionList().first();
    switch (checkAction(action)) {
    case NoActionDrag:
        event->ignore();
        break;
    case ActionDragOnSubMenu:
        m_dragging = true;
        d->accept(event);
        break;
    case AcceptActionDrag:
        m_dragging = true;
        d->accept(event);
        adjustIndicator(event->pos());
        break;
    }
}

void QDesignerMenuBar::dragMoveEvent(QDragMoveEvent *event)
{
    const ActionRepositoryMimeData *d = qobject_cast<const ActionRepositoryMimeData*>(event->mimeData());
    if (!d || d->actionList().empty()) {
        event->ignore();
        return;
    }
    QAction *action = d->actionList().first();

    switch (checkAction(action)) {
    case NoActionDrag:
        event->ignore();
        break;
    case ActionDragOnSubMenu:
        event->ignore();
        showMenu(findAction(event->pos()));
        break;
    case AcceptActionDrag:
        d->accept(event);
        adjustIndicator(event->pos());
        break;
    }
}

void QDesignerMenuBar::dragLeaveEvent(QDragLeaveEvent *)
{
    m_dragging = false;

    adjustIndicator(QPoint(-1, -1));
}

void QDesignerMenuBar::dropEvent(QDropEvent *event)
{
    m_dragging = false;

    if (const ActionRepositoryMimeData *d = qobject_cast<const ActionRepositoryMimeData*>(event->mimeData())) {

        QAction *action = d->actionList().first();
        if (checkAction(action) == AcceptActionDrag) {
            event->acceptProposedAction();
            int index = findAction(event->pos());
            index = qMin(index, actions().count() - 1);

            QDesignerFormWindowInterface *fw = formWindow();
            InsertActionIntoCommand *cmd = new InsertActionIntoCommand(fw);
            cmd->init(this, action, safeActionAt(index));
            fw->commandHistory()->push(cmd);

            m_currentIndex = index;
            update();
            adjustIndicator(QPoint(-1, -1));
            return;
        }
    }
    event->ignore();
}

void QDesignerMenuBar::actionEvent(QActionEvent *event)
{
    QMenuBar::actionEvent(event);
}

QDesignerFormWindowInterface *QDesignerMenuBar::formWindow() const
{
    return QDesignerFormWindowInterface::findFormWindow(const_cast<QDesignerMenuBar*>(this));
}

QDesignerActionProviderExtension *QDesignerMenuBar::actionProvider()
{
    if (QDesignerFormWindowInterface *fw = formWindow()) {
        QDesignerFormEditorInterface *core = fw->core();
        return qt_extension<QDesignerActionProviderExtension*>(core->extensionManager(), this);
    }

    return 0;
}

QAction *QDesignerMenuBar::currentAction() const
{
    if (m_currentIndex < 0 || m_currentIndex >= actions().count())
        return 0;

    return safeActionAt(m_currentIndex);
}

int QDesignerMenuBar::realActionCount() const
{
    return actions().count() - 1; // 1 fake actions
}

bool QDesignerMenuBar::dragging() const
{
    return m_dragging;
}

void QDesignerMenuBar::moveLeft(bool ctrl)
{
    if (layoutDirection() == Qt::LeftToRight) {
        movePrevious(ctrl);
    } else {
        moveNext(ctrl);
    }
}

void QDesignerMenuBar::moveRight(bool ctrl)
{
    if (layoutDirection() == Qt::LeftToRight) {
        moveNext(ctrl);
    } else {
        movePrevious(ctrl);
    }
}

void QDesignerMenuBar::movePrevious(bool ctrl)
{
    const bool swapped = ctrl && swapActions(m_currentIndex, m_currentIndex - 1);
    const int newIndex = qMax(0, m_currentIndex - 1);
    // Always re-select, swapping destroys order
    if (swapped || newIndex != m_currentIndex) {
        m_currentIndex = newIndex;
        updateCurrentAction(true);
    }
}

void QDesignerMenuBar::moveNext(bool ctrl)
{
    const bool swapped = ctrl && swapActions(m_currentIndex + 1, m_currentIndex);
    const int newIndex = qMin(actions().count() - 1, m_currentIndex + 1);
    if (swapped || newIndex != m_currentIndex) {
        m_currentIndex = newIndex;
        updateCurrentAction(!ctrl);
    }
}

void QDesignerMenuBar::moveUp()
{
    update();
}

void QDesignerMenuBar::moveDown()
{
    showMenu();
}

void QDesignerMenuBar::adjustSpecialActions()
{
    removeAction(m_addMenu);
    addAction(m_addMenu);
}

bool QDesignerMenuBar::interactive(bool i)
{
    const bool old = m_interactive;
    m_interactive = i;
    return old;
}

void QDesignerMenuBar::hideMenu(int index)
{
    if (index < 0 && m_currentIndex >= 0)
        index = m_currentIndex;

    if (index < 0 || index >= realActionCount())
        return;

    QAction *action = safeActionAt(index);

    if (action && action->menu()) {
        action->menu()->hide();

        if (QDesignerMenu *menu = qobject_cast<QDesignerMenu*>(action->menu())) {
            menu->closeMenuChain();
        }
    }
}

void QDesignerMenuBar::deleteMenu()
{
    deleteMenuAction(currentAction());
}

void QDesignerMenuBar::deleteMenuAction(QAction *action)
{
    if (action && !qobject_cast<SpecialMenuAction*>(action)) {
        const int pos = actions().indexOf(action);
        QAction *action_before = 0;
        if (pos != -1)
            action_before = safeActionAt(pos + 1);

        QDesignerFormWindowInterface *fw = formWindow();
        RemoveMenuActionCommand *cmd = new RemoveMenuActionCommand(fw);
        cmd->init(action, action_before, this, this);
        fw->commandHistory()->push(cmd);
    }
}

void QDesignerMenuBar::showMenu(int index)
{
    if (index < 0 && m_currentIndex >= 0)
        index = m_currentIndex;

    if (index < 0 || index >= realActionCount())
        return;

    m_currentIndex = index;
    QAction *action = currentAction();

    if (action && action->menu()) {
        if (m_lastMenuActionIndex != -1 && m_lastMenuActionIndex != index) {
            hideMenu(m_lastMenuActionIndex);
        }

        m_lastMenuActionIndex = index;
        QMenu *menu = action->menu();
        const QRect g = actionGeometry(action);

        if (!menu->isVisible()) {
            if ((menu->windowFlags() & Qt::Popup) != Qt::Popup)
                menu->setWindowFlags(Qt::Popup);
            menu->adjustSize();
            if (layoutDirection() == Qt::LeftToRight) {
                menu->move(mapToGlobal(g.bottomLeft()));
            } else {
                // The position is not initially correct due to the unknown width,
                // causing it to overlap a bit the first time it is invoked.
                QPoint point = g.bottomRight() - QPoint(menu->width(), 0);
                menu->move(mapToGlobal(point));
            }
            menu->setFocus(Qt::MouseFocusReason);
            menu->raise();
            menu->show();
        } else {
            menu->raise();
        }
    }
}

QAction *QDesignerMenuBar::safeActionAt(int index) const
{
    if (index < 0 || index >= actions().count())
        return 0;

    return actions().at(index);
}

bool QDesignerMenuBar::swapActions(int a, int b)
{
    const int left = qMin(a, b);
    int right = qMax(a, b);

    QAction *action_a = safeActionAt(left);
    QAction *action_b = safeActionAt(right);

    if (action_a == action_b
            || !action_a
            || !action_b
            || qobject_cast<SpecialMenuAction*>(action_a)
            || qobject_cast<SpecialMenuAction*>(action_b))
        return false; // nothing to do

    right = qMin(right, realActionCount());
    if (right < 0)
        return false; // nothing to do

    formWindow()->beginCommand(QApplication::translate("Command", "Move action"));

    QAction *action_b_before = safeActionAt(right + 1);

    QDesignerFormWindowInterface *fw = formWindow();
    RemoveActionFromCommand *cmd1 = new RemoveActionFromCommand(fw);
    cmd1->init(this, action_b, action_b_before, false);
    fw->commandHistory()->push(cmd1);

    QAction *action_a_before = safeActionAt(left + 1);

    InsertActionIntoCommand *cmd2 = new InsertActionIntoCommand(fw);
    cmd2->init(this, action_b, action_a_before, false);
    fw->commandHistory()->push(cmd2);

    RemoveActionFromCommand *cmd3 = new RemoveActionFromCommand(fw);
    cmd3->init(this, action_a, action_b, false);
    fw->commandHistory()->push(cmd3);

    InsertActionIntoCommand *cmd4 = new InsertActionIntoCommand(fw);
    cmd4->init(this, action_a, action_b_before, true);
    fw->commandHistory()->push(cmd4);

    fw->endCommand();

    return true;
}

void QDesignerMenuBar::keyPressEvent(QKeyEvent *event)
{
    event->ignore();
}

void QDesignerMenuBar::keyReleaseEvent(QKeyEvent *event)
{
    event->ignore();
}

void QDesignerMenuBar::updateCurrentAction(bool selectAction)
{
    update();

    if (!selectAction)
        return;

    QAction *action = currentAction();
    if (!action || action == m_addMenu)
        return;

    QMenu *menu = action->menu();
    if (!menu)
        return;

    QDesignerObjectInspector *oi = 0;
    if (QDesignerFormWindowInterface *fw = formWindow())
        oi = qobject_cast<QDesignerObjectInspector *>(fw->core()->objectInspector());

    if (!oi)
        return;

    oi->clearSelection();
    oi->selectObject(menu);
}

QT_END_NAMESPACE
