source: trunk/demos/undo/mainwindow.cpp@ 846

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

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 14.3 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 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 <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
52MainWindow::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
112void 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
151void 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
179QString 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
213void 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
226void MainWindow::setCurrentDocument(Document *doc)
227{
228 documentTabs->setCurrentWidget(doc);
229}
230
231Document *MainWindow::currentDocument() const
232{
233 return qobject_cast<Document*>(documentTabs->currentWidget());
234}
235
236void 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
254void 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
288void 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
308void MainWindow::newDocument()
309{
310 addDocument(new Document());
311}
312
313static 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
327static 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
339void 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
359void 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
372void 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
399void 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
417void 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
438void MainWindow::about()
439{
440 QMessageBox::about(this, tr("About Undo"), tr("The Undo demonstration shows how to use the Qt Undo framework."));
441}
442
443void MainWindow::aboutQt()
444{
445 QMessageBox::aboutQt(this, tr("About Qt"));
446}
Note: See TracBrowser for help on using the repository browser.