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