1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the Qt Designer of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21 ** included in the packaging of this file. Please review the following
22 ** information to ensure the GNU General Public License requirements will
23 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24 **
25 ** $QT_END_LICENSE$
26 **
27 ****************************************************************************/
28
29 #include "qdesigner_menubar_p.h"
30 #include "qdesigner_menu_p.h"
31 #include "qdesigner_command_p.h"
32 #include "qdesigner_propertycommand_p.h"
33 #include "actionrepository_p.h"
34 #include "actionprovider_p.h"
35 #include "actioneditor_p.h"
36 #include "qdesigner_utils_p.h"
37 #include "promotiontaskmenu_p.h"
38 #include "qdesigner_objectinspector_p.h"
39
40 #include <QtDesigner/abstractformwindow.h>
41 #include <QtDesigner/abstractformeditor.h>
42 #include <QtDesigner/abstractwidgetfactory.h>
43 #include <QtDesigner/qextensionmanager.h>
44
45 #include <QtCore/qmimedata.h>
46
47 #include <QtCore/qdebug.h>
48
49 #include <QtWidgets/qapplication.h>
50 #include <QtGui/qdrag.h>
51 #include <QtWidgets/qlineedit.h>
52 #include <QtGui/qpainter.h>
53 #include <QtGui/qevent.h>
54
55 Q_DECLARE_METATYPE(QAction*)
56
57 QT_BEGIN_NAMESPACE
58
59 using ActionList = QList<QAction *>;
60
61 using namespace qdesigner_internal;
62
63 namespace qdesigner_internal
64 {
65
66 /////////////////////////////////////////////////////////////////////////////////////////////////////////
SpecialMenuAction(QObject * parent)67 SpecialMenuAction::SpecialMenuAction(QObject *parent)
68 : QAction(parent)
69 {
70 }
71
72 SpecialMenuAction::~SpecialMenuAction() = default;
73
74 } // namespace qdesigner_internal
75
76
77 /////////////////////////////////////////////////////////////////////////////////////////////////////////
QDesignerMenuBar(QWidget * parent)78 QDesignerMenuBar::QDesignerMenuBar(QWidget *parent) :
79 QMenuBar(parent),
80 m_addMenu(new SpecialMenuAction(this)),
81 m_editor(new QLineEdit(this)),
82 m_promotionTaskMenu(new PromotionTaskMenu(this, PromotionTaskMenu::ModeSingleWidget, this))
83 {
84 setContextMenuPolicy(Qt::DefaultContextMenu);
85
86 setAcceptDrops(true); // ### fake
87 // Fake property: Keep the menu bar editable in the form even if a native menu bar is used.
88 setNativeMenuBar(false);
89
90 m_addMenu->setText(tr("Type Here"));
91 addAction(m_addMenu);
92
93 QFont italic;
94 italic.setItalic(true);
95 m_addMenu->setFont(italic);
96
97 m_editor->setObjectName(QStringLiteral("__qt__passive_editor"));
98 m_editor->hide();
99 m_editor->installEventFilter(this);
100 installEventFilter(this);
101 }
102
103 QDesignerMenuBar::~QDesignerMenuBar() = default;
104
paintEvent(QPaintEvent * event)105 void QDesignerMenuBar::paintEvent(QPaintEvent *event)
106 {
107 QMenuBar::paintEvent(event);
108
109 QPainter p(this);
110
111 const auto &actionList = actions();
112 for (QAction *a : actionList) {
113 if (qobject_cast<SpecialMenuAction*>(a)) {
114 const QRect g = actionGeometry(a);
115 QLinearGradient lg(g.left(), g.top(), g.left(), g.bottom());
116 lg.setColorAt(0.0, Qt::transparent);
117 lg.setColorAt(0.7, QColor(0, 0, 0, 32));
118 lg.setColorAt(1.0, Qt::transparent);
119
120 p.fillRect(g, lg);
121 }
122 }
123
124 QAction *action = currentAction();
125
126 if (m_dragging || !action)
127 return;
128
129 if (hasFocus()) {
130 const QRect g = actionGeometry(action);
131 QDesignerMenu::drawSelection(&p, g.adjusted(1, 1, -1, -1));
132 } else if (action->menu() && action->menu()->isVisible()) {
133 const QRect g = actionGeometry(action);
134 p.drawRect(g.adjusted(1, 1, -1, -1));
135 }
136 }
137
handleEvent(QWidget * widget,QEvent * event)138 bool QDesignerMenuBar::handleEvent(QWidget *widget, QEvent *event)
139 {
140 if (!formWindow())
141 return false;
142
143 if (event->type() == QEvent::FocusIn || event->type() == QEvent::FocusOut)
144 update();
145
146 switch (event->type()) {
147 default: break;
148
149 case QEvent::MouseButtonDblClick:
150 return handleMouseDoubleClickEvent(widget, static_cast<QMouseEvent*>(event));
151 case QEvent::MouseButtonPress:
152 return handleMousePressEvent(widget, static_cast<QMouseEvent*>(event));
153 case QEvent::MouseButtonRelease:
154 return handleMouseReleaseEvent(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 case QEvent::FocusIn:
162 case QEvent::FocusOut:
163 return widget != m_editor;
164 }
165
166 return true;
167 }
168
handleMouseDoubleClickEvent(QWidget *,QMouseEvent * event)169 bool QDesignerMenuBar::handleMouseDoubleClickEvent(QWidget *, QMouseEvent *event)
170 {
171 if (!rect().contains(event->pos()))
172 return true;
173
174 if ((event->buttons() & Qt::LeftButton) != Qt::LeftButton)
175 return true;
176
177 event->accept();
178
179 m_startPosition = QPoint();
180
181 m_currentIndex = actionIndexAt(this, event->pos(), Qt::Horizontal);
182 if (m_currentIndex != -1) {
183 showLineEdit();
184 }
185
186 return true;
187 }
188
handleKeyPressEvent(QWidget *,QKeyEvent * e)189 bool QDesignerMenuBar::handleKeyPressEvent(QWidget *, QKeyEvent *e)
190 {
191 if (m_editor->isHidden()) { // In navigation mode
192 switch (e->key()) {
193
194 case Qt::Key_Delete:
195 if (m_currentIndex == -1 || m_currentIndex >= realActionCount())
196 break;
197 hideMenu();
198 deleteMenu();
199 break;
200
201 case Qt::Key_Left:
202 e->accept();
203 moveLeft(e->modifiers() & Qt::ControlModifier);
204 return true;
205
206 case Qt::Key_Right:
207 e->accept();
208 moveRight(e->modifiers() & Qt::ControlModifier);
209 return true; // no update
210
211 case Qt::Key_Up:
212 e->accept();
213 moveUp();
214 return true;
215
216 case Qt::Key_Down:
217 e->accept();
218 moveDown();
219 return true;
220
221 case Qt::Key_PageUp:
222 m_currentIndex = 0;
223 break;
224
225 case Qt::Key_PageDown:
226 m_currentIndex = actions().count() - 1;
227 break;
228
229 case Qt::Key_Enter:
230 case Qt::Key_Return:
231 e->accept();
232 enterEditMode();
233 return true; // no update
234
235 case Qt::Key_Alt:
236 case Qt::Key_Shift:
237 case Qt::Key_Control:
238 case Qt::Key_Escape:
239 e->ignore();
240 setFocus(); // FIXME: this is because some other widget get the focus when CTRL is pressed
241 return true; // no update
242
243 default:
244 if (!e->text().isEmpty() && e->text().at(0).toLatin1() >= 32) {
245 showLineEdit();
246 QApplication::sendEvent(m_editor, e);
247 e->accept();
248 } else {
249 e->ignore();
250 }
251 return true;
252 }
253 } else { // In edit mode
254 switch (e->key()) {
255 default:
256 return false;
257
258 case Qt::Key_Control:
259 e->ignore();
260 return true;
261
262 case Qt::Key_Enter:
263 case Qt::Key_Return:
264 if (!m_editor->text().isEmpty()) {
265 leaveEditMode(ForceAccept);
266 if (m_lastFocusWidget)
267 m_lastFocusWidget->setFocus();
268
269 m_editor->hide();
270 showMenu();
271 break;
272 }
273 Q_FALLTHROUGH();
274
275 case Qt::Key_Escape:
276 update();
277 setFocus();
278 break;
279 }
280 }
281
282 e->accept();
283 update();
284
285 return true;
286 }
287
startDrag(const QPoint & pos)288 void QDesignerMenuBar::startDrag(const QPoint &pos)
289 {
290 const int index = findAction(pos);
291 if (m_currentIndex == -1 || index >= realActionCount())
292 return;
293
294 QAction *action = safeActionAt(index);
295
296 QDesignerFormWindowInterface *fw = formWindow();
297 RemoveActionFromCommand *cmd = new RemoveActionFromCommand(fw);
298 cmd->init(this, action, actions().at(index + 1));
299 fw->commandHistory()->push(cmd);
300
301 adjustSize();
302
303 hideMenu(index);
304
305 QDrag *drag = new QDrag(this);
306 drag->setPixmap(ActionRepositoryMimeData::actionDragPixmap(action));
307 drag->setMimeData(new ActionRepositoryMimeData(action, Qt::MoveAction));
308
309 const int old_index = m_currentIndex;
310 m_currentIndex = -1;
311
312 if (drag->exec(Qt::MoveAction) == Qt::IgnoreAction) {
313 InsertActionIntoCommand *cmd = new InsertActionIntoCommand(fw);
314 cmd->init(this, action, safeActionAt(index));
315 fw->commandHistory()->push(cmd);
316
317 m_currentIndex = old_index;
318 adjustSize();
319 }
320 }
321
handleMousePressEvent(QWidget *,QMouseEvent * event)322 bool QDesignerMenuBar::handleMousePressEvent(QWidget *, QMouseEvent *event)
323 {
324 m_startPosition = QPoint();
325 event->accept();
326
327 if (event->button() != Qt::LeftButton)
328 return true;
329
330 m_startPosition = event->pos();
331 const int newIndex = actionIndexAt(this, m_startPosition, Qt::Horizontal);
332 const bool changed = newIndex != m_currentIndex;
333 m_currentIndex = newIndex;
334 updateCurrentAction(changed);
335
336 return true;
337 }
338
handleMouseReleaseEvent(QWidget *,QMouseEvent * event)339 bool QDesignerMenuBar::handleMouseReleaseEvent(QWidget *, QMouseEvent *event)
340 {
341 m_startPosition = QPoint();
342
343 if (event->button() != Qt::LeftButton)
344 return true;
345
346 event->accept();
347 m_currentIndex = actionIndexAt(this, event->pos(), Qt::Horizontal);
348 if (!m_editor->isVisible() && m_currentIndex != -1 && m_currentIndex < realActionCount())
349 showMenu();
350
351 return true;
352 }
353
handleMouseMoveEvent(QWidget *,QMouseEvent * event)354 bool QDesignerMenuBar::handleMouseMoveEvent(QWidget *, QMouseEvent *event)
355 {
356 if ((event->buttons() & Qt::LeftButton) != Qt::LeftButton)
357 return true;
358
359 if (m_startPosition.isNull())
360 return true;
361
362 const QPoint pos = mapFromGlobal(event->globalPos());
363
364 if ((pos - m_startPosition).manhattanLength() < qApp->startDragDistance())
365 return true;
366
367 const int index = actionIndexAt(this, m_startPosition, Qt::Horizontal);
368 if (index < actions().count()) {
369 hideMenu(index);
370 update();
371 }
372
373 startDrag(m_startPosition);
374 m_startPosition = QPoint();
375
376 return true;
377 }
378
contextMenuActions()379 ActionList QDesignerMenuBar::contextMenuActions()
380 {
381 ActionList rc;
382 if (QAction *action = safeActionAt(m_currentIndex)) {
383 if (!qobject_cast<SpecialMenuAction*>(action)) {
384 QVariant itemData;
385 itemData.setValue(action);
386
387 QAction *remove_action = new QAction(tr("Remove Menu '%1'").arg(action->menu()->objectName()), nullptr);
388 remove_action->setData(itemData);
389 connect(remove_action, &QAction::triggered, this, &QDesignerMenuBar::deleteMenu);
390 rc.push_back(remove_action);
391 QAction *sep = new QAction(nullptr);
392 sep->setSeparator(true);
393 rc.push_back(sep);
394 }
395 }
396
397 m_promotionTaskMenu->addActions(formWindow(), PromotionTaskMenu::TrailingSeparator, rc);
398
399 QAction *remove_menubar = new QAction(tr("Remove Menu Bar"), nullptr);
400 connect(remove_menubar, &QAction::triggered, this, &QDesignerMenuBar::slotRemoveMenuBar);
401 rc.push_back(remove_menubar);
402 return rc;
403 }
404
handleContextMenuEvent(QWidget *,QContextMenuEvent * event)405 bool QDesignerMenuBar::handleContextMenuEvent(QWidget *, QContextMenuEvent *event)
406 {
407 event->accept();
408
409 m_currentIndex = actionIndexAt(this, mapFromGlobal(event->globalPos()), Qt::Horizontal);
410
411 update();
412
413 QMenu menu;
414 const ActionList al = contextMenuActions();
415 const ActionList::const_iterator acend = al.constEnd();
416 for (ActionList::const_iterator it = al.constBegin(); it != acend; ++it)
417 menu.addAction(*it);
418 menu.exec(event->globalPos());
419 return true;
420 }
421
slotRemoveMenuBar()422 void QDesignerMenuBar::slotRemoveMenuBar()
423 {
424 Q_ASSERT(formWindow() != nullptr);
425
426 QDesignerFormWindowInterface *fw = formWindow();
427
428 DeleteMenuBarCommand *cmd = new DeleteMenuBarCommand(fw);
429 cmd->init(this);
430 fw->commandHistory()->push(cmd);
431 }
432
focusOutEvent(QFocusEvent * event)433 void QDesignerMenuBar::focusOutEvent(QFocusEvent *event)
434 {
435 QMenuBar::focusOutEvent(event);
436 }
437
enterEditMode()438 void QDesignerMenuBar::enterEditMode()
439 {
440 if (m_currentIndex >= 0 && m_currentIndex <= realActionCount()) {
441 showLineEdit();
442 }
443 }
444
leaveEditMode(LeaveEditMode mode)445 void QDesignerMenuBar::leaveEditMode(LeaveEditMode mode)
446 {
447 m_editor->releaseKeyboard();
448
449 if (mode == Default)
450 return;
451
452 if (m_editor->text().isEmpty())
453 return;
454
455 QAction *action = nullptr;
456
457 QDesignerFormWindowInterface *fw = formWindow();
458 Q_ASSERT(fw);
459
460 if (m_currentIndex >= 0 && m_currentIndex < realActionCount()) {
461 action = safeActionAt(m_currentIndex);
462 fw->beginCommand(QApplication::translate("Command", "Change Title"));
463 } else {
464 fw->beginCommand(QApplication::translate("Command", "Insert Menu"));
465 const QString niceObjectName = ActionEditor::actionTextToName(m_editor->text(), QStringLiteral("menu"));
466 QMenu *menu = qobject_cast<QMenu*>(fw->core()->widgetFactory()->createWidget(QStringLiteral("QMenu"), this));
467 fw->core()->widgetFactory()->initialize(menu);
468 menu->setObjectName(niceObjectName);
469 menu->setTitle(tr("Menu"));
470 fw->ensureUniqueObjectName(menu);
471 action = menu->menuAction();
472 AddMenuActionCommand *cmd = new AddMenuActionCommand(fw);
473 cmd->init(action, m_addMenu, this, this);
474 fw->commandHistory()->push(cmd);
475 }
476
477 SetPropertyCommand *cmd = new SetPropertyCommand(fw);
478 cmd->init(action, QStringLiteral("text"), m_editor->text());
479 fw->commandHistory()->push(cmd);
480 fw->endCommand();
481 }
482
showLineEdit()483 void QDesignerMenuBar::showLineEdit()
484 {
485 QAction *action = nullptr;
486
487 if (m_currentIndex >= 0 && m_currentIndex < realActionCount())
488 action = safeActionAt(m_currentIndex);
489 else
490 action = m_addMenu;
491
492 if (action->isSeparator())
493 return;
494
495 // hideMenu();
496
497 m_lastFocusWidget = qApp->focusWidget();
498
499 // open edit field for item name
500 const QString text = action != m_addMenu ? action->text() : QString();
501
502 m_editor->setText(text);
503 m_editor->selectAll();
504 m_editor->setGeometry(actionGeometry(action));
505 m_editor->show();
506 qApp->setActiveWindow(m_editor);
507 m_editor->setFocus();
508 m_editor->grabKeyboard();
509 }
510
eventFilter(QObject * object,QEvent * event)511 bool QDesignerMenuBar::eventFilter(QObject *object, QEvent *event)
512 {
513 if (object != this && object != m_editor)
514 return false;
515
516 if (!m_editor->isHidden() && object == m_editor && event->type() == QEvent::FocusOut) {
517 leaveEditMode(Default);
518 m_editor->hide();
519 update();
520 return true;
521 }
522
523 bool dispatch = true;
524
525 switch (event->type()) {
526 default: break;
527
528 case QEvent::KeyPress:
529 case QEvent::KeyRelease:
530 case QEvent::ContextMenu:
531 case QEvent::MouseMove:
532 case QEvent::MouseButtonPress:
533 case QEvent::MouseButtonRelease:
534 case QEvent::MouseButtonDblClick:
535 dispatch = (object != m_editor);
536 Q_FALLTHROUGH(); // no break
537
538 case QEvent::Enter:
539 case QEvent::Leave:
540 case QEvent::FocusIn:
541 case QEvent::FocusOut:
542 {
543 QWidget *widget = qobject_cast<QWidget*>(object);
544
545 if (dispatch && widget && (widget == this || isAncestorOf(widget)))
546 return handleEvent(widget, event);
547 } break;
548
549 case QEvent::Shortcut:
550 event->accept();
551 return true;
552 }
553
554 return false;
555 };
556
findAction(const QPoint & pos) const557 int QDesignerMenuBar::findAction(const QPoint &pos) const
558 {
559 const int index = actionIndexAt(this, pos, Qt::Horizontal);
560 if (index == -1)
561 return realActionCount();
562
563 return index;
564 }
565
adjustIndicator(const QPoint & pos)566 void QDesignerMenuBar::adjustIndicator(const QPoint &pos)
567 {
568 const int index = findAction(pos);
569 QAction *action = safeActionAt(index);
570 Q_ASSERT(action != nullptr);
571
572 if (pos != QPoint(-1, -1)) {
573 QDesignerMenu *m = qobject_cast<QDesignerMenu*>(action->menu());
574 if (!m || m->parentMenu()) {
575 m_currentIndex = index;
576 showMenu(index);
577 }
578 }
579
580 if (QDesignerActionProviderExtension *a = actionProvider()) {
581 a->adjustIndicator(pos);
582 }
583 }
584
checkAction(QAction * action) const585 QDesignerMenuBar::ActionDragCheck QDesignerMenuBar::checkAction(QAction *action) const
586 {
587 // action belongs to another form
588 if (!action || !Utils::isObjectAncestorOf(formWindow()->mainContainer(), action))
589 return NoActionDrag;
590
591 if (!action->menu())
592 return ActionDragOnSubMenu; // simple action only on sub menus
593
594 QDesignerMenu *m = qobject_cast<QDesignerMenu*>(action->menu());
595 if (m && m->parentMenu())
596 return ActionDragOnSubMenu; // it looks like a submenu
597
598 if (actions().contains(action))
599 return ActionDragOnSubMenu; // we already have the action in the menubar
600
601 return AcceptActionDrag;
602 }
603
dragEnterEvent(QDragEnterEvent * event)604 void QDesignerMenuBar::dragEnterEvent(QDragEnterEvent *event)
605 {
606 const ActionRepositoryMimeData *d = qobject_cast<const ActionRepositoryMimeData*>(event->mimeData());
607 if (!d || d->actionList().isEmpty()) {
608 event->ignore();
609 return;
610 }
611
612 QAction *action = d->actionList().first();
613 switch (checkAction(action)) {
614 case NoActionDrag:
615 event->ignore();
616 break;
617 case ActionDragOnSubMenu:
618 m_dragging = true;
619 d->accept(event);
620 break;
621 case AcceptActionDrag:
622 m_dragging = true;
623 d->accept(event);
624 adjustIndicator(event->pos());
625 break;
626 }
627 }
628
dragMoveEvent(QDragMoveEvent * event)629 void QDesignerMenuBar::dragMoveEvent(QDragMoveEvent *event)
630 {
631 const ActionRepositoryMimeData *d = qobject_cast<const ActionRepositoryMimeData*>(event->mimeData());
632 if (!d || d->actionList().isEmpty()) {
633 event->ignore();
634 return;
635 }
636 QAction *action = d->actionList().first();
637
638 switch (checkAction(action)) {
639 case NoActionDrag:
640 event->ignore();
641 break;
642 case ActionDragOnSubMenu:
643 event->ignore();
644 showMenu(findAction(event->pos()));
645 break;
646 case AcceptActionDrag:
647 d->accept(event);
648 adjustIndicator(event->pos());
649 break;
650 }
651 }
652
dragLeaveEvent(QDragLeaveEvent *)653 void QDesignerMenuBar::dragLeaveEvent(QDragLeaveEvent *)
654 {
655 m_dragging = false;
656
657 adjustIndicator(QPoint(-1, -1));
658 }
659
dropEvent(QDropEvent * event)660 void QDesignerMenuBar::dropEvent(QDropEvent *event)
661 {
662 m_dragging = false;
663
664 if (const ActionRepositoryMimeData *d = qobject_cast<const ActionRepositoryMimeData*>(event->mimeData())) {
665
666 QAction *action = d->actionList().first();
667 if (checkAction(action) == AcceptActionDrag) {
668 event->acceptProposedAction();
669 int index = findAction(event->pos());
670 index = qMin(index, actions().count() - 1);
671
672 QDesignerFormWindowInterface *fw = formWindow();
673 InsertActionIntoCommand *cmd = new InsertActionIntoCommand(fw);
674 cmd->init(this, action, safeActionAt(index));
675 fw->commandHistory()->push(cmd);
676
677 m_currentIndex = index;
678 update();
679 adjustIndicator(QPoint(-1, -1));
680 return;
681 }
682 }
683 event->ignore();
684 }
685
actionEvent(QActionEvent * event)686 void QDesignerMenuBar::actionEvent(QActionEvent *event)
687 {
688 QMenuBar::actionEvent(event);
689 }
690
formWindow() const691 QDesignerFormWindowInterface *QDesignerMenuBar::formWindow() const
692 {
693 return QDesignerFormWindowInterface::findFormWindow(const_cast<QDesignerMenuBar*>(this));
694 }
695
actionProvider()696 QDesignerActionProviderExtension *QDesignerMenuBar::actionProvider()
697 {
698 if (QDesignerFormWindowInterface *fw = formWindow()) {
699 QDesignerFormEditorInterface *core = fw->core();
700 return qt_extension<QDesignerActionProviderExtension*>(core->extensionManager(), this);
701 }
702
703 return nullptr;
704 }
705
currentAction() const706 QAction *QDesignerMenuBar::currentAction() const
707 {
708 if (m_currentIndex < 0 || m_currentIndex >= actions().count())
709 return nullptr;
710
711 return safeActionAt(m_currentIndex);
712 }
713
realActionCount() const714 int QDesignerMenuBar::realActionCount() const
715 {
716 return actions().count() - 1; // 1 fake actions
717 }
718
dragging() const719 bool QDesignerMenuBar::dragging() const
720 {
721 return m_dragging;
722 }
723
moveLeft(bool ctrl)724 void QDesignerMenuBar::moveLeft(bool ctrl)
725 {
726 if (layoutDirection() == Qt::LeftToRight) {
727 movePrevious(ctrl);
728 } else {
729 moveNext(ctrl);
730 }
731 }
732
moveRight(bool ctrl)733 void QDesignerMenuBar::moveRight(bool ctrl)
734 {
735 if (layoutDirection() == Qt::LeftToRight) {
736 moveNext(ctrl);
737 } else {
738 movePrevious(ctrl);
739 }
740 }
741
movePrevious(bool ctrl)742 void QDesignerMenuBar::movePrevious(bool ctrl)
743 {
744 const bool swapped = ctrl && swapActions(m_currentIndex, m_currentIndex - 1);
745 const int newIndex = qMax(0, m_currentIndex - 1);
746 // Always re-select, swapping destroys order
747 if (swapped || newIndex != m_currentIndex) {
748 m_currentIndex = newIndex;
749 updateCurrentAction(true);
750 }
751 }
752
moveNext(bool ctrl)753 void QDesignerMenuBar::moveNext(bool ctrl)
754 {
755 const bool swapped = ctrl && swapActions(m_currentIndex + 1, m_currentIndex);
756 const int newIndex = qMin(actions().count() - 1, m_currentIndex + 1);
757 if (swapped || newIndex != m_currentIndex) {
758 m_currentIndex = newIndex;
759 updateCurrentAction(!ctrl);
760 }
761 }
762
moveUp()763 void QDesignerMenuBar::moveUp()
764 {
765 update();
766 }
767
moveDown()768 void QDesignerMenuBar::moveDown()
769 {
770 showMenu();
771 }
772
adjustSpecialActions()773 void QDesignerMenuBar::adjustSpecialActions()
774 {
775 removeAction(m_addMenu);
776 addAction(m_addMenu);
777 }
778
hideMenu(int index)779 void QDesignerMenuBar::hideMenu(int index)
780 {
781 if (index < 0 && m_currentIndex >= 0)
782 index = m_currentIndex;
783
784 if (index < 0 || index >= realActionCount())
785 return;
786
787 QAction *action = safeActionAt(index);
788
789 if (action && action->menu()) {
790 action->menu()->hide();
791
792 if (QDesignerMenu *menu = qobject_cast<QDesignerMenu*>(action->menu())) {
793 menu->closeMenuChain();
794 }
795 }
796 }
797
deleteMenu()798 void QDesignerMenuBar::deleteMenu()
799 {
800 deleteMenuAction(currentAction());
801 }
802
deleteMenuAction(QAction * action)803 void QDesignerMenuBar::deleteMenuAction(QAction *action)
804 {
805 if (action && !qobject_cast<SpecialMenuAction*>(action)) {
806 const int pos = actions().indexOf(action);
807 QAction *action_before = nullptr;
808 if (pos != -1)
809 action_before = safeActionAt(pos + 1);
810
811 QDesignerFormWindowInterface *fw = formWindow();
812 RemoveMenuActionCommand *cmd = new RemoveMenuActionCommand(fw);
813 cmd->init(action, action_before, this, this);
814 fw->commandHistory()->push(cmd);
815 }
816 }
817
showMenu(int index)818 void QDesignerMenuBar::showMenu(int index)
819 {
820 if (index < 0 && m_currentIndex >= 0)
821 index = m_currentIndex;
822
823 if (index < 0 || index >= realActionCount())
824 return;
825
826 m_currentIndex = index;
827 QAction *action = currentAction();
828
829 if (action && action->menu()) {
830 if (m_lastMenuActionIndex != -1 && m_lastMenuActionIndex != index) {
831 hideMenu(m_lastMenuActionIndex);
832 }
833
834 m_lastMenuActionIndex = index;
835 QMenu *menu = action->menu();
836 const QRect g = actionGeometry(action);
837
838 if (!menu->isVisible()) {
839 if ((menu->windowFlags() & Qt::Popup) != Qt::Popup)
840 menu->setWindowFlags(Qt::Popup);
841 menu->adjustSize();
842 if (layoutDirection() == Qt::LeftToRight) {
843 menu->move(mapToGlobal(g.bottomLeft()));
844 } else {
845 // The position is not initially correct due to the unknown width,
846 // causing it to overlap a bit the first time it is invoked.
847 QPoint point = g.bottomRight() - QPoint(menu->width(), 0);
848 menu->move(mapToGlobal(point));
849 }
850 menu->setFocus(Qt::MouseFocusReason);
851 menu->raise();
852 menu->show();
853 } else {
854 menu->raise();
855 }
856 }
857 }
858
safeActionAt(int index) const859 QAction *QDesignerMenuBar::safeActionAt(int index) const
860 {
861 if (index < 0 || index >= actions().count())
862 return nullptr;
863
864 return actions().at(index);
865 }
866
swapActions(int a,int b)867 bool QDesignerMenuBar::swapActions(int a, int b)
868 {
869 const int left = qMin(a, b);
870 int right = qMax(a, b);
871
872 QAction *action_a = safeActionAt(left);
873 QAction *action_b = safeActionAt(right);
874
875 if (action_a == action_b
876 || !action_a
877 || !action_b
878 || qobject_cast<SpecialMenuAction*>(action_a)
879 || qobject_cast<SpecialMenuAction*>(action_b))
880 return false; // nothing to do
881
882 right = qMin(right, realActionCount());
883 if (right < 0)
884 return false; // nothing to do
885
886 formWindow()->beginCommand(QApplication::translate("Command", "Move action"));
887
888 QAction *action_b_before = safeActionAt(right + 1);
889
890 QDesignerFormWindowInterface *fw = formWindow();
891 RemoveActionFromCommand *cmd1 = new RemoveActionFromCommand(fw);
892 cmd1->init(this, action_b, action_b_before, false);
893 fw->commandHistory()->push(cmd1);
894
895 QAction *action_a_before = safeActionAt(left + 1);
896
897 InsertActionIntoCommand *cmd2 = new InsertActionIntoCommand(fw);
898 cmd2->init(this, action_b, action_a_before, false);
899 fw->commandHistory()->push(cmd2);
900
901 RemoveActionFromCommand *cmd3 = new RemoveActionFromCommand(fw);
902 cmd3->init(this, action_a, action_b, false);
903 fw->commandHistory()->push(cmd3);
904
905 InsertActionIntoCommand *cmd4 = new InsertActionIntoCommand(fw);
906 cmd4->init(this, action_a, action_b_before, true);
907 fw->commandHistory()->push(cmd4);
908
909 fw->endCommand();
910
911 return true;
912 }
913
keyPressEvent(QKeyEvent * event)914 void QDesignerMenuBar::keyPressEvent(QKeyEvent *event)
915 {
916 event->ignore();
917 }
918
keyReleaseEvent(QKeyEvent * event)919 void QDesignerMenuBar::keyReleaseEvent(QKeyEvent *event)
920 {
921 event->ignore();
922 }
923
updateCurrentAction(bool selectAction)924 void QDesignerMenuBar::updateCurrentAction(bool selectAction)
925 {
926 update();
927
928 if (!selectAction)
929 return;
930
931 QAction *action = currentAction();
932 if (!action || action == m_addMenu)
933 return;
934
935 QMenu *menu = action->menu();
936 if (!menu)
937 return;
938
939 QDesignerObjectInspector *oi = nullptr;
940 if (QDesignerFormWindowInterface *fw = formWindow())
941 oi = qobject_cast<QDesignerObjectInspector *>(fw->core()->objectInspector());
942
943 if (!oi)
944 return;
945
946 oi->clearSelection();
947 oi->selectObject(menu);
948 }
949
950 QT_END_NAMESPACE
951