1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). |
4 | ** Contact: http://www.qt-project.org/legal |
5 | ** |
6 | ** This file is part of the QtSvg module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and Digia. For licensing terms and |
14 | ** conditions see http://qt.digia.com/licensing. For further information |
15 | ** use the contact form at http://qt.digia.com/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 2.1 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 2.1 requirements |
23 | ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
24 | ** |
25 | ** In addition, as a special exception, Digia gives you certain additional |
26 | ** rights. These rights are described in the Digia Qt LGPL Exception |
27 | ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
28 | ** |
29 | ** GNU General Public License Usage |
30 | ** Alternatively, this file may be used under the terms of the GNU |
31 | ** General Public License version 3.0 as published by the Free Software |
32 | ** Foundation and appearing in the file LICENSE.GPL included in the |
33 | ** packaging of this file. Please review the following information to |
34 | ** ensure the GNU General Public License version 3.0 requirements will be |
35 | ** met: http://www.gnu.org/copyleft/gpl.html. |
36 | ** |
37 | ** |
38 | ** $QT_END_LICENSE$ |
39 | ** |
40 | ****************************************************************************/ |
41 | #include "qgraphicssvgitem.h" |
42 | |
43 | #ifndef QT_NO_GRAPHICSSVGITEM |
44 | |
45 | #include "qpainter.h" |
46 | #include "qstyleoption.h" |
47 | #include "qsvgrenderer.h" |
48 | #include "qdebug.h" |
49 | |
50 | #include "private/qobject_p.h" |
51 | #include "private/qgraphicsitem_p.h" |
52 | |
53 | QT_BEGIN_NAMESPACE |
54 | |
55 | class QGraphicsSvgItemPrivate : public QGraphicsItemPrivate |
56 | { |
57 | public: |
58 | Q_DECLARE_PUBLIC(QGraphicsSvgItem) |
59 | |
60 | QGraphicsSvgItemPrivate() |
61 | : renderer(0), shared(false) |
62 | { |
63 | } |
64 | |
65 | void init(QGraphicsItem *parent) |
66 | { |
67 | Q_Q(QGraphicsSvgItem); |
68 | q->setParentItem(parent); |
69 | renderer = new QSvgRenderer(q); |
70 | QObject::connect(renderer, SIGNAL(repaintNeeded()), |
71 | q, SLOT(_q_repaintItem())); |
72 | q->setCacheMode(QGraphicsItem::DeviceCoordinateCache); |
73 | q->setMaximumCacheSize(QSize(1024, 768)); |
74 | } |
75 | |
76 | void _q_repaintItem() |
77 | { |
78 | q_func()->update(); |
79 | } |
80 | |
81 | inline void updateDefaultSize() |
82 | { |
83 | QRectF bounds; |
84 | if (elemId.isEmpty()) { |
85 | bounds = QRectF(QPointF(0, 0), renderer->defaultSize()); |
86 | } else { |
87 | bounds = renderer->boundsOnElement(elemId); |
88 | } |
89 | if (boundingRect.size() != bounds.size()) { |
90 | q_func()->prepareGeometryChange(); |
91 | boundingRect.setSize(bounds.size()); |
92 | } |
93 | } |
94 | |
95 | QSvgRenderer *renderer; |
96 | QRectF boundingRect; |
97 | bool shared; |
98 | QString elemId; |
99 | }; |
100 | |
101 | /*! |
102 | \class QGraphicsSvgItem |
103 | \ingroup graphicsview-api |
104 | \brief The QGraphicsSvgItem class is a QGraphicsItem that can be used to render |
105 | the contents of SVG files. |
106 | |
107 | \since 4.2 |
108 | |
109 | QGraphicsSvgItem provides a way of rendering SVG files onto QGraphicsView. |
110 | QGraphicsSvgItem can be created by passing the SVG file to be rendered to |
111 | its constructor or by explicit setting a shared QSvgRenderer on it. |
112 | |
113 | Note that setting QSvgRenderer on a QGraphicsSvgItem doesn't make the item take |
114 | ownership of the renderer, therefore if using setSharedRenderer() method one has |
115 | to make sure that the lifetime of the QSvgRenderer object will be at least as long |
116 | as that of the QGraphicsSvgItem. |
117 | |
118 | QGraphicsSvgItem provides a way of rendering only parts of the SVG files via |
119 | the setElementId. If setElementId() method is called, only the SVG element |
120 | (and its children) with the passed id will be renderer. This provides a convenient |
121 | way of selectively rendering large SVG files that contain a number of discrete |
122 | elements. For example the following code renders only jokers from a SVG file |
123 | containing a whole card deck: |
124 | |
125 | \snippet doc/src/snippets/code/src_svg_qgraphicssvgitem.cpp 0 |
126 | |
127 | Size of the item can be set via the \l{QRectF::setSize()} |
128 | {setSize()} method of the \l{QGraphicsSvgItem::boundingRect()} |
129 | {bounding rectangle} or via direct manipulation of the items |
130 | transformation matrix. |
131 | |
132 | By default the SVG rendering is cached using QGraphicsItem::DeviceCoordinateCache |
133 | mode to speedup the display of items. Caching can be disabled by passing |
134 | QGraphicsItem::NoCache to the QGraphicsItem::setCacheMode() method. |
135 | |
136 | \sa QSvgWidget, {QtSvg Module}, QGraphicsItem, QGraphicsView |
137 | */ |
138 | |
139 | /*! |
140 | Constructs a new SVG item with the given \a parent. |
141 | */ |
142 | QGraphicsSvgItem::QGraphicsSvgItem(QGraphicsItem *parent) |
143 | : QGraphicsObject(*new QGraphicsSvgItemPrivate(), 0, 0) |
144 | { |
145 | Q_D(QGraphicsSvgItem); |
146 | d->init(parent); |
147 | } |
148 | |
149 | /*! |
150 | Constructs a new item with the given \a parent and loads the contents of the |
151 | SVG file with the specified \a fileName. |
152 | */ |
153 | QGraphicsSvgItem::QGraphicsSvgItem(const QString &fileName, QGraphicsItem *parent) |
154 | : QGraphicsObject(*new QGraphicsSvgItemPrivate(), 0, 0) |
155 | { |
156 | Q_D(QGraphicsSvgItem); |
157 | d->init(parent); |
158 | d->renderer->load(fileName); |
159 | d->updateDefaultSize(); |
160 | } |
161 | |
162 | /*! |
163 | Returns the currently use QSvgRenderer. |
164 | */ |
165 | QSvgRenderer *QGraphicsSvgItem::renderer() const |
166 | { |
167 | return d_func()->renderer; |
168 | } |
169 | |
170 | |
171 | /*! |
172 | Returns the bounding rectangle of this item. |
173 | */ |
174 | QRectF QGraphicsSvgItem::boundingRect() const |
175 | { |
176 | Q_D(const QGraphicsSvgItem); |
177 | return d->boundingRect; |
178 | } |
179 | |
180 | /*! |
181 | \internal |
182 | |
183 | Highlights \a item as selected. |
184 | |
185 | NOTE: This function is a duplicate of qt_graphicsItem_highlightSelected() in qgraphicsitem.cpp! |
186 | */ |
187 | static void qt_graphicsItem_highlightSelected( |
188 | QGraphicsItem *item, QPainter *painter, const QStyleOptionGraphicsItem *option) |
189 | { |
190 | const QRectF murect = painter->transform().mapRect(QRectF(0, 0, 1, 1)); |
191 | if (qFuzzyIsNull(qMax(murect.width(), murect.height()))) |
192 | return; |
193 | |
194 | const QRectF mbrect = painter->transform().mapRect(item->boundingRect()); |
195 | if (qMin(mbrect.width(), mbrect.height()) < qreal(1.0)) |
196 | return; |
197 | |
198 | qreal itemPenWidth; |
199 | switch (item->type()) { |
200 | case QGraphicsEllipseItem::Type: |
201 | itemPenWidth = static_cast<QGraphicsEllipseItem *>(item)->pen().widthF(); |
202 | break; |
203 | case QGraphicsPathItem::Type: |
204 | itemPenWidth = static_cast<QGraphicsPathItem *>(item)->pen().widthF(); |
205 | break; |
206 | case QGraphicsPolygonItem::Type: |
207 | itemPenWidth = static_cast<QGraphicsPolygonItem *>(item)->pen().widthF(); |
208 | break; |
209 | case QGraphicsRectItem::Type: |
210 | itemPenWidth = static_cast<QGraphicsRectItem *>(item)->pen().widthF(); |
211 | break; |
212 | case QGraphicsSimpleTextItem::Type: |
213 | itemPenWidth = static_cast<QGraphicsSimpleTextItem *>(item)->pen().widthF(); |
214 | break; |
215 | case QGraphicsLineItem::Type: |
216 | itemPenWidth = static_cast<QGraphicsLineItem *>(item)->pen().widthF(); |
217 | break; |
218 | default: |
219 | itemPenWidth = 1.0; |
220 | } |
221 | const qreal pad = itemPenWidth / 2; |
222 | |
223 | const qreal penWidth = 0; // cosmetic pen |
224 | |
225 | const QColor fgcolor = option->palette.windowText().color(); |
226 | const QColor bgcolor( // ensure good contrast against fgcolor |
227 | fgcolor.red() > 127 ? 0 : 255, |
228 | fgcolor.green() > 127 ? 0 : 255, |
229 | fgcolor.blue() > 127 ? 0 : 255); |
230 | |
231 | painter->setPen(QPen(bgcolor, penWidth, Qt::SolidLine)); |
232 | painter->setBrush(Qt::NoBrush); |
233 | painter->drawRect(item->boundingRect().adjusted(pad, pad, -pad, -pad)); |
234 | |
235 | painter->setPen(QPen(option->palette.windowText(), 0, Qt::DashLine)); |
236 | painter->setBrush(Qt::NoBrush); |
237 | painter->drawRect(item->boundingRect().adjusted(pad, pad, -pad, -pad)); |
238 | } |
239 | |
240 | /*! |
241 | \reimp |
242 | */ |
243 | void QGraphicsSvgItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, |
244 | QWidget *widget) |
245 | { |
246 | // Q_UNUSED(option); |
247 | Q_UNUSED(widget); |
248 | |
249 | Q_D(QGraphicsSvgItem); |
250 | if (!d->renderer->isValid()) |
251 | return; |
252 | |
253 | if (d->elemId.isEmpty()) |
254 | d->renderer->render(painter, d->boundingRect); |
255 | else |
256 | d->renderer->render(painter, d->elemId, d->boundingRect); |
257 | |
258 | if (option->state & QStyle::State_Selected) |
259 | qt_graphicsItem_highlightSelected(this, painter, option); |
260 | } |
261 | |
262 | /*! |
263 | \reimp |
264 | */ |
265 | int QGraphicsSvgItem::type() const |
266 | { |
267 | return Type; |
268 | } |
269 | |
270 | /*! |
271 | \property QGraphicsSvgItem::maximumCacheSize |
272 | \since 4.6 |
273 | |
274 | This property holds the maximum size of the device coordinate cache |
275 | for this item. |
276 | */ |
277 | |
278 | /*! |
279 | Sets the maximum device coordinate cache size of the item to \a size. |
280 | If the item is cached using QGraphicsItem::DeviceCoordinateCache mode, |
281 | caching is bypassed if the extension of the item in device coordinates |
282 | is larger than \a size. |
283 | |
284 | The cache corresponds to the QPixmap which is used to cache the |
285 | results of the rendering. |
286 | Use QPixmapCache::setCacheLimit() to set limitations on the whole cache |
287 | and use setMaximumCacheSize() when setting cache size for individual |
288 | items. |
289 | |
290 | \sa QGraphicsItem::cacheMode() |
291 | */ |
292 | void QGraphicsSvgItem::setMaximumCacheSize(const QSize &size) |
293 | { |
294 | QGraphicsItem::d_ptr->setExtra(QGraphicsItemPrivate::ExtraMaxDeviceCoordCacheSize, size); |
295 | update(); |
296 | } |
297 | |
298 | /*! |
299 | Returns the current maximum size of the device coordinate cache for this item. |
300 | If the item is cached using QGraphicsItem::DeviceCoordinateCache mode, |
301 | caching is bypassed if the extension of the item in device coordinates |
302 | is larger than the maximum size. |
303 | |
304 | The default maximum cache size is 1024x768. |
305 | QPixmapCache::cacheLimit() gives the |
306 | cumulative bounds of the whole cache, whereas maximumCacheSize() refers |
307 | to a maximum cache size for this particular item. |
308 | |
309 | \sa QGraphicsItem::cacheMode() |
310 | */ |
311 | QSize QGraphicsSvgItem::maximumCacheSize() const |
312 | { |
313 | return QGraphicsItem::d_ptr->extra(QGraphicsItemPrivate::ExtraMaxDeviceCoordCacheSize).toSize(); |
314 | } |
315 | |
316 | /*! |
317 | \property QGraphicsSvgItem::elementId |
318 | \since 4.6 |
319 | |
320 | This property holds the element's XML ID. |
321 | */ |
322 | |
323 | /*! |
324 | Sets the XML ID of the element to \a id. |
325 | */ |
326 | void QGraphicsSvgItem::setElementId(const QString &id) |
327 | { |
328 | Q_D(QGraphicsSvgItem); |
329 | d->elemId = id; |
330 | d->updateDefaultSize(); |
331 | update(); |
332 | } |
333 | |
334 | /*! |
335 | Returns the XML ID the element that is currently |
336 | being rendered. Returns an empty string if the whole |
337 | file is being rendered. |
338 | */ |
339 | QString QGraphicsSvgItem::elementId() const |
340 | { |
341 | Q_D(const QGraphicsSvgItem); |
342 | return d->elemId; |
343 | } |
344 | |
345 | /*! |
346 | Sets \a renderer to be a shared QSvgRenderer on the item. By |
347 | using this method one can share the same QSvgRenderer on a number |
348 | of items. This means that the SVG file will be parsed only once. |
349 | QSvgRenderer passed to this method has to exist for as long as |
350 | this item is used. |
351 | */ |
352 | void QGraphicsSvgItem::setSharedRenderer(QSvgRenderer *renderer) |
353 | { |
354 | Q_D(QGraphicsSvgItem); |
355 | if (!d->shared) |
356 | delete d->renderer; |
357 | |
358 | d->renderer = renderer; |
359 | d->shared = true; |
360 | |
361 | d->updateDefaultSize(); |
362 | |
363 | update(); |
364 | } |
365 | |
366 | /*! |
367 | \obsolete |
368 | |
369 | Use QGraphicsItem::setCacheMode() instead. Passing true to this function is equivalent |
370 | to QGraphicsItem::setCacheMode(QGraphicsItem::DeviceCoordinateCache). |
371 | */ |
372 | void QGraphicsSvgItem::setCachingEnabled(bool caching) |
373 | { |
374 | setCacheMode(caching ? QGraphicsItem::DeviceCoordinateCache : QGraphicsItem::NoCache); |
375 | } |
376 | |
377 | /*! |
378 | \obsolete |
379 | |
380 | Use QGraphicsItem::cacheMode() instead. |
381 | */ |
382 | bool QGraphicsSvgItem::isCachingEnabled() const |
383 | { |
384 | return cacheMode() != QGraphicsItem::NoCache; |
385 | } |
386 | |
387 | QT_END_NAMESPACE |
388 | |
389 | #include "moc_qgraphicssvgitem.cpp" |
390 | |
391 | #endif // QT_NO_GRAPHICSSVGITEM |
392 | |