source: trunk/tools/designer/src/lib/shared/qdesigner_menu.cpp

Last change on this file was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 39.2 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the Qt Designer of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qdesigner_menu_p.h"
43#include "qdesigner_menubar_p.h"
44#include "qdesigner_toolbar_p.h"
45#include "qdesigner_command_p.h"
46#include "qdesigner_propertycommand_p.h"
47#include "actionrepository_p.h"
48#include "actionprovider_p.h"
49#include "actioneditor_p.h"
50#include "qdesigner_utils_p.h"
51#include "qdesigner_objectinspector_p.h"
52
53#include <QtCore/QTimer>
54#include <QtCore/qdebug.h>
55
56#include <QtDesigner/QDesignerFormEditorInterface>
57#include <QtDesigner/QDesignerWidgetFactoryInterface>
58#include <QtDesigner/QDesignerMetaDataBaseInterface>
59#include <QtDesigner/QExtensionManager>
60
61#include <QtGui/QAction>
62#include <QtGui/QApplication>
63#include <QtGui/QLineEdit>
64#include <QtGui/QPainter>
65#include <QtGui/QRubberBand>
66#include <QtGui/QToolTip>
67#include <QtGui/QToolBar>
68#include <QtGui/qevent.h>
69
70Q_DECLARE_METATYPE(QAction*)
71
72QT_BEGIN_NAMESPACE
73
74using namespace qdesigner_internal;
75
76// give the user a little more space to click on the sub menu rectangle
77static inline void extendClickableArea(QRect *subMenuRect, Qt::LayoutDirection dir)
78{
79 switch (dir) {
80 case Qt::LayoutDirectionAuto: // Should never happen
81 case Qt::LeftToRight:
82 subMenuRect->setLeft(subMenuRect->left() - 20);
83 break;
84 case Qt::RightToLeft:
85 subMenuRect->setRight(subMenuRect->right() + 20);
86 break;
87 }
88}
89
90QDesignerMenu::QDesignerMenu(QWidget *parent) :
91 QMenu(parent),
92 m_subMenuPixmap(QPixmap(QLatin1String(":/trolltech/formeditor/images/submenu.png"))),
93 m_currentIndex(0),
94 m_addItem(new SpecialMenuAction(this)),
95 m_addSeparator(new SpecialMenuAction(this)),
96 m_showSubMenuTimer(new QTimer(this)),
97 m_deactivateWindowTimer(new QTimer(this)),
98 m_adjustSizeTimer(new QTimer(this)),
99 m_editor(new QLineEdit(this)),
100 m_dragging(false),
101 m_lastSubMenuIndex(-1)
102{
103 setContextMenuPolicy(Qt::DefaultContextMenu);
104 setAcceptDrops(true); // ### fake
105 setSeparatorsCollapsible(false);
106
107 connect(m_adjustSizeTimer, SIGNAL(timeout()), this, SLOT(slotAdjustSizeNow()));
108 m_addItem->setText(tr("Type Here"));
109 addAction(m_addItem);
110
111 m_addSeparator->setText(tr("Add Separator"));
112 addAction(m_addSeparator);
113
114 connect(m_showSubMenuTimer, SIGNAL(timeout()), this, SLOT(slotShowSubMenuNow()));
115
116 connect(m_deactivateWindowTimer, SIGNAL(timeout()), this, SLOT(slotDeactivateNow()));
117
118 m_editor->setObjectName(QLatin1String("__qt__passive_editor"));
119 m_editor->hide();
120
121 m_editor->installEventFilter(this);
122 installEventFilter(this);
123}
124
125QDesignerMenu::~QDesignerMenu()
126{
127}
128
129void QDesignerMenu::slotAdjustSizeNow()
130{
131 // Not using a single-shot, since we want to compress the timers if many items are being
132 // adjusted
133 m_adjustSizeTimer->stop();
134 adjustSize();
135}
136
137bool QDesignerMenu::handleEvent(QWidget *widget, QEvent *event)
138{
139 if (event->type() == QEvent::FocusIn || event->type() == QEvent::FocusOut) {
140 update();
141
142 if (widget == m_editor)
143 return false;
144 }
145
146 switch (event->type()) {
147 default: break;
148
149 case QEvent::MouseButtonPress:
150 return handleMousePressEvent(widget, static_cast<QMouseEvent*>(event));
151 case QEvent::MouseButtonRelease:
152 return handleMouseReleaseEvent(widget, static_cast<QMouseEvent*>(event));
153 case QEvent::MouseButtonDblClick:
154 return handleMouseDoubleClickEvent(widget, static_cast<QMouseEvent*>(event));
155 case QEvent::MouseMove:
156 return handleMouseMoveEvent(widget, static_cast<QMouseEvent*>(event));
157 case QEvent::ContextMenu:
158 return handleContextMenuEvent(widget, static_cast<QContextMenuEvent*>(event));
159 case QEvent::KeyPress:
160 return handleKeyPressEvent(widget, static_cast<QKeyEvent*>(event));
161 }
162
163 return true;
164}
165
166void QDesignerMenu::startDrag(const QPoint &pos, Qt::KeyboardModifiers modifiers)
167{
168 const int index = findAction(pos);
169 if (index >= realActionCount())
170 return;
171
172 QAction *action = safeActionAt(index);
173
174 QDesignerFormWindowInterface *fw = formWindow();
175 const Qt::DropAction dropAction = (modifiers & Qt::ControlModifier) ? Qt::CopyAction : Qt::MoveAction;
176 if (dropAction == Qt::MoveAction) {
177 RemoveActionFromCommand *cmd = new RemoveActionFromCommand(fw);
178 cmd->init(this, action, actions().at(index + 1));
179 fw->commandHistory()->push(cmd);
180 }
181
182#ifndef QT_NO_DRAGANDDROP
183 QDrag *drag = new QDrag(this);
184 drag->setPixmap(ActionRepositoryMimeData::actionDragPixmap(action));
185 drag->setMimeData(new ActionRepositoryMimeData(action, dropAction));
186#endif
187
188 const int old_index = m_currentIndex;
189 m_currentIndex = -1;
190
191#ifndef QT_NO_DRAGANDDROP
192 if (drag->start(dropAction) == Qt::IgnoreAction) {
193#else
194 {
195#endif
196 if (dropAction == Qt::MoveAction) {
197 QAction *previous = safeActionAt(index);
198 InsertActionIntoCommand *cmd = new InsertActionIntoCommand(fw);
199 cmd->init(this, action, previous);
200 fw->commandHistory()->push(cmd);
201 }
202
203 m_currentIndex = old_index;
204 }
205}
206
207bool QDesignerMenu::handleKeyPressEvent(QWidget * /*widget*/, QKeyEvent *e)
208{
209 m_showSubMenuTimer->stop();
210
211 if (m_editor->isHidden() && hasFocus()) { // In navigation mode
212 switch (e->key()) {
213
214 case Qt::Key_Delete:
215 if (m_currentIndex == -1 || m_currentIndex >= realActionCount())
216 break;
217 hideSubMenu();
218 deleteAction();
219 break;
220
221 case Qt::Key_Left:
222 e->accept();
223 moveLeft();
224 return true;
225
226 case Qt::Key_Right:
227 e->accept();
228 moveRight();
229 return true; // no update
230
231 case Qt::Key_Up:
232 e->accept();
233 moveUp(e->modifiers() & Qt::ControlModifier);
234 return true;
235
236 case Qt::Key_Down:
237 e->accept();
238 moveDown(e->modifiers() & Qt::ControlModifier);
239 return true;
240
241 case Qt::Key_PageUp:
242 m_currentIndex = 0;
243 break;
244
245 case Qt::Key_PageDown:
246 m_currentIndex = actions().count() - 1;
247 break;
248
249 case Qt::Key_Enter:
250 case Qt::Key_Return:
251 case Qt::Key_F2:
252 e->accept();
253 enterEditMode();
254 return true; // no update
255
256 case Qt::Key_Escape:
257 e->ignore();
258 setFocus();
259 hide();
260 closeMenuChain();
261 return true;
262
263 case Qt::Key_Alt:
264 case Qt::Key_Shift:
265 case Qt::Key_Control:
266 e->ignore();
267 setFocus(); // FIXME: this is because some other widget get the focus when CTRL is pressed
268 return true; // no update
269
270 default: {
271 QAction *action = currentAction();
272 if (!action || action->isSeparator() || action == m_addSeparator) {
273 e->ignore();
274 return true;
275 } else if (!e->text().isEmpty() && e->text().at(0).toLatin1() >= 32) {
276 showLineEdit();
277 QApplication::sendEvent(m_editor, e);
278 e->accept();
279 } else {
280 e->ignore();
281 }
282 }
283 return true;
284 }
285 } else if (m_editor->hasFocus()) { // In edit mode
286 switch (e->key()) {
287 default:
288 e->ignore();
289 return false;
290
291 case Qt::Key_Enter:
292 case Qt::Key_Return:
293 if (!m_editor->text().isEmpty()) {
294 leaveEditMode(ForceAccept);
295 m_editor->hide();
296 setFocus();
297 moveDown(false);
298 break;
299 }
300 // fall through
301
302 case Qt::Key_Escape:
303 m_editor->hide();
304 setFocus();
305 break;
306 }
307 }
308
309 e->accept();
310 update();
311
312 return true;
313}
314
315static void sendMouseEventTo(QWidget *target, const QPoint &targetPoint, const QMouseEvent *event)
316{
317 QMouseEvent e(event->type(), targetPoint, event->globalPos(), event->button(), event->buttons(), event->modifiers());
318 QApplication::sendEvent(target, &e);
319}
320
321bool QDesignerMenu::handleMouseDoubleClickEvent(QWidget *, QMouseEvent *event)
322{
323 event->accept();
324 m_startPosition = QPoint();
325
326 if ((event->buttons() & Qt::LeftButton) != Qt::LeftButton)
327 return true;
328
329 if (!rect().contains(event->pos())) {
330 // special case for menubar
331 QWidget *target = QApplication::widgetAt(event->globalPos());
332 QMenuBar *mb = qobject_cast<QMenuBar*>(target);
333 QDesignerMenu *menu = qobject_cast<QDesignerMenu*>(target);
334 if (mb != 0 || menu != 0) {
335 const QPoint pt = target->mapFromGlobal(event->globalPos());
336 QAction *action = mb == 0 ? menu->actionAt(pt) : mb->actionAt(pt);
337 if (action)
338 sendMouseEventTo(target, pt, event);
339 }
340 return true;
341 }
342
343 m_currentIndex = findAction(event->pos());
344 QAction *action = safeActionAt(m_currentIndex);
345
346 QRect pm_rect;
347 if (action->menu() || hasSubMenuPixmap(action)) {
348 pm_rect = subMenuPixmapRect(action);
349 extendClickableArea(&pm_rect, layoutDirection());
350 }
351
352 if (!pm_rect.contains(event->pos()) && m_currentIndex != -1)
353 enterEditMode();
354
355 return true;
356}
357
358bool QDesignerMenu::handleMousePressEvent(QWidget * /*widget*/, QMouseEvent *event)
359{
360 if (!rect().contains(event->pos())) {
361 QWidget *clickedWidget = QApplication::widgetAt(event->globalPos());
362 if (QMenuBar *mb = qobject_cast<QMenuBar*>(clickedWidget)) {
363 const QPoint pt = mb->mapFromGlobal(event->globalPos());
364 if (QAction *action = mb->actionAt(pt)) {
365 QMenu * menu = action->menu();
366 if (menu == findRootMenu()) {
367 // propagate the mouse press event (but don't close the popup)
368 sendMouseEventTo(mb, pt, event);
369 return true;
370 }
371 }
372 }
373
374 if (QDesignerMenu *m = qobject_cast<QDesignerMenu *>(clickedWidget)) {
375 m->hideSubMenu();
376 sendMouseEventTo(m, m->mapFromGlobal(event->globalPos()), event);
377 } else {
378 QDesignerMenu *root = findRootMenu();
379 root->hide();
380 root->hideSubMenu();
381 }
382 if (clickedWidget) {
383 if (QWidget *focusProxy = clickedWidget->focusProxy())
384 clickedWidget = focusProxy;
385 if (clickedWidget->focusPolicy() != Qt::NoFocus)
386 clickedWidget->setFocus(Qt::OtherFocusReason);
387 }
388 return true;
389 }
390
391 m_showSubMenuTimer->stop();
392 m_startPosition = QPoint();
393 event->accept();
394
395 if (event->button() != Qt::LeftButton)
396 return true;
397
398 m_startPosition = mapFromGlobal(event->globalPos());
399
400 const int index = findAction(m_startPosition);
401
402 QAction *action = safeActionAt(index);
403 QRect pm_rect = subMenuPixmapRect(action);
404 extendClickableArea(&pm_rect, layoutDirection());
405
406 const int old_index = m_currentIndex;
407 m_currentIndex = index;
408 if ((hasSubMenuPixmap(action) || action->menu() != 0)
409 && pm_rect.contains(m_startPosition)) {
410 if (m_currentIndex == m_lastSubMenuIndex) {
411 hideSubMenu();
412 } else
413 slotShowSubMenuNow();
414 } else {
415 if (index == old_index) {
416 if (m_currentIndex == m_lastSubMenuIndex)
417 hideSubMenu();
418 } else {
419 hideSubMenu();
420 }
421 }
422
423 update();
424 if (index != old_index)
425 selectCurrentAction();
426
427 return true;
428}
429
430bool QDesignerMenu::handleMouseReleaseEvent(QWidget *, QMouseEvent *event)
431{
432 event->accept();
433 m_startPosition = QPoint();
434
435 return true;
436}
437
438bool QDesignerMenu::handleMouseMoveEvent(QWidget *, QMouseEvent *event)
439{
440 if ((event->buttons() & Qt::LeftButton) != Qt::LeftButton)
441 return true;
442
443 if (!rect().contains(event->pos())) {
444
445 if (QMenuBar *mb = qobject_cast<QMenuBar*>(QApplication::widgetAt(event->globalPos()))) {
446 const QPoint pt = mb->mapFromGlobal(event->globalPos());
447 QAction *action = mb->actionAt(pt);
448 if (action && action->menu() == findRootMenu()) {
449 // propagate the mouse press event (but don't close the popup)
450 sendMouseEventTo(mb, pt, event);
451 return true;
452 }
453 // hide the popup Qt will replay the event
454 slotDeactivateNow();
455 }
456 return true;
457 }
458
459 if (m_startPosition.isNull())
460 return true;
461
462 event->accept();
463
464 const QPoint pos = mapFromGlobal(event->globalPos());
465
466 if ((pos - m_startPosition).manhattanLength() < qApp->startDragDistance())
467 return true;
468
469 startDrag(m_startPosition, event->modifiers());
470 m_startPosition = QPoint();
471
472 return true;
473}
474
475bool QDesignerMenu::handleContextMenuEvent(QWidget *, QContextMenuEvent *event)
476{
477 event->accept();
478
479 const int index = findAction(mapFromGlobal(event->globalPos()));
480 QAction *action = safeActionAt(index);
481 if (qobject_cast<SpecialMenuAction*>(action))
482 return true;
483
484 QMenu menu;
485 QVariant itemData;
486 qVariantSetValue(itemData, action);
487
488 QAction *addSeparatorAction = menu.addAction(tr("Insert separator"));
489 addSeparatorAction->setData(itemData);
490
491 QAction *removeAction = 0;
492 if (action->isSeparator())
493 removeAction = menu.addAction(tr("Remove separator"));
494 else
495 removeAction = menu.addAction(tr("Remove action '%1'").arg(action->objectName()));
496 removeAction->setData(itemData);
497
498 connect(addSeparatorAction, SIGNAL(triggered(bool)), this, SLOT(slotAddSeparator()));
499 connect(removeAction, SIGNAL(triggered(bool)), this, SLOT(slotRemoveSelectedAction()));
500 menu.exec(event->globalPos());
501
502 return true;
503}
504
505void QDesignerMenu::slotAddSeparator()
506{
507 QAction *action = qobject_cast<QAction *>(sender());
508 if (!action)
509 return;
510
511 QAction *a = qvariant_cast<QAction*>(action->data());
512 Q_ASSERT(a != 0);
513
514 const int pos = actions().indexOf(a);
515 QAction *action_before = 0;
516 if (pos != -1)
517 action_before = safeActionAt(pos);
518
519 QDesignerFormWindowInterface *fw = formWindow();
520 fw->beginCommand(tr("Add separator"));
521 QAction *sep = createAction(QString(), true);
522
523 InsertActionIntoCommand *cmd = new InsertActionIntoCommand(fw);
524 cmd->init(this, sep, action_before);
525 fw->commandHistory()->push(cmd);
526
527 if (parentMenu()) {
528 QAction *parent_action = parentMenu()->currentAction();
529 if (parent_action->menu() == 0) {
530 CreateSubmenuCommand *cmd = new CreateSubmenuCommand(fw);
531 cmd->init(parentMenu(), parentMenu()->currentAction());
532 fw->commandHistory()->push(cmd);
533 }
534 }
535
536 fw->endCommand();
537}
538
539void QDesignerMenu::slotRemoveSelectedAction()
540{
541 if (QAction *action = qobject_cast<QAction *>(sender()))
542 if (QAction *a = qvariant_cast<QAction*>(action->data()))
543 deleteAction(a);
544}
545
546void QDesignerMenu::deleteAction(QAction *a)
547{
548 const int pos = actions().indexOf(a);
549 QAction *action_before = 0;
550 if (pos != -1)
551 action_before = safeActionAt(pos + 1);
552
553 QDesignerFormWindowInterface *fw = formWindow();
554 RemoveActionFromCommand *cmd = new RemoveActionFromCommand(fw);
555 cmd->init(this, a, action_before);
556 fw->commandHistory()->push(cmd);
557}
558
559QRect QDesignerMenu::subMenuPixmapRect(QAction *action) const
560{
561 const QRect g = actionGeometry(action);
562 const int x = layoutDirection() == Qt::LeftToRight ? (g.right() - m_subMenuPixmap.width() - 2) : 2;
563 const int y = g.top() + (g.height() - m_subMenuPixmap.height())/2 + 1;
564 return QRect(x, y, m_subMenuPixmap.width(), m_subMenuPixmap.height());
565}
566
567bool QDesignerMenu::hasSubMenuPixmap(QAction *action) const
568{
569 return action != 0
570 && qobject_cast<SpecialMenuAction*>(action) == 0
571 && !action->isSeparator()
572 && !action->menu()
573 && canCreateSubMenu(action);
574}
575
576void QDesignerMenu::showEvent ( QShowEvent * event )
577{
578 selectCurrentAction();
579 QMenu::showEvent (event);
580}
581
582void QDesignerMenu::paintEvent(QPaintEvent *event)
583{
584 QMenu::paintEvent(event);
585
586 QPainter p(this);
587
588 QAction *current = currentAction();
589
590 foreach (QAction *a, actions()) {
591 const QRect g = actionGeometry(a);
592
593 if (qobject_cast<SpecialMenuAction*>(a)) {
594 QLinearGradient lg(g.left(), g.top(), g.left(), g.bottom());
595 lg.setColorAt(0.0, Qt::transparent);
596 lg.setColorAt(0.7, QColor(0, 0, 0, 32));
597 lg.setColorAt(1.0, Qt::transparent);
598
599 p.fillRect(g, lg);
600 } else if (hasSubMenuPixmap(a)) {
601 p.drawPixmap(subMenuPixmapRect(a).topLeft(), m_subMenuPixmap);
602 }
603 }
604
605 if (!hasFocus() || !current || m_dragging)
606 return;
607
608 if (QDesignerMenu *menu = parentMenu()) {
609 if (menu->dragging())
610 return;
611 }
612
613 if (QDesignerMenuBar *menubar = qobject_cast<QDesignerMenuBar*>(parentWidget())) {
614 if (menubar->dragging())
615 return;
616 }
617
618 const QRect g = actionGeometry(current);
619 drawSelection(&p, g.adjusted(1, 1, -3, -3));
620}
621
622bool QDesignerMenu::dragging() const
623{
624 return m_dragging;
625}
626
627QDesignerMenu *QDesignerMenu::findRootMenu() const
628{
629 if (parentMenu())
630 return parentMenu()->findRootMenu();
631
632 return const_cast<QDesignerMenu*>(this);
633}
634
635QDesignerMenu *QDesignerMenu::findActivatedMenu() const
636{
637 QList<QDesignerMenu*> candidates;
638 candidates.append(const_cast<QDesignerMenu*>(this));
639 candidates += qFindChildren<QDesignerMenu*>(this);
640
641 foreach (QDesignerMenu *m, candidates) {
642 if (m == qApp->activeWindow())
643 return m;
644 }
645
646 return 0;
647}
648
649bool QDesignerMenu::eventFilter(QObject *object, QEvent *event)
650{
651 if (object != this && object != m_editor) {
652 return false;
653 }
654
655 if (!m_editor->isHidden() && object == m_editor && event->type() == QEvent::FocusOut) {
656 leaveEditMode(Default);
657 m_editor->hide();
658 update();
659 return false;
660 }
661
662 bool dispatch = true;
663
664 switch (event->type()) {
665 default: break;
666
667 case QEvent::WindowDeactivate:
668 deactivateMenu();
669 break;
670 case QEvent::ContextMenu:
671 case QEvent::MouseButtonPress:
672 case QEvent::MouseButtonRelease:
673 case QEvent::MouseButtonDblClick:
674
675 while (QApplication::activePopupWidget() && !qobject_cast<QDesignerMenu*>(QApplication::activePopupWidget())) {
676 QApplication::activePopupWidget()->close();
677 }
678
679 // fall through
680 case QEvent::KeyPress:
681 case QEvent::KeyRelease:
682 case QEvent::MouseMove:
683 dispatch = (object != m_editor);
684 // no break
685
686 case QEvent::Enter:
687 case QEvent::Leave:
688 case QEvent::FocusIn:
689 case QEvent::FocusOut:
690 if (dispatch)
691 if (QWidget *widget = qobject_cast<QWidget*>(object))
692 if (widget == this || isAncestorOf(widget))
693 return handleEvent(widget, event);
694 break;
695 }
696
697 return false;
698};
699
700int QDesignerMenu::findAction(const QPoint &pos) const
701{
702 const int index = actionIndexAt(this, pos, Qt::Vertical);
703 if (index == -1)
704 return realActionCount();
705
706 return index;
707}
708
709void QDesignerMenu::adjustIndicator(const QPoint &pos)
710{
711 if (QDesignerActionProviderExtension *a = actionProvider()) {
712 a->adjustIndicator(pos);
713 }
714}
715
716QDesignerMenu::ActionDragCheck QDesignerMenu::checkAction(QAction *action) const
717{
718 if (!action || (action->menu() && action->menu()->parentWidget() != const_cast<QDesignerMenu*>(this)))
719 return NoActionDrag; // menu action!! nothing to do
720
721 if (!Utils::isObjectAncestorOf(formWindow()->mainContainer(), action))
722 return NoActionDrag; // the action belongs to another form window
723
724 if (actions().contains(action))
725 return ActionDragOnSubMenu; // we already have the action in the menu
726
727 return AcceptActionDrag;
728}
729
730void QDesignerMenu::dragEnterEvent(QDragEnterEvent *event)
731{
732#ifndef QT_NO_DRAGANDDROP
733 const ActionRepositoryMimeData *d = qobject_cast<const ActionRepositoryMimeData*>(event->mimeData());
734 if (!d || d->actionList().empty()) {
735 event->ignore();
736 return;
737 }
738
739 QAction *action = d->actionList().first();
740
741 switch (checkAction(action)) {
742 case NoActionDrag:
743 event->ignore();
744 break;
745 case ActionDragOnSubMenu:
746 d->accept(event);
747 m_dragging = true;
748 break;
749 case AcceptActionDrag:
750 d->accept(event);
751 m_dragging = true;
752 adjustIndicator(event->pos());
753 break;
754 }
755#endif
756}
757
758void QDesignerMenu::dragMoveEvent(QDragMoveEvent *event)
759{
760#ifndef QT_NO_DRAGANDDROP
761 if (actionGeometry(m_addSeparator).contains(event->pos())) {
762 event->ignore();
763 adjustIndicator(QPoint(-1, -1));
764 return;
765 }
766
767 const ActionRepositoryMimeData *d = qobject_cast<const ActionRepositoryMimeData*>(event->mimeData());
768 if (!d || d->actionList().empty()) {
769 event->ignore();
770 return;
771 }
772
773 QAction *action = d->actionList().first();
774 const ActionDragCheck dc = checkAction(action);
775 switch (dc) {
776 case NoActionDrag:
777 event->ignore();
778 break;
779 case ActionDragOnSubMenu:
780 case AcceptActionDrag: { // Do not pop up submenu of action being dragged
781 const int newIndex = findAction(event->pos());
782 if (safeActionAt(newIndex) != action) {
783 m_currentIndex = newIndex;
784 if (m_lastSubMenuIndex != m_currentIndex)
785 m_showSubMenuTimer->start(300);
786 }
787 if (dc == AcceptActionDrag) {
788 adjustIndicator(event->pos());
789 d->accept(event);
790 } else {
791 event->ignore();
792 }
793 }
794 break;
795 }
796#endif
797}
798
799void QDesignerMenu::dragLeaveEvent(QDragLeaveEvent *)
800{
801 m_dragging = false;
802 adjustIndicator(QPoint(-1, -1));
803 m_showSubMenuTimer->stop();
804}
805
806void QDesignerMenu::dropEvent(QDropEvent *event)
807{
808#ifndef QT_NO_DRAGANDDROP
809 m_showSubMenuTimer->stop();
810 hideSubMenu();
811 m_dragging = false;
812
813 QDesignerFormWindowInterface *fw = formWindow();
814 const ActionRepositoryMimeData *d = qobject_cast<const ActionRepositoryMimeData*>(event->mimeData());
815 if (!d || d->actionList().empty()) {
816 event->ignore();
817 return;
818 }
819 QAction *action = d->actionList().first();
820 if (action && checkAction(action) == AcceptActionDrag) {
821 event->acceptProposedAction();
822 int index = findAction(event->pos());
823 index = qMin(index, actions().count() - 1);
824
825 fw->beginCommand(tr("Insert action"));
826 InsertActionIntoCommand *cmd = new InsertActionIntoCommand(fw);
827 cmd->init(this, action, safeActionAt(index));
828 fw->commandHistory()->push(cmd);
829
830 m_currentIndex = index;
831
832 if (parentMenu()) {
833 QAction *parent_action = parentMenu()->currentAction();
834 if (parent_action->menu() == 0) {
835 CreateSubmenuCommand *cmd = new CreateSubmenuCommand(fw);
836 cmd->init(parentMenu(), parentMenu()->currentAction(), action);
837 fw->commandHistory()->push(cmd);
838 }
839 }
840 update();
841 fw->endCommand();
842 } else {
843 event->ignore();
844 }
845#endif
846 adjustIndicator(QPoint(-1, -1));
847}
848
849void QDesignerMenu::actionEvent(QActionEvent *event)
850{
851 QMenu::actionEvent(event);
852 m_adjustSizeTimer->start(0);
853}
854
855QDesignerFormWindowInterface *QDesignerMenu::formWindow() const
856{
857 if (parentMenu())
858 return parentMenu()->formWindow();
859
860 return QDesignerFormWindowInterface::findFormWindow(parentWidget());
861}
862
863QDesignerActionProviderExtension *QDesignerMenu::actionProvider()
864{
865 if (QDesignerFormWindowInterface *fw = formWindow()) {
866 QDesignerFormEditorInterface *core = fw->core();
867 return qt_extension<QDesignerActionProviderExtension*>(core->extensionManager(), this);
868 }
869
870 return 0;
871}
872
873void QDesignerMenu::closeMenuChain()
874{
875 m_showSubMenuTimer->stop();
876
877 QWidget *w = this;
878 while (w && qobject_cast<QMenu*>(w))
879 w = w->parentWidget();
880
881 if (w) {
882 foreach (QMenu *subMenu, qFindChildren<QMenu*>(w)) {
883 subMenu->hide();
884 }
885 }
886
887 m_lastSubMenuIndex = -1;
888}
889
890// Close submenu using the left/right keys according to layoutDirection().
891// Return false to indicate the event must be propagated to the menu bar.
892bool QDesignerMenu::hideSubMenuOnCursorKey()
893{
894 if (parentMenu()) {
895 hide();
896 return true;
897 }
898 closeMenuChain();
899 update();
900 if (parentMenuBar())
901 return false;
902 return true;
903}
904
905// Open a submenu using the left/right keys according to layoutDirection().
906// Return false to indicate the event must be propagated to the menu bar.
907bool QDesignerMenu::showSubMenuOnCursorKey()
908{
909 const QAction *action = currentAction();
910
911 if (qobject_cast<const SpecialMenuAction*>(action) || action->isSeparator()) {
912 closeMenuChain();
913 if (parentMenuBar())
914 return false;
915 return true;
916 }
917 m_lastSubMenuIndex = -1; // force a refresh
918 slotShowSubMenuNow();
919 return true;
920}
921
922void QDesignerMenu::moveLeft()
923{
924 const bool handled = layoutDirection() == Qt::LeftToRight ?
925 hideSubMenuOnCursorKey() : showSubMenuOnCursorKey();
926 if (!handled)
927 parentMenuBar()->moveLeft();
928}
929
930void QDesignerMenu::moveRight()
931{
932 const bool handled = layoutDirection() == Qt::LeftToRight ?
933 showSubMenuOnCursorKey() : hideSubMenuOnCursorKey();
934 if (!handled)
935 parentMenuBar()->moveRight();
936}
937
938void QDesignerMenu::moveUp(bool ctrl)
939{
940 if (m_currentIndex == 0) {
941 hide();
942 return;
943 }
944
945 if (ctrl)
946 (void) swap(m_currentIndex, m_currentIndex - 1);
947 --m_currentIndex;
948 m_currentIndex = qMax(0, m_currentIndex);
949 // Always re-select, swapping destroys order
950 update();
951 selectCurrentAction();
952}
953
954void QDesignerMenu::moveDown(bool ctrl)
955{
956 if (m_currentIndex == actions().count() - 1) {
957 return;
958 }
959
960 if (ctrl)
961 (void) swap(m_currentIndex + 1, m_currentIndex);
962
963 ++m_currentIndex;
964 m_currentIndex = qMin(actions().count() - 1, m_currentIndex);
965 update();
966 if (!ctrl)
967 selectCurrentAction();
968}
969
970QAction *QDesignerMenu::currentAction() const
971{
972 if (m_currentIndex < 0 || m_currentIndex >= actions().count())
973 return 0;
974
975 return safeActionAt(m_currentIndex);
976}
977
978int QDesignerMenu::realActionCount() const
979{
980 return actions().count() - 2; // 2 fake actions
981}
982
983void QDesignerMenu::selectCurrentAction()
984{
985 QAction *action = currentAction();
986 if (!action || action == m_addSeparator || action == m_addItem)
987 return;
988
989 QDesignerObjectInspector *oi = 0;
990 if (QDesignerFormWindowInterface *fw = formWindow())
991 oi = qobject_cast<QDesignerObjectInspector *>(fw->core()->objectInspector());
992
993 if (!oi)
994 return;
995
996 oi->clearSelection();
997 if (QMenu *menu = action->menu())
998 oi->selectObject(menu);
999 else
1000 oi->selectObject(action);
1001}
1002
1003void QDesignerMenu::createRealMenuAction(QAction *action)
1004{
1005 if (action->menu())
1006 return; // nothing to do
1007
1008 QDesignerFormWindowInterface *fw = formWindow();
1009 QDesignerFormEditorInterface *core = formWindow()->core();
1010
1011 QDesignerMenu *menu = findOrCreateSubMenu(action);
1012 m_subMenus.remove(action);
1013
1014 action->setMenu(menu);
1015 menu->setTitle(action->text());
1016
1017 Q_ASSERT(fw);
1018
1019 core->widgetFactory()->initialize(menu);
1020
1021 const QString niceObjectName = ActionEditor::actionTextToName(menu->title(), QLatin1String("menu"));
1022 menu->setObjectName(niceObjectName);
1023
1024 core->metaDataBase()->add(menu);
1025 fw->ensureUniqueObjectName(menu);
1026
1027 QAction *menuAction = menu->menuAction();
1028 core->metaDataBase()->add(menuAction);
1029}
1030
1031void QDesignerMenu::removeRealMenu(QAction *action)
1032{
1033 QDesignerMenu *menu = qobject_cast<QDesignerMenu*>(action->menu());
1034 if (menu == 0)
1035 return;
1036 action->setMenu(0);
1037 m_subMenus.insert(action, menu);
1038 QDesignerFormEditorInterface *core = formWindow()->core();
1039 core->metaDataBase()->remove(menu);
1040}
1041
1042QDesignerMenu *QDesignerMenu::findOrCreateSubMenu(QAction *action)
1043{
1044 if (action->menu())
1045 return qobject_cast<QDesignerMenu*>(action->menu());
1046
1047 QDesignerMenu *menu = m_subMenus.value(action);
1048 if (!menu) {
1049 menu = new QDesignerMenu(this);
1050 m_subMenus.insert(action, menu);
1051 }
1052
1053 return menu;
1054}
1055
1056bool QDesignerMenu::canCreateSubMenu(QAction *action) const // ### improve it's a bit too slow
1057{
1058 foreach (const QWidget *aw, action->associatedWidgets())
1059 if (aw != this) {
1060 if (const QMenu *m = qobject_cast<const QMenu *>(aw)) {
1061 if (m->actions().contains(action))
1062 return false; // sorry
1063 } else {
1064 if (const QToolBar *tb = qobject_cast<const QToolBar *>(aw))
1065 if (tb->actions().contains(action))
1066 return false; // sorry
1067 }
1068 }
1069 return true;
1070}
1071
1072void QDesignerMenu::slotShowSubMenuNow()
1073{
1074 m_showSubMenuTimer->stop();
1075
1076 if (m_lastSubMenuIndex == m_currentIndex)
1077 return;
1078
1079 if (m_lastSubMenuIndex != -1)
1080 hideSubMenu();
1081
1082 if (m_currentIndex >= realActionCount())
1083 return;
1084
1085 QAction *action = currentAction();
1086
1087 if (action->isSeparator() || !canCreateSubMenu(action))
1088 return;
1089
1090 if (QMenu *menu = findOrCreateSubMenu(action)) {
1091 if (!menu->isVisible()) {
1092 if ((menu->windowFlags() & Qt::Popup) != Qt::Popup)
1093 menu->setWindowFlags(Qt::Popup);
1094 const QRect g = actionGeometry(action);
1095 if (layoutDirection() == Qt::LeftToRight) {
1096 menu->move(mapToGlobal(g.topRight()));
1097 } else {
1098 // The position is not initially correct due to the unknown width,
1099 // causing it to overlap a bit the first time it is invoked.
1100 const QSize menuSize = menu->size();
1101 QPoint point = g.topLeft() - QPoint(menu->width() + 10, 0);
1102 menu->move(mapToGlobal(point));
1103 }
1104 menu->show();
1105 menu->setFocus();
1106 } else {
1107 menu->raise();
1108 }
1109 menu->setFocus();
1110
1111 m_lastSubMenuIndex = m_currentIndex;
1112 }
1113}
1114
1115void QDesignerMenu::showSubMenu(QAction *action)
1116{
1117 m_showSubMenuTimer->stop();
1118
1119 if (m_editor->isVisible() || !action || qobject_cast<SpecialMenuAction*>(action)
1120 || action->isSeparator() || !isVisible())
1121 return;
1122
1123 m_showSubMenuTimer->start(300);
1124}
1125
1126QDesignerMenu *QDesignerMenu::parentMenu() const
1127{
1128 return qobject_cast<QDesignerMenu*>(parentWidget());
1129}
1130
1131QDesignerMenuBar *QDesignerMenu::parentMenuBar() const
1132{
1133 if (QDesignerMenuBar *mb = qobject_cast<QDesignerMenuBar*>(parentWidget())) {
1134 return mb;
1135 } else if (QDesignerMenu *m = parentMenu()) {
1136 return m->parentMenuBar();
1137 }
1138
1139 return 0;
1140}
1141
1142void QDesignerMenu::setVisible(bool visible)
1143{
1144 if (visible)
1145 m_currentIndex = 0;
1146 else
1147 m_lastSubMenuIndex = -1;
1148
1149 QMenu::setVisible(visible);
1150
1151}
1152
1153void QDesignerMenu::adjustSpecialActions()
1154{
1155 removeAction(m_addItem);
1156 removeAction(m_addSeparator);
1157 addAction(m_addItem);
1158 addAction(m_addSeparator);
1159}
1160
1161bool QDesignerMenu::interactive(bool i)
1162{
1163 const bool old = m_interactive;
1164 m_interactive = i;
1165 return old;
1166}
1167
1168void QDesignerMenu::enterEditMode()
1169{
1170 if (m_currentIndex >= 0 && m_currentIndex <= realActionCount()) {
1171 showLineEdit();
1172 } else {
1173 hideSubMenu();
1174 QDesignerFormWindowInterface *fw = formWindow();
1175 fw->beginCommand(tr("Add separator"));
1176 QAction *sep = createAction(QString(), true);
1177
1178 InsertActionIntoCommand *cmd = new InsertActionIntoCommand(fw);
1179 cmd->init(this, sep, safeActionAt(realActionCount()));
1180 fw->commandHistory()->push(cmd);
1181
1182 if (parentMenu()) {
1183 QAction *parent_action = parentMenu()->currentAction();
1184 if (parent_action->menu() == 0) {
1185 CreateSubmenuCommand *cmd = new CreateSubmenuCommand(fw);
1186 cmd->init(parentMenu(), parentMenu()->currentAction());
1187 fw->commandHistory()->push(cmd);
1188 }
1189 }
1190
1191 fw->endCommand();
1192
1193 m_currentIndex = actions().indexOf(m_addItem);
1194 update();
1195 }
1196}
1197
1198void QDesignerMenu::leaveEditMode(LeaveEditMode mode)
1199{
1200 if (mode == Default)
1201 return;
1202
1203 QAction *action = 0;
1204
1205 QDesignerFormWindowInterface *fw = formWindow();
1206 if (m_currentIndex < realActionCount()) {
1207 action = safeActionAt(m_currentIndex);
1208 fw->beginCommand(QApplication::translate("Command", "Set action text"));
1209 } else {
1210 Q_ASSERT(fw != 0);
1211 fw->beginCommand(QApplication::translate("Command", "Insert action"));
1212 action = createAction(ActionEditor::actionTextToName(m_editor->text()));
1213 InsertActionIntoCommand *cmd = new InsertActionIntoCommand(fw);
1214 cmd->init(this, action, currentAction());
1215 fw->commandHistory()->push(cmd);
1216 }
1217
1218 SetPropertyCommand *cmd = new SetPropertyCommand(fw);
1219 cmd->init(action, QLatin1String("text"), m_editor->text());
1220 fw->commandHistory()->push(cmd);
1221
1222 if (parentMenu()) {
1223 QAction *parent_action = parentMenu()->currentAction();
1224 if (parent_action->menu() == 0) {
1225 CreateSubmenuCommand *cmd = new CreateSubmenuCommand(fw);
1226 cmd->init(parentMenu(), parentMenu()->currentAction(), action);
1227 fw->commandHistory()->push(cmd);
1228 }
1229 }
1230
1231 update();
1232 fw->endCommand();
1233}
1234
1235QAction *QDesignerMenu::safeMenuAction(QDesignerMenu *menu) const
1236{
1237 QAction *action = menu->menuAction();
1238
1239 if (!action)
1240 action = m_subMenus.key(menu);
1241
1242 return action;
1243}
1244
1245void QDesignerMenu::showLineEdit()
1246{
1247 m_showSubMenuTimer->stop();
1248
1249 QAction *action = 0;
1250
1251 if (m_currentIndex < realActionCount())
1252 action = safeActionAt(m_currentIndex);
1253 else
1254 action = m_addItem;
1255
1256 if (action->isSeparator())
1257 return;
1258
1259 hideSubMenu();
1260
1261 // open edit field for item name
1262 setFocus();
1263
1264 const QString text = action != m_addItem ? action->text() : QString();
1265 m_editor->setText(text);
1266 m_editor->selectAll();
1267 m_editor->setGeometry(actionGeometry(action).adjusted(1, 1, -2, -2));
1268 m_editor->show();
1269 m_editor->setFocus();
1270}
1271
1272QAction *QDesignerMenu::createAction(const QString &objectName, bool separator)
1273{
1274 QDesignerFormWindowInterface *fw = formWindow();
1275 Q_ASSERT(fw);
1276 return ToolBarEventFilter::createAction(fw, objectName, separator);
1277}
1278
1279// ### share with QDesignerMenu::swap
1280bool QDesignerMenu::swap(int a, int b)
1281{
1282 const int left = qMin(a, b);
1283 int right = qMax(a, b);
1284
1285 QAction *action_a = safeActionAt(left);
1286 QAction *action_b = safeActionAt(right);
1287
1288 if (action_a == action_b
1289 || !action_a
1290 || !action_b
1291 || qobject_cast<SpecialMenuAction*>(action_a)
1292 || qobject_cast<SpecialMenuAction*>(action_b))
1293 return false; // nothing to do
1294
1295 right = qMin(right, realActionCount());
1296 if (right < 0)
1297 return false; // nothing to do
1298
1299 QDesignerFormWindowInterface *fw = formWindow();
1300 fw->beginCommand(QApplication::translate("Command", "Move action"));
1301
1302 QAction *action_b_before = safeActionAt(right + 1);
1303
1304 RemoveActionFromCommand *cmd1 = new RemoveActionFromCommand(fw);
1305 cmd1->init(this, action_b, action_b_before, false);
1306 fw->commandHistory()->push(cmd1);
1307
1308 QAction *action_a_before = safeActionAt(left + 1);
1309
1310 InsertActionIntoCommand *cmd2 = new InsertActionIntoCommand(fw);
1311 cmd2->init(this, action_b, action_a_before, false);
1312 fw->commandHistory()->push(cmd2);
1313
1314 RemoveActionFromCommand *cmd3 = new RemoveActionFromCommand(fw);
1315 cmd3->init(this, action_a, action_b, false);
1316 fw->commandHistory()->push(cmd3);
1317
1318 InsertActionIntoCommand *cmd4 = new InsertActionIntoCommand(fw);
1319 cmd4->init(this, action_a, action_b_before, true);
1320 fw->commandHistory()->push(cmd4);
1321
1322 fw->endCommand();
1323
1324 return true;
1325}
1326
1327QAction *QDesignerMenu::safeActionAt(int index) const
1328{
1329 if (index < 0 || index >= actions().count())
1330 return 0;
1331
1332 return actions().at(index);
1333}
1334
1335void QDesignerMenu::hideSubMenu()
1336{
1337 m_lastSubMenuIndex = -1;
1338 foreach (QMenu *subMenu, qFindChildren<QMenu*>(this)) {
1339 subMenu->hide();
1340 }
1341}
1342
1343void QDesignerMenu::deleteAction()
1344{
1345 QAction *action = currentAction();
1346 const int pos = actions().indexOf(action);
1347 QAction *action_before = 0;
1348 if (pos != -1)
1349 action_before = safeActionAt(pos + 1);
1350
1351 QDesignerFormWindowInterface *fw = formWindow();
1352 RemoveActionFromCommand *cmd = new RemoveActionFromCommand(fw);
1353 cmd->init(this, action, action_before);
1354 fw->commandHistory()->push(cmd);
1355
1356 update();
1357}
1358
1359void QDesignerMenu::deactivateMenu()
1360{
1361 m_deactivateWindowTimer->start(10);
1362}
1363
1364void QDesignerMenu::slotDeactivateNow()
1365{
1366 m_deactivateWindowTimer->stop();
1367
1368 if (m_dragging)
1369 return;
1370
1371 QDesignerMenu *root = findRootMenu();
1372
1373 if (! root->findActivatedMenu()) {
1374 root->hide();
1375 root->hideSubMenu();
1376 }
1377}
1378
1379void QDesignerMenu::drawSelection(QPainter *p, const QRect &r)
1380{
1381 p->save();
1382
1383 QColor c = Qt::blue;
1384 p->setPen(QPen(c, 1));
1385 c.setAlpha(32);
1386 p->setBrush(c);
1387 p->drawRect(r);
1388
1389 p->restore();
1390}
1391
1392void QDesignerMenu::keyPressEvent(QKeyEvent *event)
1393{
1394 event->ignore();
1395}
1396
1397void QDesignerMenu::keyReleaseEvent(QKeyEvent *event)
1398{
1399 event->ignore();
1400}
1401
1402QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.