Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
formwindowmanager.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
4// components/formeditor
7#include "formwindow.h"
8#include "formeditor.h"
12
13// shared
14#include <widgetdatabase_p.h>
15#include <iconloader_p.h>
16#include <connectionedit_p.h>
17#include <qtresourcemodel_p.h>
18#include <qdesigner_dnditem_p.h>
19#include <qdesigner_command_p.h>
20#include <qdesigner_command2_p.h>
21#include <layoutinfo_p.h>
22#include <qlayout_widget_p.h>
23#include <qdesigner_objectinspector_p.h>
24#include <actioneditor_p.h>
25#include <shared_settings_p.h>
26#include <previewmanager_p.h>
27#include <abstractdialoggui_p.h>
28#include <widgetfactory_p.h>
29#include <spacer_widget_p.h>
30
31// SDK
32#include <QtDesigner/qextensionmanager.h>
33#include <QtDesigner/abstractlanguage.h>
34#include <QtDesigner/container.h>
35#include <QtDesigner/abstractwidgetbox.h>
36#include <QtDesigner/abstractintegration.h>
37
38#include <QtWidgets/qapplication.h>
39#include <QtWidgets/qsizegrip.h>
40#include <QtWidgets/qmdiarea.h>
41#include <QtWidgets/qmdisubwindow.h>
42#include <QtWidgets/qmessagebox.h>
43#include <QtWidgets/qsplitter.h>
44
45#include <QtGui/qaction.h>
46#if QT_CONFIG(clipboard)
47# include <QtGui/qclipboard.h>
48#endif
49#include <QtGui/qevent.h>
50#include <QtGui/qundogroup.h>
51
52#include <QtCore/qdebug.h>
53
55
56using namespace Qt::StringLiterals;
57
58namespace {
59 enum { debugFWM = 0 };
60}
61
62static inline QString whatsThisFrom(const QString &str) { /// ### implement me!
63 return str;
64}
65
66// find the first child of w in a sequence
67template <class Iterator>
68static inline Iterator findFirstChildOf(Iterator it,Iterator end, const QWidget *w)
69{
70 for (;it != end; ++it) {
71 if (w->isAncestorOf(*it))
72 return it;
73 }
74 return it;
75}
76
77namespace qdesigner_internal {
78
79FormWindowManager::FormWindowManager(QDesignerFormEditorInterface *core, QObject *parent) :
80 QDesignerFormWindowManager(parent),
81 m_core(core),
82 m_activeFormWindow(nullptr),
83 m_previewManager(new PreviewManager(PreviewManager::SingleFormNonModalPreview, this)),
84 m_createLayoutContext(LayoutContainer),
85 m_morphLayoutContainer(nullptr),
86 m_actionGroupPreviewInStyle(nullptr),
87 m_actionShowFormWindowSettingsDialog(nullptr)
88{
89 setupActions();
90 qApp->installEventFilter(this);
91}
92
94{
95 qDeleteAll(m_formWindows);
96}
97
98QDesignerFormEditorInterface *FormWindowManager::core() const
99{
100 return m_core;
101}
102
103QDesignerFormWindowInterface *FormWindowManager::activeFormWindow() const
104{
105 return m_activeFormWindow;
106}
107
109{
110 return m_formWindows.size();
111}
112
113QDesignerFormWindowInterface *FormWindowManager::formWindow(int index) const
114{
115 return m_formWindows.at(index);
116}
117
118bool FormWindowManager::eventFilter(QObject *o, QEvent *e)
119{
120 if (!o->isWidgetType())
121 return false;
122
123 // If we don't have an active form, we only listen for WindowActivate to speed up integrations
124 const QEvent::Type eventType = e->type();
125 if (m_activeFormWindow == nullptr && eventType != QEvent::WindowActivate)
126 return false;
127
128 switch (eventType) { // Uninteresting events
129 case QEvent::Create:
130 case QEvent::Destroy:
131 case QEvent::ActionAdded:
132 case QEvent::ActionChanged:
133 case QEvent::ActionRemoved:
134 case QEvent::ChildAdded:
135 case QEvent::ChildPolished:
136 case QEvent::ChildRemoved:
137#if QT_CONFIG(clipboard)
138 case QEvent::Clipboard:
139#endif
140 case QEvent::ContentsRectChange:
141 case QEvent::DeferredDelete:
142 case QEvent::FileOpen:
143 case QEvent::LanguageChange:
144 case QEvent::MetaCall:
145 case QEvent::ModifiedChange:
146 case QEvent::Paint:
147 case QEvent::PaletteChange:
148 case QEvent::ParentAboutToChange:
149 case QEvent::ParentChange:
150 case QEvent::Polish:
151 case QEvent::PolishRequest:
152 case QEvent::QueryWhatsThis:
153 case QEvent::StatusTip:
154 case QEvent::StyleChange:
155 case QEvent::Timer:
156 case QEvent::ToolBarChange:
157 case QEvent::ToolTip:
158 case QEvent::WhatsThis:
159 case QEvent::WhatsThisClicked:
160 case QEvent::WinIdChange:
161 case QEvent::DynamicPropertyChange:
162 case QEvent::HoverEnter:
163 case QEvent::HoverLeave:
164 case QEvent::HoverMove:
165 case QEvent::AcceptDropsChange:
166 return false;
167 default:
168 break;
169 }
170
171 QWidget *widget = static_cast<QWidget*>(o);
172
173 if (qobject_cast<WidgetHandle*>(widget)) { // ### remove me
174 return false;
175 }
176
178 if (fw == nullptr) {
179 return false;
180 }
181
182 if (QWidget *managedWidget = findManagedWidget(fw, widget)) {
183 // Prevent MDI subwindows from being closed by clicking at the title bar
184 if (managedWidget != widget && eventType == QEvent::Close) {
185 e->ignore();
186 return true;
187 }
188 switch (eventType) {
189 case QEvent::LayoutRequest:
190 // QTBUG-61439: Suppress layout request while changing the QGridLayout
191 // span of a QTabWidget, which sends LayoutRequest in resizeEvent().
192 if (fw->handleOperation() == FormWindow::ChangeLayoutSpanHandleOperation) {
193 e->ignore();
194 return true;
195 }
196 break;
197
198 case QEvent::WindowActivate: {
199 if (fw->parentWidget()->isWindow() && fw->isMainContainer(managedWidget) && activeFormWindow() != fw) {
200 setActiveFormWindow(fw);
201 }
202 } break;
203
204 case QEvent::WindowDeactivate: {
205 if (o == fw && o == activeFormWindow())
207 } break;
208
209 case QEvent::KeyPress: {
210 QKeyEvent *ke = static_cast<QKeyEvent*>(e);
211 if (ke->key() == Qt::Key_Escape) {
212 ke->accept();
213 return true;
214 }
215 }
216 Q_FALLTHROUGH(); // don't break...
217
218 // Embedded Design: Drop on different form: Make sure the right form
219 // window/device is active before having the widget created by the factory
220 case QEvent::Drop:
221 if (activeFormWindow() != fw)
222 setActiveFormWindow(fw);
223 Q_FALLTHROUGH(); // don't break...
224 default: {
225 if (fw->handleEvent(widget, managedWidget, e)) {
226 return true;
227 }
228 } break;
229
230 } // end switch
231 }
232
233 return false;
234}
235
236void FormWindowManager::addFormWindow(QDesignerFormWindowInterface *w)
237{
238 FormWindow *formWindow = qobject_cast<FormWindow*>(w);
239 if (!formWindow || m_formWindows.contains(formWindow))
240 return;
241
242 connect(formWindow, &QDesignerFormWindowInterface::selectionChanged,
243 this, &FormWindowManager::slotUpdateActions);
244 connect(formWindow->commandHistory(), &QUndoStack::indexChanged,
245 this, &FormWindowManager::slotUpdateActions);
246 connect(formWindow, &QDesignerFormWindowInterface::toolChanged,
247 this, &FormWindowManager::slotUpdateActions);
248
249 if (ActionEditor *ae = qobject_cast<ActionEditor *>(m_core->actionEditor())) {
250 connect(w, &QDesignerFormWindowInterface::mainContainerChanged,
251 ae, &ActionEditor::mainContainerChanged);
252 }
253 if (QDesignerObjectInspector *oi = qobject_cast<QDesignerObjectInspector *>(m_core->objectInspector()))
254 connect(w, &QDesignerFormWindowInterface::mainContainerChanged,
255 oi, &QDesignerObjectInspector::mainContainerChanged);
256
257 m_formWindows.append(formWindow);
258 emit formWindowAdded(formWindow);
259}
260
261void FormWindowManager::removeFormWindow(QDesignerFormWindowInterface *w)
262{
263 FormWindow *formWindow = qobject_cast<FormWindow*>(w);
264
265 int idx = m_formWindows.indexOf(formWindow);
266 if (!formWindow || idx == -1)
267 return;
268
269 formWindow->disconnect(this);
270 m_formWindows.removeAt(idx);
271 emit formWindowRemoved(formWindow);
272
273 if (formWindow == m_activeFormWindow)
274 setActiveFormWindow(nullptr);
275
276 // Make sure that widget box is enabled by default
277 if (m_formWindows.isEmpty() && m_core->widgetBox())
278 m_core->widgetBox()->setEnabled(true);
279
280}
281
282void FormWindowManager::setActiveFormWindow(QDesignerFormWindowInterface *w)
283{
284 FormWindow *formWindow = qobject_cast<FormWindow*>(w);
285
286 if (formWindow == m_activeFormWindow)
287 return;
288
289 FormWindow *old = m_activeFormWindow;
290
291 m_activeFormWindow = formWindow;
292
293 QtResourceSet *resourceSet = nullptr;
294 if (formWindow)
295 resourceSet = formWindow->resourceSet();
296 m_core->resourceModel()->setCurrentResourceSet(resourceSet);
297
298 slotUpdateActions();
299
300 if (m_activeFormWindow) {
301 m_activeFormWindow->repaintSelection();
302 if (old)
304 }
305
306 emit activeFormWindowChanged(m_activeFormWindow);
307
308 if (m_activeFormWindow) {
309 m_activeFormWindow->emitSelectionChanged();
310 m_activeFormWindow->commandHistory()->setActive();
311 // Trigger setActiveSubWindow on mdi area unless we are in toplevel mode
312 QMdiSubWindow *mdiSubWindow = nullptr;
313 if (QWidget *formwindow = m_activeFormWindow->parentWidget()) {
314 mdiSubWindow = qobject_cast<QMdiSubWindow *>(formwindow->parentWidget());
315 }
316 if (mdiSubWindow) {
317 for (QWidget *parent = mdiSubWindow->parentWidget(); parent; parent = parent->parentWidget()) {
318 if (QMdiArea *mdiArea = qobject_cast<QMdiArea*>(parent)) {
319 mdiArea->setActiveSubWindow(mdiSubWindow);
320 break;
321 }
322 }
323 }
324 }
325}
326
328{
329 m_previewManager->closeAllPreviews();
330}
331
332QWidget *FormWindowManager::findManagedWidget(FormWindow *fw, QWidget *w)
333{
334 while (w && w != fw) {
335 if (fw->isManaged(w))
336 break;
337 w = w->parentWidget();
338 }
339 return w;
340}
341
342void FormWindowManager::setupActions()
343{
344#if QT_CONFIG(clipboard)
345 const QIcon cutIcon = createIconSet(QIcon::ThemeIcon::EditCut,
346 "editcut.png"_L1);
347 m_actionCut = new QAction(cutIcon, tr("Cu&t"), this);
348 m_actionCut->setObjectName(u"__qt_cut_action"_s);
349 m_actionCut->setShortcut(QKeySequence::Cut);
350 m_actionCut->setStatusTip(tr("Cuts the selected widgets and puts them on the clipboard"));
351 m_actionCut->setWhatsThis(whatsThisFrom(u"Edit|Cut"_s));
352 connect(m_actionCut, &QAction::triggered, this, &FormWindowManager::slotActionCutActivated);
353 m_actionCut->setEnabled(false);
354
355 const QIcon copyIcon = createIconSet(QIcon::ThemeIcon::EditCopy, "editcopy.png"_L1);
356 m_actionCopy = new QAction(copyIcon, tr("&Copy"), this);
357 m_actionCopy->setObjectName(u"__qt_copy_action"_s);
358 m_actionCopy->setShortcut(QKeySequence::Copy);
359 m_actionCopy->setStatusTip(tr("Copies the selected widgets to the clipboard"));
360 m_actionCopy->setWhatsThis(whatsThisFrom(u"Edit|Copy"_s));
361 connect(m_actionCopy, &QAction::triggered, this, &FormWindowManager::slotActionCopyActivated);
362 m_actionCopy->setEnabled(false);
363
364 const QIcon pasteIcon = createIconSet(QIcon::ThemeIcon::EditPaste, "editpaste.png"_L1);
365 m_actionPaste = new QAction(pasteIcon, tr("&Paste"), this);
366 m_actionPaste->setObjectName(u"__qt_paste_action"_s);
367 m_actionPaste->setShortcut(QKeySequence::Paste);
368 m_actionPaste->setStatusTip(tr("Pastes the clipboard's contents"));
369 m_actionPaste->setWhatsThis(whatsThisFrom(u"Edit|Paste"_s));
370 connect(m_actionPaste, &QAction::triggered, this, &FormWindowManager::slotActionPasteActivated);
371 m_actionPaste->setEnabled(false);
372#endif
373
374 m_actionDelete = new QAction(QIcon::fromTheme(QIcon::ThemeIcon::EditDelete),
375 tr("&Delete"), this);
376 m_actionDelete->setObjectName(u"__qt_delete_action"_s);
377 m_actionDelete->setStatusTip(tr("Deletes the selected widgets"));
378 m_actionDelete->setWhatsThis(whatsThisFrom(u"Edit|Delete"_s));
379 connect(m_actionDelete, &QAction::triggered, this, &FormWindowManager::slotActionDeleteActivated);
380 m_actionDelete->setEnabled(false);
381
382 m_actionSelectAll = new QAction(tr("Select &All"), this);
383 m_actionSelectAll->setObjectName(u"__qt_select_all_action"_s);
384 m_actionSelectAll->setShortcut(QKeySequence::SelectAll);
385 m_actionSelectAll->setStatusTip(tr("Selects all widgets"));
386 m_actionSelectAll->setWhatsThis(whatsThisFrom(u"Edit|Select All"_s));
387 connect(m_actionSelectAll, &QAction::triggered, this, &FormWindowManager::slotActionSelectAllActivated);
388 m_actionSelectAll->setEnabled(false);
389
390 m_actionRaise = new QAction(createIconSet("editraise.png"_L1),
391 tr("Bring to &Front"), this);
392 m_actionRaise->setObjectName(u"__qt_raise_action"_s);
393 m_actionRaise->setShortcut(Qt::CTRL | Qt::Key_L);
394 m_actionRaise->setStatusTip(tr("Raises the selected widgets"));
395 m_actionRaise->setWhatsThis(tr("Raises the selected widgets"));
396 connect(m_actionRaise, &QAction::triggered, this, &FormWindowManager::slotActionRaiseActivated);
397 m_actionRaise->setEnabled(false);
398
399 m_actionLower = new QAction(createIconSet("editlower.png"_L1),
400 tr("Send to &Back"), this);
401 m_actionLower->setObjectName(u"__qt_lower_action"_s);
402 m_actionLower->setShortcut(Qt::CTRL | Qt::Key_K);
403 m_actionLower->setStatusTip(tr("Lowers the selected widgets"));
404 m_actionLower->setWhatsThis(tr("Lowers the selected widgets"));
405 connect(m_actionLower, &QAction::triggered, this, &FormWindowManager::slotActionLowerActivated);
406 m_actionLower->setEnabled(false);
407
408 m_actionAdjustSize = new QAction(createIconSet("adjustsize.png"_L1),
409 tr("Adjust &Size"), this);
410 m_actionAdjustSize->setObjectName(u"__qt_adjust_size_action"_s);
411 m_actionAdjustSize->setShortcut(Qt::CTRL | Qt::Key_J);
412 m_actionAdjustSize->setStatusTip(tr("Adjusts the size of the selected widget"));
413 m_actionAdjustSize->setWhatsThis(whatsThisFrom(u"Layout|Adjust Size"_s));
414 connect(m_actionAdjustSize, &QAction::triggered, this, &FormWindowManager::slotActionAdjustSizeActivated);
415 m_actionAdjustSize->setEnabled(false);
416
417
418 m_actionHorizontalLayout = new QAction(createIconSet("edithlayout.png"_L1),
419 tr("Lay Out &Horizontally"), this);
420 m_actionHorizontalLayout->setObjectName(u"__qt_horizontal_layout_action"_s);
421 m_actionHorizontalLayout->setShortcut(Qt::CTRL | Qt::Key_1);
422 m_actionHorizontalLayout->setStatusTip(tr("Lays out the selected widgets horizontally"));
423 m_actionHorizontalLayout->setWhatsThis(whatsThisFrom(u"Layout|Lay Out Horizontally"_s));
424 m_actionHorizontalLayout->setData(LayoutInfo::HBox);
425 m_actionHorizontalLayout->setEnabled(false);
426 connect(m_actionHorizontalLayout, &QAction::triggered, this, &FormWindowManager::createLayout);
427
428 m_actionVerticalLayout = new QAction(createIconSet("editvlayout.png"_L1),
429 tr("Lay Out &Vertically"), this);
430 m_actionVerticalLayout->setObjectName(u"__qt_vertical_layout_action"_s);
431 m_actionVerticalLayout->setShortcut(Qt::CTRL | Qt::Key_2);
432 m_actionVerticalLayout->setStatusTip(tr("Lays out the selected widgets vertically"));
433 m_actionVerticalLayout->setWhatsThis(whatsThisFrom(u"Layout|Lay Out Vertically"_s));
434 m_actionVerticalLayout->setData(LayoutInfo::VBox);
435 m_actionVerticalLayout->setEnabled(false);
436 connect(m_actionVerticalLayout, &QAction::triggered, this, &FormWindowManager::createLayout);
437
438 m_actionFormLayout = new QAction(createIconSet("editform.png"_L1),
439 tr("Lay Out in a &Form Layout"), this);
440 m_actionFormLayout->setObjectName(u"__qt_form_layout_action"_s);
441 m_actionFormLayout->setShortcut(Qt::CTRL | Qt::Key_6);
442 m_actionFormLayout->setStatusTip(tr("Lays out the selected widgets in a form layout"));
443 m_actionFormLayout->setWhatsThis(whatsThisFrom(u"Layout|Lay Out in a Form"_s));
444 m_actionFormLayout->setData(LayoutInfo::Form);
445 m_actionFormLayout->setEnabled(false);
446 connect(m_actionFormLayout, &QAction::triggered, this, &FormWindowManager::createLayout);
447
448 m_actionGridLayout = new QAction(createIconSet("editgrid.png"_L1),
449 tr("Lay Out in a &Grid"), this);
450 m_actionGridLayout->setObjectName(u"__qt_grid_layout_action"_s);
451 m_actionGridLayout->setShortcut(Qt::CTRL | Qt::Key_5);
452 m_actionGridLayout->setStatusTip(tr("Lays out the selected widgets in a grid"));
453 m_actionGridLayout->setWhatsThis(whatsThisFrom(u"Layout|Lay Out in a Grid"_s));
454 m_actionGridLayout->setData(LayoutInfo::Grid);
455 m_actionGridLayout->setEnabled(false);
456 connect(m_actionGridLayout, &QAction::triggered, this, &FormWindowManager::createLayout);
457
458 m_actionSplitHorizontal = new QAction(createIconSet("edithlayoutsplit.png"_L1),
459 tr("Lay Out Horizontally in S&plitter"), this);
460 m_actionSplitHorizontal->setObjectName(u"__qt_split_horizontal_action"_s);
461 m_actionSplitHorizontal->setShortcut(Qt::CTRL | Qt::Key_3);
462 m_actionSplitHorizontal->setStatusTip(tr("Lays out the selected widgets horizontally in a splitter"));
463 m_actionSplitHorizontal->setWhatsThis(whatsThisFrom(u"Layout|Lay Out Horizontally in Splitter"_s));
464 m_actionSplitHorizontal->setData(LayoutInfo::HSplitter);
465 m_actionSplitHorizontal->setEnabled(false);
466 connect(m_actionSplitHorizontal, &QAction::triggered, this, &FormWindowManager::createLayout);
467
468 m_actionSplitVertical = new QAction(createIconSet("editvlayoutsplit.png"_L1),
469 tr("Lay Out Vertically in Sp&litter"), this);
470 m_actionSplitVertical->setObjectName(u"__qt_split_vertical_action"_s);
471 m_actionSplitVertical->setShortcut(Qt::CTRL | Qt::Key_4);
472 m_actionSplitVertical->setStatusTip(tr("Lays out the selected widgets vertically in a splitter"));
473 m_actionSplitVertical->setWhatsThis(whatsThisFrom(u"Layout|Lay Out Vertically in Splitter"_s));
474 connect(m_actionSplitVertical, &QAction::triggered, this, &FormWindowManager::createLayout);
475 m_actionSplitVertical->setData(LayoutInfo::VSplitter);
476
477 m_actionSplitVertical->setEnabled(false);
478
479 m_actionBreakLayout = new QAction(createIconSet("editbreaklayout.png"_L1),
480 tr("&Break Layout"), this);
481 m_actionBreakLayout->setObjectName(u"__qt_break_layout_action"_s);
482 m_actionBreakLayout->setShortcut(Qt::CTRL | Qt::Key_0);
483 m_actionBreakLayout->setStatusTip(tr("Breaks the selected layout"));
484 m_actionBreakLayout->setWhatsThis(whatsThisFrom(u"Layout|Break Layout"_s));
485 connect(m_actionBreakLayout, &QAction::triggered, this, &FormWindowManager::slotActionBreakLayoutActivated);
486 m_actionBreakLayout->setEnabled(false);
487
488 m_actionSimplifyLayout = new QAction(tr("Si&mplify Grid Layout"), this);
489 m_actionSimplifyLayout->setObjectName(u"__qt_simplify_layout_action"_s);
490 m_actionSimplifyLayout->setStatusTip(tr("Removes empty columns and rows"));
491 m_actionSimplifyLayout->setWhatsThis(whatsThisFrom(u"Layout|Simplify Layout"_s));
492 connect(m_actionSimplifyLayout, &QAction::triggered, this, &FormWindowManager::slotActionSimplifyLayoutActivated);
493 m_actionSimplifyLayout->setEnabled(false);
494
495 m_actionDefaultPreview = new QAction(tr("&Preview..."), this);
496 m_actionDefaultPreview->setObjectName(u"__qt_default_preview_action"_s);
497 m_actionDefaultPreview->setStatusTip(tr("Preview current form"));
498 m_actionDefaultPreview->setWhatsThis(whatsThisFrom(u"Form|Preview"_s));
499 connect(m_actionDefaultPreview, &QAction::triggered,
500 this, &FormWindowManager::showPreview);
501
502 m_undoGroup = new QUndoGroup(this);
503
504 m_actionUndo = m_undoGroup->createUndoAction(this);
505 m_actionUndo->setEnabled(false);
506
507 m_actionUndo->setIcon(createIconSet(QIcon::ThemeIcon::EditUndo, "undo.png"_L1));
508 m_actionRedo = m_undoGroup->createRedoAction(this);
509 m_actionRedo->setEnabled(false);
510 m_actionRedo->setIcon(createIconSet(QIcon::ThemeIcon::EditRedo, "redo.png"_L1));
511
512 m_actionShowFormWindowSettingsDialog = new QAction(tr("Form &Settings..."), this);
513 m_actionShowFormWindowSettingsDialog->setObjectName(u"__qt_form_settings_action"_s);
514 connect(m_actionShowFormWindowSettingsDialog, &QAction::triggered,
515 this, &FormWindowManager::slotActionShowFormWindowSettingsDialog);
516 m_actionShowFormWindowSettingsDialog->setEnabled(false);
517}
518
519#if QT_CONFIG(clipboard)
521{
523}
524
526{
529}
530
532{
534}
535#endif
536
537void FormWindowManager::slotActionDeleteActivated()
538{
539 m_activeFormWindow->deleteWidgets();
540}
541
542void FormWindowManager::slotActionLowerActivated()
543{
544 m_activeFormWindow->lowerWidgets();
545}
546
547void FormWindowManager::slotActionRaiseActivated()
548{
549 m_activeFormWindow->raiseWidgets();
550}
551
552static inline QWidget *findLayoutContainer(const FormWindow *fw)
553{
554 QWidgetList l(fw->selectedWidgets());
555 fw->simplifySelection(&l);
556 return l.isEmpty() ? fw->mainContainer() : l.constFirst();
557}
558
559void FormWindowManager::createLayout()
560{
561 QAction *a = qobject_cast<QAction *>(sender());
562 if (!a)
563 return;
564 const int type = a->data().toInt();
565 switch (m_createLayoutContext) {
566 case LayoutContainer:
567 // Cannot create a splitter on a container
568 if (type != LayoutInfo::HSplitter && type != LayoutInfo::VSplitter)
569 m_activeFormWindow->createLayout(type, findLayoutContainer(m_activeFormWindow));
570 break;
571 case LayoutSelection:
572 m_activeFormWindow->createLayout(type);
573 break;
574 case MorphLayout:
575 m_activeFormWindow->morphLayout(m_morphLayoutContainer, type);
576 break;
577 }
578}
579
580void FormWindowManager::slotActionBreakLayoutActivated()
581{
582 const QWidgetList layouts = layoutsToBeBroken();
583 if (layouts.isEmpty())
584 return;
585
586 if (debugFWM) {
587 qDebug() << "slotActionBreakLayoutActivated: " << layouts.size();
588 for (const QWidget *w : layouts)
589 qDebug() << w;
590 }
591
592 m_activeFormWindow->beginCommand(tr("Break Layout"));
593 for (QWidget *layout : layouts)
594 m_activeFormWindow->breakLayout(layout);
595 m_activeFormWindow->endCommand();
596}
597
598void FormWindowManager::slotActionSimplifyLayoutActivated()
599{
600 Q_ASSERT(m_activeFormWindow != nullptr);
601 QWidgetList selectedWidgets = m_activeFormWindow->selectedWidgets();
602 m_activeFormWindow->simplifySelection(&selectedWidgets);
603 if (selectedWidgets.size() != 1)
604 return;
605 SimplifyLayoutCommand *cmd = new SimplifyLayoutCommand(m_activeFormWindow);
606 if (cmd->init(selectedWidgets.constFirst())) {
607 m_activeFormWindow->commandHistory()->push(cmd);
608 } else {
609 delete cmd;
610 }
611}
612
613void FormWindowManager::slotActionAdjustSizeActivated()
614{
615 Q_ASSERT(m_activeFormWindow != nullptr);
616
617 m_activeFormWindow->beginCommand(tr("Adjust Size"));
618
619 QWidgetList selectedWidgets = m_activeFormWindow->selectedWidgets();
620 m_activeFormWindow->simplifySelection(&selectedWidgets);
621
622 if (selectedWidgets.isEmpty()) {
623 Q_ASSERT(m_activeFormWindow->mainContainer() != nullptr);
624 selectedWidgets.append(m_activeFormWindow->mainContainer());
625 }
626
627 // Always count the main container as unlaid-out
628 for (QWidget *widget : std::as_const(selectedWidgets)) {
629 bool unlaidout = LayoutInfo::layoutType(core(), widget->parentWidget()) == LayoutInfo::NoLayout;
630 bool isMainContainer = m_activeFormWindow->isMainContainer(widget);
631
632 if (unlaidout || isMainContainer) {
633 AdjustWidgetSizeCommand *cmd = new AdjustWidgetSizeCommand(m_activeFormWindow);
634 cmd->init(widget);
635 m_activeFormWindow->commandHistory()->push(cmd);
636 }
637 }
638
639 m_activeFormWindow->endCommand();
640}
641
642void FormWindowManager::slotActionSelectAllActivated()
643{
644 m_activeFormWindow->selectAll();
645}
646
648{
649 slotActionGroupPreviewInStyle(QString(), -1);
650}
651
652void FormWindowManager::slotActionGroupPreviewInStyle(const QString &style, int deviceProfileIndex)
653{
654 QDesignerFormWindowInterface *fw = activeFormWindow();
655 if (!fw)
656 return;
657
658 QString errorMessage;
659 if (!m_previewManager->showPreview(fw, style, deviceProfileIndex, &errorMessage)) {
660 const QString title = tr("Could not create form preview", "Title of warning message box");
661 core()->dialogGui()->message(fw, QDesignerDialogGuiInterface::FormEditorMessage, QMessageBox::Warning,
662 title, errorMessage);
663 }
664}
665
666// The user might click on a layout child or the actual layout container.
667QWidgetList FormWindowManager::layoutsToBeBroken(QWidget *w) const
668{
669 if (!w)
670 return QWidgetList();
671
672 if (debugFWM)
673 qDebug() << "layoutsToBeBroken: " << w;
674
675 QWidget *parent = w->parentWidget();
676 if (m_activeFormWindow->isMainContainer(w))
677 parent = nullptr;
678
679 QWidget *widget = core()->widgetFactory()->containerOfWidget(w);
680
681 // maybe we want to remove following block
682 const QDesignerWidgetDataBaseInterface *db = m_core->widgetDataBase();
683 const QDesignerWidgetDataBaseItemInterface *item = db->item(db->indexOfObject(widget));
684 if (!item) {
685 if (debugFWM)
686 qDebug() << "layoutsToBeBroken: Don't have an item, recursing for parent";
687 return layoutsToBeBroken(parent);
688 }
689
690 const bool layoutContainer = (item->isContainer() || m_activeFormWindow->isMainContainer(widget));
691
692 if (!layoutContainer) {
693 if (debugFWM)
694 qDebug() << "layoutsToBeBroken: Not a container, recursing for parent";
695 return layoutsToBeBroken(parent);
696 }
697
698 QLayout *widgetLayout = widget->layout();
699 QLayout *managedLayout = LayoutInfo::managedLayout(m_core, widgetLayout);
700 if (!managedLayout) {
701 if (qobject_cast<const QSplitter *>(widget)) {
702 if (debugFWM)
703 qDebug() << "layoutsToBeBroken: Splitter special";
704 QWidgetList list = layoutsToBeBroken(parent);
705 list.append(widget);
706 return list;
707 }
708 if (debugFWM)
709 qDebug() << "layoutsToBeBroken: Is a container but doesn't have a managed layout (has an internal layout), returning 0";
710 return QWidgetList();
711 }
712
713 if (managedLayout) {
714 QWidgetList list;
715 if (debugFWM)
716 qDebug() << "layoutsToBeBroken: Is a container and has a layout";
717 if (qobject_cast<const QLayoutWidget *>(widget)) {
718 if (debugFWM)
719 qDebug() << "layoutsToBeBroken: red layout special case";
720 list = layoutsToBeBroken(parent);
721 }
722 list.append(widget);
723 return list;
724 }
725 if (debugFWM)
726 qDebug() << "layoutsToBeBroken: Is a container but doesn't have a layout at all, returning 0";
727 return QWidgetList();
728
729}
730
731QSet<QWidget *> FormWindowManager::getUnsortedLayoutsToBeBroken(bool firstOnly) const
732{
733 // Return a set of layouts to be broken.
734 QSet<QWidget *> layouts;
735
736 QWidgetList selection = m_activeFormWindow->selectedWidgets();
737 if (selection.isEmpty() && m_activeFormWindow->mainContainer())
738 selection.append(m_activeFormWindow->mainContainer());
739
740 for (QWidget *selectedWidget : std::as_const(selection)) {
741 // find all layouts
742 const QWidgetList &list = layoutsToBeBroken(selectedWidget);
743 if (!list.isEmpty()) {
744 for (QWidget *widget : list)
745 layouts.insert(widget);
746 if (firstOnly)
747 return layouts;
748 }
749 }
750 return layouts;
751}
752
753bool FormWindowManager::hasLayoutsToBeBroken() const
754{
755 // Quick check for layouts to be broken
756 return !getUnsortedLayoutsToBeBroken(true).isEmpty();
757}
758
759QWidgetList FormWindowManager::layoutsToBeBroken() const
760{
761 // Get all layouts. This is a list of all 'red' layouts (QLayoutWidgets)
762 // up to the first 'real' widget with a layout in hierarchy order.
763 const QSet<QWidget *> unsortedLayouts = getUnsortedLayoutsToBeBroken(false);
764 // Sort in order of hierarchy
765 QWidgetList orderedLayoutList;
766 for (QWidget *wToBeInserted : unsortedLayouts) {
767 if (!orderedLayoutList.contains(wToBeInserted)) {
768 // try to find first child, use as insertion position, else append
769 const auto firstChildPos = findFirstChildOf(orderedLayoutList.begin(), orderedLayoutList.end(), wToBeInserted);
770 if (firstChildPos == orderedLayoutList.end()) {
771 orderedLayoutList.push_back(wToBeInserted);
772 } else {
773 orderedLayoutList.insert(firstChildPos, wToBeInserted);
774 }
775 }
776 }
777 return orderedLayoutList;
778}
779
780static inline bool hasManagedLayoutItems(const QDesignerFormEditorInterface *core, QWidget *w)
781{
782 if (const QLayout *ml = LayoutInfo::managedLayout(core, w)) {
783 // Try to find managed items, ignore dummy grid spacers
784 const int count = ml->count();
785 for (int i = 0; i < count; i++)
786 if (!LayoutInfo::isEmptyItem(ml->itemAt(i)))
787 return true;
788 }
789 return false;
790}
791
792void FormWindowManager::slotUpdateActions()
793{
794 m_createLayoutContext = LayoutSelection;
795 m_morphLayoutContainer = nullptr;
796 bool canMorphIntoVBoxLayout = false;
797 bool canMorphIntoHBoxLayout = false;
798 bool canMorphIntoGridLayout = false;
799 bool canMorphIntoFormLayout = false;
800 bool hasSelectedWidgets = false;
801 int unlaidoutWidgetCount = 0;
802#if QT_CONFIG(clipboard)
803 bool pasteAvailable = false;
804#endif
805 bool layoutAvailable = false;
806 bool breakAvailable = false;
807 bool simplifyAvailable = false;
808 bool layoutContainer = false;
809 bool canChangeZOrder = true;
810
811 do {
812 if (m_activeFormWindow == nullptr || m_activeFormWindow->currentTool() != 0)
813 break;
814
815 breakAvailable = hasLayoutsToBeBroken();
816
817 QWidgetList simplifiedSelection = m_activeFormWindow->selectedWidgets();
818
819 hasSelectedWidgets = !simplifiedSelection.isEmpty();
820#if QT_CONFIG(clipboard)
821 pasteAvailable = qApp->clipboard()->mimeData() && qApp->clipboard()->mimeData()->hasText();
822#endif
823
824 m_activeFormWindow->simplifySelection(&simplifiedSelection);
825 QWidget *mainContainer = m_activeFormWindow->mainContainer();
826 if (simplifiedSelection.isEmpty() && mainContainer)
827 simplifiedSelection.append(mainContainer);
828
829 // Always count the main container as unlaid-out
830 for (auto *w : std::as_const(simplifiedSelection)) {
831 if (w == mainContainer || !LayoutInfo::isWidgetLaidout(m_core, w))
832 ++unlaidoutWidgetCount;
833
834 if (qobject_cast<const QLayoutWidget *>(w) || qobject_cast<const Spacer *>(w))
835 canChangeZOrder = false;
836 }
837
838 // Figure out layouts: Looking at a group of dangling widgets
839 if (simplifiedSelection.size() != 1) {
840 layoutAvailable = unlaidoutWidgetCount > 1;
841 //breakAvailable = false;
842 break;
843 }
844 // Manipulate layout of a single widget
845 m_createLayoutContext = LayoutSelection;
846 QWidget *widget = core()->widgetFactory()->containerOfWidget(simplifiedSelection.first());
847 if (widget == nullptr) // We are looking at a page-based container with 0 pages
848 break;
849
850 const QDesignerWidgetDataBaseInterface *db = m_core->widgetDataBase();
851 const QDesignerWidgetDataBaseItemInterface *item = db->item(db->indexOfObject(widget));
852 if (!item)
853 break;
854
855 QLayout *widgetLayout = LayoutInfo::internalLayout(widget);
856 QLayout *managedLayout = LayoutInfo::managedLayout(m_core, widgetLayout);
857 // We don't touch a layout created by a custom widget
858 if (widgetLayout && !managedLayout)
859 break;
860
861 layoutContainer = (item->isContainer() || m_activeFormWindow->isMainContainer(widget));
862
863 layoutAvailable = layoutContainer && m_activeFormWindow->hasInsertedChildren(widget) && managedLayout == nullptr;
864 simplifyAvailable = SimplifyLayoutCommand::canSimplify(m_core, widget);
865 if (layoutAvailable) {
866 m_createLayoutContext = LayoutContainer;
867 } else {
868 /* Cannot create a layout, have some layouts to be broken and
869 * exactly one, non-empty layout with selected: check the morph layout options
870 * (Note that there might be > 1 layouts to broken if the selection
871 * is a red layout, however, we want the inner-most layout here). */
872 if (breakAvailable && simplifiedSelection.size() == 1
873 && hasManagedLayoutItems(m_core, widget)) {
874 int type;
875 m_morphLayoutContainer = widget; // Was: page of first selected
876 m_createLayoutContext = MorphLayout;
877 if (MorphLayoutCommand::canMorph(m_activeFormWindow, m_morphLayoutContainer, &type)) {
878 canMorphIntoVBoxLayout = type != LayoutInfo::VBox;
879 canMorphIntoHBoxLayout = type != LayoutInfo::HBox;
880 canMorphIntoGridLayout = type != LayoutInfo::Grid;
881 canMorphIntoFormLayout = type != LayoutInfo::Form;
882 }
883 }
884 }
885 } while(false);
886
887#if QT_CONFIG(clipboard)
888 m_actionCut->setEnabled(hasSelectedWidgets);
889 m_actionCopy->setEnabled(hasSelectedWidgets);
890 m_actionPaste->setEnabled(pasteAvailable);
891#endif
892 m_actionDelete->setEnabled(hasSelectedWidgets);
893 m_actionLower->setEnabled(canChangeZOrder && hasSelectedWidgets);
894 m_actionRaise->setEnabled(canChangeZOrder && hasSelectedWidgets);
895
896
897 m_actionSelectAll->setEnabled(m_activeFormWindow != nullptr);
898
899 m_actionAdjustSize->setEnabled(unlaidoutWidgetCount > 0);
900
901 m_actionHorizontalLayout->setEnabled(layoutAvailable || canMorphIntoHBoxLayout);
902 m_actionVerticalLayout->setEnabled(layoutAvailable || canMorphIntoVBoxLayout);
903 m_actionSplitHorizontal->setEnabled(layoutAvailable && !layoutContainer);
904 m_actionSplitVertical->setEnabled(layoutAvailable && !layoutContainer);
905 m_actionFormLayout->setEnabled(layoutAvailable || canMorphIntoFormLayout);
906 m_actionGridLayout->setEnabled(layoutAvailable || canMorphIntoGridLayout);
907
908 m_actionBreakLayout->setEnabled(breakAvailable);
909 m_actionSimplifyLayout->setEnabled(simplifyAvailable);
910 m_actionShowFormWindowSettingsDialog->setEnabled(m_activeFormWindow != nullptr);
911}
912
913QDesignerFormWindowInterface *FormWindowManager::createFormWindow(QWidget *parentWidget, Qt::WindowFlags flags)
914{
915 FormWindow *formWindow = new FormWindow(qobject_cast<FormEditor*>(core()), parentWidget, flags);
916 formWindow->setProperty(WidgetFactory::disableStyleCustomPaintingPropertyC, QVariant(true));
917 addFormWindow(formWindow);
918 return formWindow;
919}
920
922{
923 const QDesignerFormWindowInterface *fw = activeFormWindow();
924 if (!fw)
925 return QPixmap();
926 QString errorMessage;
927 const QPixmap pix = m_previewManager->createPreviewPixmap(fw, QString(), &errorMessage);
928 if (pix.isNull() && !errorMessage.isEmpty())
929 qWarning("Preview pixmap creation failed: %s", qPrintable(errorMessage));
930 return pix;
931}
932
934{
935 if (m_actionGroupPreviewInStyle)
936 m_actionGroupPreviewInStyle->updateDeviceProfiles();
937}
938
939// DnD stuff
940
941void FormWindowManager::dragItems(const QList<QDesignerDnDItemInterface*> &item_list)
942{
943 QDesignerMimeData::execDrag(item_list, m_core->topLevel());
944}
945
946QUndoGroup *FormWindowManager::undoGroup() const
947{
948 return m_undoGroup;
949}
950
951void FormWindowManager::slotActionShowFormWindowSettingsDialog()
952{
953 QDesignerFormWindowInterface *fw = activeFormWindow();
954 if (!fw)
955 return;
956
957 QDialog *settingsDialog = nullptr;
958 const bool wasDirty = fw->isDirty();
959
960 // Ask the language extension for a dialog. If not, create our own
961 if (QDesignerLanguageExtension *lang = qt_extension<QDesignerLanguageExtension*>(m_core->extensionManager(), m_core))
962 settingsDialog = lang->createFormWindowSettingsDialog(fw, /*parent=*/ nullptr);
963
964 if (!settingsDialog)
965 settingsDialog = new FormWindowSettings(fw);
966
967 QString title = QFileInfo(fw->fileName()).fileName();
968 if (title.isEmpty()) // Grab the title from the outer window if no filename
969 if (const QWidget *window = m_core->integration()->containerWindow(fw))
970 title = window->windowTitle();
971
972 settingsDialog->setWindowTitle(tr("Form Settings - %1").arg(title));
973 if (settingsDialog->exec())
974 if (fw->isDirty() != wasDirty)
975 emit formWindowSettingsChanged(fw);
976
977 delete settingsDialog;
978}
979
980QAction *FormWindowManager::action(Action action) const
981{
982 switch (action) {
983#if QT_CONFIG(clipboard)
984 case QDesignerFormWindowManagerInterface::CutAction:
985 return m_actionCut;
986 case QDesignerFormWindowManagerInterface::CopyAction:
987 return m_actionCopy;
988 case QDesignerFormWindowManagerInterface::PasteAction:
989 return m_actionPaste;
990#endif
991 case QDesignerFormWindowManagerInterface::DeleteAction:
992 return m_actionDelete;
993 case QDesignerFormWindowManagerInterface::SelectAllAction:
994 return m_actionSelectAll;
995 case QDesignerFormWindowManagerInterface::LowerAction:
996 return m_actionLower;
997 case QDesignerFormWindowManagerInterface::RaiseAction:
998 return m_actionRaise;
999 case QDesignerFormWindowManagerInterface::UndoAction:
1000 return m_actionUndo;
1001 case QDesignerFormWindowManagerInterface::RedoAction:
1002 return m_actionRedo;
1003 case QDesignerFormWindowManagerInterface::HorizontalLayoutAction:
1004 return m_actionHorizontalLayout;
1005 case QDesignerFormWindowManagerInterface::VerticalLayoutAction:
1006 return m_actionVerticalLayout;
1007 case QDesignerFormWindowManagerInterface::SplitHorizontalAction:
1008 return m_actionSplitHorizontal;
1009 case QDesignerFormWindowManagerInterface::SplitVerticalAction:
1010 return m_actionSplitVertical;
1011 case QDesignerFormWindowManagerInterface::GridLayoutAction:
1012 return m_actionGridLayout;
1013 case QDesignerFormWindowManagerInterface::FormLayoutAction:
1014 return m_actionFormLayout;
1015 case QDesignerFormWindowManagerInterface::BreakLayoutAction:
1016 return m_actionBreakLayout;
1017 case QDesignerFormWindowManagerInterface::AdjustSizeAction:
1018 return m_actionAdjustSize;
1019 case QDesignerFormWindowManagerInterface::SimplifyLayoutAction:
1020 return m_actionSimplifyLayout;
1021 case QDesignerFormWindowManagerInterface::DefaultPreviewAction:
1022 return m_actionDefaultPreview;
1023 case QDesignerFormWindowManagerInterface::FormWindowSettingsDialogAction:
1024 return m_actionShowFormWindowSettingsDialog;
1025 }
1026 qWarning("FormWindowManager::action: Unhanded enumeration value %d", action);
1027 return nullptr;
1028}
1029
1030QActionGroup *FormWindowManager::actionGroup(ActionGroup actionGroup) const
1031{
1032 switch (actionGroup) {
1033 case QDesignerFormWindowManagerInterface::StyledPreviewActionGroup:
1034 if (m_actionGroupPreviewInStyle == nullptr) {
1035 // Wish we could make the 'this' pointer mutable ;-)
1036 QObject *parent = const_cast<FormWindowManager*>(this);
1037 m_actionGroupPreviewInStyle = new PreviewActionGroup(m_core, parent);
1038 connect(m_actionGroupPreviewInStyle, &PreviewActionGroup::preview,
1039 this, &FormWindowManager::slotActionGroupPreviewInStyle);
1040 }
1041 return m_actionGroupPreviewInStyle;
1042 }
1043 qWarning("FormWindowManager::actionGroup: Unhanded enumeration value %d", actionGroup);
1044 return nullptr;
1045}
1046
1047}
1048
1049QT_END_NAMESPACE
friend class QWidget
Definition qpainter.h:431
void removeFormWindow(QDesignerFormWindowInterface *formWindow) override
int formWindowCount() const override
Returns the number of form windows maintained by \QD's form window manager.
QPixmap createPreviewPixmap() const override
Creates a pixmap representing the preview of the currently active form.
QActionGroup * actionGroup(ActionGroup actionGroup) const override
Returns the action group specified by the enumeration value actionGroup.
bool eventFilter(QObject *o, QEvent *e) override
Filters events if this object has been installed as an event filter for the watched object.
QDesignerFormWindowInterface * formWindow(int index) const override
Returns the form window at the given index.
QDesignerFormWindowInterface * activeFormWindow() const override
Returns the currently active form window in \QD's workspace.
QDesignerFormWindowInterface * createFormWindow(QWidget *parentWidget=nullptr, Qt::WindowFlags flags={}) override
Creates a form window with the given parent and the given window flags.
void dragItems(const QList< QDesignerDnDItemInterface * > &item_list) override
void setActiveFormWindow(QDesignerFormWindowInterface *formWindow) override
QDesignerFormEditorInterface * core() const override
Returns a pointer to \QD's current QDesignerFormEditorInterface object.
QAction * action(Action action) const override
Returns the action specified by the enumeration value action.
static FormWindow * findFormWindow(QWidget *w)
void createLayout(int type, QWidget *container=nullptr)
QWidget * mainContainer() const override
Returns the main container widget for the form window.
void endCommand() override
Ends execution of the current command.
bool isMainContainer(const QWidget *w) const
int currentTool() const override
Returns the index of the current tool in use.
void emitSelectionChanged() override
Emits the selectionChanged() signal.
bool hasInsertedChildren(QWidget *w) const
bool isManaged(QWidget *w) const override
Returns true if the specified widget is managed by the form window; otherwise returns false.
static Iterator findFirstChildOf(Iterator it, Iterator end, const QWidget *w)
static QString whatsThisFrom(const QString &str)
Auxiliary methods to store/retrieve settings.
static QWidget * findLayoutContainer(const FormWindow *fw)
static bool hasManagedLayoutItems(const QDesignerFormEditorInterface *core, QWidget *w)