Remove the 'sdi' example

It is essentially the same as the other mainwindow examples, showing
how to create a text editor. The only special code here is the tiling of
the different main windows, which - without any documentation or
explanation - is neither very helpful, nor relevant in 2023.

Change-Id: I48b92b1cf057f586e0d2842d1c0a3312154e9a13
Reviewed-by: Axel Spoerl <axel.spoerl@qt.io>
(cherry picked from commit 3c104c279911419f3d0a9e6f3ac050e6f3968e7e)
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
This commit is contained in:
Volker Hilsheimer 2023-03-01 14:56:34 +01:00
parent d32e64ab75
commit 72a132bbcd
19 changed files with 5 additions and 646 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

View File

@ -27,9 +27,8 @@
To keep the example simple, recently opened files aren't shown in To keep the example simple, recently opened files aren't shown in
the \uicontrol{File} menu, even though this feature is desired in 90% the \uicontrol{File} menu, even though this feature is desired in 90%
of applications. Furthermore, this example can only load one file at a of applications. Furthermore, this example can only load one file at a
time. The \l{mainwindows/sdi}{SDI} and \l{mainwindows/mdi}{MDI} examples time. The \l{mainwindows/mdi}{MDI} example shows how to lift these
show how to lift these restrictions and how to implement recently opened files restrictions and how to implement recently opened files handling.
handling.
\section1 MainWindow Class Definition \section1 MainWindow Class Definition

View File

@ -1,13 +0,0 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example mainwindows/sdi
\title SDI Example
\ingroup examples-mainwindow
\brief The SDI example shows how to create a Single Document Interface. It uses a number of
top-level windows to display the contents of different text files.
\image sdi-example.png
*/

View File

@ -6,4 +6,3 @@ qt_internal_add_example(dockwidgets)
qt_internal_add_example(mainwindow) qt_internal_add_example(mainwindow)
qt_internal_add_example(mdi) qt_internal_add_example(mdi)
qt_internal_add_example(menus) qt_internal_add_example(menus)
qt_internal_add_example(sdi)

View File

@ -3,5 +3,4 @@ SUBDIRS = application \
dockwidgets \ dockwidgets \
mainwindow \ mainwindow \
mdi \ mdi \
menus \ menus
sdi

View File

@ -1,54 +0,0 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(sdi LANGUAGES CXX)
if(NOT DEFINED INSTALL_EXAMPLESDIR)
set(INSTALL_EXAMPLESDIR "examples")
endif()
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/mainwindows/sdi")
find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
qt_standard_project_setup()
qt_add_executable(sdi
main.cpp
mainwindow.cpp mainwindow.h
)
set_target_properties(sdi PROPERTIES
WIN32_EXECUTABLE TRUE
MACOSX_BUNDLE TRUE
)
target_link_libraries(sdi PRIVATE
Qt6::Core
Qt6::Gui
Qt6::Widgets
)
# Resources:
set(sdi_resource_files
"images/copy.png"
"images/cut.png"
"images/new.png"
"images/open.png"
"images/paste.png"
"images/save.png"
)
qt_add_resources(sdi "sdi"
PREFIX
"/"
FILES
${sdi_resource_files}
)
install(TARGETS sdi
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 852 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -1,37 +0,0 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QApplication>
#include <QCommandLineParser>
#include "mainwindow.h"
int main(int argc, char *argv[])
{
Q_INIT_RESOURCE(sdi);
QApplication app(argc, argv);
QCoreApplication::setApplicationName("SDI Example");
QCoreApplication::setOrganizationName("QtProject");
QCoreApplication::setApplicationVersion(QT_VERSION_STR);
QCommandLineParser parser;
parser.setApplicationDescription(QCoreApplication::applicationName());
parser.addHelpOption();
parser.addVersionOption();
parser.addPositionalArgument("file", "The file(s) to open.");
parser.process(app);
MainWindow *mainWin = nullptr;
const QStringList posArgs = parser.positionalArguments();
for (const QString &file : posArgs) {
MainWindow *newWin = new MainWindow(file);
newWin->tile(mainWin);
newWin->show();
mainWin = newWin;
}
if (!mainWin)
mainWin = new MainWindow;
mainWin->show();
return app.exec();
}

View File

@ -1,443 +0,0 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#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 (screen()->availableGeometry().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::quit);
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 = screen()->availableGeometry();
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);
QGuiApplication::setOverrideCursor(Qt::WaitCursor);
textEdit->setPlainText(in.readAll());
QGuiApplication::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)
{
QString errorMessage;
QGuiApplication::setOverrideCursor(Qt::WaitCursor);
QSaveFile file(fileName);
if (file.open(QFile::WriteOnly | QFile::Text)) {
QTextStream out(&file);
out << textEdit->toPlainText();
if (!file.commit()) {
errorMessage = tr("Cannot write file %1:\n%2.")
.arg(QDir::toNativeSeparators(fileName), file.errorString());
}
} else {
errorMessage = tr("Cannot open file %1 for writing:\n%2.")
.arg(QDir::toNativeSeparators(fileName), file.errorString());
}
QGuiApplication::restoreOverrideCursor();
if (!errorMessage.isEmpty()) {
QMessageBox::warning(this, tr("SDI"), errorMessage);
return false;
}
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();
const QList<QWidget *> topLevelWidgets = QApplication::topLevelWidgets();
for (QWidget *widget : topLevelWidgets) {
MainWindow *mainWin = qobject_cast<MainWindow *>(widget);
if (mainWin && mainWin->curFile == canonicalFilePath)
return mainWin;
}
return nullptr;
}

View File

@ -1,70 +0,0 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QList>
QT_BEGIN_NAMESPACE
class QAction;
class QMenu;
class QTextEdit;
QT_END_NAMESPACE
//! [class definition with macro]
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow();
//! [class definition with macro]
explicit MainWindow(const QString &fileName);
void tile(const QMainWindow *previous);
protected:
void closeEvent(QCloseEvent *event) override;
private slots:
void newFile();
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;
};
#endif

View File

@ -1,11 +0,0 @@
QT += widgets
requires(qtConfig(filedialog))
HEADERS = mainwindow.h
SOURCES = main.cpp \
mainwindow.cpp
RESOURCES = sdi.qrc
# install
target.path = $$[QT_INSTALL_EXAMPLES]/widgets/mainwindows/sdi
INSTALLS += target

View File

@ -1,10 +0,0 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource>
<file>images/copy.png</file>
<file>images/cut.png</file>
<file>images/new.png</file>
<file>images/open.png</file>
<file>images/paste.png</file>
<file>images/save.png</file>
</qresource>
</RCC>

View File

@ -11480,7 +11480,7 @@ void QWidgetPrivate::setWindowOpacity_sys(qreal level)
its parent because other children of the parent might have been its parent because other children of the parent might have been
modified. modified.
\sa windowTitle, {Qt Widgets - Application Example}, {SDI Example}, \sa windowTitle, {Qt Widgets - Application Example},
{MDI Example} {MDI Example}
*/ */
bool QWidget::isWindowModified() const bool QWidget::isWindowModified() const

View File

@ -278,7 +278,7 @@ void QMainWindowPrivate::init()
\sa QMenuBar, QToolBar, QStatusBar, QDockWidget, \sa QMenuBar, QToolBar, QStatusBar, QDockWidget,
{Qt Widgets - Application Example}, {Dock Widgets Example}, {Qt Widgets - Application Example}, {Dock Widgets Example},
{MDI Example}, {SDI Example}, {Menus Example} {MDI Example}, {Menus Example}
*/ */
/*! /*!