demos/undo/mainwindow.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the demonstration applications of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include <QUndoGroup>
       
    43 #include <QUndoStack>
       
    44 #include <QFileDialog>
       
    45 #include <QMessageBox>
       
    46 #include <QTextStream>
       
    47 #include <QToolButton>
       
    48 #include "document.h"
       
    49 #include "mainwindow.h"
       
    50 #include "commands.h"
       
    51 
       
    52 MainWindow::MainWindow(QWidget *parent)
       
    53     : QMainWindow(parent)
       
    54 {
       
    55     setupUi(this);
       
    56 
       
    57     QWidget *w = documentTabs->widget(0);
       
    58     documentTabs->removeTab(0);
       
    59     delete w;
       
    60 
       
    61     connect(actionOpen, SIGNAL(triggered()), this, SLOT(openDocument()));
       
    62     connect(actionClose, SIGNAL(triggered()), this, SLOT(closeDocument()));
       
    63     connect(actionNew, SIGNAL(triggered()), this, SLOT(newDocument()));
       
    64     connect(actionSave, SIGNAL(triggered()), this, SLOT(saveDocument()));
       
    65     connect(actionExit, SIGNAL(triggered()), this, SLOT(close()));
       
    66     connect(actionRed, SIGNAL(triggered()), this, SLOT(setShapeColor()));
       
    67     connect(actionGreen, SIGNAL(triggered()), this, SLOT(setShapeColor()));
       
    68     connect(actionBlue, SIGNAL(triggered()), this, SLOT(setShapeColor()));
       
    69     connect(actionAddCircle, SIGNAL(triggered()), this, SLOT(addShape()));
       
    70     connect(actionAddRectangle, SIGNAL(triggered()), this, SLOT(addShape()));
       
    71     connect(actionAddTriangle, SIGNAL(triggered()), this, SLOT(addShape()));
       
    72     connect(actionRemoveShape, SIGNAL(triggered()), this, SLOT(removeShape()));
       
    73     connect(actionAddRobot, SIGNAL(triggered()), this, SLOT(addRobot()));
       
    74     connect(actionAddSnowman, SIGNAL(triggered()), this, SLOT(addSnowman()));
       
    75     connect(actionAbout, SIGNAL(triggered()), this, SLOT(about()));
       
    76     connect(actionAboutQt, SIGNAL(triggered()), this, SLOT(aboutQt()));
       
    77 
       
    78     connect(undoLimit, SIGNAL(valueChanged(int)), this, SLOT(updateActions()));
       
    79     connect(documentTabs, SIGNAL(currentChanged(int)), this, SLOT(updateActions()));
       
    80 
       
    81     actionOpen->setShortcut(QString("Ctrl+O"));
       
    82     actionClose->setShortcut(QString("Ctrl+W"));
       
    83     actionNew->setShortcut(QString("Ctrl+N"));
       
    84     actionSave->setShortcut(QString("Ctrl+S"));
       
    85     actionExit->setShortcut(QString("Ctrl+Q"));
       
    86     actionRemoveShape->setShortcut(QString("Del"));
       
    87     actionRed->setShortcut(QString("Alt+R"));
       
    88     actionGreen->setShortcut(QString("Alt+G"));
       
    89     actionBlue->setShortcut(QString("Alt+B"));
       
    90     actionAddCircle->setShortcut(QString("Alt+C"));
       
    91     actionAddRectangle->setShortcut(QString("Alt+L"));
       
    92     actionAddTriangle->setShortcut(QString("Alt+T"));
       
    93 
       
    94     m_undoGroup = new QUndoGroup(this);
       
    95     undoView->setGroup(m_undoGroup);
       
    96     undoView->setCleanIcon(QIcon(":/icons/ok.png"));
       
    97 
       
    98     QAction *undoAction = m_undoGroup->createUndoAction(this);
       
    99     QAction *redoAction = m_undoGroup->createRedoAction(this);
       
   100     undoAction->setIcon(QIcon(":/icons/undo.png"));
       
   101     redoAction->setIcon(QIcon(":/icons/redo.png"));
       
   102     menuShape->insertAction(menuShape->actions().at(0), undoAction);
       
   103     menuShape->insertAction(undoAction, redoAction);
       
   104 
       
   105     toolBar->addAction(undoAction);
       
   106     toolBar->addAction(redoAction);
       
   107 
       
   108     newDocument();
       
   109     updateActions();
       
   110 };
       
   111 
       
   112 void MainWindow::updateActions()
       
   113 {
       
   114     Document *doc = currentDocument();
       
   115     m_undoGroup->setActiveStack(doc == 0 ? 0 : doc->undoStack());
       
   116     QString shapeName = doc == 0 ? QString() : doc->currentShapeName();
       
   117 
       
   118     actionAddRobot->setEnabled(doc != 0);
       
   119     actionAddSnowman->setEnabled(doc != 0);
       
   120     actionAddCircle->setEnabled(doc != 0);
       
   121     actionAddRectangle->setEnabled(doc != 0);
       
   122     actionAddTriangle->setEnabled(doc != 0);
       
   123     actionClose->setEnabled(doc != 0);
       
   124     actionSave->setEnabled(doc != 0 && !doc->undoStack()->isClean());
       
   125     undoLimit->setEnabled(doc != 0 && doc->undoStack()->count() == 0);
       
   126 
       
   127     if (shapeName.isEmpty()) {
       
   128         actionRed->setEnabled(false);
       
   129         actionGreen->setEnabled(false);
       
   130         actionBlue->setEnabled(false);
       
   131         actionRemoveShape->setEnabled(false);
       
   132     } else {
       
   133         Shape shape = doc->shape(shapeName);
       
   134         actionRed->setEnabled(shape.color() != Qt::red);
       
   135         actionGreen->setEnabled(shape.color() != Qt::green);
       
   136         actionBlue->setEnabled(shape.color() != Qt::blue);
       
   137         actionRemoveShape->setEnabled(true);
       
   138     }
       
   139 
       
   140     if (doc != 0) {
       
   141         int index = documentTabs->indexOf(doc);
       
   142         Q_ASSERT(index != -1);
       
   143         static const QIcon unsavedIcon(":/icons/filesave.png");
       
   144         documentTabs->setTabIcon(index, doc->undoStack()->isClean() ? QIcon() : unsavedIcon);
       
   145 
       
   146         if (doc->undoStack()->count() == 0)
       
   147             doc->undoStack()->setUndoLimit(undoLimit->value());
       
   148     }
       
   149 }
       
   150 
       
   151 void MainWindow::openDocument()
       
   152 {
       
   153     QString fileName = QFileDialog::getOpenFileName(this);
       
   154     if (fileName.isEmpty())
       
   155         return;
       
   156 
       
   157     QFile file(fileName);
       
   158     if (!file.open(QIODevice::ReadOnly)) {
       
   159         QMessageBox::warning(this,
       
   160                             tr("File error"),
       
   161                             tr("Failed to open\n%1").arg(fileName));
       
   162         return;
       
   163     }
       
   164     QTextStream stream(&file);
       
   165 
       
   166     Document *doc = new Document();
       
   167     if (!doc->load(stream)) {
       
   168         QMessageBox::warning(this,
       
   169                             tr("Parse error"),
       
   170                             tr("Failed to parse\n%1").arg(fileName));
       
   171         delete doc;
       
   172         return;
       
   173     }
       
   174 
       
   175     doc->setFileName(fileName);
       
   176     addDocument(doc);
       
   177 }
       
   178 
       
   179 QString MainWindow::fixedWindowTitle(const Document *doc) const
       
   180 {
       
   181     QString title = doc->fileName();
       
   182 
       
   183     if (title.isEmpty())
       
   184         title = tr("Unnamed");
       
   185     else
       
   186         title = QFileInfo(title).fileName();
       
   187 
       
   188     QString result;
       
   189 
       
   190     for (int i = 0; ; ++i) {
       
   191         result = title;
       
   192         if (i > 0)
       
   193             result += QString::number(i);
       
   194 
       
   195         bool unique = true;
       
   196         for (int j = 0; j < documentTabs->count(); ++j) {
       
   197             const QWidget *widget = documentTabs->widget(j);
       
   198             if (widget == doc)
       
   199                 continue;
       
   200             if (result == documentTabs->tabText(j)) {
       
   201                 unique = false;
       
   202                 break;
       
   203             }
       
   204         }
       
   205 
       
   206         if (unique)
       
   207             break;
       
   208     }
       
   209 
       
   210     return result;
       
   211 }
       
   212 
       
   213 void MainWindow::addDocument(Document *doc)
       
   214 {
       
   215     if (documentTabs->indexOf(doc) != -1)
       
   216         return;
       
   217     m_undoGroup->addStack(doc->undoStack());
       
   218     documentTabs->addTab(doc, fixedWindowTitle(doc));
       
   219     connect(doc, SIGNAL(currentShapeChanged(QString)), this, SLOT(updateActions()));
       
   220     connect(doc->undoStack(), SIGNAL(indexChanged(int)), this, SLOT(updateActions()));
       
   221     connect(doc->undoStack(), SIGNAL(cleanChanged(bool)), this, SLOT(updateActions()));
       
   222 
       
   223     setCurrentDocument(doc);
       
   224 }
       
   225 
       
   226 void MainWindow::setCurrentDocument(Document *doc)
       
   227 {
       
   228     documentTabs->setCurrentWidget(doc);
       
   229 }
       
   230 
       
   231 Document *MainWindow::currentDocument() const
       
   232 {
       
   233     return qobject_cast<Document*>(documentTabs->currentWidget());
       
   234 }
       
   235 
       
   236 void MainWindow::removeDocument(Document *doc)
       
   237 {
       
   238     int index = documentTabs->indexOf(doc);
       
   239     if (index == -1)
       
   240         return;
       
   241 
       
   242     documentTabs->removeTab(index);
       
   243     m_undoGroup->removeStack(doc->undoStack());
       
   244     disconnect(doc, SIGNAL(currentShapeChanged(QString)), this, SLOT(updateActions()));
       
   245     disconnect(doc->undoStack(), SIGNAL(indexChanged(int)), this, SLOT(updateActions()));
       
   246     disconnect(doc->undoStack(), SIGNAL(cleanChanged(bool)), this, SLOT(updateActions()));
       
   247 
       
   248     if (documentTabs->count() == 0) {
       
   249         newDocument();
       
   250         updateActions();
       
   251     }
       
   252 }
       
   253 
       
   254 void MainWindow::saveDocument()
       
   255 {
       
   256     Document *doc = currentDocument();
       
   257     if (doc == 0)
       
   258         return;
       
   259 
       
   260     for (;;) {
       
   261         QString fileName = doc->fileName();
       
   262 
       
   263         if (fileName.isEmpty())
       
   264             fileName = QFileDialog::getSaveFileName(this);
       
   265         if (fileName.isEmpty())
       
   266             break;
       
   267 
       
   268         QFile file(fileName);
       
   269         if (!file.open(QIODevice::WriteOnly)) {
       
   270             QMessageBox::warning(this,
       
   271                                 tr("File error"),
       
   272                                 tr("Failed to open\n%1").arg(fileName));
       
   273             doc->setFileName(QString());
       
   274         } else {
       
   275             QTextStream stream(&file);
       
   276             doc->save(stream);
       
   277             doc->setFileName(fileName);
       
   278 
       
   279             int index = documentTabs->indexOf(doc);
       
   280             Q_ASSERT(index != -1);
       
   281             documentTabs->setTabText(index, fixedWindowTitle(doc));
       
   282 
       
   283             break;
       
   284         }
       
   285     }
       
   286 }
       
   287 
       
   288 void MainWindow::closeDocument()
       
   289 {
       
   290     Document *doc = currentDocument();
       
   291     if (doc == 0)
       
   292         return;
       
   293 
       
   294     if (!doc->undoStack()->isClean()) {
       
   295         int button
       
   296             = QMessageBox::warning(this,
       
   297                             tr("Unsaved changes"),
       
   298                             tr("Would you like to save this document?"),
       
   299                             QMessageBox::Yes, QMessageBox::No);
       
   300         if (button == QMessageBox::Yes)
       
   301             saveDocument();
       
   302     }
       
   303 
       
   304     removeDocument(doc);
       
   305     delete doc;
       
   306 }
       
   307 
       
   308 void MainWindow::newDocument()
       
   309 {
       
   310     addDocument(new Document());
       
   311 }
       
   312 
       
   313 static QColor randomColor()
       
   314 {
       
   315     int r = (int) (3.0*(rand()/(RAND_MAX + 1.0)));
       
   316     switch (r) {
       
   317         case 0:
       
   318             return Qt::red;
       
   319         case 1:
       
   320             return Qt::green;
       
   321         default:
       
   322             break;
       
   323     }
       
   324     return Qt::blue;
       
   325 }
       
   326 
       
   327 static QRect randomRect(const QSize &s)
       
   328 {
       
   329     QSize min = Shape::minSize;
       
   330 
       
   331     int left = (int) ((0.0 + s.width() - min.width())*(rand()/(RAND_MAX + 1.0)));
       
   332     int top = (int) ((0.0 + s.height() - min.height())*(rand()/(RAND_MAX + 1.0)));
       
   333     int width = (int) ((0.0 + s.width() - left - min.width())*(rand()/(RAND_MAX + 1.0))) + min.width();
       
   334     int height = (int) ((0.0 + s.height() - top - min.height())*(rand()/(RAND_MAX + 1.0))) + min.height();
       
   335 
       
   336     return QRect(left, top, width, height);
       
   337 }
       
   338 
       
   339 void MainWindow::addShape()
       
   340 {
       
   341     Document *doc = currentDocument();
       
   342     if (doc == 0)
       
   343         return;
       
   344 
       
   345     Shape::Type type;
       
   346 
       
   347     if (sender() == actionAddCircle)
       
   348         type = Shape::Circle;
       
   349     else if (sender() == actionAddRectangle)
       
   350         type = Shape::Rectangle;
       
   351     else if (sender() == actionAddTriangle)
       
   352         type = Shape::Triangle;
       
   353     else return;
       
   354 
       
   355     Shape newShape(type, randomColor(), randomRect(doc->size()));
       
   356     doc->undoStack()->push(new AddShapeCommand(doc, newShape));
       
   357 }
       
   358 
       
   359 void MainWindow::removeShape()
       
   360 {
       
   361     Document *doc = currentDocument();
       
   362     if (doc == 0)
       
   363         return;
       
   364 
       
   365     QString shapeName = doc->currentShapeName();
       
   366     if (shapeName.isEmpty())
       
   367         return;
       
   368 
       
   369     doc->undoStack()->push(new RemoveShapeCommand(doc, shapeName));
       
   370 }
       
   371 
       
   372 void MainWindow::setShapeColor()
       
   373 {
       
   374     Document *doc = currentDocument();
       
   375     if (doc == 0)
       
   376         return;
       
   377 
       
   378     QString shapeName = doc->currentShapeName();
       
   379     if (shapeName.isEmpty())
       
   380         return;
       
   381 
       
   382     QColor color;
       
   383 
       
   384     if (sender() == actionRed)
       
   385         color = Qt::red;
       
   386     else if (sender() == actionGreen)
       
   387         color = Qt::green;
       
   388     else if (sender() == actionBlue)
       
   389         color = Qt::blue;
       
   390     else
       
   391         return;
       
   392 
       
   393     if (color == doc->shape(shapeName).color())
       
   394         return;
       
   395 
       
   396     doc->undoStack()->push(new SetShapeColorCommand(doc, shapeName, color));
       
   397 }
       
   398 
       
   399 void MainWindow::addSnowman()
       
   400 {
       
   401     Document *doc = currentDocument();
       
   402     if (doc == 0)
       
   403         return;
       
   404 
       
   405     // Create a macro command using beginMacro() and endMacro()
       
   406 
       
   407     doc->undoStack()->beginMacro(tr("Add snowman"));
       
   408     doc->undoStack()->push(new AddShapeCommand(doc,
       
   409                             Shape(Shape::Circle, Qt::blue, QRect(51, 30, 97, 95))));
       
   410     doc->undoStack()->push(new AddShapeCommand(doc,
       
   411                             Shape(Shape::Circle, Qt::blue, QRect(27, 123, 150, 133))));
       
   412     doc->undoStack()->push(new AddShapeCommand(doc,
       
   413                             Shape(Shape::Circle, Qt::blue, QRect(11, 253, 188, 146))));
       
   414     doc->undoStack()->endMacro();
       
   415 }
       
   416 
       
   417 void MainWindow::addRobot()
       
   418 {
       
   419     Document *doc = currentDocument();
       
   420     if (doc == 0)
       
   421         return;
       
   422 
       
   423     // Compose a macro command by explicitly adding children to a parent command
       
   424 
       
   425     QUndoCommand *parent = new QUndoCommand(tr("Add robot"));
       
   426 
       
   427     new AddShapeCommand(doc, Shape(Shape::Rectangle, Qt::green, QRect(115, 15, 81, 70)), parent);
       
   428     new AddShapeCommand(doc, Shape(Shape::Rectangle, Qt::green, QRect(82, 89, 148, 188)), parent);
       
   429     new AddShapeCommand(doc, Shape(Shape::Rectangle, Qt::green, QRect(76, 280, 80, 165)), parent);
       
   430     new AddShapeCommand(doc, Shape(Shape::Rectangle, Qt::green, QRect(163, 280, 80, 164)), parent);
       
   431     new AddShapeCommand(doc, Shape(Shape::Circle, Qt::blue, QRect(116, 25, 80, 50)), parent);
       
   432     new AddShapeCommand(doc, Shape(Shape::Rectangle, Qt::green, QRect(232, 92, 80, 127)), parent);
       
   433     new AddShapeCommand(doc, Shape(Shape::Rectangle, Qt::green, QRect(2, 92, 80, 125)), parent);
       
   434 
       
   435     doc->undoStack()->push(parent);
       
   436 }
       
   437 
       
   438 void MainWindow::about()
       
   439 {
       
   440     QMessageBox::about(this, tr("About Undo"), tr("The Undo demonstration shows how to use the Qt Undo framework."));
       
   441 }
       
   442 
       
   443 void MainWindow::aboutQt()
       
   444 {
       
   445     QMessageBox::aboutQt(this, tr("About Qt"));
       
   446 }