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