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