demos/undo/document.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 <qevent.h>
       
    43 #include <QPainter>
       
    44 #include <QTextStream>
       
    45 #include <QUndoStack>
       
    46 #include "document.h"
       
    47 #include "commands.h"
       
    48 
       
    49 static const int resizeHandleWidth = 6;
       
    50 
       
    51 /******************************************************************************
       
    52 ** Shape
       
    53 */
       
    54 
       
    55 const QSize Shape::minSize(80, 50);
       
    56 
       
    57 Shape::Shape(Type type, const QColor &color, const QRect &rect)
       
    58     : m_type(type), m_rect(rect), m_color(color)
       
    59 {
       
    60 }
       
    61 
       
    62 Shape::Type Shape::type() const
       
    63 {
       
    64     return m_type;
       
    65 }
       
    66 
       
    67 QRect Shape::rect() const
       
    68 {
       
    69     return m_rect;
       
    70 }
       
    71 
       
    72 QColor Shape::color() const
       
    73 {
       
    74     return m_color;
       
    75 }
       
    76 
       
    77 QString Shape::name() const
       
    78 {
       
    79     return m_name;
       
    80 }
       
    81 
       
    82 QRect Shape::resizeHandle() const
       
    83 {
       
    84     QPoint br = m_rect.bottomRight();
       
    85     return QRect(br - QPoint(resizeHandleWidth, resizeHandleWidth), br);
       
    86 }
       
    87 
       
    88 QString Shape::typeToString(Type type)
       
    89 {
       
    90     QString result;
       
    91 
       
    92     switch (type) {
       
    93         case Rectangle:
       
    94             result = QLatin1String("Rectangle");
       
    95             break;
       
    96         case Circle:
       
    97             result = QLatin1String("Circle");
       
    98             break;
       
    99         case Triangle:
       
   100             result = QLatin1String("Triangle");
       
   101             break;
       
   102     }
       
   103 
       
   104     return result;
       
   105 }
       
   106 
       
   107 Shape::Type Shape::stringToType(const QString &s, bool *ok)
       
   108 {
       
   109     if (ok != 0)
       
   110         *ok = true;
       
   111 
       
   112     if (s == QLatin1String("Rectangle"))
       
   113         return Rectangle;
       
   114     if (s == QLatin1String("Circle"))
       
   115         return Circle;
       
   116     if (s == QLatin1String("Triangle"))
       
   117         return Triangle;
       
   118 
       
   119     if (ok != 0)
       
   120         *ok = false;
       
   121     return Rectangle;
       
   122 }
       
   123 
       
   124 /******************************************************************************
       
   125 ** Document
       
   126 */
       
   127 
       
   128 Document::Document(QWidget *parent)
       
   129     : QWidget(parent), m_currentIndex(-1), m_mousePressIndex(-1), m_resizeHandlePressed(false)
       
   130 {
       
   131     m_undoStack = new QUndoStack(this);
       
   132 
       
   133     setAutoFillBackground(true);
       
   134     setBackgroundRole(QPalette::Base);
       
   135 
       
   136     QPalette pal = palette();
       
   137     pal.setBrush(QPalette::Base, QPixmap(":/icons/background.png"));
       
   138     pal.setColor(QPalette::HighlightedText, Qt::red);
       
   139     setPalette(pal);
       
   140 }
       
   141 
       
   142 QString Document::addShape(const Shape &shape)
       
   143 {
       
   144     QString name = Shape::typeToString(shape.type());
       
   145     name = uniqueName(name);
       
   146 
       
   147     m_shapeList.append(shape);
       
   148     m_shapeList[m_shapeList.count() - 1].m_name = name;
       
   149     setCurrentShape(m_shapeList.count() - 1);
       
   150 
       
   151     return name;
       
   152 }
       
   153 
       
   154 void Document::deleteShape(const QString &shapeName)
       
   155 {
       
   156     int index = indexOf(shapeName);
       
   157     if (index == -1)
       
   158         return;
       
   159 
       
   160     update(m_shapeList.at(index).rect());
       
   161 
       
   162     m_shapeList.removeAt(index);
       
   163 
       
   164     if (index <= m_currentIndex) {
       
   165         m_currentIndex = -1;
       
   166         if (index == m_shapeList.count())
       
   167             --index;
       
   168         setCurrentShape(index);
       
   169     }
       
   170 }
       
   171 
       
   172 Shape Document::shape(const QString &shapeName) const
       
   173 {
       
   174     int index = indexOf(shapeName);
       
   175     if (index == -1)
       
   176         return Shape();
       
   177     return m_shapeList.at(index);
       
   178 }
       
   179 
       
   180 void Document::setShapeRect(const QString &shapeName, const QRect &rect)
       
   181 {
       
   182     int index = indexOf(shapeName);
       
   183     if (index == -1)
       
   184         return;
       
   185 
       
   186     Shape &shape = m_shapeList[index];
       
   187 
       
   188     update(shape.rect());
       
   189     update(rect);
       
   190 
       
   191     shape.m_rect = rect;
       
   192 }
       
   193 
       
   194 void Document::setShapeColor(const QString &shapeName, const QColor &color)
       
   195 {
       
   196 
       
   197     int index = indexOf(shapeName);
       
   198     if (index == -1)
       
   199         return;
       
   200 
       
   201     Shape &shape = m_shapeList[index];
       
   202     shape.m_color = color;
       
   203 
       
   204     update(shape.rect());
       
   205 }
       
   206 
       
   207 QUndoStack *Document::undoStack() const
       
   208 {
       
   209     return m_undoStack;
       
   210 }
       
   211 
       
   212 bool Document::load(QTextStream &stream)
       
   213 {
       
   214     m_shapeList.clear();
       
   215 
       
   216     while (!stream.atEnd()) {
       
   217         QString shapeType, shapeName, colorName;
       
   218         int left, top, width, height;
       
   219         stream >> shapeType >> shapeName >> colorName >> left >> top >> width >> height;
       
   220         if (stream.status() != QTextStream::Ok)
       
   221             return false;
       
   222         bool ok;
       
   223         Shape::Type type = Shape::stringToType(shapeType, &ok);
       
   224         if (!ok)
       
   225             return false;
       
   226         QColor color(colorName);
       
   227         if (!color.isValid())
       
   228             return false;
       
   229 
       
   230         Shape shape(type);
       
   231         shape.m_name = shapeName;
       
   232         shape.m_color = color;
       
   233         shape.m_rect = QRect(left, top, width, height);
       
   234 
       
   235         m_shapeList.append(shape);
       
   236     }
       
   237 
       
   238     m_currentIndex = m_shapeList.isEmpty() ? -1 : 0;
       
   239 
       
   240     return true;
       
   241 }
       
   242 
       
   243 void Document::save(QTextStream &stream)
       
   244 {
       
   245     for (int i = 0; i < m_shapeList.count(); ++i) {
       
   246         const Shape &shape = m_shapeList.at(i);
       
   247         QRect r = shape.rect();
       
   248         stream << Shape::typeToString(shape.type()) << QLatin1Char(' ')
       
   249                 << shape.name() << QLatin1Char(' ')
       
   250                 << shape.color().name() << QLatin1Char(' ')
       
   251                 << r.left() << QLatin1Char(' ')
       
   252                 << r.top() << QLatin1Char(' ')
       
   253                 << r.width() << QLatin1Char(' ')
       
   254                 << r.height();
       
   255         if (i != m_shapeList.count() - 1)
       
   256             stream << QLatin1Char('\n');
       
   257     }
       
   258     m_undoStack->setClean();
       
   259 }
       
   260 
       
   261 QString Document::fileName() const
       
   262 {
       
   263     return m_fileName;
       
   264 }
       
   265 
       
   266 void Document::setFileName(const QString &fileName)
       
   267 {
       
   268     m_fileName = fileName;
       
   269 }
       
   270 
       
   271 int Document::indexAt(const QPoint &pos) const
       
   272 {
       
   273     for (int i = m_shapeList.count() - 1; i >= 0; --i) {
       
   274         if (m_shapeList.at(i).rect().contains(pos))
       
   275             return i;
       
   276     }
       
   277     return -1;
       
   278 }
       
   279 
       
   280 void Document::mousePressEvent(QMouseEvent *event)
       
   281 {
       
   282     event->accept();
       
   283     int index = indexAt(event->pos());;
       
   284     if (index != -1) {
       
   285         setCurrentShape(index);
       
   286 
       
   287         const Shape &shape = m_shapeList.at(index);
       
   288         m_resizeHandlePressed = shape.resizeHandle().contains(event->pos());
       
   289 
       
   290         if (m_resizeHandlePressed)
       
   291             m_mousePressOffset = shape.rect().bottomRight() - event->pos();
       
   292         else
       
   293             m_mousePressOffset = event->pos() - shape.rect().topLeft();
       
   294     }
       
   295     m_mousePressIndex = index;
       
   296 }
       
   297 
       
   298 void Document::mouseReleaseEvent(QMouseEvent *event)
       
   299 {
       
   300     event->accept();
       
   301     m_mousePressIndex = -1;
       
   302 }
       
   303 
       
   304 void Document::mouseMoveEvent(QMouseEvent *event)
       
   305 {
       
   306     event->accept();
       
   307 
       
   308     if (m_mousePressIndex == -1)
       
   309         return;
       
   310 
       
   311     const Shape &shape = m_shapeList.at(m_mousePressIndex);
       
   312 
       
   313     QRect rect;
       
   314     if (m_resizeHandlePressed) {
       
   315         rect = QRect(shape.rect().topLeft(), event->pos() + m_mousePressOffset);
       
   316     } else {
       
   317         rect = shape.rect();
       
   318         rect.moveTopLeft(event->pos() - m_mousePressOffset);
       
   319     }
       
   320 
       
   321     QSize size = rect.size().expandedTo(Shape::minSize);
       
   322     rect.setSize(size);
       
   323 
       
   324     m_undoStack->push(new SetShapeRectCommand(this, shape.name(), rect));
       
   325 }
       
   326 
       
   327 static QGradient gradient(const QColor &color, const QRect &rect)
       
   328 {
       
   329     QColor c = color;
       
   330     c.setAlpha(160);
       
   331     QLinearGradient result(rect.topLeft(), rect.bottomRight());
       
   332     result.setColorAt(0, c.dark(150));
       
   333     result.setColorAt(0.5, c.light(200));
       
   334     result.setColorAt(1, c.dark(150));
       
   335     return result;
       
   336 }
       
   337 
       
   338 static QPolygon triangle(const QRect &rect)
       
   339 {
       
   340     QPolygon result(3);
       
   341     result.setPoint(0, rect.center().x(), rect.top());
       
   342     result.setPoint(1, rect.right(), rect.bottom());
       
   343     result.setPoint(2, rect.left(), rect.bottom());
       
   344     return result;
       
   345 }
       
   346 
       
   347 void Document::paintEvent(QPaintEvent *event)
       
   348 {
       
   349     QRegion paintRegion = event->region();
       
   350     QPainter painter(this);
       
   351     QPalette pal = palette();
       
   352 
       
   353     for (int i = 0; i < m_shapeList.count(); ++i) {
       
   354         const Shape &shape = m_shapeList.at(i);
       
   355 
       
   356         if (!paintRegion.contains(shape.rect()))
       
   357             continue;
       
   358 
       
   359         QPen pen = pal.text().color();
       
   360         pen.setWidth(i == m_currentIndex ? 2 : 1);
       
   361         painter.setPen(pen);
       
   362         painter.setBrush(gradient(shape.color(), shape.rect()));
       
   363 
       
   364         QRect rect = shape.rect();
       
   365         rect.adjust(1, 1, -resizeHandleWidth/2, -resizeHandleWidth/2);
       
   366 
       
   367         // paint the shape
       
   368         switch (shape.type()) {
       
   369             case Shape::Rectangle:
       
   370                 painter.drawRect(rect);
       
   371                 break;
       
   372             case Shape::Circle:
       
   373                 painter.setRenderHint(QPainter::Antialiasing);
       
   374                 painter.drawEllipse(rect);
       
   375                 painter.setRenderHint(QPainter::Antialiasing, false);
       
   376                 break;
       
   377             case Shape::Triangle:
       
   378                 painter.setRenderHint(QPainter::Antialiasing);
       
   379                 painter.drawPolygon(triangle(rect));
       
   380                 painter.setRenderHint(QPainter::Antialiasing, false);
       
   381                 break;
       
   382         }
       
   383 
       
   384         // paint the resize handle
       
   385         painter.setPen(pal.text().color());
       
   386         painter.setBrush(Qt::white);
       
   387         painter.drawRect(shape.resizeHandle().adjusted(0, 0, -1, -1));
       
   388 
       
   389         // paint the shape name
       
   390         painter.setBrush(pal.text());
       
   391         if (shape.type() == Shape::Triangle)
       
   392             rect.adjust(0, rect.height()/2, 0, 0);
       
   393         painter.drawText(rect, Qt::AlignCenter, shape.name());
       
   394     }
       
   395 }
       
   396 
       
   397 void Document::setCurrentShape(int index)
       
   398 {
       
   399     QString currentName;
       
   400 
       
   401     if (m_currentIndex != -1)
       
   402         update(m_shapeList.at(m_currentIndex).rect());
       
   403 
       
   404     m_currentIndex = index;
       
   405 
       
   406     if (m_currentIndex != -1) {
       
   407         const Shape &current = m_shapeList.at(m_currentIndex);
       
   408         update(current.rect());
       
   409         currentName = current.name();
       
   410     }
       
   411 
       
   412     emit currentShapeChanged(currentName);
       
   413 }
       
   414 
       
   415 int Document::indexOf(const QString &shapeName) const
       
   416 {
       
   417     for (int i = 0; i < m_shapeList.count(); ++i) {
       
   418         if (m_shapeList.at(i).name() == shapeName)
       
   419             return i;
       
   420     }
       
   421     return -1;
       
   422 }
       
   423 
       
   424 QString Document::uniqueName(const QString &name) const
       
   425 {
       
   426     QString unique;
       
   427 
       
   428     for (int i = 0; ; ++i) {
       
   429         unique = name;
       
   430         if (i > 0)
       
   431             unique += QString::number(i);
       
   432         if (indexOf(unique) == -1)
       
   433             break;
       
   434     }
       
   435 
       
   436     return unique;
       
   437 }
       
   438 
       
   439 QString Document::currentShapeName() const
       
   440 {
       
   441     if (m_currentIndex == -1)
       
   442         return QString();
       
   443     return m_shapeList.at(m_currentIndex).name();
       
   444 }
       
   445