Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
formwindow.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
4#include "formwindow.h"
5#include "formeditor.h"
14
15// shared
16#include <metadatabase_p.h>
17#include <qdesigner_tabwidget_p.h>
18#include <qdesigner_toolbox_p.h>
19#include <qdesigner_stackedbox_p.h>
20#include <qdesigner_resource.h>
21#include <qdesigner_command_p.h>
22#include <qdesigner_command2_p.h>
23#include <qdesigner_propertycommand_p.h>
24#include <qdesigner_taskmenu_p.h>
25#include <qdesigner_widget_p.h>
26#include <qdesigner_utils_p.h>
27#include <qlayout_widget_p.h>
28#include <spacer_widget_p.h>
29#include <invisible_widget_p.h>
30#include <layoutinfo_p.h>
31#include <qdesigner_objectinspector_p.h>
32#include <connectionedit_p.h>
33#include <actionprovider_p.h>
34#include <private/ui4_p.h>
35#include <deviceprofile_p.h>
36#include <shared_settings_p.h>
37#include <grid_p.h>
38
39#include <QtDesigner/qextensionmanager.h>
40#include <QtDesigner/abstractwidgetdatabase.h>
41#include <QtDesigner/propertysheet.h>
42#include <QtDesigner/abstractwidgetfactory.h>
43#include <QtDesigner/container.h>
44#include <QtDesigner/taskmenu.h>
45#include <QtDesigner/abstractwidgetbox.h>
46#include <QtDesigner/private/ui4_p.h>
47
48#include <abstractdialoggui_p.h>
49
50#include <QtWidgets/qmenu.h>
51#include <QtWidgets/qscrollarea.h>
52#include <QtWidgets/qrubberband.h>
53#include <QtWidgets/qapplication.h>
54#include <QtWidgets/qsplitter.h>
55#include <QtWidgets/qgroupbox.h>
56#include <QtWidgets/qdockwidget.h>
57#include <QtWidgets/qtoolbox.h>
58#include <QtWidgets/qstackedwidget.h>
59#include <QtWidgets/qtabwidget.h>
60#include <QtWidgets/qbuttongroup.h>
61
62#include <QtGui/qaction.h>
63#include <QtGui/qactiongroup.h>
64#if QT_CONFIG(clipboard)
65# include <QtGui/qclipboard.h>
66#endif
67#include <QtGui/qpainter.h>
68#include <QtGui/qundogroup.h>
69
70#include <QtCore/qdebug.h>
71#include <QtCore/qbuffer.h>
72#include <QtCore/qtimer.h>
73#include <QtCore/qlist.h>
74#include <QtCore/qxmlstream.h>
75
76#include <memory>
77
78Q_DECLARE_METATYPE(QWidget*)
79
80QT_BEGIN_NAMESPACE
81
82using namespace Qt::StringLiterals;
83
84namespace {
85class BlockSelection
86{
87 Q_DISABLE_COPY_MOVE(BlockSelection)
88public:
89 BlockSelection(qdesigner_internal::FormWindow *fw)
90 : m_formWindow(fw),
91 m_blocked(m_formWindow->blockSelectionChanged(true))
92 {
93 }
94
95 ~BlockSelection()
96 {
97 if (m_formWindow)
98 m_formWindow->blockSelectionChanged(m_blocked);
99 }
100
101private:
102 QPointer<qdesigner_internal::FormWindow> m_formWindow;
103 const bool m_blocked;
104};
105
106enum { debugFormWindow = 0 };
107}
108
109namespace qdesigner_internal {
110
111// ------------------------ FormWindow::Selection
112// Maintains a pool of WidgetSelections to be used for selected widgets.
113
115{
117public:
120
121 // Clear
122 void clear();
123
124 // Also clear out the pool. Call if reparenting of the main container occurs.
126
129
130 bool isWidgetSelected(QWidget *w) const;
132
134 // remove widget, return new current widget or 0
136
137 void raiseList(const QWidgetList& l);
139
141
142 void hide(QWidget *w);
143 void show(QWidget *w);
144
145private:
146
148 SelectionPool m_selectionPool;
149
150 QHash<QWidget *, WidgetSelection *> m_usedSelections;
151};
152
153FormWindow::Selection::Selection() = default;
154
159
161{
162 if (!m_usedSelections.isEmpty()) {
163 for (auto it = m_usedSelections.begin(), mend = m_usedSelections.end(); it != mend; ++it)
164 it.value()->setWidget(nullptr);
165 m_usedSelections.clear();
166 }
167}
168
170{
171 clear();
172 qDeleteAll(m_selectionPool);
173 m_selectionPool.clear();
174}
175
177{
178 WidgetSelection *rc = m_usedSelections.value(w);
179 if (rc != nullptr) {
180 rc->show();
182 return rc;
183 }
184 // find a free one in the pool
185 for (auto *s : std::as_const(m_selectionPool)) {
186 if (!s->isUsed()) {
187 rc = s;
188 break;
189 }
190 }
191
192 if (rc == nullptr) {
193 rc = new WidgetSelection(fw);
194 m_selectionPool.push_back(rc);
195 }
196
197 m_usedSelections.insert(w, rc);
198 rc->setWidget(w);
199 return rc;
200}
201
203{
204 WidgetSelection *s = m_usedSelections.value(w);
205 if (!s)
206 return w;
207
208 s->setWidget(nullptr);
209 m_usedSelections.remove(w);
210
211 if (m_usedSelections.isEmpty())
212 return nullptr;
213
214 return (*m_usedSelections.begin())->widget();
215}
216
218{
219 if (WidgetSelection *s = m_usedSelections.value(w))
220 s->update();
221}
222
224{
225 for (auto it = m_usedSelections.begin(), mend = m_usedSelections.end(); it != mend; ++it)
226 it.value()->update();
227}
228
230 return m_usedSelections.contains(w);
231}
232
234{
235 return m_usedSelections.keys();
236}
237
238void FormWindow::Selection::raiseList(const QWidgetList& l)
239{
240 for (auto it = m_usedSelections.constBegin(), mend = m_usedSelections.constEnd(); it != mend; ++it) {
241 WidgetSelection *w = it.value();
242 if (l.contains(w->widget()))
243 w->show();
244 }
245}
246
248{
249 if (WidgetSelection *s = m_usedSelections.value(w))
250 s->show();
251}
252
254{
255 if (WidgetSelection *s = m_usedSelections.value(w)) {
257 }
258}
259
261{
262 if (WidgetSelection *s = m_usedSelections.value(w))
263 s->hide();
264}
265
267{
268 if (WidgetSelection *s = m_usedSelections.value(w))
269 s->show();
270}
271
272// ------------------------ FormWindow
273FormWindow::FormWindow(FormEditor *core, QWidget *parent, Qt::WindowFlags flags) :
275 m_mouseState(NoMouseState),
276 m_core(core),
277 m_selection(new Selection),
280{
281 // Apply settings to formcontainer
282 deviceProfile().apply(core, m_widgetStack->formContainer(), qdesigner_internal::DeviceProfile::ApplyFormParent);
283
284 setLayout(m_widgetStack->layout());
285 init();
286
287 m_cursor = new FormWindowCursor(this, this);
288
289 core->formWindowManager()->addFormWindow(this);
290
292 setAcceptDrops(true);
293}
294
296{
297 auto *core = this->FormWindow::core();
298 Q_ASSERT(core != nullptr);
299 Q_ASSERT(core->metaDataBase() != nullptr);
300 auto *fwm = core->formWindowManager();
301 Q_ASSERT(fwm != nullptr);
302
303 core->formWindowManager()->removeFormWindow(this);
304 core->metaDataBase()->remove(this);
305
306 const QWidgetList &l = widgets();
307 for (QWidget *w : l)
308 core->metaDataBase()->remove(w);
309
310 m_widgetStack = nullptr;
311 m_rubberBand = nullptr;
312 if (resourceSet())
313 core->resourceModel()->removeResourceSet(resourceSet());
314 delete m_selection;
315
316 if (auto *manager = qobject_cast<FormWindowManager*>(fwm))
317 manager->undoGroup()->removeStack(&m_undoStack);
318 m_undoStack.disconnect();
319}
320
321QDesignerFormEditorInterface *FormWindow::core() const
322{
323 return m_core;
324}
325
327{
328 return m_cursor;
329}
330
331void FormWindow::updateWidgets()
332{
333 if (!m_mainContainer)
334 return;
335}
336
337int FormWindow::widgetDepth(const QWidget *w)
338{
339 int d = -1;
340 while (w && !w->isWindow()) {
341 d++;
342 w = w->parentWidget();
343 }
344
345 return d;
346}
347
348bool FormWindow::isChildOf(const QWidget *c, const QWidget *p)
349{
350 while (c) {
351 if (c == p)
352 return true;
353 c = c->parentWidget();
354 }
355 return false;
356}
357
358void FormWindow::setCursorToAll(const QCursor &c, QWidget *start)
359{
360#if QT_CONFIG(cursor)
361 start->setCursor(c);
362 const QWidgetList widgets = start->findChildren<QWidget*>();
363 for (QWidget *widget : widgets) {
364 if (!qobject_cast<WidgetHandle*>(widget)) {
365 widget->setCursor(c);
366 }
367 }
368#endif
369}
370
371void FormWindow::init()
372{
373 auto *core = this->FormWindow::core();
374 if (auto *manager = qobject_cast<FormWindowManager*>(core->formWindowManager()))
375 manager->undoGroup()->addStack(&m_undoStack);
376
377 m_blockSelectionChanged = false;
378
379 m_defaultMargin = INT_MIN;
380 m_defaultSpacing = INT_MIN;
381
382 connect(m_widgetStack, &FormWindowWidgetStack::currentToolChanged,
383 this, &QDesignerFormWindowInterface::toolChanged);
384
385 m_selectionChangedTimer = new QTimer(this);
386 m_selectionChangedTimer->setSingleShot(true);
387 connect(m_selectionChangedTimer, &QTimer::timeout, this,
388 &FormWindow::selectionChangedTimerDone);
389
390 m_checkSelectionTimer = new QTimer(this);
391 m_checkSelectionTimer->setSingleShot(true);
392 connect(m_checkSelectionTimer, &QTimer::timeout,
393 this, &FormWindow::checkSelectionNow);
394
395 m_geometryChangedTimer = new QTimer(this);
396 m_geometryChangedTimer->setSingleShot(true);
397 connect(m_geometryChangedTimer, &QTimer::timeout,
398 this, &QDesignerFormWindowInterface::geometryChanged);
399
400 m_rubberBand = nullptr;
401
402 setFocusPolicy(Qt::StrongFocus);
403
404 m_mainContainer = nullptr;
405 m_currentWidget = nullptr;
406
407 connect(&m_undoStack, &QUndoStack::indexChanged,
408 this, &QDesignerFormWindowInterface::changed);
409 connect(&m_undoStack, &QUndoStack::cleanChanged,
410 this, &FormWindow::slotCleanChanged);
411 connect(this, &QDesignerFormWindowInterface::changed,
412 this, &FormWindow::checkSelection);
413
414 core->metaDataBase()->add(this);
415
416 initializeCoreTools();
417
418 auto *a = new QAction(this);
419 a->setText(tr("Edit contents"));
420 a->setShortcut(tr("F2"));
421 connect(a, &QAction::triggered, this, &FormWindow::editContents);
422 addAction(a);
423}
424
426{
427 return m_mainContainer;
428}
429
430
431void FormWindow::clearMainContainer()
432{
433 if (m_mainContainer) {
435 m_widgetStack->setMainContainer(nullptr);
436 core()->metaDataBase()->remove(m_mainContainer);
437 unmanageWidget(m_mainContainer);
438 delete m_mainContainer;
439 m_mainContainer = nullptr;
440 }
441}
442
444{
445 if (w == m_mainContainer) {
446 // nothing to do
447 return;
448 }
449
450 clearMainContainer();
451
452 m_mainContainer = w;
453 const QSize sz = m_mainContainer->size();
454
455 m_widgetStack->setMainContainer(m_mainContainer);
456 m_widgetStack->setCurrentTool(m_widgetEditor);
457
458 setCurrentWidget(m_mainContainer);
459 manageWidget(m_mainContainer);
460
461 if (QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(core()->extensionManager(), m_mainContainer)) {
462 sheet->setVisible(sheet->indexOf(u"windowTitle"_s), true);
463 sheet->setVisible(sheet->indexOf(u"windowIcon"_s), true);
464 sheet->setVisible(sheet->indexOf(u"windowModality"_s), true);
465 sheet->setVisible(sheet->indexOf(u"windowOpacity"_s), true);
466 sheet->setVisible(sheet->indexOf(u"windowFilePath"_s), true);
467 // ### generalize
468 }
469
470 m_mainContainer->setFocusPolicy(Qt::StrongFocus);
471 m_mainContainer->resize(sz);
472
473 emit mainContainerChanged(m_mainContainer);
474}
475
476QWidget *FormWindow::findTargetContainer(QWidget *widget) const
477{
478 Q_ASSERT(widget);
479
480 while (QWidget *parentWidget = widget->parentWidget()) {
481 if (LayoutInfo::layoutType(m_core, parentWidget) == LayoutInfo::NoLayout && isManaged(widget))
482 return widget;
483
484 widget = parentWidget;
485 }
486
487 return mainContainer();
488}
489
490static inline void clearObjectInspectorSelection(const QDesignerFormEditorInterface *core)
491{
492 if (auto *oi = qobject_cast<QDesignerObjectInspector *>(core->objectInspector()))
493 oi->clearSelection();
494}
495
496// Find a parent of a desired selection state
497static QWidget *findSelectedParent(QDesignerFormWindowInterface *fw, const QWidget *w, bool selected)
498{
499 const QDesignerFormWindowCursorInterface *cursor = fw->cursor();
500 QWidget *mainContainer = fw->mainContainer();
501 for (QWidget *p = w->parentWidget(); p && p != mainContainer; p = p->parentWidget())
502 if (fw->isManaged(p))
503 if (cursor->isWidgetSelected(p) == selected)
504 return p;
505 return nullptr;
506}
507
508// Mouse modifiers.
509
511
512static inline unsigned mouseFlags(Qt::KeyboardModifiers mod)
513{
514 switch (mod) {
515 case Qt::ShiftModifier:
516 return CycleParentModifier;
517 break;
518#ifdef Q_OS_MACOS
519 case Qt::AltModifier: // "Alt" or "option" key on Mac means copy
520 return CopyDragModifier;
521#endif
522 case Qt::ControlModifier:
524 break;
525 default:
526 break;
527 }
528 return 0;
529}
530
531// Handle the click selection: Do toggling/cycling
532// of parents according to the modifiers.
533void FormWindow::handleClickSelection(QWidget *managedWidget, unsigned mouseMode)
534{
535 const bool sameWidget = managedWidget == m_lastClickedWidget;
536 m_lastClickedWidget = managedWidget;
537
538 const bool selected = isWidgetSelected(managedWidget);
539 if (debugFormWindow)
540 qDebug() << "handleClickSelection" << managedWidget << " same=" << sameWidget << " mouse= " << mouseMode << " selected=" << selected;
541
542 // // toggle selection state of widget
543 if (mouseMode & ToggleSelectionModifier) {
544 selectWidget(managedWidget, !selected);
545 return;
546 }
547
548 QWidget *selectionCandidate = nullptr;
549 // Hierarchy cycling: If the same widget clicked again: Attempt to cycle
550 // trough the hierarchy. Find the next currently selected parent
551 if (sameWidget && (mouseMode & CycleParentModifier))
552 if (QWidget *currentlySelectedParent = selected ? managedWidget : findSelectedParent(this, managedWidget, true))
553 selectionCandidate = findSelectedParent(this, currentlySelectedParent, false);
554 // Not the same widget, list wrapped over or there was no unselected parent
555 if (!selectionCandidate && !selected)
556 selectionCandidate = managedWidget;
557
558 if (selectionCandidate)
559 selectSingleWidget(selectionCandidate);
560}
561
562void FormWindow::selectSingleWidget(QWidget *w)
563{
564 clearSelection(false);
565 selectWidget(w, true);
567}
568
569bool FormWindow::handleMousePressEvent(QWidget * widget, QWidget *managedWidget, QMouseEvent *e)
570{
571 m_mouseState = NoMouseState;
572 m_startPos = QPoint();
573 e->accept();
574
575 BlockSelection blocker(this);
576
577 if (core()->formWindowManager()->activeFormWindow() != this)
578 core()->formWindowManager()->setActiveFormWindow(this);
579
580 const Qt::MouseButtons buttons = e->buttons();
581 if (buttons != Qt::LeftButton && buttons != Qt::MiddleButton)
582 return true;
583
584 m_startPos = mapFromGlobal(e->globalPosition().toPoint());
585
586 if (debugFormWindow)
587 qDebug() << "handleMousePressEvent:" << widget << ',' << managedWidget;
588
589 if (buttons == Qt::MiddleButton || isMainContainer(managedWidget)) { // press was on the formwindow
590 clearObjectInspectorSelection(m_core); // We might have a toolbar or non-widget selected in the object inspector.
591 clearSelection(false);
592
593 m_mouseState = MouseDrawRubber;
594 m_currRect = QRect();
595 startRectDraw(mapFromGlobal(e->globalPosition().toPoint()), this, Rubber);
596 return true;
597 }
598 if (buttons != Qt::LeftButton)
599 return true;
600
601 const unsigned mouseMode = mouseFlags(e->modifiers());
602
603 /* Normally, we want to be able to click /select-on-press to drag away
604 * the widget in the next step. However, in the case of a widget which
605 * itself or whose parent is selected, we defer the selection to the
606 * release event.
607 * This is to prevent children from being dragged away from layouts
608 * when their layouts are selected and one wants to move the layout.
609 * Note that toggle selection is only deferred if the widget is already
610 * selected, so, it is still possible to just Ctrl+Click and CopyDrag. */
611 const bool deferSelection = isWidgetSelected(managedWidget) || findSelectedParent(this, managedWidget, true);
612 if (deferSelection) {
613 m_mouseState = MouseDeferredSelection;
614 } else {
615 // Cycle the parent unless we explicitly want toggle
616 const unsigned effectiveMouseMode = (mouseMode & ToggleSelectionModifier) ? mouseMode : static_cast<unsigned>(CycleParentModifier);
617 handleClickSelection(managedWidget, effectiveMouseMode);
618 }
619 return true;
620}
621
622// We can drag widget in managed layouts except splitter.
623static bool canDragWidgetInLayout(const QDesignerFormEditorInterface *core, QWidget *w)
624{
625 bool managed = false;
626 const LayoutInfo::Type type = LayoutInfo::laidoutWidgetType(core ,w, &managed);
627 if (!managed)
628 return false;
629 switch (type) {
630 case LayoutInfo::NoLayout:
631 case LayoutInfo::HSplitter:
632 case LayoutInfo::VSplitter:
633 return false;
634 default:
635 break;
636 }
637 return true;
638}
639
640bool FormWindow::handleMouseMoveEvent(QWidget *, QWidget *, QMouseEvent *e)
641{
642 e->accept();
643 if (m_startPos.isNull())
644 return true;
645
646 const QPoint pos = mapFromGlobal(e->globalPosition().toPoint());
647
648 switch (m_mouseState) {
649 case MouseDrawRubber: // Rubber band with left/middle mouse
650 continueRectDraw(pos, this, Rubber);
651 return true;
652 case MouseMoveDrag: // Spurious move event after drag started?
653 return true;
654 default:
655 break;
656 }
657
658 if (e->buttons() != Qt::LeftButton)
659 return true;
660
661 const bool canStartDrag = (m_startPos - pos).manhattanLength() > QApplication::startDragDistance();
662
663 if (!canStartDrag) // nothing to do
664 return true;
665
666 m_mouseState = MouseMoveDrag;
667 const bool blocked = blockSelectionChanged(true);
668
669 QWidgetList sel = selectedWidgets();
670 const QWidgetList originalSelection = sel;
671 simplifySelection(&sel);
672
673 QSet<QWidget*> widget_set;
674
675 for (QWidget *child : std::as_const(sel)) { // Move parent layout or container?
676 QWidget *current = child;
677
678 bool done = false;
679 while (!isMainContainer(current) && !done) {
680 if (!isManaged(current)) {
681 current = current->parentWidget();
682 continue;
683 }
684 if (LayoutInfo::isWidgetLaidout(core(), current)) {
685 // Go up to parent of layout if shift pressed, else do that only for splitters
686 if (!canDragWidgetInLayout(core(), current)) {
687 current = current->parentWidget();
688 continue;
689 }
690 }
691 done = true;
692 }
693
694 if (current == mainContainer())
695 continue;
696
697 widget_set.insert(current);
698 }
699
700 sel = widget_set.values();
701 QDesignerFormWindowCursorInterface *c = cursor();
702 QWidget *current = c->current();
703 if (sel.contains(current)) {
704 sel.removeAll(current);
705 sel.prepend(current);
706 }
707
708 QList<QDesignerDnDItemInterface*> item_list;
709 const QPoint globalPos = mapToGlobal(m_startPos);
710 const QDesignerDnDItemInterface::DropType dropType = (mouseFlags(e->modifiers()) & CopyDragModifier) ?
711 QDesignerDnDItemInterface::CopyDrop : QDesignerDnDItemInterface::MoveDrop;
712 for (QWidget *widget : std::as_const(sel)) {
713 item_list.append(new FormWindowDnDItem(dropType, this, widget, globalPos));
714 if (dropType == QDesignerDnDItemInterface::MoveDrop) {
715 m_selection->hide(widget);
716 widget->hide();
717 }
718 }
719
720 // In case when we have reduced the selection (by calling simplifySelection()
721 // beforehand) we still need to hide selection handles for children widgets
722 for (auto *widget : originalSelection)
723 m_selection->hide(widget);
724
726
727 if (!sel.isEmpty()) // reshow selection?
728 if (QDesignerMimeData::execDrag(item_list, core()->topLevel()) == Qt::IgnoreAction && dropType == QDesignerDnDItemInterface::MoveDrop)
729 for (QWidget *widget : std::as_const(sel))
730 m_selection->show(widget);
731
732 m_startPos = QPoint();
733
734 return true;
735}
736
737bool FormWindow::handleMouseReleaseEvent(QWidget *w, QWidget *mw, QMouseEvent *e)
738{
739 const MouseState oldState = m_mouseState;
740 m_mouseState = NoMouseState;
741
742 if (debugFormWindow)
743 qDebug() << "handleMouseeleaseEvent:" << w << ',' << mw << "state=" << oldState;
744
745 if (oldState == MouseDoubleClicked)
746 return true;
747
748 e->accept();
749
750 switch (oldState) {
751 case MouseDrawRubber: { // we were drawing a rubber selection
752 endRectDraw(); // get rid of the rectangle
753 const bool blocked = blockSelectionChanged(true);
754 selectWidgets(); // select widgets which intersect the rect
756 }
757 break;
758 // Deferred select: Select the child here unless the parent was moved.
759 case MouseDeferredSelection:
760 handleClickSelection(mw, mouseFlags(e->modifiers()));
761 break;
762 default:
763 break;
764 }
765
766 m_startPos = QPoint();
767
768 /* Inform about selection changes (left/mid or context menu). Also triggers
769 * in the case of an empty rubber drag that cleared the selection in
770 * MousePressEvent. */
771 switch (e->button()) {
772 case Qt::LeftButton:
773 case Qt::MiddleButton:
774 case Qt::RightButton:
776 break;
777 default:
778 break;
779 }
780
781 return true;
782}
783
784void FormWindow::checkPreviewGeometry(QRect &r)
785{
786 if (!rect().contains(r)) {
787 if (r.left() < rect().left())
788 r.moveTopLeft(QPoint(0, r.top()));
789 if (r.right() > rect().right())
790 r.moveBottomRight(QPoint(rect().right(), r.bottom()));
791 if (r.top() < rect().top())
792 r.moveTopLeft(QPoint(r.left(), rect().top()));
793 if (r.bottom() > rect().bottom())
794 r.moveBottomRight(QPoint(r.right(), rect().bottom()));
795 }
796}
797
798void FormWindow::startRectDraw(QPoint pos, QWidget *, RectType t)
799{
800 m_rectAnchor = (t == Insert) ? designerGrid().snapPoint(pos) : pos;
801
802 m_currRect = QRect(m_rectAnchor, QSize(0, 0));
803 if (!m_rubberBand)
804 m_rubberBand = new QRubberBand(QRubberBand::Rectangle, this);
805 m_rubberBand->setGeometry(m_currRect);
806 m_rubberBand->show();
807}
808
809void FormWindow::continueRectDraw(QPoint pos, QWidget *, RectType t)
810{
811 const QPoint p2 = (t == Insert) ? designerGrid().snapPoint(pos) : pos;
812
813 QRect r(m_rectAnchor, p2);
814 r = r.normalized();
815
816 if (m_currRect == r)
817 return;
818
819 if (r.width() > 1 || r.height() > 1) {
820 m_currRect = r;
821 if (m_rubberBand)
822 m_rubberBand->setGeometry(m_currRect);
823 }
824}
825
826void FormWindow::endRectDraw()
827{
828 if (m_rubberBand) {
829 delete m_rubberBand;
830 m_rubberBand = nullptr;
831 }
832}
833
835{
836 return m_currentWidget;
837}
838
839bool FormWindow::setCurrentWidget(QWidget *currentWidget)
840{
841 if (debugFormWindow)
842 qDebug() << "setCurrentWidget:" << m_currentWidget << " --> " << currentWidget;
843 if (currentWidget == m_currentWidget)
844 return false;
845 // repaint the old widget unless it is the main window
846 if (m_currentWidget && m_currentWidget != mainContainer()) {
847 m_selection->repaintSelection(m_currentWidget);
848 }
849 // set new and repaint
850 m_currentWidget = currentWidget;
851 if (m_currentWidget && m_currentWidget != mainContainer()) {
852 m_selection->repaintSelection(m_currentWidget);
853 }
854 return true;
855}
856
857void FormWindow::selectWidget(QWidget* w, bool select)
858{
859 if (trySelectWidget(w, select))
861}
862
863// Selects a widget and determines the new current one. Returns true if a change occurs.
864bool FormWindow::trySelectWidget(QWidget *w, bool select)
865{
866 if (debugFormWindow)
867 qDebug() << "trySelectWidget:" << w << select;
868 if (!isManaged(w) && !isCentralWidget(w))
869 return false;
870
871 if (!select && !isWidgetSelected(w))
872 return false;
873
874 if (!mainContainer())
875 return false;
876
877 if (isMainContainer(w) || isCentralWidget(w)) {
878 setCurrentWidget(mainContainer());
879 return true;
880 }
881
882 if (select) {
883 setCurrentWidget(w);
884 m_selection->addWidget(this, w);
885 } else {
886 QWidget *newCurrent = m_selection->removeWidget(w);
887 if (!newCurrent)
888 newCurrent = mainContainer();
889 setCurrentWidget(newCurrent);
890 }
891 return true;
892}
893
894void FormWindow::clearSelection(bool changePropertyDisplay)
895{
896 if (debugFormWindow)
897 qDebug() << "clearSelection(" << changePropertyDisplay << ')';
898 // At all events, we need a current widget.
899 m_selection->clear();
900 setCurrentWidget(mainContainer());
901
902 if (changePropertyDisplay)
904}
905
907{
908 if (m_blockSelectionChanged) // nothing to do
909 return;
910
911 m_selectionChangedTimer->start(0);
912}
913
914void FormWindow::selectionChangedTimerDone()
915{
916 emit selectionChanged();
917}
918
920{
921 return m_selection->isWidgetSelected(w);
922}
923
924bool FormWindow::isMainContainer(const QWidget *w) const
925{
926 return w && (w == this || w == mainContainer());
927}
928
930{
931 const QWidgetList l = w->findChildren<QWidget*>();
932 for (auto *w : l) {
933 if (isManaged(w))
934 updateSelection(w);
935 }
936}
937
939{
940 m_selection->repaintSelection();
941}
942
944{
945 m_selection->raiseWidget(w);
946}
947
949{
950 if (!w->isVisibleTo(this)) {
951 selectWidget(w, false);
952 } else {
953 m_selection->updateGeometry(w);
954 }
955}
956
958{
959 while ((w && !isMainContainer(w) && !isManaged(w)) || isCentralWidget(w))
960 w = w->parentWidget();
961
962 return w;
963}
964
965bool FormWindow::isCentralWidget(QWidget *w) const
966{
967 if (auto *mainWindow = qobject_cast<QMainWindow*>(mainContainer()))
968 return w == mainWindow->centralWidget();
969
970 return false;
971}
972
973void FormWindow::ensureUniqueObjectName(QObject *object)
974{
975 QString name = object->objectName();
976 if (name.isEmpty()) {
977 QDesignerWidgetDataBaseInterface *db = core()->widgetDataBase();
978 if (QDesignerWidgetDataBaseItemInterface *item = db->item(db->indexOfObject(object)))
979 name = qdesigner_internal::qtify(item->name());
980 }
981 unify(object, name, true);
982 object->setObjectName(name);
983}
984
985template <class Iterator>
986static inline void insertNames(const QDesignerMetaDataBaseInterface *metaDataBase,
987 Iterator it, const Iterator &end,
988 QObject *excludedObject, QSet<QString> &nameSet)
989{
990 for ( ; it != end; ++it)
991 if (excludedObject != *it && metaDataBase->item(*it))
992 nameSet.insert((*it)->objectName());
993}
994
996{
997 static const QSet<QString> keywords = {
998 // C++ keywords
999 u"asm"_s,
1000 u"assert"_s,
1001 u"auto"_s,
1002 u"bool"_s,
1003 u"break"_s,
1004 u"case"_s,
1005 u"catch"_s,
1006 u"char"_s,
1007 u"class"_s,
1008 u"const"_s,
1009 u"const_cast"_s,
1010 u"continue"_s,
1011 u"default"_s,
1012 u"delete"_s,
1013 u"do"_s,
1014 u"double"_s,
1015 u"dynamic_cast"_s,
1016 u"else"_s,
1017 u"enum"_s,
1018 u"explicit"_s,
1019 u"export"_s,
1020 u"extern"_s,
1021 u"false"_s,
1022 u"final"_s,
1023 u"float"_s,
1024 u"for"_s,
1025 u"friend"_s,
1026 u"goto"_s,
1027 u"if"_s,
1028 u"inline"_s,
1029 u"int"_s,
1030 u"long"_s,
1031 u"mutable"_s,
1032 u"namespace"_s,
1033 u"new"_s,
1034 u"noexcept"_s,
1035 u"NULL"_s,
1036 u"nullptr"_s,
1037 u"operator"_s,
1038 u"override"_s,
1039 u"private"_s,
1040 u"protected"_s,
1041 u"public"_s,
1042 u"register"_s,
1043 u"reinterpret_cast"_s,
1044 u"return"_s,
1045 u"short"_s,
1046 u"signed"_s,
1047 u"sizeof"_s,
1048 u"static"_s,
1049 u"static_cast"_s,
1050 u"struct"_s,
1051 u"switch"_s,
1052 u"template"_s,
1053 u"this"_s,
1054 u"throw"_s,
1055 u"true"_s,
1056 u"try"_s,
1057 u"typedef"_s,
1058 u"typeid"_s,
1059 u"typename"_s,
1060 u"union"_s,
1061 u"unsigned"_s,
1062 u"using"_s,
1063 u"virtual"_s,
1064 u"void"_s,
1065 u"volatile"_s,
1066 u"wchar_t"_s,
1067 u"while"_s,
1068
1069 // java keywords
1070 u"abstract"_s,
1071 u"boolean"_s,
1072 u"byte"_s,
1073 u"extends"_s,
1074 u"finality"_s,
1075 u"implements"_s,
1076 u"import"_s,
1077 u"instanceof"_s,
1078 u"interface"_s,
1079 u"native"_s,
1080 u"null"_s,
1081 u"package"_s,
1082 u"strictfp"_s,
1083 u"super"_s,
1084 u"synchronized"_s,
1085 u"throws"_s,
1086 u"transient"_s,
1087 };
1088
1089 return keywords;
1090}
1091
1092bool FormWindow::unify(QObject *w, QString &s, bool changeIt)
1093{
1094 using StringSet = QSet<QString>;
1095
1096 QWidget *main = mainContainer();
1097 if (!main)
1098 return true;
1099
1100 StringSet existingNames = languageKeywords();
1101 // build a set of existing names of other widget excluding self
1102 if (!(w->isWidgetType() && isMainContainer(qobject_cast<QWidget*>(w))))
1103 existingNames.insert(main->objectName());
1104
1105 const QDesignerMetaDataBaseInterface *metaDataBase = core()->metaDataBase();
1106 const QWidgetList widgetChildren = main->findChildren<QWidget*>();
1107 if (!widgetChildren.isEmpty())
1108 insertNames(metaDataBase, widgetChildren.constBegin(), widgetChildren.constEnd(), w, existingNames);
1109
1110 const auto layoutChildren = main->findChildren<QLayout*>();
1111 if (!layoutChildren.isEmpty())
1112 insertNames(metaDataBase, layoutChildren.constBegin(), layoutChildren.constEnd(), w, existingNames);
1113
1114 const auto actionChildren = main->findChildren<QAction*>();
1115 if (!actionChildren.isEmpty())
1116 insertNames(metaDataBase, actionChildren.constBegin(), actionChildren.constEnd(), w, existingNames);
1117
1118 const auto buttonGroupChildren = main->findChildren<QButtonGroup*>();
1119 if (!buttonGroupChildren.isEmpty())
1120 insertNames(metaDataBase, buttonGroupChildren.constBegin(), buttonGroupChildren.constEnd(), w, existingNames);
1121
1122 if (!existingNames.contains(s))
1123 return true;
1124 if (!changeIt)
1125 return false;
1126
1127 // split 'name_number'
1128 qlonglong num = 0;
1129 qlonglong factor = 1;
1130 qsizetype idx = s.size() - 1;
1131 const char16_t zeroUnicode = u'0';
1132 for ( ; idx > 0 && s.at(idx).isDigit(); --idx) {
1133 num += (s.at(idx).unicode() - zeroUnicode) * factor;
1134 factor *= 10;
1135 }
1136 // Position index past '_'.
1137 const QChar underscore = u'_';
1138 if (idx >= 0 && s.at(idx) == underscore) {
1139 idx++;
1140 } else {
1141 num = 1;
1142 s += underscore;
1143 idx = s.size();
1144 }
1145 // try 'name_n', 'name_n+1'
1146 for (num++ ; ;num++) {
1147 s.truncate(idx);
1148 s += QString::number(num);
1149 if (!existingNames.contains(s))
1150 break;
1151 }
1152 return false;
1153}
1154/* already_in_form is true when we are moving a widget from one parent to another inside the same
1155 * form. All this means is that InsertWidgetCommand::undo() must not unmanage it. */
1156
1157void FormWindow::insertWidget(QWidget *w, QRect rect, QWidget *container, bool already_in_form)
1158{
1159 clearSelection(false);
1160
1161 beginCommand(tr("Insert widget '%1'").arg(WidgetFactory::classNameOf(m_core, w))); // ### use the WidgetDatabaseItem
1162
1163 /* Reparenting into a QSplitter automatically adjusts child's geometry. We create the geometry
1164 * command before we push the reparent command, so that the geometry command has the original
1165 * geometry of the widget. */
1166 QRect r = rect;
1167 Q_ASSERT(r.isValid());
1168 auto *geom_cmd = new SetPropertyCommand(this);
1169 geom_cmd->init(w, u"geometry"_s, r); // ### use rc.size()
1170
1171 if (w->parentWidget() != container) {
1172 auto *cmd = new ReparentWidgetCommand(this);
1173 cmd->init(w, container);
1174 m_undoStack.push(cmd);
1175 }
1176
1177 m_undoStack.push(geom_cmd);
1178
1179 QUndoCommand *cmd = nullptr;
1180 if (auto *dockWidget = qobject_cast<QDockWidget *>(w)) {
1181 if (auto *mainWindow = qobject_cast<QMainWindow *>(container)) {
1182 auto *addDockCmd = new AddDockWidgetCommand(this);
1183 addDockCmd->init(mainWindow, dockWidget);
1184 cmd = addDockCmd;
1185 }
1186 }
1187 if (cmd == nullptr) {
1188 auto *insertCmd = new InsertWidgetCommand(this);
1189 insertCmd->init(w, already_in_form);
1190 cmd = insertCmd;
1191 }
1192 m_undoStack.push(cmd);
1193
1195
1196 w->show();
1197}
1198
1199QWidget *FormWindow::createWidget(DomUI *ui, QRect rc, QWidget *target)
1200{
1201 QWidget *container = findContainer(target, false);
1202 if (!container)
1203 return nullptr;
1204 if (isMainContainer(container)) {
1205 if (auto *mw = qobject_cast<QMainWindow*>(container)) {
1206 Q_ASSERT(mw->centralWidget() != nullptr);
1207 container = mw->centralWidget();
1208 }
1209 }
1210 QDesignerResource resource(this);
1211 const FormBuilderClipboard clipboard = resource.paste(ui, container);
1212 if (clipboard.m_widgets.size() != 1) // multiple-paste from DomUI not supported yet
1213 return nullptr;
1214 QWidget *widget = clipboard.m_widgets.first();
1215 insertWidget(widget, rc, container);
1216 return widget;
1217}
1218
1219static bool isDescendant(const QWidget *parent, const QWidget *child)
1220{
1221 for (; child != nullptr; child = child->parentWidget()) {
1222 if (child == parent)
1223 return true;
1224 }
1225 return false;
1226}
1227
1228void FormWindow::resizeWidget(QWidget *widget, QRect geometry)
1229{
1230 Q_ASSERT(isDescendant(this, widget));
1231
1232 QRect r = geometry;
1233 auto *cmd = new SetPropertyCommand(this);
1234 cmd->init(widget, u"geometry"_s, r);
1235 cmd->setText(tr("Resize"));
1236 m_undoStack.push(cmd);
1237}
1238
1240{
1241 const QWidgetList l = w->findChildren<QWidget*>();
1242 if (l.isEmpty())
1243 return;
1244 m_selection->raiseList(l);
1245}
1246
1247QWidget *FormWindow::containerAt(QPoint pos, QWidget *notParentOf)
1248{
1249 QWidget *container = nullptr;
1250 int depth = -1;
1251 const QWidgetList selected = selectedWidgets();
1252 if (rect().contains(mapFromGlobal(pos))) {
1253 container = mainContainer();
1254 depth = widgetDepth(container);
1255 }
1256
1257 for (QWidget *wit : std::as_const(m_widgets)) {
1258 if (qobject_cast<QLayoutWidget*>(wit) || qobject_cast<QSplitter*>(wit))
1259 continue;
1260 if (!wit->isVisibleTo(this))
1261 continue;
1262 if (selected.indexOf(wit) != -1)
1263 continue;
1264 if (!core()->widgetDataBase()->isContainer(wit) &&
1265 wit != mainContainer())
1266 continue;
1267
1268 // the rectangles of all ancestors of the container must contain the insert position
1269 QWidget *w = wit;
1270 while (w && !w->isWindow()) {
1271 if (!w->rect().contains((w->mapFromGlobal(pos))))
1272 break;
1273 w = w->parentWidget();
1274 }
1275 if (!(w == nullptr || w->isWindow()))
1276 continue; // we did not get through the full while loop
1277
1278 int wd = widgetDepth(wit);
1279 if (wd == depth && container) {
1280 if (wit->parentWidget()->children().indexOf(wit) >
1281 container->parentWidget()->children().indexOf(container))
1282 wd++;
1283 }
1284 if (wd > depth && !isChildOf(wit, notParentOf)) {
1285 depth = wd;
1286 container = wit;
1287 }
1288 }
1289 return container;
1290}
1291
1293{
1294 return m_selection->selectedWidgets();
1295}
1296
1298{
1299 bool selectionChanged = false;
1300 const QWidgetList l = mainContainer()->findChildren<QWidget*>();
1301 const QRect selRect(mapToGlobal(m_currRect.topLeft()), m_currRect.size());
1302 for (QWidget *w : l) {
1303 if (w->isVisibleTo(this) && isManaged(w)) {
1304 const QPoint p = w->mapToGlobal(QPoint(0,0));
1305 const QRect r(p, w->size());
1306 if (r.intersects(selRect) && !r.contains(selRect) && trySelectWidget(w, true))
1307 selectionChanged = true;
1308 }
1309 }
1310
1311 if (selectionChanged)
1313}
1314
1315bool FormWindow::handleKeyPressEvent(QWidget *widget, QWidget *, QKeyEvent *e)
1316{
1317 if (qobject_cast<const FormWindow*>(widget) || qobject_cast<const QMenu*>(widget))
1318 return false;
1319
1320 e->accept(); // we always accept!
1321
1322 switch (e->key()) {
1323 default: break; // we don't care about the other keys
1324
1325 case Qt::Key_Delete:
1326 case Qt::Key_Backspace:
1327 if (e->modifiers() == Qt::NoModifier)
1328 deleteWidgets();
1329 break;
1330
1331 case Qt::Key_Tab:
1332 if (e->modifiers() == Qt::NoModifier)
1333 cursor()->movePosition(QDesignerFormWindowCursorInterface::Next);
1334 break;
1335
1336 case Qt::Key_Backtab:
1337 if (e->modifiers() == Qt::NoModifier)
1338 cursor()->movePosition(QDesignerFormWindowCursorInterface::Prev);
1339 break;
1340
1341 case Qt::Key_Left:
1342 case Qt::Key_Right:
1343 case Qt::Key_Up:
1344 case Qt::Key_Down:
1345 handleArrowKeyEvent(e->key(), e->modifiers());
1346 break;
1347 }
1348
1349 return true;
1350}
1351
1352int FormWindow::getValue(QRect rect, int key, bool size) const
1353{
1354 if (size) {
1355 if (key == Qt::Key_Left || key == Qt::Key_Right)
1356 return rect.width();
1357 return rect.height();
1358 }
1359 if (key == Qt::Key_Left || key == Qt::Key_Right)
1360 return rect.x();
1361 return rect.y();
1362}
1363
1364int FormWindow::calcValue(int val, bool forward, bool snap, int snapOffset) const
1365{
1366 if (snap) {
1367 const int rest = val % snapOffset;
1368 if (rest) {
1369 const int offset = forward ? snapOffset : 0;
1370 const int newOffset = rest < 0 ? offset - snapOffset : offset;
1371 return val + newOffset - rest;
1372 }
1373 return (forward ? val + snapOffset : val - snapOffset);
1374 }
1375 return (forward ? val + 1 : val - 1);
1376}
1377
1378// ArrowKeyOperation: Stores a keyboard move or resize (Shift pressed)
1379// operation.
1381{
1382 QRect apply(QRect rect) const;
1383
1384 bool resize = false; // Resize: Shift-Key->drag bottom/right corner, else just move
1385 int distance = 0;
1387};
1388
1389} // namespace
1390
1391QT_END_NAMESPACE
1392Q_DECLARE_METATYPE(qdesigner_internal::ArrowKeyOperation)
1393QT_BEGIN_NAMESPACE
1394
1395namespace qdesigner_internal {
1396
1397QRect ArrowKeyOperation::apply(QRect rect) const
1398{
1399 QRect r = rect;
1400 if (resize) {
1401 if (arrowKey == Qt::Key_Left || arrowKey == Qt::Key_Right)
1402 r.setWidth(r.width() + distance);
1403 else
1404 r.setHeight(r.height() + distance);
1405 } else {
1406 if (arrowKey == Qt::Key_Left || arrowKey == Qt::Key_Right)
1407 r.moveLeft(r.x() + distance);
1408 else
1409 r.moveTop(r.y() + distance);
1410 }
1411 return r;
1412}
1413
1414QDebug operator<<(QDebug in, ArrowKeyOperation op)
1415{
1416 in.nospace() << "Resize=" << op.resize << " dist=" << op.distance << " Key=" << op.arrowKey << ' ';
1417 return in;
1418}
1419
1420// ArrowKeyPropertyHelper: Applies a struct ArrowKeyOperation
1421// (stored as new value) to a list of widgets using to calculate the
1422// changed geometry of the widget in setValue(). Thus, the 'newValue'
1423// of the property command is the relative move distance, which is the same
1424// for all widgets (although resulting in different geometries for the widgets).
1425// The command merging can then work as it would when applying the same text
1426// to all QLabels.
1427
1428class ArrowKeyPropertyHelper : public PropertyHelper {
1429public:
1430 ArrowKeyPropertyHelper(QObject* o, SpecialProperty sp,
1431 QDesignerPropertySheetExtension *s, int i) :
1432 PropertyHelper(o, sp, s, i) {}
1433
1434 Value setValue(QDesignerFormWindowInterface *fw, const QVariant &value, bool changed,
1435 quint64 subPropertyMask) override;
1436};
1437
1438PropertyHelper::Value ArrowKeyPropertyHelper::setValue(QDesignerFormWindowInterface *fw, const QVariant &value,
1439 bool changed, quint64 subPropertyMask)
1440{
1441 // Apply operation to obtain the new geometry value.
1442 auto *w = qobject_cast<QWidget*>(object());
1443 const auto operation = qvariant_cast<ArrowKeyOperation>(value);
1444 const QRect newGeom = operation.apply(w->geometry());
1445 return PropertyHelper::setValue(fw, QVariant(newGeom), changed, subPropertyMask);
1446}
1447
1448// ArrowKeyPropertyCommand: Helper factory overwritten to create
1449// ArrowKeyPropertyHelper and a merge operation that merges values of
1450// the same direction.
1451class ArrowKeyPropertyCommand: public SetPropertyCommand {
1452public:
1453 explicit ArrowKeyPropertyCommand(QDesignerFormWindowInterface *fw,
1454 QUndoCommand *p = nullptr);
1455
1456 void init(QWidgetList &l, ArrowKeyOperation op);
1457
1458protected:
1459 std::unique_ptr<PropertyHelper>
1460 createPropertyHelper(QObject *o, SpecialProperty sp,
1461 QDesignerPropertySheetExtension *s, int i) const override
1462 { return std::make_unique<ArrowKeyPropertyHelper>(o, sp, s, i); }
1463 QVariant mergeValue(const QVariant &newValue) override;
1464};
1465
1466ArrowKeyPropertyCommand::ArrowKeyPropertyCommand(QDesignerFormWindowInterface *fw,
1467 QUndoCommand *p) :
1468 SetPropertyCommand(fw, p)
1469{
1470 static const int mid = qRegisterMetaType<qdesigner_internal::ArrowKeyOperation>();
1471 Q_UNUSED(mid);
1472}
1473
1474void ArrowKeyPropertyCommand::init(QWidgetList &l, ArrowKeyOperation op)
1475{
1476 QObjectList ol;
1477 for (QWidget *w : std::as_const(l))
1478 ol.push_back(w);
1479 SetPropertyCommand::init(ol, u"geometry"_s, QVariant::fromValue(op));
1480
1481 setText(op.resize ? FormWindow::tr("Key Resize") : FormWindow::tr("Key Move"));
1482}
1483
1484QVariant ArrowKeyPropertyCommand::mergeValue(const QVariant &newMergeValue)
1485{
1486 // Merge move operations of the same arrow key
1487 if (!newMergeValue.canConvert<ArrowKeyOperation>())
1488 return {};
1489 auto mergedOperation = qvariant_cast<ArrowKeyOperation>(newValue());
1490 const auto newMergeOperation = qvariant_cast<ArrowKeyOperation>(newMergeValue);
1491 if (mergedOperation.resize != newMergeOperation.resize || mergedOperation.arrowKey != newMergeOperation.arrowKey)
1492 return {};
1493 mergedOperation.distance += newMergeOperation.distance;
1494 return QVariant::fromValue(mergedOperation);
1495}
1496
1497void FormWindow::handleArrowKeyEvent(int key, Qt::KeyboardModifiers modifiers)
1498{
1499 const QDesignerFormWindowCursorInterface *c = cursor();
1500 if (!c->hasSelection())
1501 return;
1502
1503 QWidgetList selection;
1504
1505 // check if a laid out widget is selected
1506 const int count = c->selectedWidgetCount();
1507 for (int index = 0; index < count; ++index) {
1508 QWidget *w = c->selectedWidget(index);
1509 if (!LayoutInfo::isWidgetLaidout(m_core, w))
1510 selection.append(w);
1511 }
1512
1513 simplifySelection(&selection);
1514
1515 if (selection.isEmpty())
1516 return;
1517
1518 QWidget *current = c->current();
1519 if (!current || LayoutInfo::isWidgetLaidout(m_core, current)) {
1520 current = selection.first();
1521 }
1522
1523 const bool size = modifiers & Qt::ShiftModifier;
1524
1525 const bool snap = !(modifiers & Qt::ControlModifier);
1526 const bool forward = (key == Qt::Key_Right || key == Qt::Key_Down);
1527 const int snapPoint = (key == Qt::Key_Left || key == Qt::Key_Right) ? grid().x() : grid().y();
1528
1529 const int oldValue = getValue(current->geometry(), key, size);
1530
1531 const int newValue = calcValue(oldValue, forward, snap, snapPoint);
1532
1533 ArrowKeyOperation operation;
1534 operation.resize = modifiers & Qt::ShiftModifier;
1535 operation.distance = newValue - oldValue;
1536 operation.arrowKey = key;
1537
1538 auto *cmd = new ArrowKeyPropertyCommand(this);
1539 cmd->init(selection, operation);
1540 m_undoStack.push(cmd);
1541}
1542
1543bool FormWindow::handleKeyReleaseEvent(QWidget *, QWidget *, QKeyEvent *e)
1544{
1545 e->accept();
1546 return true;
1547}
1548
1549void FormWindow::selectAll()
1550{
1551 bool selectionChanged = false;
1552 for (QWidget *widget : std::as_const(m_widgets)) {
1553 if (widget->isVisibleTo(this) && trySelectWidget(widget, true))
1554 selectionChanged = true;
1555 }
1556 if (selectionChanged)
1557 emitSelectionChanged();
1558}
1559
1560void FormWindow::createLayout(int type, QWidget *container)
1561{
1562 if (container) {
1563 layoutContainer(container, type);
1564 } else {
1565 auto *cmd = new LayoutCommand(this);
1566 cmd->init(mainContainer(), selectedWidgets(), static_cast<LayoutInfo::Type>(type));
1567 commandHistory()->push(cmd);
1568 }
1569}
1570
1571void FormWindow::morphLayout(QWidget *container, int newType)
1572{
1573 auto *cmd = new MorphLayoutCommand(this);
1574 if (cmd->init(container, newType)) {
1575 commandHistory()->push(cmd);
1576 } else {
1577 qDebug() << "** WARNING Unable to morph layout.";
1578 delete cmd;
1579 }
1580}
1581
1582void FormWindow::deleteWidgets()
1583{
1584 QWidgetList selection = selectedWidgets();
1585 simplifySelection(&selection);
1586
1587 deleteWidgetList(selection);
1588}
1589
1590QString FormWindow::fileName() const
1591{
1592 return m_fileName;
1593}
1594
1595void FormWindow::setFileName(const QString &fileName)
1596{
1597 if (m_fileName == fileName)
1598 return;
1599
1600 m_fileName = fileName;
1601 emit fileNameChanged(fileName);
1602}
1603
1604QString FormWindow::contents() const
1605{
1606 QBuffer b;
1607 if (!mainContainer() || !b.open(QIODevice::WriteOnly))
1608 return QString();
1609
1610 QDesignerResource resource(const_cast<FormWindow*>(this));
1611 resource.save(&b, mainContainer());
1612
1613 return QString::fromUtf8(b.buffer());
1614}
1615
1616#if QT_CONFIG(clipboard)
1617void FormWindow::copy()
1618{
1619 QBuffer b;
1620 if (!b.open(QIODevice::WriteOnly))
1621 return;
1622
1623 FormBuilderClipboard clipboard;
1624 QDesignerResource resource(this);
1625 resource.setSaveRelative(false);
1626 clipboard.m_widgets = selectedWidgets();
1627 simplifySelection(&clipboard.m_widgets);
1628 resource.copy(&b, clipboard);
1629
1630 qApp->clipboard()->setText(QString::fromUtf8(b.buffer()), QClipboard::Clipboard);
1631}
1632
1633void FormWindow::cut()
1634{
1635 copy();
1636 deleteWidgets();
1637}
1638
1639void FormWindow::paste()
1640{
1641 paste(PasteAll);
1642}
1643#endif
1644
1645// for cases like QMainWindow (central widget is an inner container) or QStackedWidget (page is an inner container)
1646QWidget *FormWindow::innerContainer(QWidget *outerContainer) const
1647{
1648 if (m_core->widgetDataBase()->isContainer(outerContainer))
1649 if (const QDesignerContainerExtension *container = qt_extension<QDesignerContainerExtension*>(m_core->extensionManager(), outerContainer)) {
1650 const int currentIndex = container->currentIndex();
1651 return currentIndex >= 0 ? container->widget(currentIndex) : nullptr;
1652 }
1653 return outerContainer;
1654}
1655
1656QWidget *FormWindow::containerForPaste() const
1657{
1658 QWidget *w = mainContainer();
1659 if (!w)
1660 return nullptr;
1661 do {
1662 // Try to find a close parent, for example a non-laid-out
1663 // QFrame/QGroupBox when a widget within it is selected.
1664 QWidgetList selection = selectedWidgets();
1665 if (selection.isEmpty())
1666 break;
1667 simplifySelection(&selection);
1668
1669 QWidget *containerOfW = findContainer(selection.first(), /* exclude layouts */ true);
1670 if (!containerOfW || containerOfW == mainContainer())
1671 break;
1672 // No layouts, must be container. No empty page-based containers.
1673 containerOfW = innerContainer(containerOfW);
1674 if (!containerOfW)
1675 break;
1676 if (LayoutInfo::layoutType(m_core, containerOfW) != LayoutInfo::NoLayout || !m_core->widgetDataBase()->isContainer(containerOfW))
1677 break;
1678 w = containerOfW;
1679 } while (false);
1680 // First check for layout (note that it does not cover QMainWindow
1681 // and the like as the central widget has the layout).
1682
1683 w = innerContainer(w);
1684 if (!w)
1685 return nullptr;
1686 if (LayoutInfo::layoutType(m_core, w) != LayoutInfo::NoLayout)
1687 return nullptr;
1688 // Go up via container extension (also includes step from QMainWindow to its central widget)
1689 w = m_core->widgetFactory()->containerOfWidget(w);
1690 if (w == nullptr || LayoutInfo::layoutType(m_core, w) != LayoutInfo::NoLayout)
1691 return nullptr;
1692
1693 if (debugFormWindow)
1694 qDebug() <<"containerForPaste() " << w;
1695 return w;
1696}
1697
1698#if QT_CONFIG(clipboard)
1699// Construct DomUI from clipboard (paste) and determine number of widgets/actions.
1700static inline DomUI *domUIFromClipboard(int *widgetCount, int *actionCount)
1701{
1702 *widgetCount = *actionCount = 0;
1703 const QString clipboardText = qApp->clipboard()->text();
1704 if (clipboardText.isEmpty() || clipboardText.indexOf(u'<') == -1)
1705 return nullptr;
1706
1707 QXmlStreamReader reader(clipboardText);
1708 DomUI *ui = nullptr;
1709 while (!reader.atEnd()) {
1710 if (reader.readNext() == QXmlStreamReader::StartElement) {
1711 if (reader.name().compare("ui"_L1, Qt::CaseInsensitive) == 0 && !ui) {
1712 ui = new DomUI();
1713 ui->read(reader);
1714 break;
1715 }
1716 reader.raiseError(QCoreApplication::translate("FormWindow", "Unexpected element <%1>").arg(reader.name().toString()));
1717 }
1718 }
1719 if (reader.hasError()) {
1720 delete ui;
1721 ui = nullptr;
1722 designerWarning(QCoreApplication::translate("FormWindow", "Error while pasting clipboard contents at line %1, column %2: %3").
1723 arg(reader.lineNumber()).arg(reader.columnNumber()).arg(reader.errorString()));
1724 return nullptr;
1725 }
1726
1727 if (const DomWidget *topLevel = ui->elementWidget()) {
1728 *widgetCount = topLevel->elementWidget().size();
1729 *actionCount = topLevel->elementAction().size();
1730 }
1731 if (*widgetCount == 0 && *actionCount == 0) {
1732 delete ui;
1733 return nullptr;
1734 }
1735 return ui;
1736}
1737#endif
1738
1739static inline QString pasteCommandDescription(int widgetCount, int actionCount)
1740{
1741 if (widgetCount == 0)
1742 return FormWindow::tr("Paste %n action(s)", nullptr, actionCount);
1743 if (actionCount == 0)
1744 return FormWindow::tr("Paste %n widget(s)", nullptr, widgetCount);
1745 return FormWindow::tr("Paste (%1 widgets, %2 actions)").arg(widgetCount).arg(actionCount);
1746}
1747
1748#if QT_CONFIG(clipboard)
1749static void positionPastedWidgetsAtMousePosition(FormWindow *fw, QPoint contextMenuPosition, QWidget *parent, const QWidgetList &l)
1750{
1751 // Try to position pasted widgets at mouse position (current mouse position for Ctrl-V or position of context menu)
1752 // if it fits. If it is completely outside, force it to 0,0
1753 // If it fails, the old coordinates relative to the previous parent will be used.
1754 QPoint currentPos = contextMenuPosition.x() >=0 ? parent->mapFrom(fw, contextMenuPosition) : parent->mapFromGlobal(QCursor::pos());
1755 const Grid &grid = fw->designerGrid();
1756 QPoint cursorPos = grid.snapPoint(currentPos);
1757 const QRect parentGeometry = QRect(QPoint(0, 0), parent->size());
1758 const bool outside = !parentGeometry.contains(cursorPos);
1759 if (outside)
1760 cursorPos = grid.snapPoint(QPoint(0, 0));
1761 // Determine area of pasted widgets
1762 QRect pasteArea;
1763 for (auto *w : l)
1764 pasteArea = pasteArea.isNull() ? w->geometry() : pasteArea.united(w->geometry());
1765
1766 // Mouse on some child? (try to position bottomRight on a free spot to
1767 // get the stacked-offset effect of Designer 4.3, that is, offset by grid if Ctrl-V is pressed continuously
1768 do {
1769 const QPoint bottomRight = cursorPos + QPoint(pasteArea.width(), pasteArea.height()) - QPoint(1, 1);
1770 if (bottomRight.y() > parentGeometry.bottom() || parent->childAt(bottomRight) == nullptr)
1771 break;
1772 cursorPos += QPoint(grid.deltaX(), grid.deltaY());
1773 } while (true);
1774 // Move.
1775 const QPoint offset = cursorPos - pasteArea.topLeft();
1776 for (auto *w : l)
1777 w->move(w->pos() + offset);
1778}
1779
1780void FormWindow::paste(PasteMode pasteMode)
1781{
1782 // Avoid QDesignerResource constructing widgets that are not used as
1783 // QDesignerResource manages the widgets it creates (creating havoc if one remains unused)
1784 DomUI *ui = nullptr;
1785 do {
1786 int widgetCount = 0;
1787 int actionCount = 0;
1788 ui = domUIFromClipboard(&widgetCount, &actionCount);
1789 if (!ui)
1790 break;
1791
1792 // Check for actions
1793 if (pasteMode == PasteActionsOnly)
1794 if (widgetCount != 0 || actionCount == 0)
1795 break;
1796
1797 // Check for widgets: need a container
1798 QWidget *pasteContainer = widgetCount ? containerForPaste() : nullptr;
1799 if (widgetCount && pasteContainer == nullptr) {
1800
1801 const QString message = tr("Cannot paste widgets. Designer could not find a container "
1802 "without a layout to paste into.");
1803 const QString infoMessage = tr("Break the layout of the "
1804 "container you want to paste into, select this container "
1805 "and then paste again.");
1806 core()->dialogGui()->message(this, QDesignerDialogGuiInterface::FormEditorMessage, QMessageBox::Information,
1807 tr("Paste error"), message, infoMessage, QMessageBox::Ok);
1808 break;
1809 }
1810
1811 QDesignerResource resource(this);
1812 // Note that the widget factory must be able to locate the
1813 // form window (us) via parent, otherwise, it will not able to construct QLayoutWidgets
1814 // (It will then default to widgets) among other issues.
1815 const FormBuilderClipboard clipboard = resource.paste(ui, pasteContainer, this);
1816
1817 clearSelection(false);
1818 // Create command sequence
1819 beginCommand(pasteCommandDescription(widgetCount, actionCount));
1820
1821 if (widgetCount) {
1822 positionPastedWidgetsAtMousePosition(this, m_contextMenuPosition, pasteContainer, clipboard.m_widgets);
1823 for (QWidget *w : clipboard.m_widgets) {
1824 auto *cmd = new InsertWidgetCommand(this);
1825 cmd->init(w);
1826 m_undoStack.push(cmd);
1827 selectWidget(w);
1828 }
1829 }
1830
1831 if (actionCount)
1832 for (QAction *a : clipboard.m_actions) {
1833 ensureUniqueObjectName(a);
1834 auto *cmd = new AddActionCommand(this);
1835 cmd->init(a);
1836 m_undoStack.push(cmd);
1837 }
1838 endCommand();
1839 } while (false);
1840 delete ui;
1841}
1842#endif
1843
1844// Draw a dotted frame around containers
1845bool FormWindow::frameNeeded(QWidget *w) const
1846{
1847 if (!core()->widgetDataBase()->isContainer(w))
1848 return false;
1849 if (qobject_cast<QGroupBox *>(w))
1850 return false;
1851 if (qobject_cast<QToolBox *>(w))
1852 return false;
1853 if (qobject_cast<QTabWidget *>(w))
1854 return false;
1855 if (qobject_cast<QStackedWidget *>(w))
1856 return false;
1857 if (qobject_cast<QDockWidget *>(w))
1858 return false;
1859 if (qobject_cast<QDesignerWidget *>(w))
1860 return false;
1861 if (qobject_cast<QMainWindow *>(w))
1862 return false;
1863 if (qobject_cast<QDialog *>(w))
1864 return false;
1865 if (qobject_cast<QLayoutWidget *>(w))
1866 return false;
1867 return true;
1868}
1869
1870bool FormWindow::eventFilter(QObject *watched, QEvent *event)
1871{
1872 const bool ret = FormWindowBase::eventFilter(watched, event);
1873 if (event->type() != QEvent::Paint)
1874 return ret;
1875
1876 Q_ASSERT(watched->isWidgetType());
1877 auto *w = static_cast<QWidget *>(watched);
1878 auto *pe = static_cast<QPaintEvent*>(event);
1879 const QRect widgetRect = w->rect();
1880 const QRect paintRect = pe->rect();
1881 // Does the paint rectangle touch the borders of the widget rectangle
1882 if (paintRect.x() > widgetRect.x() && paintRect.y() > widgetRect.y() &&
1883 paintRect.right() < widgetRect.right() && paintRect.bottom() < widgetRect.bottom())
1884 return ret;
1885 QPainter p(w);
1886 const QPen pen(QColor(0, 0, 0, 32), 0, Qt::DotLine);
1887 p.setPen(pen);
1888 p.setBrush(QBrush(Qt::NoBrush));
1889 p.drawRect(widgetRect.adjusted(0, 0, -1, -1));
1890 return ret;
1891}
1892
1893void FormWindow::manageWidget(QWidget *w)
1894{
1895 if (isManaged(w))
1896 return;
1897
1898 Q_ASSERT(qobject_cast<QMenu*>(w) == 0);
1899
1900 if (w->hasFocus())
1901 setFocus();
1902
1903 core()->metaDataBase()->add(w);
1904
1905 m_insertedWidgets.insert(w);
1906 m_widgets.append(w);
1907
1908#if QT_CONFIG(cursor)
1909 setCursorToAll(Qt::ArrowCursor, w);
1910#endif
1911
1912 emit changed();
1913 emit widgetManaged(w);
1914
1915 if (frameNeeded(w))
1916 w->installEventFilter(this);
1917}
1918
1919void FormWindow::unmanageWidget(QWidget *w)
1920{
1921 if (!isManaged(w))
1922 return;
1923
1924 m_selection->removeWidget(w);
1925
1926 emit aboutToUnmanageWidget(w);
1927
1928 if (w == m_currentWidget)
1929 setCurrentWidget(mainContainer());
1930
1931 core()->metaDataBase()->remove(w);
1932
1933 m_insertedWidgets.remove(w);
1934 m_widgets.removeAt(m_widgets.indexOf(w));
1935
1936 emit changed();
1937 emit widgetUnmanaged(w);
1938
1939 if (frameNeeded(w))
1940 w->removeEventFilter(this);
1941}
1942
1943bool FormWindow::isManaged(QWidget *w) const
1944{
1945 return m_insertedWidgets.contains(w);
1946}
1947
1948void FormWindow::breakLayout(QWidget *w)
1949{
1950 if (w == this)
1951 w = mainContainer();
1952 // Find the first-order managed child widgets
1953 QWidgetList widgets;
1954
1955 const QDesignerMetaDataBaseInterface *mdb = core()->metaDataBase();
1956 for (auto *o : w->children()) {
1957 if (o->isWidgetType()) {
1958 auto *w = static_cast<QWidget*>(o);
1959 if (mdb->item(w))
1960 widgets.push_back(w);
1961 }
1962 }
1963
1964 auto *cmd = new BreakLayoutCommand(this);
1965 cmd->init(widgets, w);
1966 commandHistory()->push(cmd);
1967 clearSelection(false);
1968}
1969
1970void FormWindow::beginCommand(const QString &description)
1971{
1972 m_undoStack.beginMacro(description);
1973}
1974
1975void FormWindow::endCommand()
1976{
1977 m_undoStack.endMacro();
1978}
1979
1980void FormWindow::raiseWidgets()
1981{
1982 QWidgetList widgets = selectedWidgets();
1983 simplifySelection(&widgets);
1984
1985 if (widgets.isEmpty())
1986 return;
1987
1988 beginCommand(tr("Raise widgets"));
1989 for (QWidget *widget : std::as_const(widgets)) {
1990 auto *cmd = new RaiseWidgetCommand(this);
1991 cmd->init(widget);
1992 m_undoStack.push(cmd);
1993 }
1994 endCommand();
1995}
1996
1997void FormWindow::lowerWidgets()
1998{
1999 QWidgetList widgets = selectedWidgets();
2000 simplifySelection(&widgets);
2001
2002 if (widgets.isEmpty())
2003 return;
2004
2005 beginCommand(tr("Lower widgets"));
2006 for (QWidget *widget : std::as_const(widgets)) {
2007 auto *cmd = new LowerWidgetCommand(this);
2008 cmd->init(widget);
2009 m_undoStack.push(cmd);
2010 }
2011 endCommand();
2012}
2013
2014bool FormWindow::handleMouseButtonDblClickEvent(QWidget *w, QWidget *managedWidget, QMouseEvent *e)
2015{
2016 if (debugFormWindow)
2017 qDebug() << "handleMouseButtonDblClickEvent:" << w << ',' << managedWidget << "state=" << m_mouseState;
2018
2019 e->accept();
2020
2021 // Might be out of sync due cycling of the parent selection
2022 // In that case, do nothing
2023 if (isWidgetSelected(managedWidget))
2024 emit activated(managedWidget);
2025
2026 m_mouseState = MouseDoubleClicked;
2027 return true;
2028}
2029
2030
2031QMenu *FormWindow::initializePopupMenu(QWidget *managedWidget)
2032{
2033 if (!isManaged(managedWidget) || currentTool())
2034 return nullptr;
2035
2036 // Make sure the managedWidget is selected and current since
2037 // the SetPropertyCommands must use the right reference
2038 // object obtained from the property editor for the property group
2039 // of a multiselection to be correct.
2040 const bool selected = isWidgetSelected(managedWidget);
2041 bool update = false;
2042 if (selected) {
2043 update = setCurrentWidget(managedWidget);
2044 } else {
2045 clearObjectInspectorSelection(m_core); // We might have a toolbar or non-widget selected in the object inspector.
2046 clearSelection(false);
2047 update = trySelectWidget(managedWidget, true);
2048 raiseChildSelections(managedWidget); // raise selections and select widget
2049 }
2050
2051 if (update) {
2052 emitSelectionChanged();
2053 QMetaObject::invokeMethod(core()->formWindowManager(), "slotUpdateActions");
2054 }
2055
2056 QWidget *contextMenuWidget = nullptr;
2057
2058 if (isMainContainer(managedWidget)) { // press on a child widget
2059 contextMenuWidget = mainContainer();
2060 } else { // press on a child widget
2061 // if widget is laid out, find the first non-laid out super-widget
2062 QWidget *realWidget = managedWidget; // but store the original one
2063 auto *mw = qobject_cast<QMainWindow*>(mainContainer());
2064
2065 if (mw && mw->centralWidget() == realWidget) {
2066 contextMenuWidget = managedWidget;
2067 } else {
2068 contextMenuWidget = realWidget;
2069 }
2070 }
2071
2072 if (!contextMenuWidget)
2073 return nullptr;
2074
2075 QMenu *contextMenu = createPopupMenu(contextMenuWidget);
2076 if (!contextMenu)
2077 return nullptr;
2078
2079 emit contextMenuRequested(contextMenu, contextMenuWidget);
2080 return contextMenu;
2081}
2082
2083bool FormWindow::handleContextMenu(QWidget *, QWidget *managedWidget, QContextMenuEvent *e)
2084{
2085 QMenu *contextMenu = initializePopupMenu(managedWidget);
2086 if (!contextMenu)
2087 return false;
2088 const QPoint globalPos = e->globalPos();
2089 m_contextMenuPosition = mapFromGlobal (globalPos);
2090 contextMenu->exec(globalPos);
2091 delete contextMenu;
2092 e->accept();
2093 m_contextMenuPosition = QPoint(-1, -1);
2094 return true;
2095}
2096
2097bool FormWindow::setContents(QIODevice *dev, QString *errorMessageIn /* = 0 */)
2098{
2099 QDesignerResource r(this);
2100 std::unique_ptr<DomUI> ui(r.readUi(dev));
2101 if (!ui) {
2102 if (errorMessageIn)
2103 *errorMessageIn = r.errorString();
2104 return false;
2105 }
2106
2107 UpdateBlocker ub(this);
2108 clearSelection();
2109 m_selection->clearSelectionPool();
2110 m_insertedWidgets.clear();
2111 m_widgets.clear();
2112 // The main container is cleared as otherwise
2113 // the names of the newly loaded objects will be unified.
2114 clearMainContainer();
2115 m_undoStack.clear();
2116 emit changed();
2117
2118 QWidget *w = r.loadUi(ui.get(), formContainer());
2119 if (w) {
2120 setMainContainer(w);
2121 emit changed();
2122 }
2123 if (errorMessageIn)
2124 *errorMessageIn = r.errorString();
2125 return w != nullptr;
2126}
2127
2128bool FormWindow::setContents(const QString &contents)
2129{
2130 QString errorMessage;
2131 QByteArray data = contents.toUtf8();
2132 QBuffer b(&data);
2133 const bool success = b.open(QIODevice::ReadOnly) && setContents(&b, &errorMessage);
2134 if (!success && !errorMessage.isEmpty())
2135 designerWarning(errorMessage);
2136 return success;
2137}
2138
2139void FormWindow::layoutContainer(QWidget *w, int type)
2140{
2141 if (w == this)
2142 w = mainContainer();
2143
2144 w = core()->widgetFactory()->containerOfWidget(w);
2145
2146 // find managed widget children
2147 QWidgetList widgets;
2148 for (auto *o : w->children()) {
2149 if (o->isWidgetType() ) {
2150 auto *widget = static_cast<QWidget*>(o);
2151 if (widget->isVisibleTo(this) && isManaged(widget))
2152 widgets.append(widget);
2153 }
2154 }
2155
2156 if (widgets.isEmpty()) // QTBUG-50563, observed when using hand-edited forms.
2157 return;
2158
2159 auto *cmd = new LayoutCommand(this);
2160 cmd->init(mainContainer(), widgets, static_cast<LayoutInfo::Type>(type), w);
2161 clearSelection(false);
2162 commandHistory()->push(cmd);
2163}
2164
2165bool FormWindow::hasInsertedChildren(QWidget *widget) const // ### move
2166{
2167 if (QDesignerContainerExtension *container = qt_extension<QDesignerContainerExtension*>(core()->extensionManager(), widget)) {
2168 const int index = container->currentIndex();
2169 if (index < 0)
2170 return false;
2171 widget = container->widget(index);
2172 }
2173
2174 const QWidgetList l = widgets(widget);
2175
2176 auto pred = [this](QWidget *child) {
2177 return isManaged(child) && !LayoutInfo::isWidgetLaidout(core(), child) && child->isVisibleTo(this);
2178 };
2179 return std::any_of(l.cbegin(), l.cend(), pred);
2180}
2181
2182// "Select Ancestor" sub menu code
2183void FormWindow::slotSelectWidget(QAction *a)
2184{
2185 if (auto *w = qvariant_cast<QWidget*>(a->data()))
2186 selectSingleWidget(w);
2187}
2188
2189void FormWindow::slotCleanChanged(bool clean)
2190{
2191 if (!clean)
2192 emit changed();
2193}
2194
2195static inline QString objectNameOf(const QWidget *w)
2196{
2197 if (const auto *lw = qobject_cast<const QLayoutWidget *>(w)) {
2198 const QLayout *layout = lw->layout();
2199 const QString rc = layout->objectName();
2200 if (!rc.isEmpty())
2201 return rc;
2202 // Fall thru for 4.3 forms which have a name on the widget: Display the class name
2203 return QString::fromUtf8(layout->metaObject()->className());
2204 }
2205 return w->objectName();
2206}
2207
2208QAction *FormWindow::createSelectAncestorSubMenu(QWidget *w)
2209{
2210 // Find the managed, unselected parents
2211 QWidgetList parents;
2212 QWidget *mc = mainContainer();
2213 for (QWidget *p = w->parentWidget(); p && p != mc; p = p->parentWidget())
2214 if (isManaged(p) && !isWidgetSelected(p))
2215 parents.push_back(p);
2216 if (parents.isEmpty())
2217 return nullptr;
2218 // Create a submenu listing the managed, unselected parents
2219 auto *menu = new QMenu;
2220 auto *ag = new QActionGroup(menu);
2221 QObject::connect(ag, &QActionGroup::triggered, this, &FormWindow::slotSelectWidget);
2222 for (auto *w : std::as_const(parents)) {
2223 QAction *a = ag->addAction(objectNameOf(w));
2224 a->setData(QVariant::fromValue(w));
2225 menu->addAction(a);
2226 }
2227 auto *ma = new QAction(tr("Select Ancestor"), nullptr);
2228 ma->setMenu(menu);
2229 return ma;
2230}
2231
2232QMenu *FormWindow::createPopupMenu(QWidget *w)
2233{
2234 QMenu *popup = createExtensionTaskMenu(this, w, true);
2235 if (!popup)
2236 popup = new QMenu;
2237 // if w doesn't have a QDesignerTaskMenu as a child create one and make it a child.
2238 // insert actions from QDesignerTaskMenu
2239
2240 QDesignerFormWindowManagerInterface *manager = core()->formWindowManager();
2241 const bool isFormWindow = qobject_cast<const FormWindow*>(w);
2242
2243 // Check for special containers and obtain the page menu from them to add layout actions.
2244 if (!isFormWindow) {
2245 if (auto *stackedWidget = qobject_cast<QStackedWidget*>(w)) {
2246 QStackedWidgetEventFilter::addStackedWidgetContextMenuActions(stackedWidget, popup);
2247 } else if (auto *tabWidget = qobject_cast<QTabWidget*>(w)) {
2248 QTabWidgetEventFilter::addTabWidgetContextMenuActions(tabWidget, popup);
2249 } else if (auto *toolBox = qobject_cast<QToolBox*>(w)) {
2250 QToolBoxHelper::addToolBoxContextMenuActions(toolBox, popup);
2251 }
2252
2253 if (manager->action(QDesignerFormWindowManagerInterface::LowerAction)->isEnabled()) {
2254 popup->addAction(manager->action(QDesignerFormWindowManagerInterface::LowerAction));
2255 popup->addAction(manager->action(QDesignerFormWindowManagerInterface::RaiseAction));
2256 popup->addSeparator();
2257 }
2258#if QT_CONFIG(clipboard)
2259 popup->addAction(manager->action(QDesignerFormWindowManagerInterface::CutAction));
2260 popup->addAction(manager->action(QDesignerFormWindowManagerInterface::CopyAction));
2261#endif
2262 }
2263
2264#if QT_CONFIG(clipboard)
2265 popup->addAction(manager->action(QDesignerFormWindowManagerInterface::PasteAction));
2266#endif
2267
2268 if (QAction *selectAncestorAction = createSelectAncestorSubMenu(w))
2269 popup->addAction(selectAncestorAction);
2270 popup->addAction(manager->action(QDesignerFormWindowManagerInterface::SelectAllAction));
2271
2272 if (!isFormWindow) {
2273 popup->addAction(manager->action(QDesignerFormWindowManagerInterface::DeleteAction));
2274 }
2275
2276 popup->addSeparator();
2277 QMenu *layoutMenu = popup->addMenu(tr("Lay out"));
2278 layoutMenu->addAction(manager->action(QDesignerFormWindowManagerInterface::AdjustSizeAction));
2279 layoutMenu->addAction(manager->action(QDesignerFormWindowManagerInterface::HorizontalLayoutAction));
2280 layoutMenu->addAction(manager->action(QDesignerFormWindowManagerInterface::VerticalLayoutAction));
2281 if (!isFormWindow) {
2282 layoutMenu->addAction(manager->action(QDesignerFormWindowManagerInterface::SplitHorizontalAction));
2283 layoutMenu->addAction(manager->action(QDesignerFormWindowManagerInterface::SplitVerticalAction));
2284 }
2285 layoutMenu->addAction(manager->action(QDesignerFormWindowManagerInterface::GridLayoutAction));
2286 layoutMenu->addAction(manager->action(QDesignerFormWindowManagerInterface::FormLayoutAction));
2287 layoutMenu->addAction(manager->action(QDesignerFormWindowManagerInterface::BreakLayoutAction));
2288 layoutMenu->addAction(manager->action(QDesignerFormWindowManagerInterface::SimplifyLayoutAction));
2289
2290 return popup;
2291}
2292
2293void FormWindow::resizeEvent(QResizeEvent *e)
2294{
2295 m_geometryChangedTimer->start(10);
2296
2297 QWidget::resizeEvent(e);
2298}
2299
2300/*!
2301 Maps \a pos in \a w's coordinates to the form's coordinate system.
2302
2303 This is the equivalent to mapFromGlobal(w->mapToGlobal(pos)) but
2304 avoids the two roundtrips to the X-Server on Unix/X11.
2305 */
2306QPoint FormWindow::mapToForm(const QWidget *w, QPoint pos) const
2307{
2308 QPoint p = pos;
2309 const QWidget* i = w;
2310 while (i && !i->isWindow() && !isMainContainer(i)) {
2311 p = i->mapToParent(p);
2312 i = i->parentWidget();
2313 }
2314
2315 return mapFromGlobal(w->mapToGlobal(pos));
2316}
2317
2318bool FormWindow::canBeBuddy(QWidget *w) const // ### rename me.
2319{
2320 if (QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(core()->extensionManager(), w)) {
2321 const int index = sheet->indexOf(u"focusPolicy"_s);
2322 if (index != -1) {
2323 bool ok = false;
2324 const Qt::FocusPolicy q = static_cast<Qt::FocusPolicy>(Utils::valueOf(sheet->property(index), &ok));
2325 return ok && q != Qt::NoFocus;
2326 }
2327 }
2328
2329 return false;
2330}
2331
2332QWidget *FormWindow::findContainer(QWidget *w, bool excludeLayout) const
2333{
2334 if (!isChildOf(w, this)
2335 || const_cast<const QWidget *>(w) == this)
2336 return nullptr;
2337
2338 QDesignerWidgetFactoryInterface *widgetFactory = core()->widgetFactory();
2339 QDesignerWidgetDataBaseInterface *widgetDataBase = core()->widgetDataBase();
2340 QDesignerMetaDataBaseInterface *metaDataBase = core()->metaDataBase();
2341
2342 QWidget *container = widgetFactory->containerOfWidget(mainContainer()); // default parent for new widget is the formwindow
2343 if (!isMainContainer(w)) { // press was not on formwindow, check if we can find another parent
2344 while (w) {
2345 if (qobject_cast<InvisibleWidget*>(w) || !metaDataBase->item(w)) {
2346 w = w->parentWidget();
2347 continue;
2348 }
2349
2350 const bool isContainer = widgetDataBase->isContainer(w, true) || w == mainContainer();
2351
2352 if (!isContainer || (excludeLayout && qobject_cast<QLayoutWidget*>(w))) { // ### skip QSplitter
2353 w = w->parentWidget();
2354 } else {
2355 container = w;
2356 break;
2357 }
2358 }
2359 }
2360
2361 return container;
2362}
2363
2364void FormWindow::simplifySelection(QWidgetList *sel) const
2365{
2366 if (sel->size() < 2)
2367 return;
2368 // Figure out which widgets should be removed from selection.
2369 // We want to remove those whose parent widget is also in the
2370 // selection (because the child widgets are contained by
2371 // their parent, they shouldn't be in the selection --
2372 // they are "implicitly" selected).
2373 QWidget *mainC = mainContainer(); // Quick check for main container first
2374 if (sel->contains(mainC)) {
2375 sel->clear();
2376 sel->push_back(mainC);
2377 return;
2378 }
2379 QWidgetList toBeRemoved;
2380 toBeRemoved.reserve(sel->size());
2381 for (auto *child : std::as_const(*sel)) {
2382 for (QWidget *w = child; true ; ) { // Is any of the parents also selected?
2383 QWidget *parent = w->parentWidget();
2384 if (!parent || parent == mainC)
2385 break;
2386 if (sel->contains(parent)) {
2387 toBeRemoved.append(child);
2388 break;
2389 }
2390 w = parent;
2391 }
2392 }
2393 // Now we can actually remove the widgets that were marked
2394 // for removal in the previous pass.
2395 for (auto *r : std::as_const(toBeRemoved))
2396 sel->removeAll(r);
2397}
2398
2399FormWindow *FormWindow::findFormWindow(QWidget *w)
2400{
2401 return qobject_cast<FormWindow*>(QDesignerFormWindowInterface::findFormWindow(w));
2402}
2403
2404bool FormWindow::isDirty() const
2405{
2406 return !m_undoStack.isClean();
2407}
2408
2409void FormWindow::setDirty(bool dirty)
2410{
2411 if (dirty)
2412 m_undoStack.resetClean();
2413 else
2414 m_undoStack.setClean();
2415}
2416
2417QWidget *FormWindow::containerAt(const QPoint &pos)
2418{
2419 QWidget *widget = widgetAt(pos);
2420 return findContainer(widget, true);
2421}
2422
2423static QWidget *childAt_SkipDropLine(QWidget *w, QPoint pos)
2424{
2425 const QObjectList &child_list = w->children();
2426 for (auto i = child_list.size() - 1; i >= 0; --i) {
2427 QObject *child_obj = child_list.at(i);
2428 if (qobject_cast<WidgetHandle*>(child_obj) != nullptr)
2429 continue;
2430 QWidget *child = qobject_cast<QWidget*>(child_obj);
2431 if (!child || child->isWindow() || !child->isVisible() ||
2432 !child->geometry().contains(pos) || child->testAttribute(Qt::WA_TransparentForMouseEvents))
2433 continue;
2434 const QPoint childPos = child->mapFromParent(pos);
2435 if (QWidget *res = childAt_SkipDropLine(child, childPos))
2436 return res;
2437 if (child->testAttribute(Qt::WA_MouseNoMask) || child->mask().contains(pos)
2438 || child->mask().isEmpty())
2439 return child;
2440 }
2441
2442 return nullptr;
2443}
2444
2445QWidget *FormWindow::widgetAt(const QPoint &pos)
2446{
2447 QWidget *w = childAt(pos);
2448 if (qobject_cast<const WidgetHandle*>(w) != 0)
2449 w = childAt_SkipDropLine(this, pos);
2450 return (w == nullptr || w == formContainer()) ? this : w;
2451}
2452
2453void FormWindow::highlightWidget(QWidget *widget, const QPoint &pos, HighlightMode mode)
2454{
2455 Q_ASSERT(widget);
2456
2457 if (auto *mainWindow = qobject_cast<QMainWindow*> (widget))
2458 widget = mainWindow->centralWidget();
2459
2460 QWidget *container = findContainer(widget, false);
2461
2462 if (container == nullptr || core()->metaDataBase()->item(container) == nullptr)
2463 return;
2464
2465 if (QDesignerActionProviderExtension *g = qt_extension<QDesignerActionProviderExtension*>(core()->extensionManager(), container)) {
2466 if (mode == Restore) {
2467 g->adjustIndicator(QPoint());
2468 } else {
2469 const QPoint pt = widget->mapTo(container, pos);
2470 g->adjustIndicator(pt);
2471 }
2472 } else if (QDesignerLayoutDecorationExtension *g = qt_extension<QDesignerLayoutDecorationExtension*>(core()->extensionManager(), container)) {
2473 if (mode == Restore) {
2474 g->adjustIndicator(QPoint(), -1);
2475 } else {
2476 const QPoint pt = widget->mapTo(container, pos);
2477 const int index = g->findItemAt(pt);
2478 g->adjustIndicator(pt, index);
2479 }
2480 }
2481
2482 auto *mw = qobject_cast<QMainWindow*> (container);
2483 if (container == mainContainer() || (mw && mw->centralWidget() && mw->centralWidget() == container))
2484 return;
2485
2486 if (mode == Restore) {
2487 const auto pit = m_palettesBeforeHighlight.find(container);
2488 if (pit != m_palettesBeforeHighlight.end()) {
2489 container->setPalette(pit.value().first);
2490 container->setAutoFillBackground(pit.value().second);
2491 m_palettesBeforeHighlight.erase(pit);
2492 }
2493 } else {
2494 QPalette p = container->palette();
2495 if (!m_palettesBeforeHighlight.contains(container)) {
2496 PaletteAndFill paletteAndFill;
2497 if (container->testAttribute(Qt::WA_SetPalette))
2498 paletteAndFill.first = p;
2499 paletteAndFill.second = container->autoFillBackground();
2500 m_palettesBeforeHighlight.insert(container, paletteAndFill);
2501 }
2502
2503 p.setColor(backgroundRole(), p.midlight().color());
2504 container->setPalette(p);
2505 container->setAutoFillBackground(true);
2506 }
2507}
2508
2509QWidgetList FormWindow::widgets(QWidget *widget) const
2510{
2511 if (widget->children().isEmpty())
2512 return {};
2513 QWidgetList rc;
2514 for (QObject *o : widget->children()) {
2515 if (o->isWidgetType()) {
2516 QWidget *w = qobject_cast<QWidget*>(o);
2517 if (isManaged(w))
2518 rc.push_back(w);
2519 }
2520 }
2521 return rc;
2522}
2523
2524int FormWindow::toolCount() const
2525{
2526 return m_widgetStack->count();
2527}
2528
2529QDesignerFormWindowToolInterface *FormWindow::tool(int index) const
2530{
2531 return m_widgetStack->tool(index);
2532}
2533
2534void FormWindow::registerTool(QDesignerFormWindowToolInterface *tool)
2535{
2536 Q_ASSERT(tool != nullptr);
2537
2538 m_widgetStack->addTool(tool);
2539
2540 if (m_mainContainer)
2541 m_mainContainer->update();
2542}
2543
2544void FormWindow::setCurrentTool(int index)
2545{
2546 m_widgetStack->setCurrentTool(index);
2547}
2548
2549int FormWindow::currentTool() const
2550{
2551 return m_widgetStack->currentIndex();
2552}
2553
2554bool FormWindow::handleEvent(QWidget *widget, QWidget *managedWidget, QEvent *event)
2555{
2556 if (m_widgetStack == nullptr)
2557 return false;
2558
2559 QDesignerFormWindowToolInterface *tool = m_widgetStack->currentTool();
2560 if (tool == nullptr)
2561 return false;
2562
2563 return tool->handleEvent(widget, managedWidget, event);
2564}
2565
2566void FormWindow::initializeCoreTools()
2567{
2568 m_widgetEditor = new WidgetEditorTool(this);
2569 this->FormWindow::registerTool(m_widgetEditor);
2570}
2571
2572void FormWindow::checkSelection()
2573{
2574 m_checkSelectionTimer->start(0);
2575}
2576
2577void FormWindow::checkSelectionNow()
2578{
2579 m_checkSelectionTimer->stop();
2580
2581 const QWidgetList &sel = selectedWidgets();
2582 for (QWidget *widget : sel) {
2583 updateSelection(widget);
2584
2585 if (LayoutInfo::layoutType(core(), widget) != LayoutInfo::NoLayout)
2586 updateChildSelections(widget);
2587 }
2588}
2589
2590QString FormWindow::author() const
2591{
2592 return m_author;
2593}
2594
2595QString FormWindow::comment() const
2596{
2597 return m_comment;
2598}
2599
2600void FormWindow::setAuthor(const QString &author)
2601{
2602 m_author = author;
2603}
2604
2605void FormWindow::setComment(const QString &comment)
2606{
2607 m_comment = comment;
2608}
2609
2610void FormWindow::editWidgets()
2611{
2612 m_widgetEditor->action()->trigger();
2613}
2614
2615QStringList FormWindow::resourceFiles() const
2616{
2617 return m_resourceFiles;
2618}
2619
2620void FormWindow::addResourceFile(const QString &path)
2621{
2622 if (!m_resourceFiles.contains(path)) {
2623 m_resourceFiles.append(path);
2624 setDirty(true);
2625 emit resourceFilesChanged();
2626 }
2627}
2628
2629void FormWindow::removeResourceFile(const QString &path)
2630{
2631 if (m_resourceFiles.removeAll(path) > 0) {
2632 setDirty(true);
2633 emit resourceFilesChanged();
2634 }
2635}
2636
2637bool FormWindow::blockSelectionChanged(bool b)
2638{
2639 const bool blocked = m_blockSelectionChanged;
2640 m_blockSelectionChanged = b;
2641 return blocked;
2642}
2643
2644void FormWindow::editContents()
2645{
2646 const QWidgetList sel = selectedWidgets();
2647 if (sel.size() == 1) {
2648 QWidget *widget = sel.first();
2649
2650 if (QAction *a = preferredEditAction(core(), widget))
2651 a->trigger();
2652 }
2653}
2654
2655void FormWindow::dragWidgetWithinForm(QWidget *widget, QRect targetGeometry, QWidget *targetContainer)
2656{
2657 const bool fromLayout = canDragWidgetInLayout(core(), widget);
2658 const QDesignerLayoutDecorationExtension *targetDeco = qt_extension<QDesignerLayoutDecorationExtension*>(core()->extensionManager(), targetContainer);
2659 const bool toLayout = targetDeco != nullptr;
2660
2661 if (fromLayout) {
2662 // Drag from Layout: We need to delete the widget properly to store the layout state
2663 // Do not simplify the layout when dragging onto a layout
2664 // as this might invalidate the insertion position if it is the same layout
2665 auto *cmd = new DeleteWidgetCommand(this);
2666 unsigned deleteFlags = DeleteWidgetCommand::DoNotUnmanage;
2667 if (toLayout)
2668 deleteFlags |= DeleteWidgetCommand::DoNotSimplifyLayout;
2669 cmd->init(widget, deleteFlags);
2670 commandHistory()->push(cmd);
2671 }
2672
2673 if (toLayout) {
2674 // Drag from form to layout: just insert. Do not manage
2675 insertWidget(widget, targetGeometry, targetContainer, true);
2676 } else {
2677 // into container without layout
2678 if (targetContainer != widget->parent()) { // different parent
2679 auto *cmd = new ReparentWidgetCommand(this);
2680 cmd->init(widget, targetContainer );
2681 commandHistory()->push(cmd);
2682 }
2683 resizeWidget(widget, targetGeometry);
2684 selectWidget(widget, true);
2685 widget->show();
2686 }
2687}
2688
2689static Qt::DockWidgetArea detectDropArea(QMainWindow *mainWindow, QRect area, QPoint drop)
2690{
2691 QPoint offset = area.topLeft();
2692 QRect rect = area;
2693 rect.moveTopLeft(QPoint(0, 0));
2694 QPoint point = drop - offset;
2695 const int x = point.x();
2696 const int y = point.y();
2697 const int w = rect.width();
2698 const int h = rect.height();
2699
2700 if (rect.contains(point)) {
2701 bool topRight = false;
2702 bool topLeft = false;
2703 if (w * y < h * x) // top and right, oterwise bottom and left
2704 topRight = true;
2705 if (w * y < h * (w - x)) // top and left, otherwise bottom and right
2706 topLeft = true;
2707
2708 if (topRight && topLeft)
2709 return Qt::TopDockWidgetArea;
2710 if (topRight && !topLeft)
2711 return Qt::RightDockWidgetArea;
2712 if (!topRight && topLeft)
2713 return Qt::LeftDockWidgetArea;
2714 return Qt::BottomDockWidgetArea;
2715 }
2716
2717 if (x < 0) {
2718 if (y < 0)
2719 return mainWindow->corner(Qt::TopLeftCorner);
2720 return y > h ? mainWindow->corner(Qt::BottomLeftCorner) : Qt::LeftDockWidgetArea;
2721 }
2722 if (x > w) {
2723 if (y < 0)
2724 return mainWindow->corner(Qt::TopRightCorner);
2725 return y > h ? mainWindow->corner(Qt::BottomRightCorner) : Qt::RightDockWidgetArea;
2726 }
2727 return y < 0 ? Qt::TopDockWidgetArea :Qt::LeftDockWidgetArea;
2728}
2729
2730bool FormWindow::dropDockWidget(QDesignerDnDItemInterface *item, QPoint global_mouse_pos)
2731{
2732 DomUI *dom_ui = item->domUi();
2733
2734 auto *mw = qobject_cast<QMainWindow *>(mainContainer());
2735 if (!mw)
2736 return false;
2737
2738 QDesignerResource resource(this);
2739 const FormBuilderClipboard clipboard = resource.paste(dom_ui, mw);
2740 if (clipboard.m_widgets.size() != 1) // multiple-paste from DomUI not supported yet
2741 return false;
2742
2743 QWidget *centralWidget = mw->centralWidget();
2744 QPoint localPos = centralWidget->mapFromGlobal(global_mouse_pos);
2745 const QRect centralWidgetAreaRect = centralWidget->rect();
2746 Qt::DockWidgetArea area = detectDropArea(mw, centralWidgetAreaRect, localPos);
2747
2748 beginCommand(tr("Drop widget"));
2749
2750 clearSelection(false);
2751 highlightWidget(mw, QPoint(0, 0), FormWindow::Restore);
2752
2753 QWidget *widget = clipboard.m_widgets.first();
2754
2755 insertWidget(widget, QRect(0, 0, 1, 1), mw);
2756
2757 selectWidget(widget, true);
2758 mw->setFocus(Qt::MouseFocusReason); // in case focus was in e.g. object inspector
2759
2760 core()->formWindowManager()->setActiveFormWindow(this);
2761 mainContainer()->activateWindow();
2762
2763 auto *propertySheet = qobject_cast<QDesignerPropertySheetExtension*>(m_core->extensionManager()->extension(widget, Q_TYPEID(QDesignerPropertySheetExtension)));
2764 if (propertySheet) {
2765 const QString dockWidgetAreaName = u"dockWidgetArea"_s;
2766 auto e = qvariant_cast<PropertySheetEnumValue>(propertySheet->property(propertySheet->indexOf(dockWidgetAreaName)));
2767 e.value = area;
2768 QVariant v;
2769 v.setValue(e);
2770 auto *cmd = new SetPropertyCommand(this);
2771 cmd->init(widget, dockWidgetAreaName, v);
2772 m_undoStack.push(cmd);
2773 }
2774
2775 endCommand();
2776 return true;
2777}
2778
2779bool FormWindow::dropWidgets(const QList<QDesignerDnDItemInterface*> &item_list, QWidget *target,
2780 const QPoint &global_mouse_pos)
2781{
2782
2783 QWidget *parent = target;
2784 if (parent == nullptr)
2785 parent = mainContainer();
2786 // You can only drop stuff onto the central widget of a QMainWindow
2787 // ### generalize to use container extension
2788 if (auto *main_win = qobject_cast<QMainWindow*>(target)) {
2789 if (!main_win->centralWidget()) {
2790 designerWarning(tr("A QMainWindow-based form does not contain a central widget."));
2791 return false;
2792 }
2793 const QPoint main_win_pos = main_win->mapFromGlobal(global_mouse_pos);
2794 const QRect central_wgt_geo = main_win->centralWidget()->geometry();
2795 if (!central_wgt_geo.contains(main_win_pos))
2796 return false;
2797 }
2798
2799 QWidget *container = findContainer(parent, false);
2800 if (container == nullptr)
2801 return false;
2802
2803 beginCommand(tr("Drop widget"));
2804
2805 clearSelection(false);
2806 highlightWidget(target, target->mapFromGlobal(global_mouse_pos), FormWindow::Restore);
2807
2808 QPoint offset;
2809 QDesignerDnDItemInterface *current = nullptr;
2810 QDesignerFormWindowCursorInterface *c = cursor();
2811 for (QDesignerDnDItemInterface *item : std::as_const(item_list)) {
2812 QWidget *w = item->widget();
2813 if (!current)
2814 current = item;
2815 if (c->current() == w) {
2816 current = item;
2817 break;
2818 }
2819 }
2820 if (current) {
2821 QRect geom = current->decoration()->geometry();
2822 QPoint topLeft = container->mapFromGlobal(geom.topLeft());
2823 offset = designerGrid().snapPoint(topLeft) - topLeft;
2824 }
2825
2826 for (QDesignerDnDItemInterface *item : std::as_const(item_list)) {
2827 DomUI *dom_ui = item->domUi();
2828 QRect geometry = item->decoration()->geometry();
2829 Q_ASSERT(dom_ui != nullptr);
2830
2831 geometry.moveTopLeft(container->mapFromGlobal(geometry.topLeft()) + offset);
2832 if (item->type() == QDesignerDnDItemInterface::CopyDrop) { // from widget box or CTRL + mouse move
2833 QWidget *widget = createWidget(dom_ui, geometry, parent);
2834 if (!widget) {
2835 endCommand();
2836 return false;
2837 }
2838 selectWidget(widget, true);
2839 mainContainer()->setFocus(Qt::MouseFocusReason); // in case focus was in e.g. object inspector
2840 } else { // same form move
2841 QWidget *widget = item->widget();
2842 Q_ASSERT(widget != nullptr);
2843 QDesignerFormWindowInterface *dest = findFormWindow(widget);
2844 if (dest == this) {
2845 dragWidgetWithinForm(widget, geometry, container);
2846 } else { // from other form
2847 auto *source = qobject_cast<FormWindow*>(item->source());
2848 Q_ASSERT(source != nullptr);
2849
2850 source->deleteWidgetList(QWidgetList() << widget);
2851 auto *new_widget = createWidget(dom_ui, geometry, parent);
2852
2853 selectWidget(new_widget, true);
2854 }
2855 }
2856 }
2857
2858 core()->formWindowManager()->setActiveFormWindow(this);
2859 mainContainer()->activateWindow();
2860 endCommand();
2861 return true;
2862}
2863
2864QDir FormWindow::absoluteDir() const
2865{
2866 if (fileName().isEmpty())
2867 return QDir::current();
2868
2869 return QFileInfo(fileName()).absoluteDir();
2870}
2871
2872void FormWindow::layoutDefault(int *margin, int *spacing)
2873{
2874 *margin = m_defaultMargin;
2875 *spacing = m_defaultSpacing;
2876}
2877
2878void FormWindow::setLayoutDefault(int margin, int spacing)
2879{
2880 m_defaultMargin = margin;
2881 m_defaultSpacing = spacing;
2882}
2883
2884void FormWindow::layoutFunction(QString *margin, QString *spacing)
2885{
2886 *margin = m_marginFunction;
2887 *spacing = m_spacingFunction;
2888}
2889
2890void FormWindow::setLayoutFunction(const QString &margin, const QString &spacing)
2891{
2892 m_marginFunction = margin;
2893 m_spacingFunction = spacing;
2894}
2895
2896QString FormWindow::pixmapFunction() const
2897{
2898 return m_pixmapFunction;
2899}
2900
2901void FormWindow::setPixmapFunction(const QString &pixmapFunction)
2902{
2903 m_pixmapFunction = pixmapFunction;
2904}
2905
2906QStringList FormWindow::includeHints() const
2907{
2908 return m_includeHints;
2909}
2910
2911void FormWindow::setIncludeHints(const QStringList &includeHints)
2912{
2913 m_includeHints = includeHints;
2914}
2915
2916QString FormWindow::exportMacro() const
2917{
2918 return m_exportMacro;
2919}
2920
2921void FormWindow::setExportMacro(const QString &exportMacro)
2922{
2923 m_exportMacro = exportMacro;
2924}
2925
2926QEditorFormBuilder *FormWindow::createFormBuilder()
2927{
2928 return new QDesignerResource(this);
2929}
2930
2931QWidget *FormWindow::formContainer() const
2932{
2933 return m_widgetStack->formContainer();
2934}
2935
2936QUndoStack *FormWindow::commandHistory() const
2937{
2938 return &const_cast<QUndoStack &>(m_undoStack);
2939}
2940
2941} // namespace
2942
2943QT_END_NAMESPACE
friend class QWidget
Definition qpainter.h:431
void raiseList(const QWidgetList &l)
WidgetSelection * addWidget(FormWindow *fw, QWidget *w)
QDesignerFormEditorInterface * core() const override
Returns a pointer to \QD's current QDesignerFormEditorInterface object.
void setMainContainer(QWidget *mainContainer) override
Sets the main container widget on the form to the specified mainContainer.
QWidget * mainContainer() const override
Returns the main container widget for the form window.
QWidget * designerWidget(QWidget *w) const
void unmanageWidget(QWidget *w) override
Instructs the form window not to manage the specified widget.
QWidget * currentWidget() const
void endCommand() override
Ends execution of the current command.
bool blockSelectionChanged(bool blocked) override
void clearSelection(bool changePropertyDisplay=true) override
Clears the current selection in the form window.
void manageWidget(QWidget *w) override
Instructs the form window to manage the specified widget.
QWidgetList selectedWidgets() const
FormWindow(FormEditor *core, QWidget *parent=nullptr, Qt::WindowFlags flags={})
void raiseChildSelections(QWidget *w)
bool isMainContainer(const QWidget *w) const
void selectWidget(QWidget *w, bool select=true) override
If select is true, the given widget is selected; otherwise the widget is deselected.
void setDirty(bool dirty) override
If dirty is true, the form window is marked as dirty, meaning that it is modified but not saved.
void emitSelectionChanged() override
Emits the selectionChanged() signal.
QDesignerFormWindowCursorInterface * cursor() const override
Returns the cursor interface used by the form window.
QWidget * findContainer(QWidget *w, bool excludeLayout) const override
bool isWidgetSelected(QWidget *w) const
void updateChildSelections(QWidget *w)
bool isManaged(QWidget *w) const override
Returns true if the specified widget is managed by the form window; otherwise returns false.
void setCurrentTool(int index) override
Sets the current tool to be the one with the given index.
Auxiliary methods to store/retrieve settings.
static unsigned mouseFlags(Qt::KeyboardModifiers mod)
static void insertNames(const QDesignerMetaDataBaseInterface *metaDataBase, Iterator it, const Iterator &end, QObject *excludedObject, QSet< QString > &nameSet)
static void clearObjectInspectorSelection(const QDesignerFormEditorInterface *core)
static bool canDragWidgetInLayout(const QDesignerFormEditorInterface *core, QWidget *w)
static bool isDescendant(const QWidget *parent, const QWidget *child)
static QSet< QString > languageKeywords()
static QWidget * findSelectedParent(QDesignerFormWindowInterface *fw, const QWidget *w, bool selected)