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