mainwindow.cpp Example File
mainwindows/sdi/mainwindow.cpp
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of The Qt Company Ltd nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QtWidgets>
#include "mainwindow.h"
MainWindow:: MainWindow()
{
init();
setCurrentFile(QString ());
}
MainWindow:: MainWindow(const QString & fileName)
{
init();
loadFile(fileName);
}
void MainWindow:: closeEvent(QCloseEvent * event)
{
if (maybeSave()) {
writeSettings();
event- > accept();
} else {
event- > ignore();
}
}
void MainWindow:: newFile()
{
MainWindow * other = new MainWindow;
other- > tile(this );
other- > show();
}
void MainWindow:: open()
{
const QString fileName = QFileDialog :: getOpenFileName(this );
if (! fileName. isEmpty())
openFile(fileName);
}
void MainWindow:: openFile(const QString & fileName)
{
MainWindow * existing = findMainWindow(fileName);
if (existing) {
existing- > show();
existing- > raise();
existing- > activateWindow();
return ;
}
if (isUntitled & & textEdit- > document()- > isEmpty() & & ! isWindowModified()) {
loadFile(fileName);
return ;
}
MainWindow * other = new MainWindow(fileName);
if (other- > isUntitled) {
delete other;
return ;
}
other- > tile(this );
other- > show();
}
bool MainWindow:: save()
{
return isUntitled ? saveAs() : saveFile(curFile);
}
bool MainWindow:: saveAs()
{
QString fileName = QFileDialog :: getSaveFileName(this , tr("Save As" ),
curFile);
if (fileName. isEmpty())
return false ;
return saveFile(fileName);
}
void MainWindow:: about()
{
QMessageBox :: about(this , tr("About SDI" ),
tr("The <b>SDI</b> example demonstrates how to write single "
"document interface applications using Qt." ));
}
void MainWindow:: documentWasModified()
{
setWindowModified(true );
}
void MainWindow:: init()
{
setAttribute(Qt :: WA_DeleteOnClose);
isUntitled = true ;
textEdit = new QTextEdit ;
setCentralWidget(textEdit);
createActions();
createStatusBar();
readSettings();
connect(textEdit- > document(), & QTextDocument :: contentsChanged,
this , & MainWindow:: documentWasModified);
setUnifiedTitleAndToolBarOnMac(true );
}
void MainWindow:: tile(const QMainWindow * previous)
{
if (! previous)
return ;
int topFrameWidth = previous- > geometry(). top() - previous- > pos(). y();
if (! topFrameWidth)
topFrameWidth = 40 ;
const QPoint pos = previous- > pos() + 2 * QPoint (topFrameWidth, topFrameWidth);
if (QApplication :: desktop()- > availableGeometry(this ). contains(rect(). bottomRight() + pos))
move(pos);
}
void MainWindow:: createActions()
{
QMenu * fileMenu = menuBar()- > addMenu(tr("&File" ));
QToolBar * fileToolBar = addToolBar(tr("File" ));
const QIcon newIcon = QIcon :: fromTheme("document-new" , QIcon (":/images/new.png" ));
QAction * newAct = new QAction (newIcon, tr("&New" ), this );
newAct- > setShortcuts(QKeySequence :: New);
newAct- > setStatusTip(tr("Create a new file" ));
connect(newAct, & QAction :: triggered, this , & MainWindow:: newFile);
fileMenu- > addAction(newAct);
fileToolBar- > addAction(newAct);
const QIcon openIcon = QIcon :: fromTheme("document-open" , QIcon (":/images/open.png" ));
QAction * openAct = new QAction (openIcon, tr("&Open..." ), this );
openAct- > setShortcuts(QKeySequence :: Open);
openAct- > setStatusTip(tr("Open an existing file" ));
connect(openAct, & QAction :: triggered, this , & MainWindow:: open);
fileMenu- > addAction(openAct);
fileToolBar- > addAction(openAct);
const QIcon saveIcon = QIcon :: fromTheme("document-save" , QIcon (":/images/save.png" ));
QAction * saveAct = new QAction (saveIcon, tr("&Save" ), this );
saveAct- > setShortcuts(QKeySequence :: Save);
saveAct- > setStatusTip(tr("Save the document to disk" ));
connect(saveAct, & QAction :: triggered, this , & MainWindow:: save);
fileMenu- > addAction(saveAct);
fileToolBar- > addAction(saveAct);
const QIcon saveAsIcon = QIcon :: fromTheme("document-save-as" );
QAction * saveAsAct = fileMenu- > addAction(saveAsIcon, tr("Save &As..." ), this , & MainWindow:: saveAs);
saveAsAct- > setShortcuts(QKeySequence :: SaveAs);
saveAsAct- > setStatusTip(tr("Save the document under a new name" ));
fileMenu- > addSeparator();
QMenu * recentMenu = fileMenu- > addMenu(tr("Recent..." ));
connect(recentMenu, & QMenu :: aboutToShow, this , & MainWindow:: updateRecentFileActions);
recentFileSubMenuAct = recentMenu- > menuAction();
for (int i = 0 ; i < MaxRecentFiles; + + i) {
recentFileActs[ i] = recentMenu- > addAction(QString (), this , & MainWindow:: openRecentFile);
recentFileActs[ i] - > setVisible(false );
}
recentFileSeparator = fileMenu- > addSeparator();
setRecentFilesVisible(MainWindow:: hasRecentFiles());
QAction * closeAct = fileMenu- > addAction(tr("&Close" ), this , & QWidget :: close);
closeAct- > setShortcut(tr("Ctrl+W" ));
closeAct- > setStatusTip(tr("Close this window" ));
const QIcon exitIcon = QIcon :: fromTheme("application-exit" );
QAction * exitAct = fileMenu- > addAction(exitIcon, tr("E&xit" ), qApp, & QApplication :: closeAllWindows);
exitAct- > setShortcuts(QKeySequence :: Quit);
exitAct- > setStatusTip(tr("Exit the application" ));
QMenu * editMenu = menuBar()- > addMenu(tr("&Edit" ));
QToolBar * editToolBar = addToolBar(tr("Edit" ));
#ifndef QT_NO_CLIPBOARD
const QIcon cutIcon = QIcon :: fromTheme("edit-cut" , QIcon (":/images/cut.png" ));
QAction * cutAct = new QAction (cutIcon, tr("Cu&t" ), this );
cutAct- > setShortcuts(QKeySequence :: Cut);
cutAct- > setStatusTip(tr("Cut the current selection's contents to the "
"clipboard" ));
connect(cutAct, & QAction :: triggered, textEdit, & QTextEdit :: cut);
editMenu- > addAction(cutAct);
editToolBar- > addAction(cutAct);
const QIcon copyIcon = QIcon :: fromTheme("edit-copy" , QIcon (":/images/copy.png" ));
QAction * copyAct = new QAction (copyIcon, tr("&Copy" ), this );
copyAct- > setShortcuts(QKeySequence :: Copy);
copyAct- > setStatusTip(tr("Copy the current selection's contents to the "
"clipboard" ));
connect(copyAct, & QAction :: triggered, textEdit, & QTextEdit :: copy);
editMenu- > addAction(copyAct);
editToolBar- > addAction(copyAct);
const QIcon pasteIcon = QIcon :: fromTheme("edit-paste" , QIcon (":/images/paste.png" ));
QAction * pasteAct = new QAction (pasteIcon, tr("&Paste" ), this );
pasteAct- > setShortcuts(QKeySequence :: Paste);
pasteAct- > setStatusTip(tr("Paste the clipboard's contents into the current "
"selection" ));
connect(pasteAct, & QAction :: triggered, textEdit, & QTextEdit :: paste);
editMenu- > addAction(pasteAct);
editToolBar- > addAction(pasteAct);
menuBar()- > addSeparator();
#endif // !QT_NO_CLIPBOARD
QMenu * helpMenu = menuBar()- > addMenu(tr("&Help" ));
QAction * aboutAct = helpMenu- > addAction(tr("&About" ), this , & MainWindow:: about);
aboutAct- > setStatusTip(tr("Show the application's About box" ));
QAction * aboutQtAct = helpMenu- > addAction(tr("About &Qt" ), qApp, & QApplication :: aboutQt);
aboutQtAct- > setStatusTip(tr("Show the Qt library's About box" ));
#ifndef QT_NO_CLIPBOARD
cutAct- > setEnabled(false );
copyAct- > setEnabled(false );
connect(textEdit, & QTextEdit :: copyAvailable, cutAct, & QAction :: setEnabled);
connect(textEdit, & QTextEdit :: copyAvailable, copyAct, & QAction :: setEnabled);
#endif // !QT_NO_CLIPBOARD
}
void MainWindow:: createStatusBar()
{
statusBar()- > showMessage(tr("Ready" ));
}
void MainWindow:: readSettings()
{
QSettings settings(QCoreApplication :: organizationName(), QCoreApplication :: applicationName());
const QByteArray geometry = settings. value("geometry" , QByteArray ()). toByteArray();
if (geometry. isEmpty()) {
const QRect availableGeometry = QApplication :: desktop()- > availableGeometry(this );
resize(availableGeometry. width() / 3 , availableGeometry. height() / 2 );
move((availableGeometry. width() - width()) / 2 ,
(availableGeometry. height() - height()) / 2 );
} else {
restoreGeometry(geometry);
}
}
void MainWindow:: writeSettings()
{
QSettings settings(QCoreApplication :: organizationName(), QCoreApplication :: applicationName());
settings. setValue("geometry" , saveGeometry());
}
bool MainWindow:: maybeSave()
{
if (! textEdit- > document()- > isModified())
return true ;
const QMessageBox :: StandardButton ret
= QMessageBox :: warning(this , tr("SDI" ),
tr("The document has been modified.\n"
"Do you want to save your changes?" ),
QMessageBox :: Save | QMessageBox :: Discard
| QMessageBox :: Cancel);
switch (ret) {
case QMessageBox :: Save:
return save();
case QMessageBox :: Cancel:
return false ;
default :
break ;
}
return true ;
}
void MainWindow:: loadFile(const QString & fileName)
{
QFile file(fileName);
if (! file. open(QFile :: ReadOnly | QFile :: Text)) {
QMessageBox :: warning(this , tr("SDI" ),
tr("Cannot read file %1:\n%2." )
. arg(QDir :: toNativeSeparators(fileName), file. errorString()));
return ;
}
QTextStream in(& file);
QApplication :: setOverrideCursor(Qt :: WaitCursor);
textEdit- > setPlainText(in. readAll());
QApplication :: restoreOverrideCursor();
setCurrentFile(fileName);
statusBar()- > showMessage(tr("File loaded" ), 2000 );
}
void MainWindow:: setRecentFilesVisible(bool visible)
{
recentFileSubMenuAct- > setVisible(visible);
recentFileSeparator- > setVisible(visible);
}
static inline QString recentFilesKey() { return QStringLiteral ("recentFileList" ); }
static inline QString fileKey() { return QStringLiteral ("file" ); }
static QStringList readRecentFiles(QSettings & settings)
{
QStringList result;
const int count = settings. beginReadArray(recentFilesKey());
for (int i = 0 ; i < count; + + i) {
settings. setArrayIndex(i);
result. append(settings. value(fileKey()). toString());
}
settings. endArray();
return result;
}
static void writeRecentFiles(const QStringList & files, QSettings & settings)
{
const int count = files. size();
settings. beginWriteArray(recentFilesKey());
for (int i = 0 ; i < count; + + i) {
settings. setArrayIndex(i);
settings. setValue(fileKey(), files. at(i));
}
settings. endArray();
}
bool MainWindow:: hasRecentFiles()
{
QSettings settings(QCoreApplication :: organizationName(), QCoreApplication :: applicationName());
const int count = settings. beginReadArray(recentFilesKey());
settings. endArray();
return count > 0 ;
}
void MainWindow:: prependToRecentFiles(const QString & fileName)
{
QSettings settings(QCoreApplication :: organizationName(), QCoreApplication :: applicationName());
const QStringList oldRecentFiles = readRecentFiles(settings);
QStringList recentFiles = oldRecentFiles;
recentFiles. removeAll(fileName);
recentFiles. prepend(fileName);
if (oldRecentFiles ! = recentFiles)
writeRecentFiles(recentFiles, settings);
setRecentFilesVisible(! recentFiles. isEmpty());
}
void MainWindow:: updateRecentFileActions()
{
QSettings settings(QCoreApplication :: organizationName(), QCoreApplication :: applicationName());
const QStringList recentFiles = readRecentFiles(settings);
const int count = qMin (int (MaxRecentFiles), recentFiles. size());
int i = 0 ;
for ( ; i < count; + + i) {
const QString fileName = MainWindow:: strippedName(recentFiles. at(i));
recentFileActs[ i] - > setText(tr("&%1 %2" ). arg(i + 1 ). arg(fileName));
recentFileActs[ i] - > setData(recentFiles. at(i));
recentFileActs[ i] - > setVisible(true );
}
for ( ; i < MaxRecentFiles; + + i)
recentFileActs[ i] - > setVisible(false );
}
void MainWindow:: openRecentFile()
{
if (const QAction * action = qobject_cast< const QAction * > (sender()))
openFile(action- > data(). toString());
}
bool MainWindow:: saveFile(const QString & fileName)
{
QFile file(fileName);
if (! file. open(QFile :: WriteOnly | QFile :: Text)) {
QMessageBox :: warning(this , tr("SDI" ),
tr("Cannot write file %1:\n%2." )
. arg(QDir :: toNativeSeparators(fileName), file. errorString()));
return false ;
}
QTextStream out(& file);
QApplication :: setOverrideCursor(Qt :: WaitCursor);
out < < textEdit- > toPlainText();
QApplication :: restoreOverrideCursor();
setCurrentFile(fileName);
statusBar()- > showMessage(tr("File saved" ), 2000 );
return true ;
}
void MainWindow:: setCurrentFile(const QString & fileName)
{
static int sequenceNumber = 1 ;
isUntitled = fileName. isEmpty();
if (isUntitled) {
curFile = tr("document%1.txt" ). arg(sequenceNumber+ + );
} else {
curFile = QFileInfo (fileName). canonicalFilePath();
}
textEdit- > document()- > setModified(false );
setWindowModified(false );
if (! isUntitled & & windowFilePath() ! = curFile)
MainWindow:: prependToRecentFiles(curFile);
setWindowFilePath(curFile);
}
QString MainWindow:: strippedName(const QString & fullFileName)
{
return QFileInfo (fullFileName). fileName();
}
MainWindow * MainWindow:: findMainWindow(const QString & fileName) const
{
QString canonicalFilePath = QFileInfo (fileName). canonicalFilePath();
foreach (QWidget * widget, QApplication :: topLevelWidgets()) {
MainWindow * mainWin = qobject_cast< MainWindow * > (widget);
if (mainWin & & mainWin- > curFile = = canonicalFilePath)
return mainWin;
}
return 0 ;
}