1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the Qt Designer of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT |
21 | ** included in the packaging of this file. Please review the following |
22 | ** information to ensure the GNU General Public License requirements will |
23 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
24 | ** |
25 | ** $QT_END_LICENSE$ |
26 | ** |
27 | ****************************************************************************/ |
28 | |
29 | #include "formwindowbase_p.h" |
30 | #include "connectionedit_p.h" |
31 | #include "qdesigner_command_p.h" |
32 | #include "qdesigner_propertysheet_p.h" |
33 | #include "qdesigner_propertyeditor_p.h" |
34 | #include "qdesigner_menu_p.h" |
35 | #include "qdesigner_menubar_p.h" |
36 | #include "shared_settings_p.h" |
37 | #include "grid_p.h" |
38 | #include "deviceprofile_p.h" |
39 | #include "qdesigner_utils_p.h" |
40 | #include "spacer_widget_p.h" |
41 | |
42 | #include <QtDesigner/abstractformeditor.h> |
43 | #include <QtDesigner/container.h> |
44 | #include <QtDesigner/qextensionmanager.h> |
45 | #include <QtDesigner/taskmenu.h> |
46 | #include <QtDesigner/abstractintegration.h> |
47 | |
48 | #include <QtCore/qdebug.h> |
49 | #include <QtCore/qlist.h> |
50 | #include <QtCore/qset.h> |
51 | #include <QtCore/qtimer.h> |
52 | #include <QtWidgets/qmenu.h> |
53 | #include <QtWidgets/qlistwidget.h> |
54 | #include <QtWidgets/qtreewidget.h> |
55 | #include <QtWidgets/qtablewidget.h> |
56 | #include <QtWidgets/qcombobox.h> |
57 | #include <QtWidgets/qtabwidget.h> |
58 | #include <QtWidgets/qtoolbox.h> |
59 | #include <QtWidgets/qtoolbar.h> |
60 | #include <QtWidgets/qstatusbar.h> |
61 | #include <QtWidgets/qmenu.h> |
62 | #include <QtWidgets/qaction.h> |
63 | #include <QtWidgets/qlabel.h> |
64 | |
65 | QT_BEGIN_NAMESPACE |
66 | |
67 | namespace qdesigner_internal { |
68 | |
69 | class FormWindowBasePrivate { |
70 | public: |
71 | explicit FormWindowBasePrivate(QDesignerFormEditorInterface *core); |
72 | |
73 | static Grid m_defaultGrid; |
74 | |
75 | QDesignerFormWindowInterface::Feature m_feature; |
76 | Grid m_grid; |
77 | bool m_hasFormGrid; |
78 | DesignerPixmapCache *m_pixmapCache; |
79 | DesignerIconCache *m_iconCache; |
80 | QtResourceSet *m_resourceSet; |
81 | QMap<QDesignerPropertySheet *, QMap<int, bool> > m_reloadableResources; // bool is dummy, QMap used as QSet |
82 | QMap<QDesignerPropertySheet *, QObject *> m_reloadablePropertySheets; |
83 | const DeviceProfile m_deviceProfile; |
84 | FormWindowBase::LineTerminatorMode m_lineTerminatorMode; |
85 | FormWindowBase::ResourceFileSaveMode m_saveResourcesBehaviour; |
86 | bool m_useIdBasedTranslations; |
87 | bool m_connectSlotsByName; |
88 | }; |
89 | |
90 | FormWindowBasePrivate::FormWindowBasePrivate(QDesignerFormEditorInterface *core) : |
91 | m_feature(QDesignerFormWindowInterface::DefaultFeature), |
92 | m_grid(m_defaultGrid), |
93 | m_hasFormGrid(false), |
94 | m_pixmapCache(nullptr), |
95 | m_iconCache(nullptr), |
96 | m_resourceSet(nullptr), |
97 | m_deviceProfile(QDesignerSharedSettings(core).currentDeviceProfile()), |
98 | m_lineTerminatorMode(FormWindowBase::NativeLineTerminator), |
99 | m_saveResourcesBehaviour(FormWindowBase::SaveAllResourceFiles), |
100 | m_useIdBasedTranslations(false), |
101 | m_connectSlotsByName(true) |
102 | { |
103 | } |
104 | |
105 | Grid FormWindowBasePrivate::m_defaultGrid; |
106 | |
107 | FormWindowBase::FormWindowBase(QDesignerFormEditorInterface *core, QWidget *parent, Qt::WindowFlags flags) : |
108 | QDesignerFormWindowInterface(parent, flags), |
109 | m_d(new FormWindowBasePrivate(core)) |
110 | { |
111 | syncGridFeature(); |
112 | m_d->m_pixmapCache = new DesignerPixmapCache(this); |
113 | m_d->m_iconCache = new DesignerIconCache(m_d->m_pixmapCache, this); |
114 | if (core->integration()->hasFeature(f: QDesignerIntegrationInterface::DefaultWidgetActionFeature)) |
115 | connect(sender: this, signal: &QDesignerFormWindowInterface::activated, receiver: this, slot: &FormWindowBase::triggerDefaultAction); |
116 | } |
117 | |
118 | FormWindowBase::~FormWindowBase() |
119 | { |
120 | QSet<QDesignerPropertySheet *> sheets; |
121 | for (auto it = m_d->m_reloadableResources.cbegin(), end = m_d->m_reloadableResources.cend(); it != end; ++it) |
122 | sheets.insert(value: it.key()); |
123 | for (auto it = m_d->m_reloadablePropertySheets.cbegin(), end = m_d->m_reloadablePropertySheets.cend(); it != end; ++it) |
124 | sheets.insert(value: it.key()); |
125 | |
126 | m_d->m_reloadableResources.clear(); |
127 | m_d->m_reloadablePropertySheets.clear(); |
128 | |
129 | for (QDesignerPropertySheet *sheet : sheets) |
130 | disconnectSheet(sheet); |
131 | |
132 | delete m_d; |
133 | } |
134 | |
135 | DesignerPixmapCache *FormWindowBase::pixmapCache() const |
136 | { |
137 | return m_d->m_pixmapCache; |
138 | } |
139 | |
140 | DesignerIconCache *FormWindowBase::iconCache() const |
141 | { |
142 | return m_d->m_iconCache; |
143 | } |
144 | |
145 | QtResourceSet *FormWindowBase::resourceSet() const |
146 | { |
147 | return m_d->m_resourceSet; |
148 | } |
149 | |
150 | void FormWindowBase::setResourceSet(QtResourceSet *resourceSet) |
151 | { |
152 | m_d->m_resourceSet = resourceSet; |
153 | } |
154 | |
155 | void FormWindowBase::addReloadableProperty(QDesignerPropertySheet *sheet, int index) |
156 | { |
157 | connectSheet(sheet); |
158 | m_d->m_reloadableResources[sheet][index] = true; |
159 | } |
160 | |
161 | void FormWindowBase::removeReloadableProperty(QDesignerPropertySheet *sheet, int index) |
162 | { |
163 | m_d->m_reloadableResources[sheet].remove(akey: index); |
164 | if (!m_d->m_reloadableResources[sheet].count()) { |
165 | m_d->m_reloadableResources.remove(akey: sheet); |
166 | disconnectSheet(sheet); |
167 | } |
168 | } |
169 | |
170 | void FormWindowBase::addReloadablePropertySheet(QDesignerPropertySheet *sheet, QObject *object) |
171 | { |
172 | if (qobject_cast<QTreeWidget *>(object) || |
173 | qobject_cast<QTableWidget *>(object) || |
174 | qobject_cast<QListWidget *>(object) || |
175 | qobject_cast<QComboBox *>(object)) { |
176 | connectSheet(sheet); |
177 | m_d->m_reloadablePropertySheets[sheet] = object; |
178 | } |
179 | } |
180 | |
181 | void FormWindowBase::connectSheet(QDesignerPropertySheet *sheet) |
182 | { |
183 | if (m_d->m_reloadableResources.contains(akey: sheet) |
184 | || m_d->m_reloadablePropertySheets.contains(akey: sheet)) { |
185 | // already connected |
186 | return; |
187 | } |
188 | connect(sender: sheet, signal: &QObject::destroyed, receiver: this, slot: &FormWindowBase::sheetDestroyed); |
189 | } |
190 | |
191 | void FormWindowBase::disconnectSheet(QDesignerPropertySheet *sheet) |
192 | { |
193 | if (m_d->m_reloadableResources.contains(akey: sheet) |
194 | || m_d->m_reloadablePropertySheets.contains(akey: sheet)) { |
195 | // still need to be connected |
196 | return; |
197 | } |
198 | disconnect(sender: sheet, signal: &QObject::destroyed, receiver: this, slot: &FormWindowBase::sheetDestroyed); |
199 | } |
200 | |
201 | void FormWindowBase::sheetDestroyed(QObject *object) |
202 | { |
203 | // qobject_cast<QDesignerPropertySheet *>(object) |
204 | // will fail since the destructor of QDesignerPropertySheet |
205 | // has already finished |
206 | |
207 | for (auto it = m_d->m_reloadableResources.begin(); |
208 | it != m_d->m_reloadableResources.end(); ++it) { |
209 | if (it.key() == object) { |
210 | m_d->m_reloadableResources.erase(it); |
211 | break; |
212 | } |
213 | } |
214 | |
215 | for (auto it = m_d->m_reloadablePropertySheets.begin(); |
216 | it != m_d->m_reloadablePropertySheets.end(); ++it) { |
217 | if (it.key() == object) { |
218 | m_d->m_reloadablePropertySheets.erase(it); |
219 | break; |
220 | } |
221 | } |
222 | } |
223 | |
224 | void FormWindowBase::reloadProperties() |
225 | { |
226 | pixmapCache()->clear(); |
227 | iconCache()->clear(); |
228 | for (auto it = m_d->m_reloadableResources.cbegin(), end = m_d->m_reloadableResources.cend(); it != end; ++it) { |
229 | QDesignerPropertySheet *sheet = it.key(); |
230 | for (auto jt = it.value().begin(), end = it.value().end(); jt != end; ++jt) { |
231 | const int index = jt.key(); |
232 | const QVariant newValue = sheet->property(index); |
233 | if (qobject_cast<QLabel *>(object: sheet->object()) && sheet->propertyName(index) == QStringLiteral("text" )) { |
234 | const PropertySheetStringValue newString = qvariant_cast<PropertySheetStringValue>(v: newValue); |
235 | // optimize a bit, reset only if the text value might contain a reference to qt resources |
236 | // (however reloading of icons other than taken from resources might not work here) |
237 | if (newString.value().contains(QStringLiteral(":/" ))) { |
238 | const QVariant resetValue = QVariant::fromValue(value: PropertySheetStringValue()); |
239 | sheet->setProperty(index, value: resetValue); |
240 | } |
241 | } |
242 | sheet->setProperty(index, value: newValue); |
243 | } |
244 | if (QTabWidget *tabWidget = qobject_cast<QTabWidget *>(object: sheet->object())) { |
245 | const int count = tabWidget->count(); |
246 | const int current = tabWidget->currentIndex(); |
247 | const QString currentTabIcon = QStringLiteral("currentTabIcon" ); |
248 | for (int i = 0; i < count; i++) { |
249 | tabWidget->setCurrentIndex(i); |
250 | const int index = sheet->indexOf(name: currentTabIcon); |
251 | sheet->setProperty(index, value: sheet->property(index)); |
252 | } |
253 | tabWidget->setCurrentIndex(current); |
254 | } else if (QToolBox *toolBox = qobject_cast<QToolBox *>(object: sheet->object())) { |
255 | const int count = toolBox->count(); |
256 | const int current = toolBox->currentIndex(); |
257 | const QString currentItemIcon = QStringLiteral("currentItemIcon" ); |
258 | for (int i = 0; i < count; i++) { |
259 | toolBox->setCurrentIndex(i); |
260 | const int index = sheet->indexOf(name: currentItemIcon); |
261 | sheet->setProperty(index, value: sheet->property(index)); |
262 | } |
263 | toolBox->setCurrentIndex(current); |
264 | } |
265 | } |
266 | for (QObject *object : qAsConst(t&: m_d->m_reloadablePropertySheets)) { |
267 | reloadIconResources(iconCache: iconCache(), object); |
268 | } |
269 | } |
270 | |
271 | void FormWindowBase::resourceSetActivated(QtResourceSet *resource, bool resourceSetChanged) |
272 | { |
273 | if (resource == resourceSet() && resourceSetChanged) { |
274 | reloadProperties(); |
275 | emit pixmapCache()->reloaded(); |
276 | emit iconCache()->reloaded(); |
277 | if (QDesignerPropertyEditor *propertyEditor = qobject_cast<QDesignerPropertyEditor *>(object: core()->propertyEditor())) |
278 | propertyEditor->reloadResourceProperties(); |
279 | } |
280 | } |
281 | |
282 | QVariantMap FormWindowBase::formData() |
283 | { |
284 | QVariantMap rc; |
285 | if (m_d->m_hasFormGrid) |
286 | m_d->m_grid.addToVariantMap(vm&: rc, forceKeys: true); |
287 | return rc; |
288 | } |
289 | |
290 | void FormWindowBase::setFormData(const QVariantMap &vm) |
291 | { |
292 | Grid formGrid; |
293 | m_d->m_hasFormGrid = formGrid.fromVariantMap(vm); |
294 | if (m_d->m_hasFormGrid) |
295 | m_d->m_grid = formGrid; |
296 | } |
297 | |
298 | QPoint FormWindowBase::grid() const |
299 | { |
300 | return QPoint(m_d->m_grid.deltaX(), m_d->m_grid.deltaY()); |
301 | } |
302 | |
303 | void FormWindowBase::setGrid(const QPoint &grid) |
304 | { |
305 | m_d->m_grid.setDeltaX(grid.x()); |
306 | m_d->m_grid.setDeltaY(grid.y()); |
307 | } |
308 | |
309 | bool FormWindowBase::hasFeature(Feature f) const |
310 | { |
311 | return f & m_d->m_feature; |
312 | } |
313 | |
314 | static void recursiveUpdate(QWidget *w) |
315 | { |
316 | w->update(); |
317 | |
318 | const QObjectList &l = w->children(); |
319 | const QObjectList::const_iterator cend = l.constEnd(); |
320 | for (QObjectList::const_iterator it = l.constBegin(); it != cend; ++it) { |
321 | if (QWidget *w = qobject_cast<QWidget*>(o: *it)) |
322 | recursiveUpdate(w); |
323 | } |
324 | } |
325 | |
326 | void FormWindowBase::setFeatures(Feature f) |
327 | { |
328 | m_d->m_feature = f; |
329 | const bool enableGrid = f & GridFeature; |
330 | m_d->m_grid.setVisible(enableGrid); |
331 | m_d->m_grid.setSnapX(enableGrid); |
332 | m_d->m_grid.setSnapY(enableGrid); |
333 | emit featureChanged(f); |
334 | recursiveUpdate(w: this); |
335 | } |
336 | |
337 | FormWindowBase::Feature FormWindowBase::features() const |
338 | { |
339 | return m_d->m_feature; |
340 | } |
341 | |
342 | bool FormWindowBase::gridVisible() const |
343 | { |
344 | return m_d->m_grid.visible() && currentTool() == 0; |
345 | } |
346 | |
347 | FormWindowBase::ResourceFileSaveMode FormWindowBase::resourceFileSaveMode() const |
348 | { |
349 | return m_d->m_saveResourcesBehaviour; |
350 | } |
351 | |
352 | void FormWindowBase::setResourceFileSaveMode(ResourceFileSaveMode behaviour) |
353 | { |
354 | m_d->m_saveResourcesBehaviour = behaviour; |
355 | } |
356 | |
357 | void FormWindowBase::syncGridFeature() |
358 | { |
359 | if (m_d->m_grid.snapX() || m_d->m_grid.snapY()) |
360 | m_d->m_feature |= GridFeature; |
361 | else |
362 | m_d->m_feature &= ~GridFeature; |
363 | } |
364 | |
365 | void FormWindowBase::setDesignerGrid(const Grid& grid) |
366 | { |
367 | m_d->m_grid = grid; |
368 | syncGridFeature(); |
369 | recursiveUpdate(w: this); |
370 | } |
371 | |
372 | const Grid &FormWindowBase::designerGrid() const |
373 | { |
374 | return m_d->m_grid; |
375 | } |
376 | |
377 | bool FormWindowBase::hasFormGrid() const |
378 | { |
379 | return m_d->m_hasFormGrid; |
380 | } |
381 | |
382 | void FormWindowBase::setHasFormGrid(bool b) |
383 | { |
384 | m_d->m_hasFormGrid = b; |
385 | } |
386 | |
387 | void FormWindowBase::setDefaultDesignerGrid(const Grid& grid) |
388 | { |
389 | FormWindowBasePrivate::m_defaultGrid = grid; |
390 | } |
391 | |
392 | const Grid &FormWindowBase::defaultDesignerGrid() |
393 | { |
394 | return FormWindowBasePrivate::m_defaultGrid; |
395 | } |
396 | |
397 | QMenu *FormWindowBase::(QWidget * /*managedWidget*/) |
398 | { |
399 | return nullptr; |
400 | } |
401 | |
402 | // Widget under mouse for finding the Widget to highlight |
403 | // when doing DnD. Restricts to pages by geometry if a container with |
404 | // a container extension (or one of its helper widgets) is hit; otherwise |
405 | // returns the widget as such (be it managed/unmanaged) |
406 | |
407 | QWidget *FormWindowBase::widgetUnderMouse(const QPoint &formPos, WidgetUnderMouseMode /* wum */) |
408 | { |
409 | // widget_under_mouse might be some temporary thing like the dropLine. We need |
410 | // the actual widget that's part of the edited GUI. |
411 | QWidget *rc = widgetAt(pos: formPos); |
412 | if (!rc || qobject_cast<ConnectionEdit*>(object: rc)) |
413 | return nullptr; |
414 | |
415 | if (rc == mainContainer()) { |
416 | // Refuse main container areas if the main container has a container extension, |
417 | // for example when hitting QToolBox/QTabWidget empty areas. |
418 | if (qt_extension<QDesignerContainerExtension*>(manager: core()->extensionManager(), object: rc)) |
419 | return nullptr; |
420 | return rc; |
421 | } |
422 | |
423 | // If we hit on container extension type container, make sure |
424 | // we use the top-most current page |
425 | if (QWidget *container = findContainer(w: rc, excludeLayout: false)) |
426 | if (QDesignerContainerExtension *c = qt_extension<QDesignerContainerExtension*>(manager: core()->extensionManager(), object: container)) { |
427 | // For container that do not have a "stacked" nature (QToolBox, QMdiArea), |
428 | // make sure the position is within the current page |
429 | const int ci = c->currentIndex(); |
430 | if (ci < 0) |
431 | return nullptr; |
432 | QWidget *page = c->widget(index: ci); |
433 | QRect pageGeometry = page->geometry(); |
434 | pageGeometry.moveTo(p: page->mapTo(this, pageGeometry.topLeft())); |
435 | if (!pageGeometry.contains(p: formPos)) |
436 | return nullptr; |
437 | return page; |
438 | } |
439 | |
440 | return rc; |
441 | } |
442 | |
443 | void FormWindowBase::deleteWidgetList(const QWidgetList &widget_list) |
444 | { |
445 | // We need a macro here even for single widgets because the some components (for example, |
446 | // the signal slot editor are connected to widgetRemoved() and add their |
447 | // own commands (for example, to delete w's connections) |
448 | const QString description = widget_list.size() == 1 ? |
449 | tr(s: "Delete '%1'" ).arg(a: widget_list.constFirst()->objectName()) : tr(s: "Delete" ); |
450 | |
451 | commandHistory()->beginMacro(text: description); |
452 | for (QWidget *w : qAsConst(t: widget_list)) { |
453 | emit widgetRemoved(w); |
454 | DeleteWidgetCommand *cmd = new DeleteWidgetCommand(this); |
455 | cmd->init(widget: w); |
456 | commandHistory()->push(cmd); |
457 | } |
458 | commandHistory()->endMacro(); |
459 | } |
460 | |
461 | QMenu *FormWindowBase::(QDesignerFormWindowInterface *fw, QObject *o, bool trailingSeparator) |
462 | { |
463 | using ActionList = QList<QAction *>; |
464 | ActionList actions; |
465 | // 1) Standard public extension |
466 | QExtensionManager *em = fw->core()->extensionManager(); |
467 | if (const QDesignerTaskMenuExtension * = qt_extension<QDesignerTaskMenuExtension*>(manager: em, object: o)) |
468 | actions += extTaskMenu->taskActions(); |
469 | if (const QDesignerTaskMenuExtension * = qobject_cast<QDesignerTaskMenuExtension *>(object: em->extension(object: o, QStringLiteral("QDesignerInternalTaskMenuExtension" )))) { |
470 | if (!actions.isEmpty()) { |
471 | QAction *a = new QAction(fw); |
472 | a->setSeparator(true); |
473 | actions.push_back(t: a); |
474 | } |
475 | actions += intTaskMenu->taskActions(); |
476 | } |
477 | if (actions.isEmpty()) |
478 | return nullptr; |
479 | if (trailingSeparator && !actions.constLast()->isSeparator()) { |
480 | QAction *a = new QAction(fw); |
481 | a->setSeparator(true); |
482 | actions.push_back(t: a); |
483 | } |
484 | QMenu *rc = new QMenu; |
485 | const ActionList::const_iterator cend = actions.constEnd(); |
486 | for (ActionList::const_iterator it = actions.constBegin(); it != cend; ++it) |
487 | rc->addAction(action: *it); |
488 | return rc; |
489 | } |
490 | |
491 | void FormWindowBase::emitObjectRemoved(QObject *o) |
492 | { |
493 | emit objectRemoved(o); |
494 | } |
495 | |
496 | DeviceProfile FormWindowBase::deviceProfile() const |
497 | { |
498 | return m_d->m_deviceProfile; |
499 | } |
500 | |
501 | QString FormWindowBase::styleName() const |
502 | { |
503 | return m_d->m_deviceProfile.isEmpty() ? QString() : m_d->m_deviceProfile.style(); |
504 | } |
505 | |
506 | void FormWindowBase::emitWidgetRemoved(QWidget *w) |
507 | { |
508 | emit widgetRemoved(w); |
509 | } |
510 | |
511 | QString FormWindowBase::deviceProfileName() const |
512 | { |
513 | return m_d->m_deviceProfile.isEmpty() ? QString() : m_d->m_deviceProfile.name(); |
514 | } |
515 | |
516 | void FormWindowBase::setLineTerminatorMode(FormWindowBase::LineTerminatorMode mode) |
517 | { |
518 | m_d->m_lineTerminatorMode = mode; |
519 | } |
520 | |
521 | FormWindowBase::LineTerminatorMode FormWindowBase::lineTerminatorMode() const |
522 | { |
523 | return m_d->m_lineTerminatorMode; |
524 | } |
525 | |
526 | void FormWindowBase::triggerDefaultAction(QWidget *widget) |
527 | { |
528 | if (QAction *action = qdesigner_internal::preferredEditAction(core: core(), managedWidget: widget)) |
529 | QTimer::singleShot(interval: 0, receiver: action, slot: &QAction::trigger); |
530 | } |
531 | |
532 | bool FormWindowBase::useIdBasedTranslations() const |
533 | { |
534 | return m_d->m_useIdBasedTranslations; |
535 | } |
536 | |
537 | void FormWindowBase::setUseIdBasedTranslations(bool v) |
538 | { |
539 | m_d->m_useIdBasedTranslations = v; |
540 | } |
541 | |
542 | bool FormWindowBase::connectSlotsByName() const |
543 | { |
544 | return m_d->m_connectSlotsByName; |
545 | } |
546 | |
547 | void FormWindowBase::setConnectSlotsByName(bool v) |
548 | { |
549 | m_d->m_connectSlotsByName = v; |
550 | } |
551 | |
552 | QStringList FormWindowBase::checkContents() const |
553 | { |
554 | if (!mainContainer()) |
555 | return QStringList(tr(s: "Invalid form" )); |
556 | // Test for non-laid toplevel spacers, which will not be saved |
557 | // as not to throw off uic. |
558 | QStringList problems; |
559 | const auto &spacers = mainContainer()->findChildren<Spacer *>(); |
560 | for (const Spacer *spacer : spacers) { |
561 | if (spacer->parentWidget() && !spacer->parentWidget()->layout()) { |
562 | problems.push_back(t: tr(s: "<p>This file contains top level spacers.<br/>" |
563 | "They will <b>not</b> be saved.</p><p>" |
564 | "Perhaps you forgot to create a layout?</p>" )); |
565 | break; |
566 | } |
567 | } |
568 | return problems; |
569 | } |
570 | |
571 | } // namespace qdesigner_internal |
572 | |
573 | QT_END_NAMESPACE |
574 | |