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