From ad4d25589fcd5efd4faa3074c19928d195f615bb Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Tue, 21 Jul 2015 08:43:39 +0200 Subject: [PATCH] Add recent file handling to SDI/MDI and remove the recentfiles example. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The existing recentfiles example was basically a clone of the SDI example with a "Recent" menu added. Assuming it is better to have it all in one place, the functionality is merged into the existing SDI/MDI examples. - Implement recently opened files handling using a submenu and a QSettings array in the SDI/MDI examples. - Remove recentfiles example. Change-Id: Id5a1ab9fa1c2e6b9ec81309cfe74cf86f450392a Reviewed-by: Topi Reiniƶ --- doc/src/images/recentfiles-example.png | Bin 5400 -> 0 bytes examples/widgets/doc/src/application.qdoc | 9 +- examples/widgets/doc/src/recentfiles.qdoc | 37 --- examples/widgets/mainwindows/mainwindows.pro | 1 - examples/widgets/mainwindows/mdi/main.cpp | 2 + .../widgets/mainwindows/mdi/mainwindow.cpp | 123 ++++++++- examples/widgets/mainwindows/mdi/mainwindow.h | 11 + .../widgets/mainwindows/recentfiles/main.cpp | 53 ---- .../mainwindows/recentfiles/mainwindow.cpp | 251 ------------------ .../mainwindows/recentfiles/mainwindow.h | 96 ------- .../mainwindows/recentfiles/recentfiles.pro | 9 - .../widgets/mainwindows/sdi/mainwindow.cpp | 101 ++++++- examples/widgets/mainwindows/sdi/mainwindow.h | 13 + 13 files changed, 241 insertions(+), 465 deletions(-) delete mode 100644 doc/src/images/recentfiles-example.png delete mode 100644 examples/widgets/doc/src/recentfiles.qdoc delete mode 100644 examples/widgets/mainwindows/recentfiles/main.cpp delete mode 100644 examples/widgets/mainwindows/recentfiles/mainwindow.cpp delete mode 100644 examples/widgets/mainwindows/recentfiles/mainwindow.h delete mode 100644 examples/widgets/mainwindows/recentfiles/recentfiles.pro diff --git a/doc/src/images/recentfiles-example.png b/doc/src/images/recentfiles-example.png deleted file mode 100644 index 8a1f2e5509426ab96a3e3280225908f37b3ab14e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5400 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 a6+n7ejiTp3u^eNN; diff --git a/examples/widgets/doc/src/application.qdoc b/examples/widgets/doc/src/application.qdoc index 048b4bfd211..cd284ecba0b 100644 --- a/examples/widgets/doc/src/application.qdoc +++ b/examples/widgets/doc/src/application.qdoc @@ -50,11 +50,10 @@ To keep the example simple, recently opened files aren't shown in the \uicontrol{File} menu, even though this feature is desired in 90% - of applications. The \l{mainwindows/recentfiles}{Recent Files} - example shows how to implement this. Furthermore, this example - can only load one file at a time. The \l{mainwindows/sdi}{SDI} - and \l{mainwindows/mdi}{MDI} examples shows how to lift these - restrictions. + of applications. Furthermore, this example can only load one file at a + time. The \l{mainwindows/sdi}{SDI} and \l{mainwindows/mdi}{MDI} examples + show how to lift these restrictions and how to implement recently opened files + handling. \section1 MainWindow Class Definition diff --git a/examples/widgets/doc/src/recentfiles.qdoc b/examples/widgets/doc/src/recentfiles.qdoc deleted file mode 100644 index b58c9a1f767..00000000000 --- a/examples/widgets/doc/src/recentfiles.qdoc +++ /dev/null @@ -1,37 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the documentation of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:FDL$ -** 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 http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Free Documentation License Usage -** Alternatively, this file may be used under the terms of the GNU Free -** Documentation License version 1.3 as published by the Free Software -** Foundation and appearing in the file included in the packaging of -** this file. Please review the following information to ensure -** the GNU Free Documentation License version 1.3 requirements -** will be met: http://www.gnu.org/copyleft/fdl.html. -** $QT_END_LICENSE$ -** -****************************************************************************/ - -/*! - \example mainwindows/recentfiles - \title Recent Files Example - \ingroup examples-mainwindow - - \brief The Recent Files example shows how a standard File menu can be extended to show - the most recent files loaded by a main window application. - - \image recentfiles-example.png -*/ diff --git a/examples/widgets/mainwindows/mainwindows.pro b/examples/widgets/mainwindows/mainwindows.pro index 52179ec9bd9..dcda89abaf6 100644 --- a/examples/widgets/mainwindows/mainwindows.pro +++ b/examples/widgets/mainwindows/mainwindows.pro @@ -4,5 +4,4 @@ SUBDIRS = application \ mainwindow \ mdi \ menus \ - recentfiles \ sdi diff --git a/examples/widgets/mainwindows/mdi/main.cpp b/examples/widgets/mainwindows/mdi/main.cpp index 5976c85c1c2..f02285d1cf7 100644 --- a/examples/widgets/mainwindows/mdi/main.cpp +++ b/examples/widgets/mainwindows/mdi/main.cpp @@ -49,6 +49,8 @@ int main(int argc, char *argv[]) Q_INIT_RESOURCE(mdi); QApplication app(argc, argv); + QCoreApplication::setApplicationName("MDI Example"); + QCoreApplication::setOrganizationName("QtProject"); QCoreApplication::setApplicationVersion(QT_VERSION_STR); QCommandLineParser parser; parser.setApplicationDescription("Qt MDI Example"); diff --git a/examples/widgets/mainwindows/mdi/mainwindow.cpp b/examples/widgets/mainwindows/mdi/mainwindow.cpp index 4dada5ce709..35136c81c4c 100644 --- a/examples/widgets/mainwindows/mdi/mainwindow.cpp +++ b/examples/widgets/mainwindows/mdi/mainwindow.cpp @@ -82,19 +82,24 @@ void MainWindow::newFile() void MainWindow::open() { - QString fileName = QFileDialog::getOpenFileName(this); - if (!fileName.isEmpty()) { - if (QMdiSubWindow *existing = findMdiChild(fileName)) { - mdiArea->setActiveSubWindow(existing); - return; - } - - if (openFile(fileName)) - statusBar()->showMessage(tr("File loaded"), 2000); - } + const QString fileName = QFileDialog::getOpenFileName(this); + if (!fileName.isEmpty()) + openFile(fileName); } bool MainWindow::openFile(const QString &fileName) +{ + if (QMdiSubWindow *existing = findMdiChild(fileName)) { + mdiArea->setActiveSubWindow(existing); + return true; + } + const bool succeeded = loadFile(fileName); + if (succeeded) + statusBar()->showMessage(tr("File loaded"), 2000); + return succeeded; +} + +bool MainWindow::loadFile(const QString &fileName) { MdiChild *child = createMdiChild(); const bool succeeded = child->loadFile(fileName); @@ -102,9 +107,87 @@ bool MainWindow::openFile(const QString &fileName) child->show(); else child->close(); + MainWindow::prependToRecentFiles(fileName); return succeeded; } +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::setRecentFilesVisible(bool visible) +{ + recentFileSubMenuAct->setVisible(visible); + recentFileSeparator->setVisible(visible); +} + +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 = QFileInfo(recentFiles.at(i)).fileName(); + 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(sender())) + openFile(action->data().toString()); +} + void MainWindow::save() { if (activeMdiChild() && activeMdiChild()->save()) @@ -113,8 +196,11 @@ void MainWindow::save() void MainWindow::saveAs() { - if (activeMdiChild() && activeMdiChild()->saveAs()) + MdiChild *child = activeMdiChild(); + if (child && child->saveAs()) { statusBar()->showMessage(tr("File saved"), 2000); + MainWindow::prependToRecentFiles(child->currentFile()); + } } #ifndef QT_NO_CLIPBOARD @@ -262,8 +348,23 @@ void MainWindow::createActions() 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()); + fileMenu->addAction(tr("Switch layout direction"), this, &MainWindow::switchLayoutDirection); + fileMenu->addSeparator(); + //! [0] const QIcon exitIcon = QIcon::fromTheme("application-exit"); QAction *exitAct = fileMenu->addAction(exitIcon, tr("E&xit"), qApp, &QApplication::closeAllWindows); diff --git a/examples/widgets/mainwindows/mdi/mainwindow.h b/examples/widgets/mainwindows/mdi/mainwindow.h index bdc2a6a09d4..3ac60282fd8 100644 --- a/examples/widgets/mainwindows/mdi/mainwindow.h +++ b/examples/widgets/mainwindows/mdi/mainwindow.h @@ -68,6 +68,8 @@ private slots: void open(); void save(); void saveAs(); + void updateRecentFileActions(); + void openRecentFile(); #ifndef QT_NO_CLIPBOARD void cut(); void copy(); @@ -80,10 +82,16 @@ private slots: void switchLayoutDirection(); private: + enum { MaxRecentFiles = 5 }; + void createActions(); void createStatusBar(); void readSettings(); void writeSettings(); + bool loadFile(const QString &fileName); + static bool hasRecentFiles(); + void prependToRecentFiles(const QString &fileName); + void setRecentFilesVisible(bool visible); MdiChild *activeMdiChild() const; QMdiSubWindow *findMdiChild(const QString &fileName) const; @@ -93,6 +101,9 @@ private: QAction *newAct; QAction *saveAct; QAction *saveAsAct; + QAction *recentFileActs[MaxRecentFiles]; + QAction *recentFileSeparator; + QAction *recentFileSubMenuAct; #ifndef QT_NO_CLIPBOARD QAction *cutAct; QAction *copyAct; diff --git a/examples/widgets/mainwindows/recentfiles/main.cpp b/examples/widgets/mainwindows/recentfiles/main.cpp deleted file mode 100644 index 23ff3eda160..00000000000 --- a/examples/widgets/mainwindows/recentfiles/main.cpp +++ /dev/null @@ -1,53 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the examples of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** 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 - -#include "mainwindow.h" - -int main(int argc, char *argv[]) -{ - QApplication app(argc, argv); - app.setOrganizationName("QtProject"); - app.setApplicationName("Recent Files Example"); - MainWindow *mainWin = new MainWindow; - mainWin->show(); - return app.exec(); -} diff --git a/examples/widgets/mainwindows/recentfiles/mainwindow.cpp b/examples/widgets/mainwindows/recentfiles/mainwindow.cpp deleted file mode 100644 index b89797092a3..00000000000 --- a/examples/widgets/mainwindows/recentfiles/mainwindow.cpp +++ /dev/null @@ -1,251 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the examples of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** 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 - -#include "mainwindow.h" - -MainWindow::MainWindow() -{ - setAttribute(Qt::WA_DeleteOnClose); - - textEdit = new QTextEdit; - setCentralWidget(textEdit); - - createActions(); - createMenus(); - (void)statusBar(); - - setWindowFilePath(QString()); - resize(400, 300); -} - -void MainWindow::newFile() -{ - MainWindow *other = new MainWindow; - other->show(); -} - -void MainWindow::open() -{ - QString fileName = QFileDialog::getOpenFileName(this); - if (!fileName.isEmpty()) - loadFile(fileName); -} - -void MainWindow::save() -{ - if (curFile.isEmpty()) - saveAs(); - else - saveFile(curFile); -} - -void MainWindow::saveAs() -{ - QString fileName = QFileDialog::getSaveFileName(this); - if (fileName.isEmpty()) - return; - - saveFile(fileName); -} - -void MainWindow::openRecentFile() -{ - QAction *action = qobject_cast(sender()); - if (action) - loadFile(action->data().toString()); -} - -void MainWindow::about() -{ - QMessageBox::about(this, tr("About Recent Files"), - tr("The Recent Files example demonstrates how to provide a " - "recently used file menu in a Qt application.")); -} - -void MainWindow::createActions() -{ - newAct = new QAction(tr("&New"), this); - newAct->setShortcuts(QKeySequence::New); - newAct->setStatusTip(tr("Create a new file")); - connect(newAct, SIGNAL(triggered()), this, SLOT(newFile())); - - openAct = new QAction(tr("&Open..."), this); - openAct->setShortcuts(QKeySequence::Open); - openAct->setStatusTip(tr("Open an existing file")); - connect(openAct, SIGNAL(triggered()), this, SLOT(open())); - - saveAct = new QAction(tr("&Save"), this); - saveAct->setShortcuts(QKeySequence::Save); - saveAct->setStatusTip(tr("Save the document to disk")); - connect(saveAct, SIGNAL(triggered()), this, SLOT(save())); - - saveAsAct = new QAction(tr("Save &As..."), this); - saveAsAct->setShortcuts(QKeySequence::SaveAs); - saveAsAct->setStatusTip(tr("Save the document under a new name")); - connect(saveAsAct, SIGNAL(triggered()), this, SLOT(saveAs())); - - for (int i = 0; i < MaxRecentFiles; ++i) { - recentFileActs[i] = new QAction(this); - recentFileActs[i]->setVisible(false); - connect(recentFileActs[i], SIGNAL(triggered()), - this, SLOT(openRecentFile())); - } - - exitAct = new QAction(tr("E&xit"), this); - exitAct->setShortcuts(QKeySequence::Quit); - exitAct->setStatusTip(tr("Exit the application")); - connect(exitAct, SIGNAL(triggered()), qApp, SLOT(closeAllWindows())); - - aboutAct = new QAction(tr("&About"), this); - aboutAct->setStatusTip(tr("Show the application's About box")); - connect(aboutAct, SIGNAL(triggered()), this, SLOT(about())); - - aboutQtAct = new QAction(tr("About &Qt"), this); - aboutQtAct->setStatusTip(tr("Show the Qt library's About box")); - connect(aboutQtAct, SIGNAL(triggered()), qApp, SLOT(aboutQt())); -} - -void MainWindow::createMenus() -{ - fileMenu = menuBar()->addMenu(tr("&File")); - fileMenu->addAction(newAct); - fileMenu->addAction(openAct); - fileMenu->addAction(saveAct); - fileMenu->addAction(saveAsAct); - separatorAct = fileMenu->addSeparator(); - for (int i = 0; i < MaxRecentFiles; ++i) - fileMenu->addAction(recentFileActs[i]); - fileMenu->addSeparator(); - fileMenu->addAction(exitAct); - updateRecentFileActions(); - - menuBar()->addSeparator(); - - helpMenu = menuBar()->addMenu(tr("&Help")); - helpMenu->addAction(aboutAct); - helpMenu->addAction(aboutQtAct); -} - -void MainWindow::loadFile(const QString &fileName) -{ - QFile file(fileName); - if (!file.open(QFile::ReadOnly | QFile::Text)) { - QMessageBox::warning(this, tr("Recent Files"), - tr("Cannot read file %1:\n%2.") - .arg(fileName) - .arg(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::saveFile(const QString &fileName) -{ - QFile file(fileName); - if (!file.open(QFile::WriteOnly | QFile::Text)) { - QMessageBox::warning(this, tr("Recent Files"), - tr("Cannot write file %1:\n%2.") - .arg(fileName) - .arg(file.errorString())); - return; - } - - QTextStream out(&file); - QApplication::setOverrideCursor(Qt::WaitCursor); - out << textEdit->toPlainText(); - QApplication::restoreOverrideCursor(); - - setCurrentFile(fileName); - statusBar()->showMessage(tr("File saved"), 2000); -} - -void MainWindow::setCurrentFile(const QString &fileName) -{ - curFile = fileName; - setWindowFilePath(curFile); - - QSettings settings; - QStringList files = settings.value("recentFileList").toStringList(); - files.removeAll(fileName); - files.prepend(fileName); - while (files.size() > MaxRecentFiles) - files.removeLast(); - - settings.setValue("recentFileList", files); - - foreach (QWidget *widget, QApplication::topLevelWidgets()) { - MainWindow *mainWin = qobject_cast(widget); - if (mainWin) - mainWin->updateRecentFileActions(); - } -} - -void MainWindow::updateRecentFileActions() -{ - QSettings settings; - QStringList files = settings.value("recentFileList").toStringList(); - - int numRecentFiles = qMin(files.size(), (int)MaxRecentFiles); - - for (int i = 0; i < numRecentFiles; ++i) { - QString text = tr("&%1 %2").arg(i + 1).arg(strippedName(files[i])); - recentFileActs[i]->setText(text); - recentFileActs[i]->setData(files[i]); - recentFileActs[i]->setVisible(true); - } - for (int j = numRecentFiles; j < MaxRecentFiles; ++j) - recentFileActs[j]->setVisible(false); - - separatorAct->setVisible(numRecentFiles > 0); -} - -QString MainWindow::strippedName(const QString &fullFileName) -{ - return QFileInfo(fullFileName).fileName(); -} diff --git a/examples/widgets/mainwindows/recentfiles/mainwindow.h b/examples/widgets/mainwindows/recentfiles/mainwindow.h deleted file mode 100644 index 95252ca5258..00000000000 --- a/examples/widgets/mainwindows/recentfiles/mainwindow.h +++ /dev/null @@ -1,96 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the examples of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** 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$ -** -****************************************************************************/ - -#ifndef MAINWINDOW_H -#define MAINWINDOW_H - -#include -#include - -QT_BEGIN_NAMESPACE -class QAction; -class QMenu; -class QTextEdit; -QT_END_NAMESPACE - -class MainWindow : public QMainWindow -{ - Q_OBJECT - -public: - MainWindow(); - -private slots: - void newFile(); - void open(); - void save(); - void saveAs(); - void openRecentFile(); - void about(); - -private: - void createActions(); - void createMenus(); - void loadFile(const QString &fileName); - void saveFile(const QString &fileName); - void setCurrentFile(const QString &fileName); - void updateRecentFileActions(); - QString strippedName(const QString &fullFileName); - - QString curFile; - - QTextEdit *textEdit; - QMenu *fileMenu; - QMenu *recentFilesMenu; - QMenu *helpMenu; - QAction *newAct; - QAction *openAct; - QAction *saveAct; - QAction *saveAsAct; - QAction *exitAct; - QAction *aboutAct; - QAction *aboutQtAct; - QAction *separatorAct; - - enum { MaxRecentFiles = 5 }; - QAction *recentFileActs[MaxRecentFiles]; -}; - -#endif diff --git a/examples/widgets/mainwindows/recentfiles/recentfiles.pro b/examples/widgets/mainwindows/recentfiles/recentfiles.pro deleted file mode 100644 index ccf948f5602..00000000000 --- a/examples/widgets/mainwindows/recentfiles/recentfiles.pro +++ /dev/null @@ -1,9 +0,0 @@ -QT += widgets - -HEADERS = mainwindow.h -SOURCES = main.cpp \ - mainwindow.cpp - -# install -target.path = $$[QT_INSTALL_EXAMPLES]/widgets/mainwindows/recentfiles -INSTALLS += target diff --git a/examples/widgets/mainwindows/sdi/mainwindow.cpp b/examples/widgets/mainwindows/sdi/mainwindow.cpp index 0fba7f792b7..29618f9ac2f 100644 --- a/examples/widgets/mainwindows/sdi/mainwindow.cpp +++ b/examples/widgets/mainwindows/sdi/mainwindow.cpp @@ -74,9 +74,12 @@ void MainWindow::newFile() void MainWindow::open() { const QString fileName = QFileDialog::getOpenFileName(this); - if (fileName.isEmpty()) - return; + if (!fileName.isEmpty()) + openFile(fileName); +} +void MainWindow::openFile(const QString &fileName) +{ MainWindow *existing = findMainWindow(fileName); if (existing) { existing->show(); @@ -196,6 +199,19 @@ void MainWindow::createActions() 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")); @@ -316,6 +332,83 @@ void MainWindow::loadFile(const QString &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(sender())) + openFile(action->data().toString()); +} + bool MainWindow::saveFile(const QString &fileName) { QFile file(fileName); @@ -349,6 +442,10 @@ void MainWindow::setCurrentFile(const QString &fileName) textEdit->document()->setModified(false); setWindowModified(false); + + if (!isUntitled && windowFilePath() != curFile) + MainWindow::prependToRecentFiles(curFile); + setWindowFilePath(curFile); } diff --git a/examples/widgets/mainwindows/sdi/mainwindow.h b/examples/widgets/mainwindows/sdi/mainwindow.h index 3c01f38566d..66ac618c72e 100644 --- a/examples/widgets/mainwindows/sdi/mainwindow.h +++ b/examples/widgets/mainwindows/sdi/mainwindow.h @@ -70,23 +70,36 @@ private slots: void open(); bool save(); bool saveAs(); + void updateRecentFileActions(); + void openRecentFile(); void about(); void documentWasModified(); private: + enum { MaxRecentFiles = 5 }; + void init(); void createActions(); void createStatusBar(); void readSettings(); void writeSettings(); bool maybeSave(); + void openFile(const QString &fileName); void loadFile(const QString &fileName); + static bool hasRecentFiles(); + void prependToRecentFiles(const QString &fileName); + void setRecentFilesVisible(bool visible); bool saveFile(const QString &fileName); void setCurrentFile(const QString &fileName); static QString strippedName(const QString &fullFileName); MainWindow *findMainWindow(const QString &fileName) const; QTextEdit *textEdit; + + QAction *recentFileActs[MaxRecentFiles]; + QAction *recentFileSeparator; + QAction *recentFileSubMenuAct; + QString curFile; bool isUntitled; };