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 examples of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:BSD$
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** BSD License Usage
18** Alternatively, you may use this file under the terms of the BSD license
19** as follows:
20**
21** "Redistribution and use in source and binary forms, with or without
22** modification, are permitted provided that the following conditions are
23** met:
24** * Redistributions of source code must retain the above copyright
25** notice, this list of conditions and the following disclaimer.
26** * Redistributions in binary form must reproduce the above copyright
27** notice, this list of conditions and the following disclaimer in
28** the documentation and/or other materials provided with the
29** distribution.
30** * Neither the name of The Qt Company Ltd nor the names of its
31** contributors may be used to endorse or promote products derived
32** from this software without specific prior written permission.
33**
34**
35** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
36** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
37** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
38** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
39** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
42** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
43** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
44** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
45** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
46**
47** $QT_END_LICENSE$
48**
49****************************************************************************/
50
51//! [0]
52#include <QtWidgets>
53
54#include "mainwindow.h"
55//! [0]
56
57//! [1]
58MainWindow::MainWindow()
59 : textEdit(new QPlainTextEdit)
60//! [1] //! [2]
61{
62 setCentralWidget(textEdit);
63
64 createActions();
65 createStatusBar();
66
67 readSettings();
68
69 connect(sender: textEdit->document(), signal: &QTextDocument::contentsChanged,
70 receiver: this, slot: &MainWindow::documentWasModified);
71
72#ifndef QT_NO_SESSIONMANAGER
73 QGuiApplication::setFallbackSessionManagementEnabled(false);
74 connect(qApp, signal: &QGuiApplication::commitDataRequest,
75 receiver: this, slot: &MainWindow::commitData);
76#endif
77
78 setCurrentFile(QString());
79 setUnifiedTitleAndToolBarOnMac(true);
80}
81//! [2]
82
83//! [3]
84void MainWindow::closeEvent(QCloseEvent *event)
85//! [3] //! [4]
86{
87 if (maybeSave()) {
88 writeSettings();
89 event->accept();
90 } else {
91 event->ignore();
92 }
93}
94//! [4]
95
96//! [5]
97void MainWindow::newFile()
98//! [5] //! [6]
99{
100 if (maybeSave()) {
101 textEdit->clear();
102 setCurrentFile(QString());
103 }
104}
105//! [6]
106
107//! [7]
108void MainWindow::open()
109//! [7] //! [8]
110{
111 if (maybeSave()) {
112 QString fileName = QFileDialog::getOpenFileName(parent: this);
113 if (!fileName.isEmpty())
114 loadFile(fileName);
115 }
116}
117//! [8]
118
119//! [9]
120bool MainWindow::save()
121//! [9] //! [10]
122{
123 if (curFile.isEmpty()) {
124 return saveAs();
125 } else {
126 return saveFile(fileName: curFile);
127 }
128}
129//! [10]
130
131//! [11]
132bool MainWindow::saveAs()
133//! [11] //! [12]
134{
135 QFileDialog dialog(this);
136 dialog.setWindowModality(Qt::WindowModal);
137 dialog.setAcceptMode(QFileDialog::AcceptSave);
138 if (dialog.exec() != QDialog::Accepted)
139 return false;
140 return saveFile(fileName: dialog.selectedFiles().first());
141}
142//! [12]
143
144//! [13]
145void MainWindow::about()
146//! [13] //! [14]
147{
148 QMessageBox::about(parent: this, title: tr(s: "About Application"),
149 text: tr(s: "The <b>Application</b> example demonstrates how to "
150 "write modern GUI applications using Qt, with a menu bar, "
151 "toolbars, and a status bar."));
152}
153//! [14]
154
155//! [15]
156void MainWindow::documentWasModified()
157//! [15] //! [16]
158{
159 setWindowModified(textEdit->document()->isModified());
160}
161//! [16]
162
163//! [17]
164void MainWindow::createActions()
165//! [17] //! [18]
166{
167
168 QMenu *fileMenu = menuBar()->addMenu(title: tr(s: "&File"));
169 QToolBar *fileToolBar = addToolBar(title: tr(s: "File"));
170 const QIcon newIcon = QIcon::fromTheme(name: "document-new", fallback: QIcon(":/images/new.png"));
171 QAction *newAct = new QAction(newIcon, tr(s: "&New"), this);
172 newAct->setShortcuts(QKeySequence::New);
173 newAct->setStatusTip(tr(s: "Create a new file"));
174 connect(sender: newAct, signal: &QAction::triggered, receiver: this, slot: &MainWindow::newFile);
175 fileMenu->addAction(action: newAct);
176 fileToolBar->addAction(action: newAct);
177
178//! [19]
179 const QIcon openIcon = QIcon::fromTheme(name: "document-open", fallback: QIcon(":/images/open.png"));
180 QAction *openAct = new QAction(openIcon, tr(s: "&Open..."), this);
181 openAct->setShortcuts(QKeySequence::Open);
182 openAct->setStatusTip(tr(s: "Open an existing file"));
183 connect(sender: openAct, signal: &QAction::triggered, receiver: this, slot: &MainWindow::open);
184 fileMenu->addAction(action: openAct);
185 fileToolBar->addAction(action: openAct);
186//! [18] //! [19]
187
188 const QIcon saveIcon = QIcon::fromTheme(name: "document-save", fallback: QIcon(":/images/save.png"));
189 QAction *saveAct = new QAction(saveIcon, tr(s: "&Save"), this);
190 saveAct->setShortcuts(QKeySequence::Save);
191 saveAct->setStatusTip(tr(s: "Save the document to disk"));
192 connect(sender: saveAct, signal: &QAction::triggered, receiver: this, slot: &MainWindow::save);
193 fileMenu->addAction(action: saveAct);
194 fileToolBar->addAction(action: saveAct);
195
196 const QIcon saveAsIcon = QIcon::fromTheme(name: "document-save-as");
197 QAction *saveAsAct = fileMenu->addAction(actionIcon: saveAsIcon, text: tr(s: "Save &As..."), object: this, slot: &MainWindow::saveAs);
198 saveAsAct->setShortcuts(QKeySequence::SaveAs);
199 saveAsAct->setStatusTip(tr(s: "Save the document under a new name"));
200
201//! [20]
202
203 fileMenu->addSeparator();
204
205 const QIcon exitIcon = QIcon::fromTheme(name: "application-exit");
206 QAction *exitAct = fileMenu->addAction(actionIcon: exitIcon, text: tr(s: "E&xit"), object: this, slot: &QWidget::close);
207 exitAct->setShortcuts(QKeySequence::Quit);
208//! [20]
209 exitAct->setStatusTip(tr(s: "Exit the application"));
210
211//! [21]
212 QMenu *editMenu = menuBar()->addMenu(title: tr(s: "&Edit"));
213 QToolBar *editToolBar = addToolBar(title: tr(s: "Edit"));
214//!
215#ifndef QT_NO_CLIPBOARD
216 const QIcon cutIcon = QIcon::fromTheme(name: "edit-cut", fallback: QIcon(":/images/cut.png"));
217 QAction *cutAct = new QAction(cutIcon, tr(s: "Cu&t"), this);
218//! [21]
219 cutAct->setShortcuts(QKeySequence::Cut);
220 cutAct->setStatusTip(tr(s: "Cut the current selection's contents to the "
221 "clipboard"));
222 connect(sender: cutAct, signal: &QAction::triggered, receiver: textEdit, slot: &QPlainTextEdit::cut);
223 editMenu->addAction(action: cutAct);
224 editToolBar->addAction(action: cutAct);
225
226 const QIcon copyIcon = QIcon::fromTheme(name: "edit-copy", fallback: QIcon(":/images/copy.png"));
227 QAction *copyAct = new QAction(copyIcon, tr(s: "&Copy"), this);
228 copyAct->setShortcuts(QKeySequence::Copy);
229 copyAct->setStatusTip(tr(s: "Copy the current selection's contents to the "
230 "clipboard"));
231 connect(sender: copyAct, signal: &QAction::triggered, receiver: textEdit, slot: &QPlainTextEdit::copy);
232 editMenu->addAction(action: copyAct);
233 editToolBar->addAction(action: copyAct);
234
235 const QIcon pasteIcon = QIcon::fromTheme(name: "edit-paste", fallback: QIcon(":/images/paste.png"));
236 QAction *pasteAct = new QAction(pasteIcon, tr(s: "&Paste"), this);
237 pasteAct->setShortcuts(QKeySequence::Paste);
238 pasteAct->setStatusTip(tr(s: "Paste the clipboard's contents into the current "
239 "selection"));
240 connect(sender: pasteAct, signal: &QAction::triggered, receiver: textEdit, slot: &QPlainTextEdit::paste);
241 editMenu->addAction(action: pasteAct);
242 editToolBar->addAction(action: pasteAct);
243
244 menuBar()->addSeparator();
245
246#endif // !QT_NO_CLIPBOARD
247
248 QMenu *helpMenu = menuBar()->addMenu(title: tr(s: "&Help"));
249 QAction *aboutAct = helpMenu->addAction(text: tr(s: "&About"), object: this, slot: &MainWindow::about);
250 aboutAct->setStatusTip(tr(s: "Show the application's About box"));
251
252//! [22]
253
254 QAction *aboutQtAct = helpMenu->addAction(text: tr(s: "About &Qt"), qApp, slot: &QApplication::aboutQt);
255 aboutQtAct->setStatusTip(tr(s: "Show the Qt library's About box"));
256//! [22]
257
258//! [23]
259#ifndef QT_NO_CLIPBOARD
260 cutAct->setEnabled(false);
261//! [23] //! [24]
262 copyAct->setEnabled(false);
263 connect(sender: textEdit, signal: &QPlainTextEdit::copyAvailable, receiver: cutAct, slot: &QAction::setEnabled);
264 connect(sender: textEdit, signal: &QPlainTextEdit::copyAvailable, receiver: copyAct, slot: &QAction::setEnabled);
265#endif // !QT_NO_CLIPBOARD
266}
267//! [24]
268
269//! [32]
270void MainWindow::createStatusBar()
271//! [32] //! [33]
272{
273 statusBar()->showMessage(text: tr(s: "Ready"));
274}
275//! [33]
276
277//! [34] //! [35]
278void MainWindow::readSettings()
279//! [34] //! [36]
280{
281 QSettings settings(QCoreApplication::organizationName(), QCoreApplication::applicationName());
282 const QByteArray geometry = settings.value(key: "geometry", defaultValue: QByteArray()).toByteArray();
283 if (geometry.isEmpty()) {
284 const QRect availableGeometry = screen()->availableGeometry();
285 resize(w: availableGeometry.width() / 3, h: availableGeometry.height() / 2);
286 move(ax: (availableGeometry.width() - width()) / 2,
287 ay: (availableGeometry.height() - height()) / 2);
288 } else {
289 restoreGeometry(geometry);
290 }
291}
292//! [35] //! [36]
293
294//! [37] //! [38]
295void MainWindow::writeSettings()
296//! [37] //! [39]
297{
298 QSettings settings(QCoreApplication::organizationName(), QCoreApplication::applicationName());
299 settings.setValue(key: "geometry", value: saveGeometry());
300}
301//! [38] //! [39]
302
303//! [40]
304bool MainWindow::maybeSave()
305//! [40] //! [41]
306{
307 if (!textEdit->document()->isModified())
308 return true;
309 const QMessageBox::StandardButton ret
310 = QMessageBox::warning(parent: this, title: tr(s: "Application"),
311 text: tr(s: "The document has been modified.\n"
312 "Do you want to save your changes?"),
313 buttons: QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);
314 switch (ret) {
315 case QMessageBox::Save:
316 return save();
317 case QMessageBox::Cancel:
318 return false;
319 default:
320 break;
321 }
322 return true;
323}
324//! [41]
325
326//! [42]
327void MainWindow::loadFile(const QString &fileName)
328//! [42] //! [43]
329{
330 QFile file(fileName);
331 if (!file.open(flags: QFile::ReadOnly | QFile::Text)) {
332 QMessageBox::warning(parent: this, title: tr(s: "Application"),
333 text: tr(s: "Cannot read file %1:\n%2.")
334 .arg(args: QDir::toNativeSeparators(pathName: fileName), args: file.errorString()));
335 return;
336 }
337
338 QTextStream in(&file);
339#ifndef QT_NO_CURSOR
340 QGuiApplication::setOverrideCursor(Qt::WaitCursor);
341#endif
342 textEdit->setPlainText(in.readAll());
343#ifndef QT_NO_CURSOR
344 QGuiApplication::restoreOverrideCursor();
345#endif
346
347 setCurrentFile(fileName);
348 statusBar()->showMessage(text: tr(s: "File loaded"), timeout: 2000);
349}
350//! [43]
351
352//! [44]
353bool MainWindow::saveFile(const QString &fileName)
354//! [44] //! [45]
355{
356 QString errorMessage;
357
358 QGuiApplication::setOverrideCursor(Qt::WaitCursor);
359 QSaveFile file(fileName);
360 if (file.open(flags: QFile::WriteOnly | QFile::Text)) {
361 QTextStream out(&file);
362 out << textEdit->toPlainText();
363 if (!file.commit()) {
364 errorMessage = tr(s: "Cannot write file %1:\n%2.")
365 .arg(args: QDir::toNativeSeparators(pathName: fileName), args: file.errorString());
366 }
367 } else {
368 errorMessage = tr(s: "Cannot open file %1 for writing:\n%2.")
369 .arg(args: QDir::toNativeSeparators(pathName: fileName), args: file.errorString());
370 }
371 QGuiApplication::restoreOverrideCursor();
372
373 if (!errorMessage.isEmpty()) {
374 QMessageBox::warning(parent: this, title: tr(s: "Application"), text: errorMessage);
375 return false;
376 }
377
378 setCurrentFile(fileName);
379 statusBar()->showMessage(text: tr(s: "File saved"), timeout: 2000);
380 return true;
381}
382//! [45]
383
384//! [46]
385void MainWindow::setCurrentFile(const QString &fileName)
386//! [46] //! [47]
387{
388 curFile = fileName;
389 textEdit->document()->setModified(false);
390 setWindowModified(false);
391
392 QString shownName = curFile;
393 if (curFile.isEmpty())
394 shownName = "untitled.txt";
395 setWindowFilePath(shownName);
396}
397//! [47]
398
399//! [48]
400QString MainWindow::strippedName(const QString &fullFileName)
401//! [48] //! [49]
402{
403 return QFileInfo(fullFileName).fileName();
404}
405//! [49]
406#ifndef QT_NO_SESSIONMANAGER
407void MainWindow::commitData(QSessionManager &manager)
408{
409 if (manager.allowsInteraction()) {
410 if (!maybeSave())
411 manager.cancel();
412 } else {
413 // Non-interactive: save without asking
414 if (textEdit->document()->isModified())
415 save();
416 }
417}
418#endif
419

source code of qtbase/examples/widgets/mainwindows/application/mainwindow.cpp