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 "qdesigner_formwindow.h"
30#include "qdesigner_workbench.h"
31#include "formwindowbase_p.h"
32
33// sdk
34#include <QtDesigner/abstractformwindow.h>
35#include <QtDesigner/abstractformeditor.h>
36#include <QtDesigner/propertysheet.h>
37#include <QtDesigner/abstractpropertyeditor.h>
38#include <QtDesigner/abstractformwindowmanager.h>
39#include <QtDesigner/taskmenu.h>
40#include <QtDesigner/qextensionmanager.h>
41
42#include <QtCore/qfile.h>
43#include <QtCore/qregularexpression.h>
44
45#include <QtWidgets/qaction.h>
46#include <QtWidgets/qfiledialog.h>
47#include <QtWidgets/qmessagebox.h>
48#include <QtWidgets/qpushbutton.h>
49#include <QtWidgets/qboxlayout.h>
50#include <QtWidgets/qundostack.h>
51
52#include <QtGui/qevent.h>
53QT_BEGIN_NAMESPACE
54
55QDesignerFormWindow::QDesignerFormWindow(QDesignerFormWindowInterface *editor, QDesignerWorkbench *workbench, QWidget *parent, Qt::WindowFlags flags)
56 : QWidget(parent, flags),
57 m_editor(editor),
58 m_workbench(workbench),
59 m_action(new QAction(this)),
60 m_initialized(false),
61 m_windowTitleInitialized(false)
62{
63 Q_ASSERT(workbench);
64
65 setMaximumSize(maxw: 0xFFF, maxh: 0xFFF);
66 QDesignerFormEditorInterface *core = workbench->core();
67
68 if (m_editor) {
69 m_editor->setParent(this);
70 } else {
71 m_editor = core->formWindowManager()->createFormWindow(parentWidget: this);
72 }
73
74 QVBoxLayout *l = new QVBoxLayout(this);
75 l->setContentsMargins(QMargins());
76 l->addWidget(m_editor);
77
78 m_action->setCheckable(true);
79
80 connect(sender: m_editor->commandHistory(), signal: &QUndoStack::indexChanged, receiver: this, slot: &QDesignerFormWindow::updateChanged);
81 connect(sender: m_editor.data(), signal: &QDesignerFormWindowInterface::geometryChanged,
82 receiver: this, slot: &QDesignerFormWindow::slotGeometryChanged);
83}
84
85QDesignerFormWindow::~QDesignerFormWindow()
86{
87 if (workbench())
88 workbench()->removeFormWindow(formWindow: this);
89}
90
91QAction *QDesignerFormWindow::action() const
92{
93 return m_action;
94}
95
96void QDesignerFormWindow::changeEvent(QEvent *e)
97{
98 switch (e->type()) {
99 case QEvent::WindowTitleChange:
100 m_action->setText(windowTitle().remove(QStringLiteral("[*]")));
101 break;
102 case QEvent::WindowIconChange:
103 m_action->setIcon(windowIcon());
104 break;
105 case QEvent::WindowStateChange: {
106 const QWindowStateChangeEvent *wsce = static_cast<const QWindowStateChangeEvent *>(e);
107 const bool wasMinimized = Qt::WindowMinimized & wsce->oldState();
108 const bool isMinimizedNow = isMinimized();
109 if (wasMinimized != isMinimizedNow )
110 emit minimizationStateChanged(formWindow: m_editor, minimized: isMinimizedNow);
111 }
112 break;
113 default:
114 break;
115 }
116 QWidget::changeEvent(e);
117}
118
119QRect QDesignerFormWindow::geometryHint() const
120{
121 const QPoint point(0, 0);
122 // If we have a container, we want to be just as big.
123 // QMdiSubWindow attempts to resize its children to sizeHint() when switching user interface modes.
124 if (QWidget *mainContainer = m_editor->mainContainer())
125 return QRect(point, mainContainer->size());
126
127 return QRect(point, sizeHint());
128}
129
130QDesignerFormWindowInterface *QDesignerFormWindow::editor() const
131{
132 return m_editor;
133}
134
135QDesignerWorkbench *QDesignerFormWindow::workbench() const
136{
137 return m_workbench;
138}
139
140void QDesignerFormWindow::firstShow()
141{
142 // Set up handling of file name changes and set initial title.
143 if (!m_windowTitleInitialized) {
144 m_windowTitleInitialized = true;
145 if (m_editor) {
146 connect(sender: m_editor.data(), signal: &QDesignerFormWindowInterface::fileNameChanged,
147 receiver: this, slot: &QDesignerFormWindow::updateWindowTitle);
148 updateWindowTitle(fileName: m_editor->fileName());
149 updateChanged();
150 }
151 }
152 show();
153}
154
155int QDesignerFormWindow::getNumberOfUntitledWindows() const
156{
157 const int totalWindows = m_workbench->formWindowCount();
158 if (!totalWindows)
159 return 0;
160
161 int maxUntitled = 0;
162 // Find the number of untitled windows excluding ourselves.
163 // Do not fall for 'untitled.ui', match with modified place holder.
164 // This will cause some problems with i18n, but for now I need the string to be "static"
165 static const QRegularExpression rx(QStringLiteral("untitled( (\\d+))?\\[\\*\\]$"));
166 Q_ASSERT(rx.isValid());
167 for (int i = 0; i < totalWindows; ++i) {
168 QDesignerFormWindow *fw = m_workbench->formWindow(index: i);
169 if (fw != this) {
170 const QString title = m_workbench->formWindow(index: i)->windowTitle();
171 const QRegularExpressionMatch match = rx.match(subject: title);
172 if (match.hasMatch()) {
173 if (maxUntitled == 0)
174 ++maxUntitled;
175 if (match.lastCapturedIndex() >= 2) {
176 const auto numberCapture = match.capturedRef(nth: 2);
177 if (!numberCapture.isEmpty())
178 maxUntitled = qMax(a: numberCapture.toInt(), b: maxUntitled);
179 }
180 }
181 }
182 }
183 return maxUntitled;
184}
185
186void QDesignerFormWindow::updateWindowTitle(const QString &fileName)
187{
188 if (!m_windowTitleInitialized) {
189 m_windowTitleInitialized = true;
190 if (m_editor)
191 connect(sender: m_editor.data(), signal: &QDesignerFormWindowInterface::fileNameChanged,
192 receiver: this, slot: &QDesignerFormWindow::updateWindowTitle);
193 }
194
195 QString fileNameTitle;
196 if (fileName.isEmpty()) {
197 fileNameTitle = QStringLiteral("untitled");
198 if (const int maxUntitled = getNumberOfUntitledWindows()) {
199 fileNameTitle += QLatin1Char(' ');
200 fileNameTitle += QString::number(maxUntitled + 1);
201 }
202 } else {
203 fileNameTitle = QFileInfo(fileName).fileName();
204 }
205
206 if (const QWidget *mc = m_editor->mainContainer()) {
207 setWindowIcon(mc->windowIcon());
208 setWindowTitle(tr(s: "%1 - %2[*]").arg(args: mc->windowTitle(), args&: fileNameTitle));
209 } else {
210 setWindowTitle(fileNameTitle);
211 }
212}
213
214void QDesignerFormWindow::closeEvent(QCloseEvent *ev)
215{
216 if (m_editor->isDirty()) {
217 raise();
218 QMessageBox box(QMessageBox::Information, tr(s: "Save Form?"),
219 tr(s: "Do you want to save the changes to this document before closing?"),
220 QMessageBox::Discard | QMessageBox::Cancel | QMessageBox::Save, m_editor);
221 box.setInformativeText(tr(s: "If you don't save, your changes will be lost."));
222 box.setWindowModality(Qt::WindowModal);
223 static_cast<QPushButton *>(box.button(which: QMessageBox::Save))->setDefault(true);
224
225 switch (box.exec()) {
226 case QMessageBox::Save: {
227 bool ok = workbench()->saveForm(fw: m_editor);
228 ev->setAccepted(ok);
229 m_editor->setDirty(!ok);
230 break;
231 }
232 case QMessageBox::Discard:
233 m_editor->setDirty(false); // Not really necessary, but stops problems if we get close again.
234 ev->accept();
235 break;
236 case QMessageBox::Cancel:
237 ev->ignore();
238 break;
239 }
240 }
241}
242
243void QDesignerFormWindow::updateChanged()
244{
245 // Sometimes called after form window destruction.
246 if (m_editor) {
247 setWindowModified(m_editor->isDirty());
248 updateWindowTitle(fileName: m_editor->fileName());
249 }
250}
251
252void QDesignerFormWindow::resizeEvent(QResizeEvent *rev)
253{
254 if(m_initialized) {
255 m_editor->setDirty(true);
256 setWindowModified(true);
257 }
258
259 m_initialized = true;
260 QWidget::resizeEvent(event: rev);
261}
262
263void QDesignerFormWindow::slotGeometryChanged()
264{
265 // If the form window changes, re-update the geometry of the current widget in the property editor.
266 // Note that in the case of layouts, non-maincontainer widgets must also be updated,
267 // so, do not do it for the main container only
268 const QDesignerFormEditorInterface *core = m_editor->core();
269 QObject *object = core->propertyEditor()->object();
270 if (object == nullptr || !object->isWidgetType())
271 return;
272 static const QString geometryProperty = QStringLiteral("geometry");
273 const QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(manager: core->extensionManager(), object);
274 const int geometryIndex = sheet->indexOf(name: geometryProperty);
275 if (geometryIndex == -1)
276 return;
277 core->propertyEditor()->setPropertyValue(name: geometryProperty, value: sheet->property(index: geometryIndex));
278}
279
280QT_END_NAMESPACE
281

source code of qttools/src/designer/src/designer/qdesigner_formwindow.cpp