source: trunk/demos/undo/document.cpp@ 561

Last change on this file since 561 was 561, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.1 sources.

File size: 11.5 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the demonstration applications of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
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** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
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
49static const int resizeHandleWidth = 6;
50
51/******************************************************************************
52** Shape
53*/
54
55const QSize Shape::minSize(80, 50);
56
57Shape::Shape(Type type, const QColor &color, const QRect &rect)
58 : m_type(type), m_rect(rect), m_color(color)
59{
60}
61
62Shape::Type Shape::type() const
63{
64 return m_type;
65}
66
67QRect Shape::rect() const
68{
69 return m_rect;
70}
71
72QColor Shape::color() const
73{
74 return m_color;
75}
76
77QString Shape::name() const
78{
79 return m_name;
80}
81
82QRect Shape::resizeHandle() const
83{
84 QPoint br = m_rect.bottomRight();
85 return QRect(br - QPoint(resizeHandleWidth, resizeHandleWidth), br);
86}
87
88QString 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
107Shape::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
128Document::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
142QString 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
154void 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
172Shape 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
180void 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
194void 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
207QUndoStack *Document::undoStack() const
208{
209 return m_undoStack;
210}
211
212bool 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
243void 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
261QString Document::fileName() const
262{
263 return m_fileName;
264}
265
266void Document::setFileName(const QString &fileName)
267{
268 m_fileName = fileName;
269}
270
271int 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
280void 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
298void Document::mouseReleaseEvent(QMouseEvent *event)
299{
300 event->accept();
301 m_mousePressIndex = -1;
302}
303
304void 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
327static 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
338static 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
347void 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
397void 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
415int 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
424QString 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
439QString Document::currentShapeName() const
440{
441 if (m_currentIndex == -1)
442 return QString();
443 return m_shapeList.at(m_currentIndex).name();
444}
445
Note: See TracBrowser for help on using the repository browser.