diff options
Diffstat (limited to 'tools/designer/src/lib/shared/qdesigner_menu.cpp')
-rw-r--r-- | tools/designer/src/lib/shared/qdesigner_menu.cpp | 1355 |
1 files changed, 1355 insertions, 0 deletions
diff --git a/tools/designer/src/lib/shared/qdesigner_menu.cpp b/tools/designer/src/lib/shared/qdesigner_menu.cpp new file mode 100644 index 0000000..7aea52d --- /dev/null +++ b/tools/designer/src/lib/shared/qdesigner_menu.cpp @@ -0,0 +1,1355 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer 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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesigner_menu_p.h" +#include "qdesigner_menubar_p.h" +#include "qdesigner_toolbar_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 "qdesigner_objectinspector_p.h" + +#include <QtCore/QTimer> +#include <QtCore/qdebug.h> + +#include <QtDesigner/QDesignerFormEditorInterface> +#include <QtDesigner/QDesignerWidgetFactoryInterface> +#include <QtDesigner/QDesignerMetaDataBaseInterface> +#include <QtDesigner/QExtensionManager> + +#include <QtGui/QAction> +#include <QtGui/QApplication> +#include <QtGui/QLineEdit> +#include <QtGui/QPainter> +#include <QtGui/QRubberBand> +#include <QtGui/QToolTip> +#include <QtGui/QToolBar> +#include <QtGui/qevent.h> + +Q_DECLARE_METATYPE(QAction*) + +QT_BEGIN_NAMESPACE + +using namespace qdesigner_internal; + +QDesignerMenu::QDesignerMenu(QWidget *parent) : + QMenu(parent), + m_currentIndex(0), + m_addItem(new SpecialMenuAction(this)), + m_addSeparator(new SpecialMenuAction(this)), + m_showSubMenuTimer(new QTimer(this)), + m_deactivateWindowTimer(new QTimer(this)), + m_adjustSizeTimer(new QTimer(this)), + m_editor(new QLineEdit(this)), + m_dragging(false), + m_lastSubMenuIndex(-1) +{ + setContextMenuPolicy(Qt::DefaultContextMenu); + setAcceptDrops(true); // ### fake + setSeparatorsCollapsible(false); + + connect(m_adjustSizeTimer, SIGNAL(timeout()), this, SLOT(slotAdjustSizeNow())); + m_addItem->setText(tr("Type Here")); + addAction(m_addItem); + + m_addSeparator->setText(tr("Add Separator")); + addAction(m_addSeparator); + + connect(m_showSubMenuTimer, SIGNAL(timeout()), this, SLOT(slotShowSubMenuNow())); + + connect(m_deactivateWindowTimer, SIGNAL(timeout()), this, SLOT(slotDeactivateNow())); + + m_editor->setObjectName(QLatin1String("__qt__passive_editor")); + m_editor->hide(); + + m_editor->installEventFilter(this); + installEventFilter(this); +} + +QDesignerMenu::~QDesignerMenu() +{ +} + +void QDesignerMenu::slotAdjustSizeNow() +{ + // Not using a single-shot, since we want to compress the timers if many items are being + // adjusted + m_adjustSizeTimer->stop(); + adjustSize(); +} + +bool QDesignerMenu::handleEvent(QWidget *widget, QEvent *event) +{ + if (event->type() == QEvent::FocusIn || event->type() == QEvent::FocusOut) { + update(); + + if (widget == m_editor) + return false; + } + + switch (event->type()) { + default: break; + + case QEvent::MouseButtonPress: + return handleMousePressEvent(widget, static_cast<QMouseEvent*>(event)); + case QEvent::MouseButtonRelease: + return handleMouseReleaseEvent(widget, static_cast<QMouseEvent*>(event)); + case QEvent::MouseButtonDblClick: + return handleMouseDoubleClickEvent(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)); + } + + return true; +} + +void QDesignerMenu::startDrag(const QPoint &pos, Qt::KeyboardModifiers modifiers) +{ + const int index = findAction(pos); + if (index >= realActionCount()) + return; + + QAction *action = safeActionAt(index); + + QDesignerFormWindowInterface *fw = formWindow(); + const Qt::DropAction dropAction = (modifiers & Qt::ControlModifier) ? Qt::CopyAction : Qt::MoveAction; + if (dropAction == Qt::MoveAction) { + RemoveActionFromCommand *cmd = new RemoveActionFromCommand(fw); + cmd->init(this, action, actions().at(index + 1)); + fw->commandHistory()->push(cmd); + } + + QDrag *drag = new QDrag(this); + drag->setPixmap(ActionRepositoryMimeData::actionDragPixmap(action)); + drag->setMimeData(new ActionRepositoryMimeData(action, dropAction)); + + const int old_index = m_currentIndex; + m_currentIndex = -1; + + if (drag->start(dropAction) == Qt::IgnoreAction) { + if (dropAction == Qt::MoveAction) { + QAction *previous = safeActionAt(index); + InsertActionIntoCommand *cmd = new InsertActionIntoCommand(fw); + cmd->init(this, action, previous); + fw->commandHistory()->push(cmd); + } + + m_currentIndex = old_index; + } +} + +bool QDesignerMenu::handleKeyPressEvent(QWidget * /*widget*/, QKeyEvent *e) +{ + m_showSubMenuTimer->stop(); + + if (m_editor->isHidden() && hasFocus()) { // In navigation mode + switch (e->key()) { + + case Qt::Key_Delete: + if (m_currentIndex == -1 || m_currentIndex >= realActionCount()) + break; + hideSubMenu(); + deleteAction(); + break; + + case Qt::Key_Left: + e->accept(); + moveLeft(); + return true; + + case Qt::Key_Right: + e->accept(); + moveRight(); + return true; // no update + + case Qt::Key_Up: + e->accept(); + moveUp(e->modifiers() & Qt::ControlModifier); + return true; + + case Qt::Key_Down: + e->accept(); + moveDown(e->modifiers() & Qt::ControlModifier); + 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: + case Qt::Key_F2: + e->accept(); + enterEditMode(); + return true; // no update + + case Qt::Key_Escape: + e->ignore(); + setFocus(); + hide(); + closeMenuChain(); + return true; + + case Qt::Key_Alt: + case Qt::Key_Shift: + case Qt::Key_Control: + e->ignore(); + setFocus(); // FIXME: this is because some other widget get the focus when CTRL is pressed + return true; // no update + + default: { + QAction *action = currentAction(); + if (!action || action->isSeparator() || action == m_addSeparator) { + e->ignore(); + return true; + } else if (!e->text().isEmpty() && e->text().at(0).toLatin1() >= 32) { + showLineEdit(); + QApplication::sendEvent(m_editor, e); + e->accept(); + } else { + e->ignore(); + } + } + return true; + } + } else if (m_editor->hasFocus()) { // In edit mode + switch (e->key()) { + default: + e->ignore(); + return false; + + case Qt::Key_Enter: + case Qt::Key_Return: + if (!m_editor->text().isEmpty()) { + leaveEditMode(ForceAccept); + m_editor->hide(); + setFocus(); + moveDown(false); + break; + } + // fall through + + case Qt::Key_Escape: + m_editor->hide(); + setFocus(); + break; + } + } + + e->accept(); + update(); + + return true; +} + +static void sendMouseEventTo(QWidget *target, const QPoint &targetPoint, const QMouseEvent *event) +{ + QMouseEvent e(event->type(), targetPoint, event->globalPos(), event->button(), event->buttons(), event->modifiers()); + QApplication::sendEvent(target, &e); +} + +bool QDesignerMenu::handleMouseDoubleClickEvent(QWidget *, QMouseEvent *event) +{ + event->accept(); + m_startPosition = QPoint(); + + if ((event->buttons() & Qt::LeftButton) != Qt::LeftButton) + return true; + + if (!rect().contains(event->pos())) { + // special case for menubar + QWidget *target = QApplication::widgetAt(event->globalPos()); + QMenuBar *mb = qobject_cast<QMenuBar*>(target); + QDesignerMenu *menu = qobject_cast<QDesignerMenu*>(target); + if (mb != 0 || menu != 0) { + const QPoint pt = target->mapFromGlobal(event->globalPos()); + QAction *action = mb == 0 ? menu->actionAt(pt) : mb->actionAt(pt); + if (action) + sendMouseEventTo(target, pt, event); + } + return true; + } + + m_currentIndex = findAction(event->pos()); + QAction *action = safeActionAt(m_currentIndex); + + QRect pm_rect; + if (action->menu() || hasSubMenuPixmap(action)) { + pm_rect = subMenuPixmapRect(action); + pm_rect.setLeft(pm_rect.left() - 20); // give the user a little more + // space to click + } + + if (!pm_rect.contains(event->pos()) && m_currentIndex != -1) + enterEditMode(); + + return true; +} + +bool QDesignerMenu::handleMousePressEvent(QWidget * /*widget*/, QMouseEvent *event) +{ + if (!rect().contains(event->pos())) { + QWidget *clickedWidget = QApplication::widgetAt(event->globalPos()); + if (QMenuBar *mb = qobject_cast<QMenuBar*>(clickedWidget)) { + const QPoint pt = mb->mapFromGlobal(event->globalPos()); + if (QAction *action = mb->actionAt(pt)) { + QMenu * menu = action->menu(); + if (menu == findRootMenu()) { + // propagate the mouse press event (but don't close the popup) + sendMouseEventTo(mb, pt, event); + return true; + } + } + } + + if (QDesignerMenu *m = qobject_cast<QDesignerMenu *>(clickedWidget)) { + m->hideSubMenu(); + sendMouseEventTo(m, m->mapFromGlobal(event->globalPos()), event); + } else { + QDesignerMenu *root = findRootMenu(); + root->hide(); + root->hideSubMenu(); + } + if (clickedWidget) { + if (QWidget *focusProxy = clickedWidget->focusProxy()) + clickedWidget = focusProxy; + if (clickedWidget->focusPolicy() != Qt::NoFocus) + clickedWidget->setFocus(Qt::OtherFocusReason); + } + return true; + } + + m_showSubMenuTimer->stop(); + m_startPosition = QPoint(); + event->accept(); + + if (event->button() != Qt::LeftButton) + return true; + + m_startPosition = mapFromGlobal(event->globalPos()); + + const int index = findAction(m_startPosition); + + QAction *action = safeActionAt(index); + QRect pm_rect = subMenuPixmapRect(action); + pm_rect.setLeft(pm_rect.left() - 20); // give the user a little more space to click + + const int old_index = m_currentIndex; + m_currentIndex = index; + if ((hasSubMenuPixmap(action) || action->menu() != 0) + && pm_rect.contains(m_startPosition)) { + if (m_currentIndex == m_lastSubMenuIndex) { + hideSubMenu(); + } else + slotShowSubMenuNow(); + } else { + if (index == old_index) { + if (m_currentIndex == m_lastSubMenuIndex) + hideSubMenu(); + } else { + hideSubMenu(); + } + } + + update(); + if (index != old_index) + selectCurrentAction(); + + return true; +} + +bool QDesignerMenu::handleMouseReleaseEvent(QWidget *, QMouseEvent *event) +{ + event->accept(); + m_startPosition = QPoint(); + + return true; +} + +bool QDesignerMenu::handleMouseMoveEvent(QWidget *, QMouseEvent *event) +{ + if ((event->buttons() & Qt::LeftButton) != Qt::LeftButton) + return true; + + if (!rect().contains(event->pos())) { + + if (QMenuBar *mb = qobject_cast<QMenuBar*>(QApplication::widgetAt(event->globalPos()))) { + const QPoint pt = mb->mapFromGlobal(event->globalPos()); + QAction *action = mb->actionAt(pt); + if (action && action->menu() == findRootMenu()) { + // propagate the mouse press event (but don't close the popup) + sendMouseEventTo(mb, pt, event); + return true; + } + // hide the popup Qt will replay the event + slotDeactivateNow(); + } + return true; + } + + if (m_startPosition.isNull()) + return true; + + event->accept(); + + const QPoint pos = mapFromGlobal(event->globalPos()); + + if ((pos - m_startPosition).manhattanLength() < qApp->startDragDistance()) + return true; + + startDrag(m_startPosition, event->modifiers()); + m_startPosition = QPoint(); + + return true; +} + +bool QDesignerMenu::handleContextMenuEvent(QWidget *, QContextMenuEvent *event) +{ + event->accept(); + + const int index = findAction(mapFromGlobal(event->globalPos())); + QAction *action = safeActionAt(index); + if (qobject_cast<SpecialMenuAction*>(action)) + return true; + + QMenu menu; + QVariant itemData; + qVariantSetValue(itemData, action); + + QAction *addSeparatorAction = menu.addAction(tr("Insert separator")); + addSeparatorAction->setData(itemData); + + QAction *removeAction = 0; + if (action->isSeparator()) + removeAction = menu.addAction(tr("Remove separator")); + else + removeAction = menu.addAction(tr("Remove action '%1'").arg(action->objectName())); + removeAction->setData(itemData); + + connect(addSeparatorAction, SIGNAL(triggered(bool)), this, SLOT(slotAddSeparator())); + connect(removeAction, SIGNAL(triggered(bool)), this, SLOT(slotRemoveSelectedAction())); + menu.exec(event->globalPos()); + + return true; +} + +void QDesignerMenu::slotAddSeparator() +{ + QAction *action = qobject_cast<QAction *>(sender()); + if (!action) + return; + + QAction *a = qvariant_cast<QAction*>(action->data()); + Q_ASSERT(a != 0); + + const int pos = actions().indexOf(a); + QAction *action_before = 0; + if (pos != -1) + action_before = safeActionAt(pos); + + QDesignerFormWindowInterface *fw = formWindow(); + fw->beginCommand(tr("Add separator")); + QAction *sep = createAction(QString(), true); + + InsertActionIntoCommand *cmd = new InsertActionIntoCommand(fw); + cmd->init(this, sep, action_before); + fw->commandHistory()->push(cmd); + + if (parentMenu()) { + QAction *parent_action = parentMenu()->currentAction(); + if (parent_action->menu() == 0) { + CreateSubmenuCommand *cmd = new CreateSubmenuCommand(fw); + cmd->init(parentMenu(), parentMenu()->currentAction()); + fw->commandHistory()->push(cmd); + } + } + + fw->endCommand(); +} + +void QDesignerMenu::slotRemoveSelectedAction() +{ + if (QAction *action = qobject_cast<QAction *>(sender())) + if (QAction *a = qvariant_cast<QAction*>(action->data())) + deleteAction(a); +} + +void QDesignerMenu::deleteAction(QAction *a) +{ + const int pos = actions().indexOf(a); + QAction *action_before = 0; + if (pos != -1) + action_before = safeActionAt(pos + 1); + + QDesignerFormWindowInterface *fw = formWindow(); + RemoveActionFromCommand *cmd = new RemoveActionFromCommand(fw); + cmd->init(this, a, action_before); + fw->commandHistory()->push(cmd); +} + +QRect QDesignerMenu::subMenuPixmapRect(QAction *action) const +{ + static const QPixmap pm(QLatin1String(":/trolltech/formeditor/images/submenu.png")); + const QRect g = actionGeometry(action); + const int x = g.right() - pm.width() - 2; + const int y = g.top() + (g.height() - pm.height())/2 + 1; + return QRect(x, y, pm.width(), pm.height()); +} + +bool QDesignerMenu::hasSubMenuPixmap(QAction *action) const +{ + return action != 0 + && qobject_cast<SpecialMenuAction*>(action) == 0 + && !action->isSeparator() + && !action->menu() + && canCreateSubMenu(action); +} + +void QDesignerMenu::showEvent ( QShowEvent * event ) +{ + selectCurrentAction(); + QMenu::showEvent (event); +} + +void QDesignerMenu::paintEvent(QPaintEvent *event) +{ + QMenu::paintEvent(event); + + QPainter p(this); + + QAction *current = currentAction(); + + foreach (QAction *a, actions()) { + const QRect g = actionGeometry(a); + + if (qobject_cast<SpecialMenuAction*>(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); + } else if (hasSubMenuPixmap(a)) { + static const QPixmap pm(QLatin1String(":/trolltech/formeditor/images/submenu.png")); + p.drawPixmap(subMenuPixmapRect(a).topLeft(), pm); + } + } + + if (!hasFocus() || !current || m_dragging) + return; + + if (QDesignerMenu *menu = parentMenu()) { + if (menu->dragging()) + return; + } + + if (QDesignerMenuBar *menubar = qobject_cast<QDesignerMenuBar*>(parentWidget())) { + if (menubar->dragging()) + return; + } + + const QRect g = actionGeometry(current); + drawSelection(&p, g.adjusted(1, 1, -3, -3)); +} + +bool QDesignerMenu::dragging() const +{ + return m_dragging; +} + +QDesignerMenu *QDesignerMenu::findRootMenu() const +{ + if (parentMenu()) + return parentMenu()->findRootMenu(); + + return const_cast<QDesignerMenu*>(this); +} + +QDesignerMenu *QDesignerMenu::findActivatedMenu() const +{ + QList<QDesignerMenu*> candidates; + candidates.append(const_cast<QDesignerMenu*>(this)); + candidates += qFindChildren<QDesignerMenu*>(this); + + foreach (QDesignerMenu *m, candidates) { + if (m == qApp->activeWindow()) + return m; + } + + return 0; +} + +bool QDesignerMenu::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 false; + } + + bool dispatch = true; + + switch (event->type()) { + default: break; + + case QEvent::WindowDeactivate: + deactivateMenu(); + break; + case QEvent::ContextMenu: + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + case QEvent::MouseButtonDblClick: + + while (QApplication::activePopupWidget() && !qobject_cast<QDesignerMenu*>(QApplication::activePopupWidget())) { + QApplication::activePopupWidget()->close(); + } + + // fall through + case QEvent::KeyPress: + case QEvent::KeyRelease: + case QEvent::MouseMove: + dispatch = (object != m_editor); + // no break + + case QEvent::Enter: + case QEvent::Leave: + case QEvent::FocusIn: + case QEvent::FocusOut: + if (dispatch) + if (QWidget *widget = qobject_cast<QWidget*>(object)) + if (widget == this || isAncestorOf(widget)) + return handleEvent(widget, event); + break; + } + + return false; +}; + +int QDesignerMenu::findAction(const QPoint &pos) const +{ + const int index = actionIndexAt(this, pos, Qt::Vertical); + if (index == -1) + return realActionCount(); + + return index; +} + +void QDesignerMenu::adjustIndicator(const QPoint &pos) +{ + if (QDesignerActionProviderExtension *a = actionProvider()) { + a->adjustIndicator(pos); + } +} + +QDesignerMenu::ActionDragCheck QDesignerMenu::checkAction(QAction *action) const +{ + if (!action || (action->menu() && action->menu()->parentWidget() != const_cast<QDesignerMenu*>(this))) + return NoActionDrag; // menu action!! nothing to do + + if (!Utils::isObjectAncestorOf(formWindow()->mainContainer(), action)) + return NoActionDrag; // the action belongs to another form window + + if (actions().contains(action)) + return ActionDragOnSubMenu; // we already have the action in the menu + + return AcceptActionDrag; +} + +void QDesignerMenu::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: + d->accept(event); + m_dragging = true; + break; + case AcceptActionDrag: + d->accept(event); + m_dragging = true; + adjustIndicator(event->pos()); + break; + } +} + +void QDesignerMenu::dragMoveEvent(QDragMoveEvent *event) +{ + if (actionGeometry(m_addSeparator).contains(event->pos())) { + event->ignore(); + adjustIndicator(QPoint(-1, -1)); + return; + } + + const ActionRepositoryMimeData *d = qobject_cast<const ActionRepositoryMimeData*>(event->mimeData()); + if (!d || d->actionList().empty()) { + event->ignore(); + return; + } + + QAction *action = d->actionList().first(); + const ActionDragCheck dc = checkAction(action); + switch (dc) { + case NoActionDrag: + event->ignore(); + break; + case ActionDragOnSubMenu: + case AcceptActionDrag: { // Do not pop up submenu of action being dragged + const int newIndex = findAction(event->pos()); + if (safeActionAt(newIndex) != action) { + m_currentIndex = newIndex; + if (m_lastSubMenuIndex != m_currentIndex) + m_showSubMenuTimer->start(300); + } + if (dc == AcceptActionDrag) { + adjustIndicator(event->pos()); + d->accept(event); + } else { + event->ignore(); + } + } + break; + } +} + +void QDesignerMenu::dragLeaveEvent(QDragLeaveEvent *) +{ + m_dragging = false; + adjustIndicator(QPoint(-1, -1)); + m_showSubMenuTimer->stop(); +} + +void QDesignerMenu::dropEvent(QDropEvent *event) +{ + m_showSubMenuTimer->stop(); + hideSubMenu(); + m_dragging = false; + + QDesignerFormWindowInterface *fw = formWindow(); + const ActionRepositoryMimeData *d = qobject_cast<const ActionRepositoryMimeData*>(event->mimeData()); + if (!d || d->actionList().empty()) { + event->ignore(); + return; + } + QAction *action = d->actionList().first(); + if (action && checkAction(action) == AcceptActionDrag) { + event->acceptProposedAction(); + int index = findAction(event->pos()); + index = qMin(index, actions().count() - 1); + + fw->beginCommand(tr("Insert action")); + InsertActionIntoCommand *cmd = new InsertActionIntoCommand(fw); + cmd->init(this, action, safeActionAt(index)); + fw->commandHistory()->push(cmd); + + m_currentIndex = index; + + if (parentMenu()) { + QAction *parent_action = parentMenu()->currentAction(); + if (parent_action->menu() == 0) { + CreateSubmenuCommand *cmd = new CreateSubmenuCommand(fw); + cmd->init(parentMenu(), parentMenu()->currentAction(), action); + fw->commandHistory()->push(cmd); + } + } + update(); + fw->endCommand(); + } else { + event->ignore(); + } + adjustIndicator(QPoint(-1, -1)); +} + +void QDesignerMenu::actionEvent(QActionEvent *event) +{ + QMenu::actionEvent(event); + m_adjustSizeTimer->start(0); +} + +QDesignerFormWindowInterface *QDesignerMenu::formWindow() const +{ + if (parentMenu()) + return parentMenu()->formWindow(); + + return QDesignerFormWindowInterface::findFormWindow(parentWidget()); +} + +QDesignerActionProviderExtension *QDesignerMenu::actionProvider() +{ + if (QDesignerFormWindowInterface *fw = formWindow()) { + QDesignerFormEditorInterface *core = fw->core(); + return qt_extension<QDesignerActionProviderExtension*>(core->extensionManager(), this); + } + + return 0; +} + +void QDesignerMenu::closeMenuChain() +{ + m_showSubMenuTimer->stop(); + + QWidget *w = this; + while (w && qobject_cast<QMenu*>(w)) + w = w->parentWidget(); + + if (w) { + foreach (QMenu *subMenu, qFindChildren<QMenu*>(w)) { + subMenu->hide(); + } + } + + m_lastSubMenuIndex = -1; +} + +void QDesignerMenu::moveLeft() +{ + if (parentMenu()) { + hide(); + } else { + closeMenuChain(); + if (QDesignerMenuBar *mb = parentMenuBar()) { + if (QApplication::layoutDirection() == Qt::LeftToRight) + mb->moveLeft(); + else + mb->moveRight(); + } + } + update(); +} + +void QDesignerMenu::moveRight() +{ + QAction *action = currentAction(); + + if (qobject_cast<SpecialMenuAction*>(action) || action->isSeparator()) { + closeMenuChain(); + if (QDesignerMenuBar *mb = parentMenuBar()) { + if (QApplication::layoutDirection() == Qt::LeftToRight) + mb->moveRight(); + else + mb->moveLeft(); + } + } else { + m_lastSubMenuIndex = -1; // force a refresh + slotShowSubMenuNow(); + } +} + +void QDesignerMenu::moveUp(bool ctrl) +{ + if (m_currentIndex == 0) { + hide(); + return; + } + + if (ctrl) + (void) swap(m_currentIndex, m_currentIndex - 1); + + m_currentIndex = qMax(0, --m_currentIndex); + // Always re-select, swapping destroys order + update(); + selectCurrentAction(); +} + +void QDesignerMenu::moveDown(bool ctrl) +{ + if (m_currentIndex == actions().count() - 1) { + return; + } + + if (ctrl) + (void) swap(m_currentIndex + 1, m_currentIndex); + + m_currentIndex = qMin(actions().count() - 1, ++m_currentIndex); + update(); + if (!ctrl) + selectCurrentAction(); +} + +QAction *QDesignerMenu::currentAction() const +{ + if (m_currentIndex < 0 || m_currentIndex >= actions().count()) + return 0; + + return safeActionAt(m_currentIndex); +} + +int QDesignerMenu::realActionCount() const +{ + return actions().count() - 2; // 2 fake actions +} + +void QDesignerMenu::selectCurrentAction() +{ + QAction *action = currentAction(); + if (!action || action == m_addSeparator || action == m_addItem) + return; + + QDesignerObjectInspector *oi = 0; + if (QDesignerFormWindowInterface *fw = formWindow()) + oi = qobject_cast<QDesignerObjectInspector *>(fw->core()->objectInspector()); + + if (!oi) + return; + + oi->clearSelection(); + if (QMenu *menu = action->menu()) + oi->selectObject(menu); + else + oi->selectObject(action); +} + +void QDesignerMenu::createRealMenuAction(QAction *action) +{ + if (action->menu()) + return; // nothing to do + + QDesignerFormWindowInterface *fw = formWindow(); + QDesignerFormEditorInterface *core = formWindow()->core(); + + QDesignerMenu *menu = findOrCreateSubMenu(action); + m_subMenus.remove(action); + + action->setMenu(menu); + menu->setTitle(action->text()); + + Q_ASSERT(fw); + + core->widgetFactory()->initialize(menu); + + const QString niceObjectName = ActionEditor::actionTextToName(menu->title(), QLatin1String("menu")); + menu->setObjectName(niceObjectName); + + core->metaDataBase()->add(menu); + fw->ensureUniqueObjectName(menu); + + QAction *menuAction = menu->menuAction(); + core->metaDataBase()->add(menuAction); +} + +void QDesignerMenu::removeRealMenu(QAction *action) +{ + QDesignerMenu *menu = qobject_cast<QDesignerMenu*>(action->menu()); + if (menu == 0) + return; + action->setMenu(0); + m_subMenus.insert(action, menu); + QDesignerFormEditorInterface *core = formWindow()->core(); + core->metaDataBase()->remove(menu); +} + +QDesignerMenu *QDesignerMenu::findOrCreateSubMenu(QAction *action) +{ + if (action->menu()) + return qobject_cast<QDesignerMenu*>(action->menu()); + + QDesignerMenu *menu = m_subMenus.value(action); + if (!menu) { + menu = new QDesignerMenu(this); + m_subMenus.insert(action, menu); + } + + return menu; +} + +bool QDesignerMenu::canCreateSubMenu(QAction *action) const // ### improve it's a bit too slow +{ + foreach (const QWidget *aw, action->associatedWidgets()) + if (aw != this) { + if (const QMenu *m = qobject_cast<const QMenu *>(aw)) { + if (m->actions().contains(action)) + return false; // sorry + } else { + if (const QToolBar *tb = qobject_cast<const QToolBar *>(aw)) + if (tb->actions().contains(action)) + return false; // sorry + } + } + return true; +} + +void QDesignerMenu::slotShowSubMenuNow() +{ + m_showSubMenuTimer->stop(); + + if (m_lastSubMenuIndex == m_currentIndex) + return; + + if (m_lastSubMenuIndex != -1) + hideSubMenu(); + + if (m_currentIndex >= realActionCount()) + return; + + QAction *action = currentAction(); + + if (action->isSeparator() || !canCreateSubMenu(action)) + return; + + if (QMenu *menu = findOrCreateSubMenu(action)) { + if (!menu->isVisible()) { + if ((menu->windowFlags() & Qt::Popup) != Qt::Popup) + menu->setWindowFlags(Qt::Popup); + const QRect g = actionGeometry(action); + menu->move(mapToGlobal(g.topRight())); + menu->show(); + menu->setFocus(); + } else { + menu->raise(); + } + menu->setFocus(); + + m_lastSubMenuIndex = m_currentIndex; + } +} + +void QDesignerMenu::showSubMenu(QAction *action) +{ + m_showSubMenuTimer->stop(); + + if (m_editor->isVisible() || !action || qobject_cast<SpecialMenuAction*>(action) + || action->isSeparator() || !isVisible()) + return; + + m_showSubMenuTimer->start(300); +} + +QDesignerMenu *QDesignerMenu::parentMenu() const +{ + return qobject_cast<QDesignerMenu*>(parentWidget()); +} + +QDesignerMenuBar *QDesignerMenu::parentMenuBar() const +{ + if (QDesignerMenuBar *mb = qobject_cast<QDesignerMenuBar*>(parentWidget())) { + return mb; + } else if (QDesignerMenu *m = parentMenu()) { + return m->parentMenuBar(); + } + + return 0; +} + +void QDesignerMenu::setVisible(bool visible) +{ + if (visible) + m_currentIndex = 0; + else + m_lastSubMenuIndex = -1; + + QMenu::setVisible(visible); + +} + +void QDesignerMenu::adjustSpecialActions() +{ + removeAction(m_addItem); + removeAction(m_addSeparator); + addAction(m_addItem); + addAction(m_addSeparator); +} + +bool QDesignerMenu::interactive(bool i) +{ + const bool old = m_interactive; + m_interactive = i; + return old; +} + +void QDesignerMenu::enterEditMode() +{ + if (m_currentIndex >= 0 && m_currentIndex <= realActionCount()) { + showLineEdit(); + } else { + hideSubMenu(); + QDesignerFormWindowInterface *fw = formWindow(); + fw->beginCommand(tr("Add separator")); + QAction *sep = createAction(QString(), true); + + InsertActionIntoCommand *cmd = new InsertActionIntoCommand(fw); + cmd->init(this, sep, safeActionAt(realActionCount())); + fw->commandHistory()->push(cmd); + + if (parentMenu()) { + QAction *parent_action = parentMenu()->currentAction(); + if (parent_action->menu() == 0) { + CreateSubmenuCommand *cmd = new CreateSubmenuCommand(fw); + cmd->init(parentMenu(), parentMenu()->currentAction()); + fw->commandHistory()->push(cmd); + } + } + + fw->endCommand(); + + m_currentIndex = actions().indexOf(m_addItem); + update(); + } +} + +void QDesignerMenu::leaveEditMode(LeaveEditMode mode) +{ + if (mode == Default) + return; + + QAction *action = 0; + + QDesignerFormWindowInterface *fw = formWindow(); + if (m_currentIndex < realActionCount()) { + action = safeActionAt(m_currentIndex); + fw->beginCommand(QApplication::translate("Command", "Set action text")); + } else { + Q_ASSERT(fw != 0); + fw->beginCommand(QApplication::translate("Command", "Insert action")); + action = createAction(ActionEditor::actionTextToName(m_editor->text())); + InsertActionIntoCommand *cmd = new InsertActionIntoCommand(fw); + cmd->init(this, action, currentAction()); + fw->commandHistory()->push(cmd); + } + + SetPropertyCommand *cmd = new SetPropertyCommand(fw); + cmd->init(action, QLatin1String("text"), m_editor->text()); + fw->commandHistory()->push(cmd); + + if (parentMenu()) { + QAction *parent_action = parentMenu()->currentAction(); + if (parent_action->menu() == 0) { + CreateSubmenuCommand *cmd = new CreateSubmenuCommand(fw); + cmd->init(parentMenu(), parentMenu()->currentAction(), action); + fw->commandHistory()->push(cmd); + } + } + + update(); + fw->endCommand(); +} + +QAction *QDesignerMenu::safeMenuAction(QDesignerMenu *menu) const +{ + QAction *action = menu->menuAction(); + + if (!action) + action = m_subMenus.key(menu); + + return action; +} + +void QDesignerMenu::showLineEdit() +{ + m_showSubMenuTimer->stop(); + + QAction *action = 0; + + if (m_currentIndex < realActionCount()) + action = safeActionAt(m_currentIndex); + else + action = m_addItem; + + if (action->isSeparator()) + return; + + hideSubMenu(); + + // open edit field for item name + setFocus(); + + const QString text = action != m_addItem ? action->text() : QString(); + m_editor->setText(text); + m_editor->selectAll(); + m_editor->setGeometry(actionGeometry(action).adjusted(1, 1, -2, -2)); + m_editor->show(); + m_editor->setFocus(); +} + +QAction *QDesignerMenu::createAction(const QString &objectName, bool separator) +{ + QDesignerFormWindowInterface *fw = formWindow(); + Q_ASSERT(fw); + return ToolBarEventFilter::createAction(fw, objectName, separator); +} + +// ### share with QDesignerMenu::swap +bool QDesignerMenu::swap(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 + + QDesignerFormWindowInterface *fw = formWindow(); + fw->beginCommand(QApplication::translate("Command", "Move action")); + + QAction *action_b_before = safeActionAt(right + 1); + + 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; +} + +QAction *QDesignerMenu::safeActionAt(int index) const +{ + if (index < 0 || index >= actions().count()) + return 0; + + return actions().at(index); +} + +void QDesignerMenu::hideSubMenu() +{ + m_lastSubMenuIndex = -1; + foreach (QMenu *subMenu, qFindChildren<QMenu*>(this)) { + subMenu->hide(); + } +} + +void QDesignerMenu::deleteAction() +{ + QAction *action = currentAction(); + const int pos = actions().indexOf(action); + QAction *action_before = 0; + if (pos != -1) + action_before = safeActionAt(pos + 1); + + QDesignerFormWindowInterface *fw = formWindow(); + RemoveActionFromCommand *cmd = new RemoveActionFromCommand(fw); + cmd->init(this, action, action_before); + fw->commandHistory()->push(cmd); + + update(); +} + +void QDesignerMenu::deactivateMenu() +{ + m_deactivateWindowTimer->start(10); +} + +void QDesignerMenu::slotDeactivateNow() +{ + m_deactivateWindowTimer->stop(); + + if (m_dragging) + return; + + QDesignerMenu *root = findRootMenu(); + + if (! root->findActivatedMenu()) { + root->hide(); + root->hideSubMenu(); + } +} + +void QDesignerMenu::drawSelection(QPainter *p, const QRect &r) +{ + p->save(); + + QColor c = Qt::blue; + p->setPen(QPen(c, 1)); + c.setAlpha(32); + p->setBrush(c); + p->drawRect(r); + + p->restore(); +} + +void QDesignerMenu::keyPressEvent(QKeyEvent *event) +{ + event->ignore(); +} + +void QDesignerMenu::keyReleaseEvent(QKeyEvent *event) +{ + event->ignore(); +} + +QT_END_NAMESPACE |