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 <QSignalMapper>
  #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)
  {
      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"));
  }

  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 = Q_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);
  }