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(textEdit->document(), &QTextDocument::contentsChanged,
70 this, &MainWindow::documentWasModified);
71
72#ifndef QT_NO_SESSIONMANAGER
73 connect(qApp, &QGuiApplication::commitDataRequest,
74 this, &MainWindow::commitData);
75#endif
76
77 setCurrentFile(QString());
78 setUnifiedTitleAndToolBarOnMac(true);
79}
80//! [2]
81
82//! [3]
83void MainWindow::closeEvent(QCloseEvent *event)
84//! [3] //! [4]
85{
86 if (maybeSave()) {
87 writeSettings();
88 event->accept();
89 } else {
90 event->ignore();
91 }
92}
93//! [4]
94
95//! [5]
96void MainWindow::newFile()
97//! [5] //! [6]
98{
99 if (maybeSave()) {
100 textEdit->clear();
101 setCurrentFile(QString());
102 }
103}
104//! [6]
105
106//! [7]
107void MainWindow::open()
108//! [7] //! [8]
109{
110 if (maybeSave()) {
111 QString fileName = QFileDialog::getOpenFileName(this);
112 if (!fileName.isEmpty())
113 loadFile(fileName);
114 }
115}
116//! [8]
117
118//! [9]
119bool MainWindow::save()
120//! [9] //! [10]
121{
122 if (curFile.isEmpty()) {
123 return saveAs();
124 } else {
125 return saveFile(curFile);
126 }
127}
128//! [10]
129
130//! [11]
131bool MainWindow::saveAs()
132//! [11] //! [12]
133{
134 QFileDialog dialog(this);
135 dialog.setWindowModality(Qt::WindowModal);
136 dialog.setAcceptMode(QFileDialog::AcceptSave);
137 if (dialog.exec() != QDialog::Accepted)
138 return false;
139 return saveFile(dialog.selectedFiles().first());
140}
141//! [12]
142
143//! [13]
144void MainWindow::about()
145//! [13] //! [14]
146{
147 QMessageBox::about(this, tr("About Application"),
148 tr("The <b>Application</b> example demonstrates how to "
149 "write modern GUI applications using Qt, with a menu bar, "
150 "toolbars, and a status bar."));
151}
152//! [14]
153
154//! [15]
155void MainWindow::documentWasModified()
156//! [15] //! [16]
157{
158 setWindowModified(textEdit->document()->isModified());
159}
160//! [16]
161
162//! [17]
163void MainWindow::createActions()
164//! [17] //! [18]
165{
166
167 QMenu *fileMenu = menuBar()->addMenu(tr("&File"));
168 QToolBar *fileToolBar = addToolBar(tr("File"));
169 const QIcon newIcon = QIcon::fromTheme("document-new", QIcon(":/images/new.png"));
170 QAction *newAct = new QAction(newIcon, tr("&New"), this);
171 newAct->setShortcuts(QKeySequence::New);
172 newAct->setStatusTip(tr("Create a new file"));
173 connect(newAct, &QAction::triggered, this, &MainWindow::newFile);
174 fileMenu->addAction(newAct);
175 fileToolBar->addAction(newAct);
176
177//! [19]
178 const QIcon openIcon = QIcon::fromTheme("document-open", QIcon(":/images/open.png"));
179 QAction *openAct = new QAction(openIcon, tr("&Open..."), this);
180 openAct->setShortcuts(QKeySequence::Open);
181 openAct->setStatusTip(tr("Open an existing file"));
182 connect(openAct, &QAction::triggered, this, &MainWindow::open);
183 fileMenu->addAction(openAct);
184 fileToolBar->addAction(openAct);
185//! [18] //! [19]
186
187 const QIcon saveIcon = QIcon::fromTheme("document-save", QIcon(":/images/save.png"));
188 QAction *saveAct = new QAction(saveIcon, tr("&Save"), this);
189 saveAct->setShortcuts(QKeySequence::Save);
190 saveAct->setStatusTip(tr("Save the document to disk"));
191 connect(saveAct, &QAction::triggered, this, &MainWindow::save);
192 fileMenu->addAction(saveAct);
193 fileToolBar->addAction(saveAct);
194
195 const QIcon saveAsIcon = QIcon::fromTheme("document-save-as");
196 QAction *saveAsAct = fileMenu->addAction(saveAsIcon, tr("Save &As..."), this, &MainWindow::saveAs);
197 saveAsAct->setShortcuts(QKeySequence::SaveAs);
198 saveAsAct->setStatusTip(tr("Save the document under a new name"));
199
200//! [20]
201
202 fileMenu->addSeparator();
203
204 const QIcon exitIcon = QIcon::fromTheme("application-exit");
205 QAction *exitAct = fileMenu->addAction(exitIcon, tr("E&xit"), this, &QWidget::close);
206 exitAct->setShortcuts(QKeySequence::Quit);
207//! [20]
208 exitAct->setStatusTip(tr("Exit the application"));
209
210//! [21]
211 QMenu *editMenu = menuBar()->addMenu(tr("&Edit"));
212 QToolBar *editToolBar = addToolBar(tr("Edit"));
213//!
214#ifndef QT_NO_CLIPBOARD
215 const QIcon cutIcon = QIcon::fromTheme("edit-cut", QIcon(":/images/cut.png"));
216 QAction *cutAct = new QAction(cutIcon, tr("Cu&t"), this);
217//! [21]
218 cutAct->setShortcuts(QKeySequence::Cut);
219 cutAct->setStatusTip(tr("Cut the current selection's contents to the "
220 "clipboard"));
221 connect(cutAct, &QAction::triggered, textEdit, &QPlainTextEdit::cut);
222 editMenu->addAction(cutAct);
223 editToolBar->addAction(cutAct);
224
225 const QIcon copyIcon = QIcon::fromTheme("edit-copy", QIcon(":/images/copy.png"));
226 QAction *copyAct = new QAction(copyIcon, tr("&Copy"), this);
227 copyAct->setShortcuts(QKeySequence::Copy);
228 copyAct->setStatusTip(tr("Copy the current selection's contents to the "
229 "clipboard"));
230 connect(copyAct, &QAction::triggered, textEdit, &QPlainTextEdit::copy);
231 editMenu->addAction(copyAct);
232 editToolBar->addAction(copyAct);
233
234 const QIcon pasteIcon = QIcon::fromTheme("edit-paste", QIcon(":/images/paste.png"));
235 QAction *pasteAct = new QAction(pasteIcon, tr("&Paste"), this);
236 pasteAct->setShortcuts(QKeySequence::Paste);
237 pasteAct->setStatusTip(tr("Paste the clipboard's contents into the current "
238 "selection"));
239 connect(pasteAct, &QAction::triggered, textEdit, &QPlainTextEdit::paste);
240 editMenu->addAction(pasteAct);
241 editToolBar->addAction(pasteAct);
242
243 menuBar()->addSeparator();
244
245#endif // !QT_NO_CLIPBOARD
246
247 QMenu *helpMenu = menuBar()->addMenu(tr("&Help"));
248 QAction *aboutAct = helpMenu->addAction(tr("&About"), this, &MainWindow::about);
249 aboutAct->setStatusTip(tr("Show the application's About box"));
250
251//! [22]
252
253 QAction *aboutQtAct = helpMenu->addAction(tr("About &Qt"), qApp, &QApplication::aboutQt);
254 aboutQtAct->setStatusTip(tr("Show the Qt library's About box"));
255//! [22]
256
257//! [23]
258#ifndef QT_NO_CLIPBOARD
259 cutAct->setEnabled(false);
260//! [23] //! [24]
261 copyAct->setEnabled(false);
262 connect(textEdit, &QPlainTextEdit::copyAvailable, cutAct, &QAction::setEnabled);
263 connect(textEdit, &QPlainTextEdit::copyAvailable, copyAct, &QAction::setEnabled);
264#endif // !QT_NO_CLIPBOARD
265}
266//! [24]
267
268//! [32]
269void MainWindow::createStatusBar()
270//! [32] //! [33]
271{
272 statusBar()->showMessage(tr("Ready"));
273}
274//! [33]
275
276//! [34] //! [35]
277void MainWindow::readSettings()
278//! [34] //! [36]
279{
280 QSettings settings(QCoreApplication::organizationName(), QCoreApplication::applicationName());
281 const QByteArray geometry = settings.value("geometry", QByteArray()).toByteArray();
282 if (geometry.isEmpty()) {
283 const QRect availableGeometry = screen()->availableGeometry();
284 resize(availableGeometry.width() / 3, availableGeometry.height() / 2);
285 move((availableGeometry.width() - width()) / 2,
286 (availableGeometry.height() - height()) / 2);
287 } else {
288 restoreGeometry(geometry);
289 }
290}
291//! [35] //! [36]
292
293//! [37] //! [38]
294void MainWindow::writeSettings()
295//! [37] //! [39]
296{
297 QSettings settings(QCoreApplication::organizationName(), QCoreApplication::applicationName());
298 settings.setValue("geometry", saveGeometry());
299}
300//! [38] //! [39]
301
302//! [40]
303bool MainWindow::maybeSave()
304//! [40] //! [41]
305{
306 if (!textEdit->document()->isModified())
307 return true;
308 const QMessageBox::StandardButton ret
309 = QMessageBox::warning(this, tr("Application"),
310 tr("The document has been modified.\n"
311 "Do you want to save your changes?"),
312 QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);
313 switch (ret) {
314 case QMessageBox::Save:
315 return save();
316 case QMessageBox::Cancel:
317 return false;
318 default:
319 break;
320 }
321 return true;
322}
323//! [41]
324
325//! [42]
326void MainWindow::loadFile(const QString &fileName)
327//! [42] //! [43]
328{
329 QFile file(fileName);
330 if (!file.open(QFile::ReadOnly | QFile::Text)) {
331 QMessageBox::warning(this, tr("Application"),
332 tr("Cannot read file %1:\n%2.")
333 .arg(QDir::toNativeSeparators(fileName), file.errorString()));
334 return;
335 }
336
337 QTextStream in(&file);
338#ifndef QT_NO_CURSOR
339 QGuiApplication::setOverrideCursor(Qt::WaitCursor);
340#endif
341 textEdit->setPlainText(in.readAll());
342#ifndef QT_NO_CURSOR
343 QGuiApplication::restoreOverrideCursor();
344#endif
345
346 setCurrentFile(fileName);
347 statusBar()->showMessage(tr("File loaded"), 2000);
348}
349//! [43]
350
351//! [44]
352bool MainWindow::saveFile(const QString &fileName)
353//! [44] //! [45]
354{
355 QString errorMessage;
356
357 QGuiApplication::setOverrideCursor(Qt::WaitCursor);
358 QSaveFile file(fileName);
359 if (file.open(QFile::WriteOnly | QFile::Text)) {
360 QTextStream out(&file);
361 out << textEdit->toPlainText();
362 if (!file.commit()) {
363 errorMessage = tr("Cannot write file %1:\n%2.")
364 .arg(QDir::toNativeSeparators(fileName), file.errorString());
365 }
366 } else {
367 errorMessage = tr("Cannot open file %1 for writing:\n%2.")
368 .arg(QDir::toNativeSeparators(fileName), file.errorString());
369 }
370 QGuiApplication::restoreOverrideCursor();
371
372 if (!errorMessage.isEmpty()) {
373 QMessageBox::warning(this, tr("Application"), errorMessage);
374 return false;
375 }
376
377 setCurrentFile(fileName);
378 statusBar()->showMessage(tr("File saved"), 2000);
379 return true;
380}
381//! [45]
382
383//! [46]
384void MainWindow::setCurrentFile(const QString &fileName)
385//! [46] //! [47]
386{
387 curFile = fileName;
388 textEdit->document()->setModified(false);
389 setWindowModified(false);
390
391 QString shownName = curFile;
392 if (curFile.isEmpty())
393 shownName = "untitled.txt";
394 setWindowFilePath(shownName);
395}
396//! [47]
397
398//! [48]
399QString MainWindow::strippedName(const QString &fullFileName)
400//! [48] //! [49]
401{
402 return QFileInfo(fullFileName).fileName();
403}
404//! [49]
405#ifndef QT_NO_SESSIONMANAGER
406void MainWindow::commitData(QSessionManager &manager)
407{
408 if (manager.allowsInteraction()) {
409 if (!maybeSave())
410 manager.cancel();
411 } else {
412 // Non-interactive: save without asking
413 if (textEdit->document()->isModified())
414 save();
415 }
416}
417#endif
418