source: trunk/src/qt3support/canvas/q3canvas.cpp@ 497

Last change on this file since 497 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 131.4 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** This file is part of the Qt3Support module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** 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 are unsure which license is appropriate for your use, please
37** contact the sales department at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "q3canvas.h"
43#include "qapplication.h"
44#include "qbitmap.h"
45#include "qdesktopwidget.h"
46#include "qimage.h"
47#include "q3ptrdict.h"
48#include "qpainter.h"
49#include "q3polygonscanner.h"
50#include "qtimer.h"
51#include "q3tl.h"
52
53#include <stdlib.h>
54
55QT_BEGIN_NAMESPACE
56
57using namespace Qt;
58
59class Q3CanvasData {
60public:
61 Q3CanvasData() :
62 itemDict(1013), animDict(503)
63 {
64 }
65
66 Q3PtrList<Q3CanvasView> viewList;
67 Q3PtrDict<void> itemDict;
68 Q3PtrDict<void> animDict;
69};
70
71class Q3CanvasViewData {
72public:
73 Q3CanvasViewData() {}
74#ifndef QT_NO_TRANSFORMATIONS
75 QMatrix xform;
76 QMatrix ixform;
77#endif
78 QRegion eraseRegion;
79};
80
81// clusterizer
82
83class Q3CanvasClusterizer {
84public:
85 Q3CanvasClusterizer(int maxclusters);
86 ~Q3CanvasClusterizer();
87
88 void add(int x, int y); // 1x1 rectangle (point)
89 void add(int x, int y, int w, int h);
90 void add(const QRect& rect);
91
92 void clear();
93 int clusters() const { return count; }
94 const QRect& operator[](int i) const;
95
96private:
97 QRect* cluster;
98 int count;
99 const int maxcl;
100};
101
102static
103void include(QRect& r, const QRect& rect)
104{
105 if (rect.left()<r.left()) {
106 r.setLeft(rect.left());
107 }
108 if (rect.right()>r.right()) {
109 r.setRight(rect.right());
110 }
111 if (rect.top()<r.top()) {
112 r.setTop(rect.top());
113 }
114 if (rect.bottom()>r.bottom()) {
115 r.setBottom(rect.bottom());
116 }
117}
118
119/*
120A Q3CanvasClusterizer groups rectangles (QRects) into non-overlapping rectangles
121by a merging heuristic.
122*/
123Q3CanvasClusterizer::Q3CanvasClusterizer(int maxclusters) :
124 cluster(new QRect[maxclusters]),
125 count(0),
126 maxcl(maxclusters)
127{ }
128
129Q3CanvasClusterizer::~Q3CanvasClusterizer()
130{
131 delete [] cluster;
132}
133
134void Q3CanvasClusterizer::clear()
135{
136 count=0;
137}
138
139void Q3CanvasClusterizer::add(int x, int y)
140{
141 add(QRect(x,y,1,1));
142}
143
144void Q3CanvasClusterizer::add(int x, int y, int w, int h)
145{
146 add(QRect(x,y,w,h));
147}
148
149void Q3CanvasClusterizer::add(const QRect& rect)
150{
151 QRect biggerrect(rect.x()-1,rect.y()-1,rect.width()+2,rect.height()+2);
152
153 //Q_ASSERT(rect.width()>0 && rect.height()>0);
154
155 int cursor;
156
157 for (cursor=0; cursor<count; cursor++) {
158 if (cluster[cursor].contains(rect)) {
159 // Wholly contained already.
160 return;
161 }
162 }
163
164 int lowestcost=9999999;
165 int cheapest=-1;
166 cursor = 0;
167 while(cursor<count) {
168 if (cluster[cursor].intersects(biggerrect)) {
169 QRect larger=cluster[cursor];
170 include(larger,rect);
171 int cost = larger.width()*larger.height() -
172 cluster[cursor].width()*cluster[cursor].height();
173
174 if (cost < lowestcost) {
175 bool bad=false;
176 for (int c=0; c<count && !bad; c++) {
177 bad=cluster[c].intersects(larger) && c!=cursor;
178 }
179 if (!bad) {
180 cheapest=cursor;
181 lowestcost=cost;
182 }
183 }
184 }
185 cursor++;
186 }
187
188 if (cheapest>=0) {
189 include(cluster[cheapest],rect);
190 return;
191 }
192
193 if (count < maxcl) {
194 cluster[count++]=rect;
195 return;
196 }
197
198 // Do cheapest of:
199 // add to closest cluster
200 // do cheapest cluster merge, add to new cluster
201
202 lowestcost=9999999;
203 cheapest=-1;
204 cursor=0;
205 while(cursor<count) {
206 QRect larger=cluster[cursor];
207 include(larger,rect);
208 int cost=larger.width()*larger.height()
209 - cluster[cursor].width()*cluster[cursor].height();
210 if (cost < lowestcost) {
211 bool bad=false;
212 for (int c=0; c<count && !bad; c++) {
213 bad=cluster[c].intersects(larger) && c!=cursor;
214 }
215 if (!bad) {
216 cheapest=cursor;
217 lowestcost=cost;
218 }
219 }
220 cursor++;
221 }
222
223 // ###
224 // could make an heuristic guess as to whether we need to bother
225 // looking for a cheap merge.
226
227 int cheapestmerge1 = -1;
228 int cheapestmerge2 = -1;
229
230 int merge1 = 0;
231 while(merge1 < count) {
232 int merge2=0;
233 while(merge2 < count) {
234 if(merge1!=merge2) {
235 QRect larger=cluster[merge1];
236 include(larger,cluster[merge2]);
237 int cost=larger.width()*larger.height()
238 - cluster[merge1].width()*cluster[merge1].height()
239 - cluster[merge2].width()*cluster[merge2].height();
240 if (cost < lowestcost) {
241 bool bad=false;
242 for (int c=0; c<count && !bad; c++) {
243 bad=cluster[c].intersects(larger) && c!=cursor;
244 }
245 if (!bad) {
246 cheapestmerge1=merge1;
247 cheapestmerge2=merge2;
248 lowestcost=cost;
249 }
250 }
251 }
252 merge2++;
253 }
254 merge1++;
255 }
256
257 if (cheapestmerge1>=0) {
258 include(cluster[cheapestmerge1],cluster[cheapestmerge2]);
259 cluster[cheapestmerge2]=cluster[count--];
260 } else {
261 // if (!cheapest) debugRectangles(rect);
262 include(cluster[cheapest],rect);
263 }
264
265 // NB: clusters do not intersect (or intersection will
266 // overwrite). This is a result of the above algorithm,
267 // given the assumption that (x,y) are ordered topleft
268 // to bottomright.
269
270 // ###
271 //
272 // add explicit x/y ordering to that comment, move it to the top
273 // and rephrase it as pre-/post-conditions.
274}
275
276const QRect& Q3CanvasClusterizer::operator[](int i) const
277{
278 return cluster[i];
279}
280
281// end of clusterizer
282
283// there's no more device coordinate clipping done, so introduce these
284// clip setting compat functions
285
286static void qt_setclipregion(QPainter *p, const QRegion &r)
287{
288 QMatrix matrix = p->worldMatrix();
289 p->setWorldMatrix(QMatrix());
290 p->setClipRegion(r);
291 p->setWorldMatrix(matrix);
292}
293
294static void qt_setcliprect(QPainter *p, const QRect &r)
295{
296 qt_setclipregion(p, QRegion(r));
297}
298
299
300class Q_COMPAT_EXPORT Q3CanvasItemPtr {
301public:
302 Q3CanvasItemPtr() : ptr(0) { }
303 Q3CanvasItemPtr(Q3CanvasItem* p) : ptr(p) { }
304
305 bool operator<=(const Q3CanvasItemPtr& that) const
306 {
307 // Order same-z objects by identity.
308 if (that.ptr->z()==ptr->z())
309 return that.ptr <= ptr;
310 return that.ptr->z() <= ptr->z();
311 }
312 bool operator<(const Q3CanvasItemPtr& that) const
313 {
314 // Order same-z objects by identity.
315 if (that.ptr->z()==ptr->z())
316 return that.ptr < ptr;
317 return that.ptr->z() < ptr->z();
318 }
319 bool operator>(const Q3CanvasItemPtr& that) const
320 {
321 // Order same-z objects by identity.
322 if (that.ptr->z()==ptr->z())
323 return that.ptr > ptr;
324 return that.ptr->z() > ptr->z();
325 }
326 bool operator==(const Q3CanvasItemPtr& that) const
327 {
328 return that.ptr == ptr;
329 }
330 operator Q3CanvasItem*() const { return ptr; }
331
332private:
333 Q3CanvasItem* ptr;
334};
335
336
337/*!
338 \class Q3CanvasItemList
339 \compat
340 \brief The Q3CanvasItemList class is a list of Q3CanvasItems.
341
342 Q3CanvasItemList is a Q3ValueList of pointers to \l{Q3CanvasItem}s.
343 This class is used by some methods in Q3Canvas that need to return
344 a list of canvas items.
345
346 The \l Q3ValueList documentation describes how to use this list.
347
348 \sa QtCanvas, {Porting to Graphics View}
349*/
350
351/*!
352 \internal
353*/
354void Q3CanvasItemList::sort()
355{
356 qHeapSort(*((Q3ValueList<Q3CanvasItemPtr>*)this));
357}
358
359/*!
360 \internal
361*/
362void Q3CanvasItemList::drawUnique(QPainter& painter)
363{
364 Q3CanvasItem* prev=0;
365 for (Iterator it=fromLast(); it!=end(); --it) {
366 Q3CanvasItem *g=*it;
367 if (g!=prev) {
368 g->draw(painter);
369 prev=g;
370 }
371 }
372}
373
374/*!
375 Returns the concatenation of this list and list \a l.
376*/
377Q3CanvasItemList Q3CanvasItemList::operator+(const Q3CanvasItemList &l) const
378{
379 Q3CanvasItemList l2(*this);
380 for(const_iterator it = l.begin(); it != l.end(); ++it)
381 l2.append(*it);
382 return l2;
383}
384
385class Q3CanvasChunk {
386public:
387 Q3CanvasChunk() : changed(true) { }
388 // Other code assumes lists are not deleted. Assignment is also
389 // done on ChunkRecs. So don't add that sort of thing here.
390
391 void sort()
392 {
393 list.sort();
394 }
395
396 const Q3CanvasItemList* listPtr() const
397 {
398 return &list;
399 }
400
401 void add(Q3CanvasItem* item)
402 {
403 list.prepend(item);
404 changed = true;
405 }
406
407 void remove(Q3CanvasItem* item)
408 {
409 list.remove(item);
410 changed = true;
411 }
412
413 void change()
414 {
415 changed = true;
416 }
417
418 bool hasChanged() const
419 {
420 return changed;
421 }
422
423 bool takeChange()
424 {
425 bool y = changed;
426 changed = false;
427 return y;
428 }
429
430private:
431 Q3CanvasItemList list;
432 bool changed;
433};
434
435
436static int gcd(int a, int b)
437{
438 int r;
439 while ((r = a%b)) {
440 a=b;
441 b=r;
442 }
443 return b;
444}
445
446static int scm(int a, int b)
447{
448 int g = gcd(a,b);
449 return a/g*b;
450}
451
452
453
454/*!
455 \class Q3Canvas
456 \compat
457 \brief The Q3Canvas class provides a 2D area that can contain Q3CanvasItem objects.
458
459 The Q3Canvas class manages its 2D graphic area and all the canvas
460 items the area contains. The canvas has no visual appearance of
461 its own. Instead, it is displayed on screen using a Q3CanvasView.
462 Multiple Q3CanvasView widgets may be associated with a canvas to
463 provide multiple views of the same canvas.
464
465 The canvas is optimized for large numbers of items, particularly
466 where only a small percentage of the items change at any
467 one time. If the entire display changes very frequently, you should
468 consider using your own custom Q3ScrollView subclass.
469
470 Qt provides a rich
471 set of canvas item classes, e.g. Q3CanvasEllipse, Q3CanvasLine,
472 Q3CanvasPolygon, Q3CanvasPolygonalItem, Q3CanvasRectangle, Q3CanvasSpline,
473 Q3CanvasSprite and Q3CanvasText. You can subclass to create your own
474 canvas items; Q3CanvasPolygonalItem is the most common base class used
475 for this purpose.
476
477 Items appear on the canvas after their \link Q3CanvasItem::show()
478 show()\endlink function has been called (or \link
479 Q3CanvasItem::setVisible() setVisible(true)\endlink), and \e after
480 update() has been called. The canvas only shows items that are
481 \link Q3CanvasItem::setVisible() visible\endlink, and then only if
482 \l update() is called. (By default the canvas is white and so are
483 canvas items, so if nothing appears try changing colors.)
484
485 If you created the canvas without passing a width and height to
486 the constructor you must also call resize().
487
488 Although a canvas may appear to be similar to a widget with child
489 widgets, there are several notable differences:
490
491 \list
492 \i Canvas items are usually much faster to manipulate and redraw than
493 child widgets, with the speed advantage becoming especially great when
494 there are \e many canvas items and non-rectangular items. In most
495 situations canvas items are also a lot more memory efficient than child
496 widgets.
497
498 \i It's easy to detect overlapping items (collision detection).
499
500 \i The canvas can be larger than a widget. A million-by-million canvas
501 is perfectly possible. At such a size a widget might be very
502 inefficient, and some window systems might not support it at all,
503 whereas Q3Canvas scales well. Even with a billion pixels and a million
504 items, finding a particular canvas item, detecting collisions, etc.,
505 is still fast (though the memory consumption may be prohibitive
506 at such extremes).
507
508 \i Two or more Q3CanvasView objects can view the same canvas.
509
510 \i An arbitrary transformation matrix can be set on each Q3CanvasView
511 which makes it easy to zoom, rotate or shear the viewed canvas.
512
513 \i Widgets provide a lot more functionality, such as input (QKeyEvent,
514 QMouseEvent etc.) and layout management (QGridLayout etc.).
515
516 \endlist
517
518 A canvas consists of a background, a number of canvas items organized by
519 x, y and z coordinates, and a foreground. A canvas item's z coordinate
520 can be treated as a layer number -- canvas items with a higher z
521 coordinate appear in front of canvas items with a lower z coordinate.
522
523 The background is white by default, but can be set to a different color
524 using setBackgroundColor(), or to a repeated pixmap using
525 setBackgroundPixmap() or to a mosaic of smaller pixmaps using
526 setTiles(). Individual tiles can be set with setTile(). There
527 are corresponding get functions, e.g. backgroundColor() and
528 backgroundPixmap().
529
530 Note that Q3Canvas does not inherit from QWidget, even though it has some
531 functions which provide the same functionality as those in QWidget. One
532 of these is setBackgroundPixmap(); some others are resize(), size(),
533 width() and height(). \l Q3CanvasView is the widget used to display a
534 canvas on the screen.
535
536 Canvas items are added to a canvas by constructing them and passing the
537 canvas to the canvas item's constructor. An item can be moved to a
538 different canvas using Q3CanvasItem::setCanvas().
539
540 Canvas items are movable (and in the case of Q3CanvasSprites, animated)
541 objects that inherit Q3CanvasItem. Each canvas item has a position on the
542 canvas (x, y coordinates) and a height (z coordinate), all of which are
543 held as floating-point numbers. Moving canvas items also have x and y
544 velocities. It's possible for a canvas item to be outside the canvas
545 (for example Q3CanvasItem::x() is greater than width()). When a canvas
546 item is off the canvas, onCanvas() returns false and the canvas
547 disregards the item. (Canvas items off the canvas do not slow down any
548 of the common operations on the canvas.)
549
550 Canvas items can be moved with Q3CanvasItem::move(). The advance()
551 function moves all Q3CanvasItem::animated() canvas items and
552 setAdvancePeriod() makes Q3Canvas move them automatically on a periodic
553 basis. In the context of the Q3Canvas classes, to `animate' a canvas item
554 is to set it in motion, i.e. using Q3CanvasItem::setVelocity(). Animation