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