mainwindow.cpp Example File

mainwindows/mainwindow/mainwindow.cpp
 /****************************************************************************
 **
 ** Copyright (C) 2016 The Qt Company Ltd.
 ** Contact: https://www.qt.io/licensing/
 **
 ** This file is part of the demonstration applications 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 "mainwindow.h"
 #include "colorswatch.h"
 #include "toolbar.h"

 #include <QAction>
 #include <QLayout>
 #include <QMenu>
 #include <QMenuBar>
 #include <QStatusBar>
 #include <QTextEdit>
 #include <QFile>
 #include <QDataStream>
 #include <QFileDialog>
 #include <QDialogButtonBox>
 #include <QMessageBox>
 #include <QApplication>
 #include <QPainter>
 #include <QMouseEvent>
 #include <QLineEdit>
 #include <QComboBox>
 #include <QLabel>
 #include <QPushButton>
 #include <QTextEdit>
 #include <QDebug>

 static const char message[] =
     "<p><b>Qt Main Window Example</b></p>"

     "<p>This is a demonstration of the QMainWindow, QToolBar and "
     "QDockWidget classes.</p>"

     "<p>The tool bar and dock widgets can be dragged around and rearranged "
     "using the mouse or via the menu.</p>"

     "<p>Each dock widget contains a colored frame and a context "
     "(right-click) menu.</p>"

 #ifdef Q_OS_MAC
     "<p>On OS X, the \"Black\" dock widget has been created as a "
     "<em>Drawer</em>, which is a special kind of QDockWidget.</p>"
 #endif
     ;

 Q_DECLARE_METATYPE(QDockWidget::DockWidgetFeatures)

 MainWindow::MainWindow(const CustomSizeHintMap &customSizeHints,
                        QWidget *parent, Qt::WindowFlags flags)
     : QMainWindow(parent, flags)
 {
     Q_UNUSED(message);
     setObjectName("MainWindow");
     setWindowTitle("Qt Main Window Example");

     QTextEdit *center = new QTextEdit(this);
     center->setReadOnly(true);
     center->setMinimumSize(400, 205);
     setCentralWidget(center);

     setupToolBar();
     setupMenuBar();
     setupDockWidgets(customSizeHints);

     statusBar()->showMessage(tr("Status Bar"));
 }

 void MainWindow::actionTriggered(QAction *action)
 {
     qDebug("action '%s' triggered", action->text().toLocal8Bit().data());
 }

 void MainWindow::setupToolBar()
 {
 #ifdef Q_OS_OSX
     setUnifiedTitleAndToolBarOnMac(true);
 #endif

     for (int i = 0; i < 3; ++i) {
         ToolBar *tb = new ToolBar(QString::fromLatin1("Tool Bar %1").arg(i + 1), this);
         toolBars.append(tb);
         addToolBar(tb);
     }
 }

 void MainWindow::setupMenuBar()
 {
     QMenu *menu = menuBar()->addMenu(tr("&File"));

     menu->addAction(tr("Save layout..."), this, &MainWindow::saveLayout);
     menu->addAction(tr("Load layout..."), this, &MainWindow::loadLayout);
     menu->addAction(tr("Switch layout direction"),this, &MainWindow::switchLayoutDirection);

     menu->addSeparator();
     menu->addAction(tr("&Quit"), this, &QWidget::close);

     mainWindowMenu = menuBar()->addMenu(tr("Main window"));

     QAction *action = mainWindowMenu->addAction(tr("Animated docks"));
     action->setCheckable(true);
     action->setChecked(dockOptions() & AnimatedDocks);
     connect(action, &QAction::toggled, this, &MainWindow::setDockOptions);

     action = mainWindowMenu->addAction(tr("Allow nested docks"));
     action->setCheckable(true);
     action->setChecked(dockOptions() & AllowNestedDocks);
     connect(action, &QAction::toggled, this, &MainWindow::setDockOptions);

     action = mainWindowMenu->addAction(tr("Allow tabbed docks"));
     action->setCheckable(true);
     action->setChecked(dockOptions() & AllowTabbedDocks);
     connect(action, &QAction::toggled, this, &MainWindow::setDockOptions);

     action = mainWindowMenu->addAction(tr("Force tabbed docks"));
     action->setCheckable(true);
     action->setChecked(dockOptions() & ForceTabbedDocks);
     connect(action, &QAction::toggled, this, &MainWindow::setDockOptions);

     action = mainWindowMenu->addAction(tr("Vertical tabs"));
     action->setCheckable(true);
     action->setChecked(dockOptions() & VerticalTabs);
     connect(action, &QAction::toggled, this, &MainWindow::setDockOptions);

     action = mainWindowMenu->addAction(tr("Grouped dragging"));
     action->setCheckable(true);
     action->setChecked(dockOptions() & GroupedDragging);
     connect(action, &QAction::toggled, this, &MainWindow::setDockOptions);

     QMenu *toolBarMenu = menuBar()->addMenu(tr("Tool bars"));
     for (int i = 0; i < toolBars.count(); ++i)
         toolBarMenu->addMenu(toolBars.at(i)->toolbarMenu());

 #ifdef Q_OS_OSX
     toolBarMenu->addSeparator();

     action = toolBarMenu->addAction(tr("Unified"));
     action->setCheckable(true);
     action->setChecked(unifiedTitleAndToolBarOnMac());
     connect(action, &QAction::toggled, this, &QMainWindow::setUnifiedTitleAndToolBarOnMac);
 #endif

     dockWidgetMenu = menuBar()->addMenu(tr("&Dock Widgets"));

     QMenu *aboutMenu = menuBar()->addMenu(tr("About"));
     QAction *aboutAct = aboutMenu->addAction(tr("&About"), this, &MainWindow::about);
     aboutAct->setStatusTip(tr("Show the application's About box"));

     QAction *aboutQtAct = aboutMenu->addAction(tr("About &Qt"), qApp, &QApplication::aboutQt);
     aboutQtAct->setStatusTip(tr("Show the Qt library's About box"));
 }

 void MainWindow::setDockOptions()
 {
     DockOptions opts;
     QList<QAction*> actions = mainWindowMenu->actions();

     if (actions.at(0)->isChecked())
         opts |= AnimatedDocks;
     if (actions.at(1)->isChecked())
         opts |= AllowNestedDocks;
     if (actions.at(2)->isChecked())
         opts |= AllowTabbedDocks;
     if (actions.at(3)->isChecked())
         opts |= ForceTabbedDocks;
     if (actions.at(4)->isChecked())
         opts |= VerticalTabs;
     if (actions.at(5)->isChecked())
         opts |= GroupedDragging;

     QMainWindow::setDockOptions(opts);
 }

 void MainWindow::saveLayout()
 {
     QString fileName
         = QFileDialog::getSaveFileName(this, tr("Save layout"));
     if (fileName.isEmpty())
         return;
     QFile file(fileName);
     if (!file.open(QFile::WriteOnly)) {
         QString msg = tr("Failed to open %1\n%2")
                         .arg(QDir::toNativeSeparators(fileName), file.errorString());
         QMessageBox::warning(this, tr("Error"), msg);
         return;
     }

     QByteArray geo_data = saveGeometry();
     QByteArray layout_data = saveState();

     bool ok = file.putChar((uchar)geo_data.size());
     if (ok)
         ok = file.write(geo_data) == geo_data.size();
     if (ok)
         ok = file.write(layout_data) == layout_data.size();

     if (!ok) {
         QString msg = tr("Error writing to %1\n%2")
                         .arg(QDir::toNativeSeparators(fileName), file.errorString());
         QMessageBox::warning(this, tr("Error"), msg);
         return;
     }
 }

 void MainWindow::loadLayout()
 {
     QString fileName
         = QFileDialog::getOpenFileName(this, tr("Load layout"));
     if (fileName.isEmpty())
         return;
     QFile file(fileName);
     if (!file.open(QFile::ReadOnly)) {
         QString msg = tr("Failed to open %1\n%2")
                         .arg(QDir::toNativeSeparators(fileName), file.errorString());
         QMessageBox::warning(this, tr("Error"), msg);
         return;
     }

     uchar geo_size;
     QByteArray geo_data;
     QByteArray layout_data;

     bool ok = file.getChar((char*)&geo_size);
     if (ok) {
         geo_data = file.read(geo_size);
         ok = geo_data.size() == geo_size;
     }
     if (ok) {
         layout_data = file.readAll();
         ok = layout_data.size() > 0;
     }

     if (ok)
         ok = restoreGeometry(geo_data);
     if (ok)
         ok = restoreState(layout_data);

     if (!ok) {
         QString msg = tr("Error reading %1").arg(QDir::toNativeSeparators(fileName));
         QMessageBox::warning(this, tr("Error"), msg);
         return;
     }
 }

 static QAction *addCornerAction(const QString &text, QMainWindow *mw, QMenu *menu, QActionGroup *group,
                                 Qt::Corner c, Qt::DockWidgetArea a)
 {
     QAction *result = menu->addAction(text, mw, [=]() { mw->setCorner(c, a); });
     result->setCheckable(true);
     group->addAction(result);
     return result;
 }

 void MainWindow::setupDockWidgets(const CustomSizeHintMap &customSizeHints)
 {
     qRegisterMetaType<QDockWidget::DockWidgetFeatures>();

     QMenu *cornerMenu = dockWidgetMenu->addMenu(tr("Top left corner"));
     QActionGroup *group = new QActionGroup(this);
     group->setExclusive(true);
     QAction *cornerAction = addCornerAction(tr("Top dock area"), this, cornerMenu, group, Qt::TopLeftCorner, Qt::TopDockWidgetArea);
     cornerAction->setChecked(true);
     addCornerAction(tr("Left dock area"), this, cornerMenu, group, Qt::TopLeftCorner, Qt::LeftDockWidgetArea);

     cornerMenu = dockWidgetMenu->addMenu(tr("Top right corner"));
     group = new QActionGroup(this);
     group->setExclusive(true);
     cornerAction = addCornerAction(tr("Top dock area"), this, cornerMenu, group, Qt::TopRightCorner, Qt::TopDockWidgetArea);
     cornerAction->setChecked(true);
     addCornerAction(tr("Right dock area"), this, cornerMenu, group, Qt::TopRightCorner, Qt::RightDockWidgetArea);

     cornerMenu = dockWidgetMenu->addMenu(tr("Bottom left corner"));
     group = new QActionGroup(this);
     group->setExclusive(true);
     cornerAction = addCornerAction(tr("Bottom dock area"), this, cornerMenu, group, Qt::BottomLeftCorner, Qt::BottomDockWidgetArea);
     cornerAction->setChecked(true);
     addCornerAction(tr("Left dock area"), this, cornerMenu, group, Qt::BottomLeftCorner, Qt::LeftDockWidgetArea);

     cornerMenu = dockWidgetMenu->addMenu(tr("Bottom right corner"));
     group = new QActionGroup(this);
     group->setExclusive(true);
     cornerAction = addCornerAction(tr("Bottom dock area"), this, cornerMenu, group, Qt::BottomRightCorner, Qt::BottomDockWidgetArea);
     cornerAction->setChecked(true);
     addCornerAction(tr("Right dock area"), this, cornerMenu, group, Qt::BottomRightCorner, Qt::RightDockWidgetArea);

     dockWidgetMenu->addSeparator();

     static const struct Set {
         const char * name;
         uint flags;
         Qt::DockWidgetArea area;
     } sets [] = {
 #ifndef Q_OS_MAC
         { "Black", 0, Qt::LeftDockWidgetArea },
 #else
         { "Black", Qt::Drawer, Qt::LeftDockWidgetArea },
 #endif
         { "White", 0, Qt::RightDockWidgetArea },
         { "Red", 0, Qt::TopDockWidgetArea },
         { "Green", 0, Qt::TopDockWidgetArea },
         { "Blue", 0, Qt::BottomDockWidgetArea },
         { "Yellow", 0, Qt::BottomDockWidgetArea }
     };
     const int setCount = sizeof(sets) / sizeof(Set);

     const QIcon qtIcon(QPixmap(":/res/qt.png"));
     for (int i = 0; i < setCount; ++i) {
         ColorSwatch *swatch = new ColorSwatch(tr(sets[i].name), this, Qt::WindowFlags(sets[i].flags));
         if (i % 2)
             swatch->setWindowIcon(qtIcon);
         if (qstrcmp(sets[i].name, "Blue") == 0) {
             BlueTitleBar *titlebar = new BlueTitleBar(swatch);
             swatch->setTitleBarWidget(titlebar);
             connect(swatch, &QDockWidget::topLevelChanged, titlebar, &BlueTitleBar::updateMask);
             connect(swatch, &QDockWidget::featuresChanged, titlebar, &BlueTitleBar::updateMask, Qt::QueuedConnection);
         }

         QString name = QString::fromLatin1(sets[i].name);
         if (customSizeHints.contains(name))
             swatch->setCustomSizeHint(customSizeHints.value(name));

         addDockWidget(sets[i].area, swatch);
         dockWidgetMenu->addMenu(swatch->colorSwatchMenu());
     }

     destroyDockWidgetMenu = new QMenu(tr("Destroy dock widget"), this);
     destroyDockWidgetMenu->setEnabled(false);
     connect(destroyDockWidgetMenu, &QMenu::triggered, this, &MainWindow::destroyDockWidget);

     dockWidgetMenu->addSeparator();
     dockWidgetMenu->addAction(tr("Add dock widget..."), this, &MainWindow::createDockWidget);
     dockWidgetMenu->addMenu(destroyDockWidgetMenu);
 }

 void MainWindow::switchLayoutDirection()
 {
     if (layoutDirection() == Qt::LeftToRight)
         QApplication::setLayoutDirection(Qt::RightToLeft);
     else
         QApplication::setLayoutDirection(Qt::LeftToRight);
 }

 class CreateDockWidgetDialog : public QDialog
 {
 public:
     explicit CreateDockWidgetDialog(QWidget *parent = nullptr);

     QString enteredObjectName() const { return m_objectName->text(); }
     Qt::DockWidgetArea location() const;

 private:
     QLineEdit *m_objectName;
     QComboBox *m_location;
 };

 CreateDockWidgetDialog::CreateDockWidgetDialog(QWidget *parent)
     : QDialog(parent)
     , m_objectName(new QLineEdit(this))
     , m_location(new QComboBox(this))
 {
     setWindowTitle(tr("Add Dock Widget"));
     setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
     QGridLayout *layout = new QGridLayout(this);

     layout->addWidget(new QLabel(tr("Object name:")), 0, 0);
     layout->addWidget(m_objectName, 0, 1);

     layout->addWidget(new QLabel(tr("Location:")), 1, 0);
     m_location->setEditable(false);
     m_location->addItem(tr("Top"));
     m_location->addItem(tr("Left"));
     m_location->addItem(tr("Right"));
     m_location->addItem(tr("Bottom"));
     m_location->addItem(tr("Restore"));
     layout->addWidget(m_location, 1, 1);

     QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
     connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
     connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
     layout->addWidget(buttonBox, 2, 0, 1, 2);
 }

 Qt::DockWidgetArea CreateDockWidgetDialog::location() const
 {
     switch (m_location->currentIndex()) {
         case 0: return Qt::TopDockWidgetArea;
         case 1: return Qt::LeftDockWidgetArea;
         case 2: return Qt::RightDockWidgetArea;
         case 3: return Qt::BottomDockWidgetArea;
         default:
             break;
     }
     return Qt::NoDockWidgetArea;
 }

 void MainWindow::createDockWidget()
 {
     CreateDockWidgetDialog dialog(this);
     if (dialog.exec() == QDialog::Rejected)
         return;

     QDockWidget *dw = new QDockWidget;
     const QString name = dialog.enteredObjectName();
     dw->setObjectName(name);
     dw->setWindowTitle(name);
     dw->setWidget(new QTextEdit);

     Qt::DockWidgetArea area = dialog.location();
     switch (area) {
         case Qt::LeftDockWidgetArea:
         case Qt::RightDockWidgetArea:
         case Qt::TopDockWidgetArea:
         case Qt::BottomDockWidgetArea:
             addDockWidget(area, dw);
             break;
         default:
             if (!restoreDockWidget(dw)) {
                 QMessageBox::warning(this, QString(), tr("Failed to restore dock widget"));
                 delete dw;
                 return;
             }
             break;
     }

     extraDockWidgets.append(dw);
     destroyDockWidgetMenu->setEnabled(true);
     destroyDockWidgetMenu->addAction(new QAction(name, this));
 }

 void MainWindow::destroyDockWidget(QAction *action)
 {
     int index = destroyDockWidgetMenu->actions().indexOf(action);
     delete extraDockWidgets.takeAt(index);
     destroyDockWidgetMenu->removeAction(action);
     action->deleteLater();

     if (destroyDockWidgetMenu->isEmpty())
         destroyDockWidgetMenu->setEnabled(false);
 }

 void MainWindow::about()
 {
     QMessageBox::about(this, tr("About MainWindows"), message);
 }