~ubuntu-branches/ubuntu/karmic/psi/karmic

« back to all changes in this revision

Viewing changes to src/wbitems.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Jan Niehusmann
  • Date: 2008-04-14 18:57:30 UTC
  • mfrom: (2.1.9 hardy)
  • Revision ID: james.westby@ubuntu.com-20080414185730-528re3zp0m2hdlhi
Tags: 0.11-8
* added CONFIG -= link_prl to .pro files and removed dependencies
  which are made unnecessary by this change
* Fix segfault when closing last chat tab with qt4.4
  (This is from upstream svn, rev. 1101) (Closes: Bug#476122)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * wbitems.cpp - the item classes for the SVG WB
 
3
 * Copyright (C) 2006  Joonas Govenius
 
4
 *
 
5
 * This program is free software; you can redistribute it and/or
 
6
 * modify it under the terms of the GNU General Public License
 
7
 * as published by the Free Software Foundation; either version 2
 
8
 * of the License, or (at your option) any later version.
 
9
 *
 
10
 * This program is distributed in the hope that it will be useful,
 
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
 * GNU General Public License for more details.
 
14
 *
 
15
 * You should have received a copy of the GNU General Public License
 
16
 * along with this library; if not, write to the Free Software
 
17
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
18
 *
 
19
 */
 
20
 
 
21
#include "wbitems.h"
 
22
#include "wbscene.h"
 
23
#include <QDebug>
 
24
#ifndef M_PI
 
25
#define M_PI 3.14159265358979323846
 
26
#endif
 
27
 
 
28
/*
 
29
 *      Edit
 
30
 */
 
31
 
 
32
Edit::Edit(Type _type, const QDomElement &_xml) {
 
33
        type = _type;
 
34
        xml = _xml.cloneNode().toElement();
 
35
}
 
36
 
 
37
Edit::Edit(Type _type, const QString &_target, const QDomElement &edit, const QString &_oldValue) {
 
38
        type = _type;
 
39
        target = _target;
 
40
        xml = edit.cloneNode().toElement();
 
41
        if(_type == AttributeEdit)
 
42
                oldValue = _oldValue;
 
43
        else if(_type == AttributeEdit)
 
44
                oldParent = _oldValue;
 
45
}
 
46
 
 
47
Edit::Edit(const QString &_target, const QDomElement &edit, QDomNodeList _oldContent) {
 
48
        type = Edit::ContentEdit;
 
49
        target = _target;
 
50
        xml = edit.cloneNode().toElement();
 
51
        oldContent = _oldContent;
 
52
}
 
53
 
 
54
/*
 
55
 *      EditUndo
 
56
 */
 
57
 
 
58
EditUndo::EditUndo(const int &_version, const Edit &edit) {
 
59
        type = edit.type;
 
60
        version = _version;
 
61
        if(type == Edit::AttributeEdit) {
 
62
                attribute = edit.xml.attribute("name");
 
63
                oldValue = edit.oldValue;
 
64
        } else if(type == Edit::ParentEdit) {
 
65
                oldParent = edit.oldParent;
 
66
        } else if(type == Edit::ContentEdit) {
 
67
                oldContent = edit.oldContent;
 
68
        }
 
69
}
 
70
 
 
71
EditUndo::EditUndo(const int &_version, const QString &_attribute, const QString &_oldValue) {
 
72
        type = Edit::AttributeEdit;
 
73
        version = _version;
 
74
        attribute = _attribute;
 
75
        oldValue = _oldValue;
 
76
}
 
77
 
 
78
EditUndo::EditUndo(const int &_version, const QString &_oldParent) {
 
79
        type = Edit::ParentEdit;
 
80
        version = _version;
 
81
        oldParent = _oldParent;
 
82
}
 
83
 
 
84
EditUndo::EditUndo(const int &_version, QDomNodeList _oldContent) {
 
85
        type = Edit::ContentEdit;
 
86
        version = _version;
 
87
        oldContent = _oldContent;
 
88
}
 
89
 
 
90
/*
 
91
 *      WbItemMenu
 
92
 */
 
93
 
 
94
WbItemMenu::WbItemMenu(QWidget* parent) : QMenu(parent) {
 
95
        connect(this, SIGNAL(aboutToHide()), SLOT(destructSelf()));
 
96
}
 
97
 
 
98
WbItemMenu::~WbItemMenu() {
 
99
        foreach(QActionGroup* g, groups_) {
 
100
                foreach(QAction* a, g->actions()) {
 
101
                        a->deleteLater();
 
102
                }
 
103
                g->deleteLater();
 
104
        }
 
105
}
 
106
 
 
107
void WbItemMenu::addActionGroup(QActionGroup* g) {
 
108
        groups_.append(g);
 
109
        addActions(g->actions());
 
110
}
 
111
 
 
112
void WbItemMenu::destructSelf() {
 
113
        deleteLater();
 
114
}
 
115
 
 
116
 
 
117
/*
 
118
 *      WbItem
 
119
 */
 
120
 
 
121
WbItem::WbItem(const QString &id) {
 
122
        id_ = id;
 
123
        parent_ = "root";
 
124
        version = 0;
 
125
}
 
126
 
 
127
QString WbItem::parentWbItem() {
 
128
                return parent_;
 
129
}
 
130
 
 
131
void WbItem::setParentWbItem(const QString &newParent, bool emitChanges) {
 
132
        if(newParent != parent_) {
 
133
                if(newParent.isEmpty()) {
 
134
                        if(parent_ != "root") {
 
135
                                graphicsItem()->setParentItem(0);
 
136
                                if(emitChanges)
 
137
                                        emit parentChanged(id(), "root", parent_);
 
138
                                parent_ = "root";
 
139
                        }
 
140
                } else {
 
141
                        if(graphicsItem()) {
 
142
                                WbScene* wbscene = qobject_cast<WbScene*>(graphicsItem()->scene());
 
143
                                if(wbscene) {
 
144
                                        WbItem* p = wbscene->findWbItem(newParent);
 
145
                                        if(p && p->graphicsItem())
 
146
                                                graphicsItem()->setParentItem(p->graphicsItem());
 
147
                                }
 
148
                        }
 
149
                        if(emitChanges)
 
150
                                emit parentChanged(id(), newParent, parent_);
 
151
                        parent_ = newParent;
 
152
                }
 
153
        }
 
154
}
 
155
 
 
156
QString WbItem::id() {
 
157
        return id_;
 
158
}
 
159
 
 
160
qreal WbItem::index() {
 
161
        return index_;
 
162
}
 
163
 
 
164
void WbItem::setIndex(qreal index, bool emitChanges) {
 
165
        if(index_ != index) {
 
166
                if(emitChanges)
 
167
                        emit indexChanged(id(), index - index_);
 
168
                index_ = index;
 
169
                if(graphicsItem()) {
 
170
                        QString z;
 
171
                        if(index_ - static_cast<int>(index_))
 
172
                                z = QString("%1").arg(index_);
 
173
                        else
 
174
                                z = QString("%1.").arg(index_);
 
175
                        int c;
 
176
                        for(int i=0; i < id_.length(); i++) {
 
177
                                c = id_.at(i).unicode();
 
178
                                //TODO: what should be the minumum width? at least 3 but can a jid contain characters that have a value beyond 999
 
179
                                z.append(QString("%1").arg(QString("%1").arg(c), 4, QLatin1Char('0')));
 
180
                        }
 
181
        //              qDebug() << "z: " << z;
 
182
                        graphicsItem()->setZValue(z.toDouble());
 
183
                }
 
184
        }
 
185
}
 
186
 
 
187
WbItemMenu* WbItem::constructContextMenu() {
 
188
        WbItemMenu* menu = new WbItemMenu(0);
 
189
        WbScene* wbscene = qobject_cast<WbScene*>(graphicsItem()->scene());
 
190
        if(wbscene) {
 
191
                // Add the default actions
 
192
                QActionGroup* group = new QActionGroup(this);
 
193
                QPixmap pixmap(2, 2);
 
194
                pixmap.fill(QColor(Qt::black));
 
195
                QAction* qaction = new QAction(QIcon(pixmap), tr("Thin stroke"), group);
 
196
                qaction->setData(QVariant(1));
 
197
                pixmap = QPixmap(6, 6);
 
198
                pixmap.fill(QColor(Qt::black));
 
199
                qaction = new QAction(QIcon(pixmap), tr("Medium stroke"), group);
 
200
                qaction->setData(QVariant(3));
 
201
                pixmap = QPixmap(12, 12);
 
202
                pixmap.fill(QColor(Qt::black));
 
203
                qaction = new QAction(QIcon(pixmap), tr("Thick stroke"), group);
 
204
                qaction->setData(QVariant(6));
 
205
                connect(group, SIGNAL(triggered(QAction*)), wbscene, SLOT(setStrokeWidth(QAction*)));
 
206
                menu->addActionGroup(group);
 
207
 
 
208
                menu->addSeparator();
 
209
                group = new QActionGroup(this);
 
210
                pixmap = QPixmap(16, 16);
 
211
                pixmap.fill(QColor(Qt::darkCyan));
 
212
                qaction = new QAction(QIcon(pixmap), tr("Change stroke color"), group);
 
213
                connect(qaction, SIGNAL(triggered()), wbscene, SLOT(setStrokeColor()));
 
214
                pixmap.fill(QColor(Qt::darkYellow));
 
215
                qaction = new QAction(QIcon(pixmap), tr("Change fill color"), group);
 
216
                connect(qaction, SIGNAL(triggered()), wbscene, SLOT(setFillColor()));
 
217
                menu->addActionGroup(group);
 
218
 
 
219
                menu->addSeparator();
 
220
                group = new QActionGroup(this);
 
221
                IconAction* action = new IconAction(tr("Bring forward"), "psi/bringForwards", tr("Bring forward"), 0, group);
 
222
                connect(action, SIGNAL(triggered()), wbscene, SLOT(bringForward()));
 
223
                action = new IconAction(tr("Bring to front"), "psi/bringToFront", tr("Bring to front"), 0, group);
 
224
                connect(action, SIGNAL(triggered()), wbscene, SLOT(bringToFront()));
 
225
                action = new IconAction(tr("Send backwards"), "psi/sendBackwards", tr("Send backwards"), 0, group);
 
226
                connect(action, SIGNAL(triggered()), wbscene, SLOT(sendBackwards()));
 
227
                action = new IconAction(tr("Send to back"), "psi/sendToBack", tr("Send to back"), 0, group);
 
228
                connect(action, SIGNAL(triggered()), wbscene, SLOT(sendToBack()));
 
229
                menu->addActionGroup(group);
 
230
 
 
231
                menu->addSeparator();
 
232
                group = new QActionGroup(this);
 
233
                action = new IconAction(tr("Group"), "psi/group", tr("Group"), 0, group);
 
234
                connect(action, SIGNAL(triggered()), wbscene, SLOT(group()));
 
235
                action = new IconAction(tr("Ungroup"), "psi/ungroup", tr("Ungroup"), 0, group);
 
236
                connect(action, SIGNAL(triggered()), wbscene, SLOT(ungroup()));
 
237
                menu->addActionGroup(group);
 
238
        }
 
239
        return menu;
 
240
}
 
241
 
 
242
void WbItem::setStrokeColor(QColor color) {
 
243
        if(graphicsItem()) {
 
244
                QDomElement _svg = svg();
 
245
                _svg.setAttribute("stroke", QString("rgb(%1,%2,%3)").arg(color.red()).arg(color.green()).arg(color.blue()));
 
246
                if(color.alphaF() != 1 || _svg.hasAttribute("stroke-opacity"))
 
247
                        _svg.setAttribute("stroke-opacity", QString("%1").arg(color.alphaF()));
 
248
                parseSvg(_svg, true);
 
249
        }
 
250
}
 
251
 
 
252
void WbItem::setFillColor(QColor color) {
 
253
        if(graphicsItem()) {
 
254
                QDomElement _svg = svg();
 
255
                if(color == Qt::transparent) {
 
256
                        _svg.removeAttribute("fill");
 
257
                        _svg.removeAttribute("fill-opacity");
 
258
                } else {
 
259
                        _svg.setAttribute("fill", QString("rgb(%1,%2,%3)").arg(color.red()).arg(color.green()).arg(color.blue()));
 
260
                        if(color.alphaF() != 1 || _svg.hasAttribute("fill-opacity"))
 
261
                                _svg.setAttribute("fill-opacity", QString("%1").arg(color.alphaF()));
 
262
                }
 
263
                parseSvg(_svg, true);
 
264
        }
 
265
}
 
266
 
 
267
void WbItem::setStrokeWidth(int width) {
 
268
        if(graphicsItem()) {
 
269
                QDomElement _svg = svg();
 
270
                _svg.setAttribute("stroke-width", QString("%1").arg(width));
 
271
                parseSvg(_svg, true);
 
272
        }
 
273
}
 
274
 
 
275
QDomElement WbItem::svg() {
 
276
        QDomDocument doc;
 
277
        QDomElement _svg = doc.createElement("item");
 
278
        foreach(QString a, attributes.keys()) {
 
279
                if(!attributes[a].isNull())
 
280
                        _svg.setAttribute(a, attributes[a]);
 
281
                else
 
282
                        attributes.remove(a);
 
283
        }
 
284
        switch(type()) {
 
285
                case 87654000:
 
286
                        _svg.setTagName("root");
 
287
                        break;
 
288
                case 87654001:
 
289
                        _svg.setTagName("path");
 
290
                        break;
 
291
                case 87654002:
 
292
                        _svg.setTagName("ellipse");
 
293
                        break;
 
294
                case 87654003:
 
295
                        _svg.setTagName("circle");
 
296
                        break;
 
297
                case 87654004:
 
298
                        _svg.setTagName("rect");
 
299
                        break;
 
300
                case 87654005:
 
301
                        _svg.setTagName("line");
 
302
                        break;
 
303
                case 87654006:
 
304
                        _svg.setTagName("polyline");
 
305
                        break;
 
306
                case 87654007:
 
307
                        _svg.setTagName("polygon");
 
308
                        break;
 
309
                case 87654101:
 
310
                        _svg.setTagName("text");
 
311
                        break;
 
312
                case 87654102:
 
313
                        _svg.setTagName("image");
 
314
                        break;
 
315
                case 87654201:
 
316
                        _svg.setTagName("g");
 
317
                        break;
 
318
        }
 
319
        return _svg;
 
320
};
 
321
 
 
322
QList<QString> WbItem::parseSvg(QDomElement &_svg, bool emitChanges) {
 
323
        QList<QString> changed;
 
324
        // First process changes to existing elements
 
325
        foreach(QString a, attributes.keys()) {
 
326
                if(attributes[a] != _svg.attribute(a)) {
 
327
                        if(a.startsWith("stroke") || a == "opacity")
 
328
                                changed.append("pen");
 
329
                        else
 
330
                                changed.append(a);
 
331
                }
 
332
                if(_svg.hasAttribute(a)) {
 
333
                        if(attributes[a] != _svg.attribute(a) && emitChanges)
 
334
                                emit attributeChanged(id(), a, _svg.attribute(a), attributes[a]);
 
335
                        attributes[a] = _svg.attribute(a);
 
336
                        _svg.removeAttribute(a);
 
337
                } else {
 
338
                        if(emitChanges)
 
339
                                emit attributeChanged(id(), a, QString(), attributes[a]);
 
340
                        attributes.remove(a);
 
341
                }
 
342
        }
 
343
        QDomNode a;
 
344
        for(uint i = 0; i < _svg.attributes().length(); i++) {
 
345
                a = _svg.attributes().item(i).cloneNode();
 
346
                if(emitChanges)
 
347
                        emit attributeChanged(id(), a.nodeName(), a.nodeValue(), QString());
 
348
                attributes[a.nodeName()] = a.nodeValue();
 
349
                if(a.nodeName().startsWith("stroke") || a.nodeName() == "opacity")
 
350
                        changed.append("pen");
 
351
                else
 
352
                        changed.append(a.nodeName());
 
353
        }
 
354
 
 
355
        if(graphicsItem()) {
 
356
                // 'x' & 'y' & 'cx' & 'cy'
 
357
                if(changed.contains("x") || changed.contains("y") || changed.contains("cx") || changed.contains("cy")) {
 
358
                        bool xOk, yOk;
 
359
                        qreal xPos, yPos;
 
360
                        if(changed.contains("x") || changed.contains("y")) {
 
361
                                xPos = attributes["x"].toDouble(&xOk);
 
362
                                yPos = attributes["y"].toDouble(&yOk);
 
363
                        } else {
 
364
                                xPos = attributes["cx"].toDouble(&xOk);
 
365
                                yPos = attributes["cy"].toDouble(&yOk);
 
366
                        }
 
367
                        if(xOk && yOk)
 
368
                                graphicsItem()->setPos(QPointF(xPos, yPos));
 
369
//                              graphicsItem()->setPos(graphicsItem()->mapToParent(graphicsItem()->mapFromScene(QPointF(xPos, yPos))));
 
370
                }
 
371
 
 
372
                // 'fill'
 
373
                if(changed.contains("fill")) {
 
374
                        QBrush brush;
 
375
                        if(!attributes["fill"].isEmpty())
 
376
                                brush = QBrush(constructColor(attributes["fill"], attributes["fill-opacity"]));
 
377
                        QAbstractGraphicsShapeItem* s = qgraphicsitem_cast<QAbstractGraphicsShapeItem*>(graphicsItem());
 
378
                        if(s) {
 
379
                                // Only set pens for items that implement it
 
380
                                s->setBrush(brush);
 
381
                        }
 
382
                }
 
383
 
 
384
                // 'stroke' & stroke-dasharray & stroke-dashoffset & stroke-dashoffset & stroke-linecap & stroke-linejoin & stroke-miterlimit & stroke-opacity &
 
385
                if(changed.contains("pen")) {
 
386
                        QPen pen;
 
387
                        pen = parseQPen(attributes["stroke"],
 
388
                                        attributes["stroke-dasharray"],
 
389
                                        attributes["stroke-dashoffset"],
 
390
                                        attributes["stroke-linecap"],
 
391
                                        attributes["stroke-linejoin"],
 
392
                                        attributes["stroke-miterlimit"],
 
393
                                        attributes["stroke-opacity"],
 
394
                                        attributes["opacity"],
 
395
                                        attributes["stroke-width"]);
 
396
                        QAbstractGraphicsShapeItem* s = qgraphicsitem_cast<QAbstractGraphicsShapeItem*>(graphicsItem());
 
397
                        if(s) {
 
398
                                // Only set pens for items that implement it
 
399
                                s->setPen(pen);
 
400
                        }/* else if(typerange == 101) {
 
401
                                QGraphicsTextItem* s = dynamic_cast<QGraphicsTextItem*>(graphicsItem());
 
402
                                s->setPen(pen);
 
403
                        }*/
 
404
                }
 
405
 
 
406
                if(changed.contains("transform"))
 
407
                        graphicsItem()->setMatrix(parseTransformationMatrix(attributes["transform"]));
 
408
 
 
409
                graphicsItem()->update();
 
410
        }
 
411
        return changed;
 
412
}
 
413
 
 
414
void WbItem::regenerateTransform() {
 
415
        if(!graphicsItem())
 
416
                return;
 
417
        QMatrix m = graphicsItem()->matrix();
 
418
        QString old = attributes["transform"];
 
419
        if(m.isIdentity())
 
420
                attributes["transform"] = QString();
 
421
        else
 
422
                attributes["transform"] = QString("matrix(%1 %2 %3 %4 %5 %6)").arg(m.m11()).arg(m.m12()).arg(m.m21()).arg(m.m22()).arg(m.dx()).arg(m.dy());
 
423
        if(attributes["transform"] != old)
 
424
                emit attributeChanged(id(), "transform", attributes["transform"], old);
 
425
}
 
426
 
 
427
QPointF WbItem::center() {
 
428
        if(graphicsItem()) {
 
429
                // Determine the center of the item in item coordinates before transformation
 
430
                QRectF r = graphicsItem()->boundingRect();
 
431
                QPointF c(r.x() + r.width()/2, r.y() + r.height()/2);
 
432
//              qDebug() << QString("center: %1 + %2    %3 + %4").arg(r.x()).arg(r.width()/2).arg(r.y()).arg(r.height()/2);
 
433
                // return the center with transformation applied
 
434
                return graphicsItem()->matrix().map(c);
 
435
        }
 
436
        return QPointF();
 
437
}
 
438
 
 
439
void WbItem::handleMouseMoveEvent(QGraphicsSceneMouseEvent * event) {
 
440
        event->accept();
 
441
        if(graphicsItem() && graphicsItem()->isSelected()) {
 
442
                if(event->modifiers() == Qt::KeyboardModifiers(Qt::ControlModifier)) {
 
443
                        // Ctrl: Translate
 
444
                        // Translate each selected item
 
445
                        WbScene* wbscene = qobject_cast<WbScene*>(graphicsItem()->scene());
 
446
                        if(wbscene) {
 
447
                                foreach(QGraphicsItem* graphicsitem, graphicsItem()->scene()->selectedItems()) {
 
448
                                        if (!graphicsitem->parentItem() || !graphicsitem->parentItem()->isSelected()) {
 
449
                                                QPointF d = graphicsitem->mapFromScene(event->scenePos()) - graphicsitem->mapFromScene(event->lastScenePos());
 
450
                                                graphicsitem->translate(d.x(), d.y());
 
451
                                                // Regenerate the SVG transformation matrix later
 
452
                                                wbscene->queueTransformationChange(wbscene->findWbItem(graphicsitem));
 
453
                                        }
 
454
                                }
 
455
                        }
 
456
                } else if(event->modifiers() == Qt::KeyboardModifiers(Qt::ControlModifier + Qt::AltModifier)) {
 
457
                        // Ctrl + Alt: Rotate
 
458
                        // get center coordinates in item coordinates
 
459
                        QPointF c = center();
 
460
                        QMatrix translation, delta;
 
461
                        // translates the the item's center to the origin of the item coordinates
 
462
                        translation.translate(-c.x(), -c.y());
 
463
                        // Rotate. Determine the direction relative to the center
 
464
                        // Divide the sum by two to reduce the "speed" of rotation
 
465
                        QPointF difference = event->scenePos() - event->lastScenePos();
 
466
        //              qDebug() << QString("d: %1 %2 = %3 %4 + %5 %6").arg(difference.x()).arg(difference.y()).arg(event->scenePos().x()).arg(event->scenePos().y()).arg(event->lastScenePos().x()).arg(event->lastScenePos().y());
 
467
                        QPointF p = event->scenePos();
 
468
                        QPointF sceneCenter = graphicsItem()->mapToScene(c);
 
469
                        if(p.x() >= sceneCenter.x() && p.y() >= sceneCenter.y()) {
 
470
                                delta.rotate((-difference.x() + difference.y()) / 2);
 
471
                        } else if(p.x() < sceneCenter.x() && p.y() >= sceneCenter.y()) {
 
472
                                delta.rotate((-difference.x() - difference.y()) / 2);
 
473
                        } else if(p.x() < sceneCenter.x() && p.y() < sceneCenter.y()) {
 
474
                                delta.rotate((difference.x() - difference.y()) / 2);
 
475
                        } else if(p.x() >= sceneCenter.x() && p.y() < sceneCenter.y()) {
 
476
                                delta.rotate((difference.x() + difference.y()) / 2);
 
477
                        }
 
478
                        // set the matrix
 
479
                        graphicsItem()->setMatrix(graphicsItem()->matrix() * translation * delta * translation.inverted());
 
480
                        // Regenerate the SVG transformation matrix later
 
481
                        WbScene* wbscene = qobject_cast<WbScene*>(graphicsItem()->scene());
 
482
                        if(wbscene)
 
483
                                wbscene->queueTransformationChange(this);
 
484
                } else if(event->modifiers() == Qt::KeyboardModifiers(Qt::ControlModifier + Qt::ShiftModifier)) {
 
485
                        // Ctrl + Shift: Scale
 
486
                        // get center coordinates in item coordinates
 
487
                        QPointF c = center();
 
488
                        QMatrix translation, delta;
 
489
                        // translates the the item's center to the origin of the item coordinates
 
490
                        translation.translate(-c.x(), -c.y());
 
491
                        // Scale.
 
492
                        // Moving mouse up enlarges, down shrinks, y axis.
 
493
                        // Moving mouse right enlarges, left shrinks, x axis.
 
494
                        QPointF difference = event->scenePos() - event->lastScenePos();
 
495
                        // Note: the y axis points downwards in scene coordinates
 
496
                        delta.scale(1 + difference.x()/50, 1 - difference.y()/50);
 
497
                        // set the matrix
 
498
                        graphicsItem()->setMatrix(graphicsItem()->matrix() * translation * delta * translation.inverted());
 
499
                        // Regenerate the SVG transformation matrix later
 
500
                        WbScene* wbscene = qobject_cast<WbScene*>(graphicsItem()->scene());
 
501
                        if(wbscene)
 
502
                                wbscene->queueTransformationChange(this);
 
503
                }
 
504
        }
 
505
}
 
506
 
 
507
void WbItem::handleMouseReleaseEvent(QGraphicsSceneMouseEvent * event) {        
 
508
        WbScene * wbscene = qobject_cast<WbScene*>(graphicsItem()->scene());
 
509
        wbscene->regenerateTransformations();
 
510
        event->ignore();
 
511
}
 
512
 
 
513
/*
 
514
 *      WbUnknown
 
515
 */
 
516
 
 
517
WbUnknown::WbUnknown(QDomElement &_svg, const QString &id, const qreal &index, const QString &parent, QGraphicsScene *) : WbItem(id) {
 
518
        setParentWbItem(parent);
 
519
        setIndex(index);
 
520
        parseSvg(_svg);
 
521
}
 
522
 
 
523
QDomElement WbUnknown::svg() {
 
524
        return svgElement_.cloneNode().toElement();
 
525
}
 
526
 
 
527
QList<QString> WbUnknown::parseSvg(QDomElement &_svg, bool) {
 
528
        svgElement_ = _svg.cloneNode().toElement();
 
529
        return QList<QString>();
 
530
}
 
531
 
 
532
WbItem* WbUnknown::clone() {
 
533
        QDomElement _svg = svg();
 
534
        WbItem* cloned = new WbUnknown(_svg, id(), index(), parentWbItem(), 0);
 
535
        cloned->undos = undos;
 
536
        return cloned;
 
537
}
 
538
 
 
539
/*
 
540
 *      WbRoot
 
541
 */
 
542
 
 
543
WbRoot::WbRoot(QGraphicsScene* scene) : WbItem("root") {
 
544
        scene_ = scene;
 
545
}
 
546
 
 
547
QList<QString> WbRoot::parseSvg(QDomElement &_svg, bool emitChanges) {
 
548
        QList<QString> changed = WbItem::parseSvg(_svg, emitChanges);
 
549
        if(changed.contains("viewBox")) {
 
550
                QString::const_iterator itr = attributes["viewBox"].constBegin();
 
551
                QList<qreal> points = WbItem::parseNumbersList(itr);
 
552
                if(points.size() == 4)
 
553
                        scene_->setSceneRect(points.at(0), points.at(1), points.at(2), points.at(3));
 
554
        }
 
555
        return changed;
 
556
}
 
557
 
 
558
WbItem* WbRoot::clone() {
 
559
        WbItem* cloned = new WbRoot(scene_);
 
560
        QDomElement _svg = svg();
 
561
        cloned->parseSvg(_svg, false);
 
562
        cloned->undos = undos;
 
563
        return cloned;
 
564
}
 
565
 
 
566
/*
 
567
 *      WbPath
 
568
 */
 
569
 
 
570
WbPath::WbPath(QDomElement &_svg, const QString &id, const qreal &index, const QString &parent, QGraphicsScene * scene) : QGraphicsPathItem(0, scene), WbItem(id) {
 
571
        setParentWbItem(parent);
 
572
        setIndex(index);
 
573
        parseSvg(_svg);
 
574
        setFlag(QGraphicsItem::ItemIsSelectable);
 
575
        setFlag(QGraphicsItem::ItemIsMovable);
 
576
        setAcceptDrops(true);
 
577
}
 
578
 
 
579
QPainterPath WbPath::shape() const {
 
580
        QPainterPath qpath = path();
 
581
        qpath.lineTo(qpath.currentPosition() + QPointF(2, 0));
 
582
        qpath.lineTo(qpath.currentPosition() + QPointF(-2, 2));
 
583
        qpath.closeSubpath();
 
584
        qpath.lineTo(qpath.currentPosition() + QPointF(2, 0));
 
585
        qpath.lineTo(qpath.currentPosition() + QPointF(-2, 2));
 
586
        return qpath;
 
587
}
 
588
 
 
589
QList<QString> WbPath::parseSvg(QDomElement &_svg, bool emitChanges) {
 
590
        QList<QString> changed = WbItem::parseSvg(_svg, emitChanges);
 
591
        QPainterPath qpath;
 
592
        // 'd' && 'fill-rule'
 
593
        if(changed.contains("d")) {
 
594
                parsePathDataFast(attributes["d"], qpath);
 
595
        } else
 
596
                qpath = this->path();
 
597
        if(changed.contains("fill-rule")) {
 
598
                if (attributes["fill-rule"] == QLatin1String("nonzero"))
 
599
                        qpath.setFillRule(Qt::WindingFill);
 
600
                else
 
601
                        qpath.setFillRule(Qt::OddEvenFill);
 
602
        } else
 
603
                qpath.setFillRule(this->path().fillRule());
 
604
        setPath(qpath);
 
605
        return changed;
 
606
}
 
607
 
 
608
WbItem* WbPath::clone() {
 
609
        QDomElement _svg = svg();
 
610
        WbItem* cloned = new WbPath(_svg, id(), index(), parentWbItem(), 0);
 
611
        cloned->undos = undos;
 
612
        return cloned;
 
613
}
 
614
 
 
615
void WbPath::lineTo(const QPointF &newPoint) {
 
616
        QPainterPath qpath = path();
 
617
        qpath.lineTo(newPoint);
 
618
        setPath(qpath);
 
619
        QString old = attributes["d"];
 
620
        attributes["d"] += QString("L%1,%2").arg(newPoint.x()).arg(newPoint.y());
 
621
        emit attributeChanged(id(), "d", attributes["d"], old);
 
622
}
 
623
 
 
624
void WbPath::quadTo(const QPointF &controlPoint, const QPointF &newPoint) {
 
625
        QPainterPath qpath = path();
 
626
        qpath.quadTo(controlPoint, newPoint);
 
627
        setPath(qpath);
 
628
        QString old = attributes["d"];
 
629
        attributes["d"] += QString("Q%1,%2 %3,%4").arg(controlPoint.x()).arg(controlPoint.y()).arg(newPoint.x()).arg(newPoint.y());
 
630
        emit attributeChanged(id(), "d", attributes["d"], old);
 
631
}
 
632
 
 
633
void WbPath::contextMenuEvent (QGraphicsSceneContextMenuEvent * event) {
 
634
        constructContextMenu()->exec(event->screenPos());
 
635
        regenerateTransform();
 
636
}
 
637
 
 
638
void WbPath::mouseMoveEvent(QGraphicsSceneMouseEvent * event) {
 
639
        handleMouseMoveEvent(event);
 
640
        if(!event->isAccepted())
 
641
                QGraphicsPathItem::mouseMoveEvent(event);
 
642
}
 
643
 
 
644
void WbPath::mouseReleaseEvent(QGraphicsSceneMouseEvent * event) {
 
645
        handleMouseReleaseEvent(event);
 
646
        if(!event->isAccepted())
 
647
                QGraphicsPathItem::mouseReleaseEvent(event);
 
648
}
 
649
 
 
650
/*
 
651
 *      WbEllipse
 
652
 */
 
653
 
 
654
WbEllipse::WbEllipse(QDomElement &_svg, const QString &id, const qreal &index, const QString &parent, QGraphicsScene * scene) :  QGraphicsEllipseItem(0, scene), WbItem(id) {
 
655
        setParentWbItem(parent);
 
656
        setIndex(index);
 
657
        parseSvg(_svg);
 
658
        setFlag(QGraphicsItem::ItemIsSelectable);
 
659
        setFlag(QGraphicsItem::ItemIsMovable);
 
660
        setAcceptDrops(true);
 
661
}
 
662
 
 
663
QList<QString> WbEllipse::parseSvg(QDomElement &_svg, bool emitChanges) {
 
664
        QList<QString> changed = WbItem::parseSvg(_svg, emitChanges);
 
665
        // 'rx' & 'ry'
 
666
        if(changed.contains("rx") || changed.contains("ry")) {
 
667
                bool okX, okY;
 
668
                qreal rx = attributes["rx"].toDouble(&okX);
 
669
                qreal ry = attributes["ry"].toDouble(&okY);
 
670
                // FIXME: I think it's an issue with Qt, but with very small value I sometime get when setRect()
 
671
                // QPainterPath::lineTo: Adding point where x or y is NaN, results are undefined
 
672
                // QPainterPath::cubicTo: Adding point where x or y is NaN, results are undefined
 
673
                // That's why i'm putting those minumum values in
 
674
                if(okX && okY) {
 
675
                        if(rx <= 1)
 
676
                                rx = 1;
 
677
                        if(ry <= 1)
 
678
                                ry = 1;
 
679
                        setRect(-rx, -ry, 2 * rx, 2 * ry);
 
680
                } else
 
681
                        setRect(0, 0, 2, 2);
 
682
        }
 
683
        return changed;
 
684
}
 
685
 
 
686
WbItem* WbEllipse::clone() {
 
687
        QDomElement _svg = svg();
 
688
        WbItem* cloned = new WbEllipse(_svg, id(), index(), parentWbItem(), 0);
 
689
        cloned->undos = undos;
 
690
        return cloned;
 
691
}
 
692
 
 
693
void WbEllipse::contextMenuEvent (QGraphicsSceneContextMenuEvent * event) {
 
694
        WbItemMenu* menu = constructContextMenu();
 
695
        menu->exec(event->screenPos());
 
696
        event->accept();
 
697
}
 
698
 
 
699
void WbEllipse::mouseMoveEvent(QGraphicsSceneMouseEvent *event) {
 
700
        handleMouseMoveEvent(event);
 
701
        if(!event->isAccepted())
 
702
                QGraphicsEllipseItem::mouseMoveEvent(event);
 
703
}
 
704
 
 
705
void WbEllipse::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) {
 
706
        handleMouseReleaseEvent(event);
 
707
        if(!event->isAccepted())
 
708
                QGraphicsEllipseItem::mouseReleaseEvent(event);
 
709
}
 
710
 
 
711
/*
 
712
 *      WbCircle
 
713
 */
 
714
 
 
715
WbCircle::WbCircle(QDomElement &_svg, const QString &id, const qreal &index, const QString &parent, QGraphicsScene * scene) :  WbEllipse(_svg, id, index, parent, scene) {
 
716
        QDomElement _svg_ = svg();
 
717
        attributes.remove("r");
 
718
        parseSvg(_svg_);
 
719
}
 
720
 
 
721
QList<QString> WbCircle::parseSvg(QDomElement &_svg, bool emitChanges) {
 
722
        QList<QString> changed = WbItem::parseSvg(_svg, emitChanges);
 
723
        // 'r'
 
724
        if(changed.contains("r")) {
 
725
                bool ok;
 
726
                qreal r = attributes["r"].toDouble(&ok);
 
727
                if(ok && r > 0)
 
728
                        setRect(-r, -r, 2 * r, 2 * r);
 
729
                else
 
730
                        setRect(0, 0, 0, 0);
 
731
        }
 
732
        return changed;
 
733
}
 
734
 
 
735
WbItem* WbCircle::clone() {
 
736
        QDomElement _svg = svg();
 
737
        WbItem* cloned = new WbCircle(_svg, id(), index(), parentWbItem(), 0);
 
738
        cloned->undos = undos;
 
739
        return cloned;
 
740
}
 
741
 
 
742
/*
 
743
 *      WbRectangle
 
744
 */
 
745
 
 
746
WbRectangle::WbRectangle(QDomElement &_svg, const QString &id, const qreal &index, const QString &parent, QGraphicsScene * scene) :  QGraphicsRectItem(0, scene), WbItem(id) {
 
747
        setParentWbItem(parent);
 
748
        setIndex(index);
 
749
        parseSvg(_svg);
 
750
        setFlag(QGraphicsItem::ItemIsSelectable);
 
751
        setFlag(QGraphicsItem::ItemIsMovable);
 
752
        setAcceptDrops(true);
 
753
}
 
754
 
 
755
QList<QString> WbRectangle::parseSvg(QDomElement &_svg, bool emitChanges) {
 
756
        QList<QString> changed = WbItem::parseSvg(_svg, emitChanges);
 
757
        // TODO: support rx and ry
 
758
        // 'width' & 'height'
 
759
        if(changed.contains("width") || changed.contains("height")) {
 
760
                bool okW, okH;
 
761
                qreal width = attributes["width"].toDouble(&okW);
 
762
                qreal height = attributes["height"].toDouble(&okH);
 
763
                if(okW && okH && width > 0 && height > 0)
 
764
                        setRect(-width/2, -height/2, width, height);
 
765
                else
 
766
                        setRect(0, 0, 0, 0);
 
767
        }
 
768
        return changed;
 
769
}
 
770
 
 
771
WbItem* WbRectangle::clone() {
 
772
        QDomElement _svg = svg();
 
773
        WbItem* cloned = new WbRectangle(_svg, id(), index(), parentWbItem(), 0);
 
774
        cloned->undos = undos;
 
775
        return cloned;
 
776
}
 
777
 
 
778
void WbRectangle::contextMenuEvent (QGraphicsSceneContextMenuEvent * event) {
 
779
        WbItemMenu* menu = constructContextMenu();
 
780
        menu->exec(event->screenPos());
 
781
        event->accept();
 
782
}
 
783
 
 
784
void WbRectangle::mouseMoveEvent(QGraphicsSceneMouseEvent *event) {
 
785
        handleMouseMoveEvent(event);
 
786
        if(!event->isAccepted())
 
787
                QGraphicsRectItem::mouseMoveEvent(event);
 
788
}
 
789
 
 
790
void WbRectangle::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) {
 
791
        handleMouseReleaseEvent(event);
 
792
        if(!event->isAccepted())
 
793
                QGraphicsRectItem::mouseReleaseEvent(event);
 
794
}
 
795
 
 
796
/*
 
797
 *      WbLine
 
798
 */
 
799
 
 
800
WbLine::WbLine(QDomElement &_svg, const QString &id, const qreal &index, const QString &parent, QGraphicsScene * scene) : QGraphicsPathItem(0, scene), WbItem(id) {
 
801
        setParentWbItem(parent);
 
802
        setIndex(index);
 
803
        parseSvg(_svg);
 
804
        setFlag(QGraphicsItem::ItemIsSelectable);
 
805
        setFlag(QGraphicsItem::ItemIsMovable);
 
806
        setAcceptDrops(true);
 
807
}
 
808
 
 
809
QPainterPath WbLine::shape() const {
 
810
        QPainterPath qpath = path();
 
811
        qpath.lineTo(qpath.currentPosition() + QPointF(2, 0));
 
812
        qpath.lineTo(qpath.currentPosition() + QPointF(-2, 2));
 
813
        qpath.closeSubpath();
 
814
        qpath.lineTo(qpath.currentPosition() + QPointF(2, 0));
 
815
        qpath.lineTo(qpath.currentPosition() + QPointF(-2, 2));
 
816
        return qpath;
 
817
}
 
818
 
 
819
QList<QString> WbLine::parseSvg(QDomElement &_svg, bool emitChanges) {
 
820
        QList<QString> changed= WbItem::parseSvg(_svg, emitChanges);
 
821
        // 'x1' & 'y1' & 'x2' & 'y2'
 
822
        if(changed.contains("x1") || changed.contains("y1") || changed.contains("x2") || changed.contains("y2")) {
 
823
                bool okX1, okY1, okX2, okY2;
 
824
                qreal x1 = attributes["x1"].toDouble(&okX1);
 
825
                qreal y1 = attributes["y1"].toDouble(&okY1);
 
826
                qreal x2 = attributes["x2"].toDouble(&okX2);
 
827
                qreal y2 = attributes["y2"].toDouble(&okY2);
 
828
                if(okX1 && okY1 && okX2 && okY2) {
 
829
                        QPainterPath qpath(QPointF(x1, y1));
 
830
                        qpath.lineTo(QPointF(x2, y2));
 
831
                        setPath(qpath);
 
832
                } else
 
833
                        setPath(QPainterPath());
 
834
        }
 
835
        return changed;
 
836
}
 
837
 
 
838
WbItem* WbLine::clone() {
 
839
        QDomElement _svg = svg();
 
840
        WbItem* cloned = new WbLine(_svg, id(), index(), parentWbItem(), 0);
 
841
        cloned->undos = undos;
 
842
        return cloned;
 
843
}
 
844
 
 
845
void WbLine::contextMenuEvent (QGraphicsSceneContextMenuEvent * event) {
 
846
        constructContextMenu()->exec(event->screenPos());
 
847
        regenerateTransform();
 
848
}
 
849
 
 
850
void WbLine::mouseMoveEvent(QGraphicsSceneMouseEvent * event) {
 
851
        handleMouseMoveEvent(event);
 
852
        if(!event->isAccepted())
 
853
                QGraphicsPathItem::mouseMoveEvent(event);
 
854
}
 
855
 
 
856
void WbLine::mouseReleaseEvent(QGraphicsSceneMouseEvent * event) {
 
857
        handleMouseReleaseEvent(event);
 
858
        if(!event->isAccepted())
 
859
                QGraphicsPathItem::mouseReleaseEvent(event);
 
860
}
 
861
 
 
862
/*
 
863
 *      WbPolyline
 
864
 */
 
865
 
 
866
WbPolyline::WbPolyline(QDomElement &_svg, const QString &id, const qreal &index, const QString &parent, QGraphicsScene * scene) : QGraphicsPathItem(0, scene), WbItem(id) {
 
867
        setParentWbItem(parent);
 
868
        setIndex(index);
 
869
        parseSvg(_svg);
 
870
        setFlag(QGraphicsItem::ItemIsSelectable);
 
871
        setFlag(QGraphicsItem::ItemIsMovable);
 
872
        setAcceptDrops(true);
 
873
}
 
874
 
 
875
QPainterPath WbPolyline::shape() const {
 
876
        QPainterPath qpath = path();
 
877
        qpath.lineTo(qpath.currentPosition() + QPointF(2, 0));
 
878
        qpath.lineTo(qpath.currentPosition() + QPointF(-2, 2));
 
879
        qpath.closeSubpath();
 
880
        qpath.lineTo(qpath.currentPosition() + QPointF(2, 0));
 
881
        qpath.lineTo(qpath.currentPosition() + QPointF(-2, 2));
 
882
        return qpath;
 
883
}
 
884
 
 
885
QList<QString> WbPolyline::parseSvg(QDomElement &_svg, bool emitChanges) {
 
886
        QList<QString> changed= WbItem::parseSvg(_svg, emitChanges);
 
887
        // 'points'
 
888
        if(changed.contains("points")) {
 
889
                QString::const_iterator itr = attributes["points"].constBegin();
 
890
                QList<qreal> points = WbItem::parseNumbersList(itr);
 
891
                if(points.size() > 1) {
 
892
                        QPainterPath qpath(QPointF(points.takeFirst(), points.takeFirst()));
 
893
                        while(points.size() > 1) {
 
894
                                qpath.lineTo(QPointF(points.takeFirst(), points.takeFirst()));
 
895
                        }
 
896
                        setPath(qpath);
 
897
                } else
 
898
                        setPath(QPainterPath());
 
899
        }
 
900
        return changed;
 
901
}
 
902
 
 
903
WbItem* WbPolyline::clone() {
 
904
        QDomElement _svg = svg();
 
905
        WbItem* cloned = new WbPolyline(_svg, id(), index(), parentWbItem(), 0);
 
906
        cloned->undos = undos;
 
907
        return cloned;
 
908
}
 
909
 
 
910
void WbPolyline::contextMenuEvent (QGraphicsSceneContextMenuEvent * event) {
 
911
        constructContextMenu()->exec(event->screenPos());
 
912
        regenerateTransform();
 
913
}
 
914
 
 
915
void WbPolyline::mouseMoveEvent(QGraphicsSceneMouseEvent * event) {
 
916
        handleMouseMoveEvent(event);
 
917
        if(!event->isAccepted())
 
918
                QGraphicsPathItem::mouseMoveEvent(event);
 
919
}
 
920
 
 
921
void WbPolyline::mouseReleaseEvent(QGraphicsSceneMouseEvent * event) {
 
922
        handleMouseReleaseEvent(event);
 
923
        if(!event->isAccepted())
 
924
                QGraphicsPathItem::mouseReleaseEvent(event);
 
925
}
 
926
 
 
927
/*
 
928
 *      WbPolygon
 
929
 */
 
930
 
 
931
WbPolygon::WbPolygon(QDomElement &_svg, const QString &id, const qreal &index, const QString &parent, QGraphicsScene * scene) :  WbPolyline(_svg, id, index, parent, scene) {
 
932
        QDomElement _svg_ = svg();
 
933
        attributes.remove("points");
 
934
        parseSvg(_svg_);
 
935
}
 
936
 
 
937
QList<QString> WbPolygon::parseSvg(QDomElement &_svg, bool emitChanges) {
 
938
        QList<QString> changed = WbPolyline::parseSvg(_svg, emitChanges);
 
939
        // 'points'
 
940
        if(changed.contains("points")) {
 
941
                QPainterPath qpath = path();
 
942
                qpath.closeSubpath();
 
943
                setPath(qpath);
 
944
        }
 
945
        return changed;
 
946
}
 
947
 
 
948
WbItem* WbPolygon::clone() {
 
949
        QDomElement _svg = svg();
 
950
        WbItem* cloned = new WbCircle(_svg, id(), index(), parentWbItem(), 0);
 
951
        cloned->undos = undos;
 
952
        return cloned;
 
953
}
 
954
 
 
955
/*
 
956
 *      WbGraphicsTextItem
 
957
 */
 
958
 
 
959
WbGraphicsTextItem::WbGraphicsTextItem(WbText* wbtext, QGraphicsScene * scene) : QGraphicsTextItem(0, scene) {
 
960
        wbText_ = wbtext;
 
961
        setFlag(QGraphicsItem::ItemIsSelectable);
 
962
        setFlag(QGraphicsItem::ItemIsMovable);
 
963
        setTextInteractionFlags(Qt::TextInteractionFlags(Qt::NoTextInteraction));
 
964
        setAcceptDrops(true);
 
965
}
 
966
 
 
967
void WbGraphicsTextItem::contextMenuEvent (QGraphicsSceneContextMenuEvent * event) {
 
968
        if(hasFocus())
 
969
                QGraphicsTextItem::contextMenuEvent(event);
 
970
        else {
 
971
                WbItemMenu* menu = wbText_->constructContextMenu();
 
972
                // Add the default actions
 
973
                menu->addSeparator();
 
974
                QActionGroup* group = new QActionGroup(menu);
 
975
                IconAction* action = new IconAction(QObject::tr("Font..."), "psi/compact", QObject::tr("Font..."), 0, group);
 
976
                QObject::connect(action, SIGNAL(triggered()), wbText_, SLOT(setFont()));
 
977
                menu->addActionGroup(group);
 
978
        
 
979
                menu->exec(event->screenPos());
 
980
                event->accept();
 
981
        }
 
982
}
 
983
 
 
984
void WbGraphicsTextItem::focusOutEvent (QFocusEvent *event) {
 
985
        QGraphicsTextItem::focusOutEvent(event);
 
986
        wbText_->checkTextChanges();
 
987
        setTextInteractionFlags(Qt::TextInteractionFlags(Qt::NoTextInteraction));
 
988
//      setFlag(QGraphicsItem::ItemIsFocusable, false);
 
989
}
 
990
 
 
991
void WbGraphicsTextItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) {
 
992
        if(event->modifiers() == Qt::KeyboardModifiers(Qt::NoModifier)) {
 
993
                setTextInteractionFlags(Qt::TextInteractionFlags(Qt::TextEditorInteraction));
 
994
                setFlag(QGraphicsItem::ItemIsFocusable);
 
995
        }
 
996
        QGraphicsTextItem::mouseDoubleClickEvent(event);
 
997
}
 
998
 
 
999
void WbGraphicsTextItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) {
 
1000
        wbText_->handleMouseMoveEvent(event);
 
1001
        if(!event->isAccepted())
 
1002
                QGraphicsTextItem::mouseMoveEvent(event);
 
1003
}
 
1004
 
 
1005
void WbGraphicsTextItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) {
 
1006
        wbText_->handleMouseReleaseEvent(event);
 
1007
        if(!event->isAccepted())
 
1008
                QGraphicsTextItem::mouseReleaseEvent(event);
 
1009
}
 
1010
 
 
1011
/*
 
1012
 *      WbText
 
1013
 */
 
1014
 
 
1015
WbText::WbText(QDomElement &_svg, const QString &id, const qreal &index, const QString &parent, QGraphicsScene * scene) : WbItem(id) {
 
1016
        graphicsitem_ = new WbGraphicsTextItem(this, scene);
 
1017
        setParentWbItem(parent);
 
1018
        setIndex(index);
 
1019
        parseSvg(_svg);
 
1020
}
 
1021
 
 
1022
WbText::~WbText() {
 
1023
        graphicsitem_->deleteLater();
 
1024
}
 
1025
 
 
1026
QDomElement WbText::svg() {
 
1027
        QDomElement _svg = WbItem::svg();
 
1028
        QDomDocument d;
 
1029
        _svg.appendChild(d.createTextNode(text_));
 
1030
        return _svg;
 
1031
};
 
1032
 
 
1033
QList<QString> WbText::parseSvg(QDomElement &_svg, bool emitChanges) {
 
1034
        QList<QString> changed = WbItem::parseSvg(_svg, emitChanges);
 
1035
        QFont _font = graphicsitem_->font();
 
1036
        // 'font-family'
 
1037
        if(changed.contains("font-family"))
 
1038
                _font.setFamily(attributes["font-family"]);
 
1039
        // 'font-size'
 
1040
        if(changed.contains("font-size"))
 
1041
                _font.setPointSize(attributes["font-size"].toInt());
 
1042
        // 'font-style'
 
1043
        if(changed.contains("font-style")) {
 
1044
                if(attributes["font-style"] == "normal")
 
1045
                        _font.setStyle(QFont::StyleNormal);
 
1046
                else if(attributes["font-style"] == "italic")
 
1047
                        _font.setStyle(QFont::StyleItalic);
 
1048
                else if(attributes["font-style"] == "oblique")
 
1049
                        _font.setStyle(QFont::StyleOblique);
 
1050
        }
 
1051
        // 'font-weight'
 
1052
        if(changed.contains("font-weight"))
 
1053
                _font.setWeight(((attributes["font-size"].toInt() - 100) / 800) * 99);
 
1054
        if(graphicsitem_->font() != _font)
 
1055
                graphicsitem_->setFont(_font);
 
1056
        // text content
 
1057
        QString t = _svg.text();
 
1058
        if(text_ != t) {
 
1059
                if(emitChanges) {
 
1060
                        QDomDocument d;
 
1061
                        QDomElement oldSvg = d.createElement("e");
 
1062
                        oldSvg.appendChild(d.createTextNode(text_));
 
1063
                        emit contentChanged(id(), _svg.cloneNode().childNodes(), oldSvg.childNodes());
 
1064
                        graphicsitem_->adjustSize();
 
1065
                }
 
1066
                text_ = t;
 
1067
                graphicsitem_->setPlainText(t);
 
1068
        }
 
1069
 
 
1070
        return changed;
 
1071
}
 
1072
 
 
1073
WbItem* WbText::clone() {
 
1074
        QDomElement _svg = svg();
 
1075
        WbItem* cloned = new WbText(_svg, id(), index(), parentWbItem(), 0);
 
1076
        cloned->undos = undos;
 
1077
        return cloned;
 
1078
}
 
1079
 
 
1080
void WbText::checkTextChanges() {
 
1081
        if(graphicsitem_->toPlainText() != text_) {
 
1082
                QDomDocument d;
 
1083
                QDomElement _svg = d.createElement("e");
 
1084
                _svg.appendChild(d.createTextNode(graphicsitem_->toPlainText()));
 
1085
                QDomElement oldSvg = d.createElement("e");
 
1086
                oldSvg.appendChild(d.createTextNode(text_));
 
1087
                emit contentChanged(id(), _svg.cloneNode().childNodes(), oldSvg.childNodes());
 
1088
                text_ = graphicsitem_->toPlainText();
 
1089
                graphicsitem_->adjustSize();
 
1090
        }
 
1091
}
 
1092
 
 
1093
void WbText::setFont() {
 
1094
        bool ok;
 
1095
        QFont _font = QFontDialog::getFont(&ok, graphicsitem_->font());
 
1096
        if(ok) {
 
1097
                // Not supported features
 
1098
                _font.setUnderline(false);
 
1099
                _font.setOverline(false);
 
1100
                _font.setStrikeOut(false);
 
1101
                _font.setFixedPitch(false);
 
1102
                // end: Not supported features
 
1103
                if(_font != graphicsitem_->font()) {
 
1104
                        if(_font.family() != graphicsitem_->font().family())
 
1105
                                emit attributeChanged(id(), "font-family", _font.family(), graphicsitem_->font().family());
 
1106
                        if(_font.pointSize() != graphicsitem_->font().pointSize())
 
1107
                                emit attributeChanged(id(), "font-size", QString("%1").arg(_font.pointSize()), QString("%1").arg(graphicsitem_->font().pointSize()));
 
1108
                        if(_font.style() != graphicsitem_->font().style()) {
 
1109
                                QString oldStyle, newStyle;
 
1110
                                switch(graphicsitem_->font().style()) {
 
1111
                                        case QFont::StyleNormal:
 
1112
                                                oldStyle = "normal";
 
1113
                                                break;
 
1114
                                        case QFont::StyleItalic:
 
1115
                                                oldStyle = "italic";
 
1116
                                                break;
 
1117
                                        case QFont::StyleOblique:
 
1118
                                                oldStyle = "oblique";
 
1119
                                                break;
 
1120
                                }
 
1121
                                switch(_font.style()) {
 
1122
                                        case QFont::StyleNormal:
 
1123
                                                newStyle = "normal";
 
1124
                                                break;
 
1125
                                        case QFont::StyleItalic:
 
1126
                                                newStyle = "italic";
 
1127
                                                break;
 
1128
                                        case QFont::StyleOblique:
 
1129
                                                newStyle = "oblique";
 
1130
                                                break;
 
1131
                                }
 
1132
                                emit attributeChanged(id(), "font-style", newStyle, oldStyle);
 
1133
                        }
 
1134
                        if(_font.weight() != graphicsitem_->font().weight()) {
 
1135
                                int oldWeight = (graphicsitem_->font().weight() - (graphicsitem_->font().weight() % 10)) * 10;
 
1136
                                int newWeight = (_font.weight() - (_font.weight() % 10)) * 10;
 
1137
                                if(oldWeight < 100)
 
1138
                                        oldWeight = 100;
 
1139
                                if(newWeight < 100)
 
1140
                                        newWeight = 100;
 
1141
                                emit attributeChanged(id(), "font-weight", QString("%1").arg(newWeight), QString("%1").arg(oldWeight));
 
1142
                                _font.setWeight(((newWeight - 100) / 800) * 99);
 
1143
                        }
 
1144
                        graphicsitem_->setFont(_font);
 
1145
                }
 
1146
        }
 
1147
}
 
1148
 
 
1149
/*
 
1150
 *      WbImage
 
1151
 */
 
1152
 
 
1153
// WbImage::WbImage(const QString &id, qreal index, const QPointF &startPoint, const QString &parent, QGraphicsScene * scene) : QGraphicsPathItem(0, scene), WbItem(id) {
 
1154
//      setIndex(index);
 
1155
//      attributes["d"] = QString("M %1 %2").arg(startPoint.x()).arg(startPoint.y());
 
1156
//      setPath(QPainterPath(startPoint));
 
1157
//      setFlag(QGraphicsItem::ItemIsSelectable);
 
1158
//      setFlag(QGraphicsItem::ItemIsMovable);
 
1159
//      setAcceptDrops(true);
 
1160
// }
 
1161
 
 
1162
WbImage::WbImage(QDomElement &_svg, const QString &id, const qreal &index, const QString &parent, QGraphicsScene * scene) : QGraphicsPixmapItem(0, scene), WbItem(id) {
 
1163
        setParentWbItem(parent);
 
1164
        setIndex(index);
 
1165
        parseSvg(_svg);
 
1166
        setFlag(QGraphicsItem::ItemIsSelectable);
 
1167
        setFlag(QGraphicsItem::ItemIsMovable);
 
1168
        setAcceptDrops(true);
 
1169
}
 
1170
 
 
1171
QList<QString> WbImage::parseSvg(QDomElement &_svg, bool emitChanges) {
 
1172
        QList<QString> changed = WbItem::parseSvg(_svg, emitChanges);
 
1173
        if(changed.contains("xlink:href") || changed.contains("width") || changed.contains("height")) {
 
1174
                QString header = attributes["xlink:href"].left(23);
 
1175
                // we're looking for something like this "data:;image/png;base64,"
 
1176
                if(header.startsWith("data:")) {
 
1177
                        if(header.left(12).contains("image/")) {
 
1178
                                if(header.left(15).contains("png") || header.left(15).contains("jpg") || header.left(16).contains("jpeg") || header.left(15).contains("bmp") || header.left(15).contains("gif") || header.left(15).contains("pbm") || header.left(15).contains("pgm") || header.left(15).contains("ppm") || header.left(15).contains("xbm") || header.left(15).contains("xpm")) {
 
1179
                                        if(header.contains("base64,")) {
 
1180
//                                              qDebug() << "data: " << attributes["xlink:href"].mid(header.indexOf("base64,") + 7);
 
1181
                                                QCA::Base64 decoder(QCA::Decode);
 
1182
                                                decoder.setLineBreaksEnabled(true);
 
1183
                                                QImage image = QImage::fromData(decoder.stringToArray(attributes["xlink:href"].mid(header.indexOf("base64,") + 7)).toByteArray());
 
1184
                                                bool okW, okH;
 
1185
                                                int width = static_cast<int>(attributes["width"].toDouble(&okW));
 
1186
                                                int height = static_cast<int>(attributes["height"].toDouble(&okH));
 
1187
                                                // TODO: aspect ratio
 
1188
                                                if(okW && okH)
 
1189
                                                        image = image.scaled(QSize(width, height), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
 
1190
                                                setPixmap(QPixmap::fromImage(image));
 
1191
                                        }
 
1192
                                }
 
1193
                        }
 
1194
                }
 
1195
        }
 
1196
        return changed;
 
1197
}
 
1198
 
 
1199
WbItem* WbImage::clone() {
 
1200
        QDomElement _svg = svg();
 
1201
        WbItem* cloned = new WbImage(_svg, id(), index(), parentWbItem(), 0);
 
1202
        cloned->undos = undos;
 
1203
        return cloned;
 
1204
}
 
1205
 
 
1206
void WbImage::contextMenuEvent (QGraphicsSceneContextMenuEvent * event) {
 
1207
        constructContextMenu()->exec(event->screenPos());
 
1208
        regenerateTransform();
 
1209
}
 
1210
 
 
1211
void WbImage::mouseMoveEvent(QGraphicsSceneMouseEvent * event) {
 
1212
        handleMouseMoveEvent(event);
 
1213
        if(!event->isAccepted())
 
1214
                QGraphicsPixmapItem::mouseMoveEvent(event);
 
1215
}
 
1216
 
 
1217
void WbImage::mouseReleaseEvent(QGraphicsSceneMouseEvent * event) {
 
1218
        handleMouseReleaseEvent(event);
 
1219
        if(!event->isAccepted())
 
1220
                QGraphicsPixmapItem::mouseReleaseEvent(event);
 
1221
}
 
1222
 
 
1223
/*
 
1224
 *      WbGroup
 
1225
 */
 
1226
 
 
1227
WbGroup::WbGroup(QDomElement &_svg, const QString &id, const qreal &index, const QString &parent, QGraphicsScene * scene) : QGraphicsItem(0, scene), WbItem(id) {
 
1228
        setParentWbItem(parent);
 
1229
        setIndex(index);
 
1230
        parseSvg(_svg);
 
1231
        setFlag(QGraphicsItem::ItemIsSelectable);
 
1232
        setFlag(QGraphicsItem::ItemIsMovable);
 
1233
        setAcceptDrops(true);
 
1234
        setHandlesChildEvents(true);
 
1235
}
 
1236
 
 
1237
WbItem* WbGroup::clone() {
 
1238
        QDomElement _svg = svg();
 
1239
        WbItem* cloned = new WbGroup(_svg, id(), index(), parentWbItem(), 0);
 
1240
        cloned->undos = undos;
 
1241
        return cloned;
 
1242
}
 
1243
 
 
1244
void WbGroup::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *) {
 
1245
        if (option->state & QStyle::State_Selected) {
 
1246
                painter->setBrush(Qt::NoBrush);
 
1247
                painter->drawRect(boundingRect_);
 
1248
        }
 
1249
}
 
1250
 
 
1251
QRectF WbGroup::boundingRect() const {
 
1252
        return boundingRect_;
 
1253
}
 
1254
 
 
1255
QVariant WbGroup::itemChange(GraphicsItemChange change, const QVariant &value) {
 
1256
        if(change == QGraphicsItem::ItemChildAddedChange) {
 
1257
                QGraphicsItem* child = qVariantValue<QGraphicsItem *>(value);
 
1258
                if(child) {
 
1259
                        if(QGraphicsItem::children().size() == 1)
 
1260
                                boundingRect_ = mapFromItem(child, child->boundingRect()).boundingRect();
 
1261
                        else
 
1262
                                addToBoundingRect(mapFromItem(child, child->boundingRect()).boundingRect());
 
1263
                }
 
1264
        } else if(change == QGraphicsItem::ItemChildRemovedChange) {
 
1265
                QGraphicsItem* child = qVariantValue<QGraphicsItem *>(value);
 
1266
                if(child) {
 
1267
                        QRectF itemrect = mapFromItem(child, child->boundingRect()).boundingRect();
 
1268
                        if(itemrect.x() == boundingRect_.x() || itemrect.y() == boundingRect_.y() || itemrect.right() == boundingRect_.right() || itemrect.bottom() == boundingRect_.bottom()) {
 
1269
                                // If the item was on one of the borders of the boundingRect_ recalculate it
 
1270
                                if(!QGraphicsItem::children().isEmpty()) {
 
1271
                                        boundingRect_ = QGraphicsItem::children().first()->boundingRect();
 
1272
                                        for(int i = 1; i < QGraphicsItem::children().size(); i++)
 
1273
                                                addToBoundingRect(QGraphicsItem::children().at(i)->boundingRect());
 
1274
                                } else
 
1275
                                        boundingRect_ = QRectF();
 
1276
                        }
 
1277
                }
 
1278
        }
 
1279
        return value;
 
1280
}
 
1281
 
 
1282
void WbGroup::contextMenuEvent (QGraphicsSceneContextMenuEvent * event) {
 
1283
        constructContextMenu()->exec(event->screenPos());
 
1284
        regenerateTransform();
 
1285
}
 
1286
 
 
1287
void WbGroup::mouseMoveEvent(QGraphicsSceneMouseEvent * event) {
 
1288
        handleMouseMoveEvent(event);
 
1289
        if(!event->isAccepted())
 
1290
                QGraphicsItem::mouseMoveEvent(event);
 
1291
}
 
1292
 
 
1293
void WbGroup::mouseReleaseEvent(QGraphicsSceneMouseEvent * event) {
 
1294
        handleMouseReleaseEvent(event);
 
1295
        if(!event->isAccepted())
 
1296
                QGraphicsItem::mouseReleaseEvent(event);
 
1297
}
 
1298
 
 
1299
void WbGroup::addToBoundingRect(const QRectF &itemRect) {
 
1300
        if(itemRect.left() < boundingRect_.left())
 
1301
                boundingRect_.adjust(itemRect.left() - boundingRect_.left(), 0, 0, 0);
 
1302
        if(itemRect.top() < boundingRect_.top())
 
1303
                boundingRect_.adjust(0, itemRect.top() - boundingRect_.top(), 0, 0);
 
1304
        if(itemRect.right() > boundingRect_.right())
 
1305
                boundingRect_.adjust(0, 0, itemRect.right() - boundingRect_.right(), 0);
 
1306
        if(itemRect.bottom() > boundingRect_.bottom())
 
1307
                boundingRect_.adjust(0, 0, 0, itemRect.bottom() - boundingRect_.bottom());
 
1308
}
 
1309
 
 
1310
/*
 
1311
 * Parsing
 
1312
 ******************/
 
1313
 
 
1314
/*
 
1315
 * Many of he following methods are copied and/or adapted from
 
1316
 * the Qt Svg module's file called qsvghandler.cpp.
 
1317
 * The original header is shown below:
 
1318
 */
 
1319
 
 
1320
/****************************************************************************
 
1321
**
 
1322
** Copyright (C) 1992-2006 Trolltech AS. All rights reserved.
 
1323
**
 
1324
** This file is part of the QtSVG module of the Qt Toolkit.
 
1325
**
 
1326
** This file may be used under the terms of the GNU General Public
 
1327
** License version 2.0 as published by the Free Software Foundation
 
1328
** and appearing in the file LICENSE.GPL included in the packaging of
 
1329
** this file.  Please review the following information to ensure GNU
 
1330
** General Public Licensing requirements will be met:
 
1331
** http://www.trolltech.com/products/qt/opensource.html
 
1332
**
 
1333
** If you are unsure which license is appropriate for your use, please
 
1334
** review the following information:
 
1335
** http://www.trolltech.com/products/qt/licensing.html or contact the
 
1336
** sales department at sales@trolltech.com.
 
1337
**
 
1338
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
 
1339
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 
1340
**
 
1341
****************************************************************************/
 
1342
 
 
1343
// WbItem::WbItem(const QDomElement &svg, QObject * parent, QGraphicsScene * scene) : QGraphicsItem(parent, scene) {
 
1344
// 
 
1345
// }
 
1346
 
 
1347
/*
 
1348
 * Parsing: General
 
1349
 */
 
1350
 
 
1351
// QString WbItem::xmlSimplify(const QString &str)
 
1352
// {
 
1353
//       QString dummy = str;
 
1354
//       dummy.remove('\n');
 
1355
//       if (dummy.trimmed().isEmpty())
 
1356
//               return QString();
 
1357
//       QString temp;
 
1358
//       QString::const_iterator itr = dummy.constBegin();
 
1359
//       bool wasSpace = false;
 
1360
//       for (;itr != dummy.constEnd(); ++itr) {
 
1361
//               if ((*itr).isSpace()) {
 
1362
//                       if (wasSpace || !(*itr).isPrint()) {
 
1363
//                               continue;
 
1364
//                       }
 
1365
//                       temp += *itr;
 
1366
//                       wasSpace = true;
 
1367
//               } else {
 
1368
//                       temp += *itr;
 
1369
//                       wasSpace = false;
 
1370
//               }
 
1371
//       }
 
1372
//       return temp;
 
1373
// }
 
1374
 
 
1375
QList<qreal> WbItem::parseNumbersList(QString::const_iterator &itr)
 
1376
{
 
1377
        QList<qreal> points;
 
1378
        QString temp;
 
1379
        while ((*itr).isSpace())
 
1380
                ++itr;
 
1381
        while ((*itr).isNumber() ||
 
1382
                   (*itr) == '-' || (*itr) == '+' || (*itr) == '.') {
 
1383
                temp = QString();
 
1384
 
 
1385
                if ((*itr) == '-')
 
1386
                        temp += *itr++;
 
1387
                else if ((*itr) == '+')
 
1388
                        temp += *itr++;
 
1389
                while ((*itr).isDigit())
 
1390
                        temp += *itr++;
 
1391
                if ((*itr) == '.')
 
1392
                        temp += *itr++;
 
1393
                while ((*itr).isDigit())
 
1394
                        temp += *itr++;
 
1395
                if (( *itr) == 'e') {
 
1396
                        temp += *itr++;
 
1397
                        if ((*itr) == '-' ||
 
1398
                                (*itr) == '+')
 
1399
                                temp += *itr++;
 
1400
                }
 
1401
                while ((*itr).isDigit())
 
1402
                        temp += *itr++;
 
1403
                while ((*itr).isSpace())
 
1404
                        ++itr;
 
1405
                if ((*itr) == ',')
 
1406
                        ++itr;
 
1407
                points.append(temp.toDouble());
 
1408
                //eat the rest of space
 
1409
                while ((*itr).isSpace())
 
1410
                        ++itr;
 
1411
        }
 
1412
 
 
1413
        return points;
 
1414
}
 
1415
 
 
1416
QList<qreal> WbItem::parsePercentageList(QString::const_iterator &itr)
 
1417
{
 
1418
        QList<qreal> points;
 
1419
        QString temp;
 
1420
        while ((*itr).isSpace())
 
1421
                ++itr;
 
1422
        while ((*itr).isNumber() ||
 
1423
                   (*itr) == '-' || (*itr) == '+') {
 
1424
                temp = QString();
 
1425
 
 
1426
                if ((*itr) == '-')
 
1427
                        temp += *itr++;
 
1428
                else if ((*itr) == '+')
 
1429
                        temp += *itr++;
 
1430
                while ((*itr).isDigit())
 
1431
                        temp += *itr++;
 
1432
                if ((*itr) == '.')
 
1433
                        temp += *itr++;
 
1434
                while ((*itr).isDigit())
 
1435
                        temp += *itr++;
 
1436
                if (( *itr) == '%') {
 
1437
                        itr++;
 
1438
                }
 
1439
                while ((*itr).isSpace())
 
1440
                        ++itr;
 
1441
                if ((*itr) == ',')
 
1442
                        ++itr;
 
1443
                points.append(temp.toDouble());
 
1444
                //eat the rest of space
 
1445
                while ((*itr).isSpace())
 
1446
                        ++itr;
 
1447
        }
 
1448
 
 
1449
        return points;
 
1450
}
 
1451
 
 
1452
//inline 
 
1453
bool WbItem::isUnreserved(const QChar &c, bool first_char)
 
1454
{
 
1455
        return (c.isLetterOrNumber() || c == QLatin1Char('_') || c == QLatin1Char(':')
 
1456
                        || (!first_char && (c == QLatin1Char('.') || c == QLatin1Char('-'))));
 
1457
 
 
1458
}
 
1459
 
 
1460
QString WbItem::idFromUrl(const QString &url)
 
1461
{
 
1462
        QString::const_iterator itr = url.constBegin();
 
1463
        while ((*itr).isSpace())
 
1464
                ++itr;
 
1465
        if ((*itr) == '(')
 
1466
                ++itr;
 
1467
        while ((*itr).isSpace())
 
1468
                ++itr;
 
1469
        if ((*itr) == '#')
 
1470
                ++itr;
 
1471
        QString id;
 
1472
 
 
1473
        if (isUnreserved(*itr, true)) {         
 
1474
                while (isUnreserved(*itr)) {
 
1475
                        id += *itr;
 
1476
                        ++itr;  
 
1477
                }
 
1478
        }
 
1479
 
 
1480
        return id;
 
1481
}
 
1482
 
 
1483
QMatrix WbItem::parseTransformationMatrix(const QString &value)
 
1484
{
 
1485
        if(value.isEmpty())
 
1486
                return QMatrix();
 
1487
        QMatrix matrix;
 
1488
        QString::const_iterator itr = value.constBegin();
 
1489
 
 
1490
        while (itr != value.constEnd()) {
 
1491
                if ((*itr) == 'm') {  //matrix
 
1492
                        QString temp("m");
 
1493
                        int remains = 6;
 
1494
                        while (remains--) {
 
1495
                                temp += *itr++;
 
1496
                        }
 
1497
 
 
1498
                        while ((*itr).isSpace())
 
1499
                                ++itr;
 
1500
                        ++itr;// '('
 
1501
                        QList<qreal> points = WbItem::parseNumbersList(itr);
 
1502
                        ++itr; // ')'
 
1503
 
 
1504
                        Q_ASSERT(points.count() == 6);
 
1505
                        matrix = matrix * QMatrix(points[0], points[1],
 
1506
                                                                          points[2], points[3],
 
1507
                                                                          points[4], points[5]);
 
1508
 
 
1509
                        //qDebug()<<"matrix is "<<temp;
 
1510
                } else if ((*itr) == 't') { //translate
 
1511
                        QString trans;
 
1512
                        int remains = 9;
 
1513
                        while (remains--) {
 
1514
                                trans += *itr++;
 
1515
                        }
 
1516
                        while ((*itr).isSpace())
 
1517
                                ++itr;
 
1518
                        ++itr;// '('
 
1519
                        QList<qreal> points = WbItem::parseNumbersList(itr);
 
1520
                        ++itr; // ')'
 
1521
 
 
1522
                        Q_ASSERT(points.count() == 2 ||
 
1523
                                         points.count() == 1);
 
1524
                        if (points.count() == 2)
 
1525
                                matrix.translate(points[0], points[1]);
 
1526
                        else
 
1527
                                matrix.translate(points[0], 0);
 
1528
 
 
1529
                        //qDebug()<<"trans is "<<points;
 
1530
                } else if ((*itr) == 'r') { //rotate
 
1531
                        QString rot;
 
1532
                        int remains = 6;
 
1533
                        while (remains--) {
 
1534
                                rot += *itr++;
 
1535
                        }
 
1536
                        while ((*itr).isSpace())
 
1537
                                ++itr;
 
1538
 
 
1539
                        ++itr;// '('
 
1540
                        QList<qreal> points = WbItem::parseNumbersList(itr);
 
1541
                        ++itr;// ')'
 
1542
                        Q_ASSERT(points.count() == 3 ||
 
1543
                                         points.count() == 1);
 
1544
                        if (points.count() == 3) {
 
1545
                                matrix.translate(points[1], points[2]);
 
1546
                                matrix.rotate(points[0]);
 
1547
                                matrix.translate(-points[1], -points[2]);
 
1548
                        }
 
1549
                        else
 
1550
                                matrix.rotate(points[0]);
 
1551
 
 
1552
                        //qDebug()<<"rot is "<<points;
 
1553
                } else if ((*itr) == 's') { //scale | skewX | skewY
 
1554
                        QString temp;
 
1555
                        int remains = 5;
 
1556
                        while (remains--) {
 
1557
                                temp += *itr++;
 
1558
                        }
 
1559
                        while ((*itr).isSpace())
 
1560
                                ++itr;
 
1561
 
 
1562
                        ++itr;// '('
 
1563
                        QList<qreal> points = WbItem::parseNumbersList(itr);
 
1564
                        ++itr;// ')'
 
1565
                        Q_ASSERT(points.count() == 2 ||
 
1566
                                         points.count() == 1);
 
1567
                        if (temp == QLatin1String("scale")) {
 
1568
                                if (points.count() == 2) {
 
1569
                                        matrix.scale(points[0], points[1]);
 
1570
                                }
 
1571
                                else
 
1572
                                        matrix.scale(points[0], points[0]);
 
1573
                        } else if (temp == QLatin1String("skewX")) {
 
1574
                                const qreal deg2rad = qreal(0.017453292519943295769);
 
1575
                                matrix.shear(tan(points[0]*deg2rad), 0);
 
1576
                        } else if (temp == QLatin1String("skewY")) {
 
1577
                                const qreal deg2rad = qreal(0.017453292519943295769);
 
1578
                                matrix.shear(0, tan(points[0]*deg2rad));
 
1579
                        }
 
1580
                } else if ((*itr) == ' '  ||
 
1581
                                   (*itr) == '\t' ||
 
1582
                                   (*itr) == '\n') {
 
1583
                        ++itr;
 
1584
                }
 
1585
                if (itr != value.constEnd())
 
1586
                        ++itr;
 
1587
        }
 
1588
        return matrix;
 
1589
}
 
1590
 
 
1591
QPen WbItem::parseQPen(QString stroke, const QString &dashArray, const QString &dashOffset, const QString &linecap, const QString &linejoin, const QString &miterlimit, QString strokeopacity, QString opacity, const QString &width)
 
1592
{
 
1593
        // TODO: do something with 'dashOffset'
 
1594
        Q_UNUSED(dashOffset);
 
1595
 
 
1596
        QPen pen;
 
1597
 
 
1598
        if (!strokeopacity.isEmpty())
 
1599
                opacity = strokeopacity;
 
1600
 
 
1601
        if (!stroke.isEmpty() || !width.isEmpty()) {
 
1602
                if (stroke != QLatin1String("none")) {
 
1603
                        if (!stroke.isEmpty()) {
 
1604
                                if (stroke.startsWith("url")) {
 
1605
                                        // TODO: support stroke='url...'
 
1606
//                                       stroke = stroke.remove(0, 3);
 
1607
//                                       QString id = idFromUrl(stroke);
 
1608
//                                       QSvgStructureNode *group = 0;
 
1609
//                                       QSvgNode *dummy = node;
 
1610
//                                       while (dummy && (dummy->type() != QSvgNode::DOC  &&
 
1611
//                                                                        dummy->type() != QSvgNode::G  &&
 
1612
//                                                                        dummy->type() != QSvgNode::DEFS &&
 
1613
//                                                                        dummy->type() != QSvgNode::SWITCH)) {
 
1614
//                                               dummy = dummy->parent();
 
1615
//                                       }
 
1616
//                                       if (dummy)
 
1617
//                                               group = static_cast<QSvgStructureNode*>(dummy);
 
1618
//                                       if (group) {
 
1619
//                                               QSvgStyleProperty *style =
 
1620
//                                                       group->scopeStyle(id);
 
1621
//                                               if (style->type() == QSvgStyleProperty::GRADIENT) {
 
1622
//                                                       QBrush b(*((QSvgGradientStyle*)style)->qgradient());
 
1623
//                                                       pen.setBrush(b);
 
1624
//                                               } else if (style->type() == QSvgStyleProperty::SOLID_COLOR) {
 
1625
//                                                       pen.setColor(
 
1626
//                                                               ((QSvgSolidColorStyle*)style)->qcolor());
 
1627
//                                               }
 
1628
//                                       } else {
 
1629
//                                               qDebug()<<"QSvgHandler::parsePen no parent group?";
 
1630
//                                       }
 
1631
                                } else {
 
1632
                                        pen.setColor(constructColor(stroke, opacity));
 
1633
                                }
 
1634
                                //since we could inherit stroke="none"
 
1635
                                //we need to reset the style of our stroke to something
 
1636
                                pen.setStyle(Qt::SolidLine);
 
1637
                        }
 
1638
                        if (!width.isEmpty()) {
 
1639
// //                           QSvgHandler::LengthType lt;
 
1640
                                qreal widthF = parseLength(width);
 
1641
                                // TODO: support percentage widths
 
1642
                                if (!widthF) {
 
1643
                                        pen.setWidthF(1);
 
1644
//                                       return;
 
1645
                                }
 
1646
                                pen.setWidthF(widthF);
 
1647
                        }
 
1648
                        qreal penw = pen.widthF();
 
1649
 
 
1650
                        if (!linejoin.isEmpty()) {
 
1651
                                if (linejoin == "miter")
 
1652
                                        pen.setJoinStyle(Qt::MiterJoin);
 
1653
                                else if (linejoin == "round")
 
1654
                                        pen.setJoinStyle(Qt::RoundJoin);
 
1655
                                else if (linejoin == "bevel")
 
1656
                                        pen.setJoinStyle(Qt::BevelJoin);
 
1657
                        }
 
1658
                        if (!miterlimit.isEmpty()) {
 
1659
                                pen.setMiterLimit(miterlimit.toDouble()/2);
 
1660
                        }
 
1661
 
 
1662
                        if (!linecap.isEmpty()) {
 
1663
                                if (linecap == "butt")
 
1664
                                        pen.setCapStyle(Qt::FlatCap);
 
1665
                                else if (linecap == "round")
 
1666
                                        pen.setCapStyle(Qt::RoundCap);
 
1667
                                else if (linecap == "square")
 
1668
                                        pen.setCapStyle(Qt::SquareCap);
 
1669
                        }
 
1670
 
 
1671
                        if (!dashArray.isEmpty()) {
 
1672
                                QString::const_iterator itr = dashArray.constBegin();
 
1673
                                QList<qreal> dashes = parseNumbersList(itr);
 
1674
                                QVector<qreal> vec(dashes.size());
 
1675
 
 
1676
                                int i = 0;
 
1677
                                foreach(qreal dash, dashes) {
 
1678
                                        vec[i++] = dash/penw;
 
1679
                                }
 
1680
                                pen.setDashPattern(vec);
 
1681
                        }
 
1682
 
 
1683
                } else {
 
1684
                        pen.setStyle(Qt::NoPen);
 
1685
                }
 
1686
        } else {
 
1687
        // TODO: check what the actual defaults are
 
1688
                pen = QPen(QColor(Qt::black), 1, Qt::NoPen, Qt::FlatCap, Qt::MiterJoin);
 
1689
        }
 
1690
        return pen;
 
1691
}
 
1692
 
 
1693
QColor WbItem::constructColor(const QString &colorStr, const QString &opacity)
 
1694
{
 
1695
        QColor c = resolveColor(colorStr);
 
1696
        if (!opacity.isEmpty()) {
 
1697
                qreal op = opacity.toDouble();
 
1698
                if (op <= 1)
 
1699
                        op *= 255;
 
1700
                c.setAlpha(int(op));
 
1701
        }
 
1702
        return c;
 
1703
}
 
1704
 
 
1705
QColor WbItem::resolveColor(const QString &colorStr)
 
1706
{
 
1707
        QColor color;
 
1708
        static QHash<QString, QColor> colors;
 
1709
        QString colorStrTr = colorStr.trimmed();
 
1710
        if (colors.isEmpty()) {
 
1711
                colors.insert("black",   QColor(  0,   0,   0));
 
1712
                colors.insert("green",   QColor(  0, 128,   0));
 
1713
                colors.insert("silver",  QColor(192, 192, 192));
 
1714
                colors.insert("lime",   QColor(  0, 255,   0));
 
1715
                colors.insert("gray",   QColor(128, 128, 128));
 
1716
                colors.insert("olive",   QColor(128, 128,   0));
 
1717
                colors.insert("white",   QColor(255, 255, 255));
 
1718
                colors.insert("yellow",  QColor(255, 255,   0));
 
1719
                colors.insert("maroon",  QColor(128,   0,   0));
 
1720
                colors.insert("navy",   QColor(  0,   0, 128));
 
1721
                colors.insert("red",     QColor(255,   0,   0));
 
1722
                colors.insert("blue",   QColor(  0,   0, 255));
 
1723
                colors.insert("purple",  QColor(128,   0, 128));
 
1724
                colors.insert("teal",   QColor(  0, 128, 128));
 
1725
                colors.insert("fuchsia", QColor(255,   0, 255));
 
1726
                colors.insert("aqua",   QColor(  0, 255, 255));
 
1727
        }
 
1728
        if (colors.contains(colorStrTr)) {
 
1729
                color = colors[colorStrTr];
 
1730
        } else if (colorStr.startsWith("rgb(")) {
 
1731
                QString::const_iterator itr = colorStr.constBegin();
 
1732
                ++itr; ++itr; ++itr; ++itr;
 
1733
                QString::const_iterator itr_back = itr;
 
1734
                QList<qreal> compo = parseNumbersList(itr);
 
1735
                //1 means that it failed after reaching non-parsable
 
1736
                //character which is going to be "%"
 
1737
                if (compo.size() == 1) {
 
1738
                        itr = itr_back;
 
1739
                        compo = parsePercentageList(itr);
 
1740
                        compo[0] *= 2.55;
 
1741
                        compo[1] *= 2.55;
 
1742
                        compo[2] *= 2.55;
 
1743
                }
 
1744
 
 
1745
                color = QColor(int(compo[0]),
 
1746
                                           int(compo[1]),
 
1747
                                           int(compo[2]));
 
1748
        } else if (colorStr == QLatin1String("inherited") ||
 
1749
                           colorStr == QLatin1String("inherit"))  {
 
1750
                // TODO: handle inherited color
 
1751
                color = QColor(Qt::black);
 
1752
        } else if (colorStr == QLatin1String("currentColor")) {
 
1753
                //TODO: handle current Color
 
1754
                color = QColor(Qt::black);
 
1755
        }
 
1756
        return color;
 
1757
}
 
1758
 
 
1759
qreal WbItem::parseLength(const QString &str)
 
1760
{
 
1761
        QString numStr = str.trimmed();
 
1762
        qreal len = 0;
 
1763
 
 
1764
        if (numStr.endsWith("%")) {
 
1765
                numStr.chop(1);
 
1766
                len = numStr.toDouble();
 
1767
//              type = QSvgHandler::PERCENT;
 
1768
        } else if (numStr.endsWith("px")) {
 
1769
                numStr.chop(2);
 
1770
                len = numStr.toDouble();
 
1771
//              type = QSvgHandler::PX;
 
1772
        } else if (numStr.endsWith("pc")) {
 
1773
                numStr.chop(2);
 
1774
                len = numStr.toDouble();
 
1775
//              type = QSvgHandler::PC;
 
1776
        } else if (numStr.endsWith("pt")) {
 
1777
                numStr.chop(2);
 
1778
                len = numStr.toDouble();
 
1779
//              type = QSvgHandler::PT;
 
1780
        } else if (numStr.endsWith("mm")) {
 
1781
                numStr.chop(2);
 
1782
                len = numStr.toDouble();
 
1783
//              type = QSvgHandler::MM;
 
1784
        } else if (numStr.endsWith("cm")) {
 
1785
                numStr.chop(2);
 
1786
                len = numStr.toDouble();
 
1787
//              type = QSvgHandler::CM;
 
1788
        } else if (numStr.endsWith("in")) {
 
1789
                numStr.chop(2);
 
1790
                len = numStr.toDouble();
 
1791
//              type = QSvgHandler::IN;
 
1792
        } else {
 
1793
                len = numStr.toDouble();
 
1794
//              type = handler->defaultCoordinateSystem();
 
1795
                //type = QSvgHandler::OTHER;
 
1796
        }
 
1797
        //qDebug()<<"len is "<<len<<", from "<<numStr;
 
1798
        return len;
 
1799
}
 
1800
 
 
1801
/*!
 
1802
 *      Parsing: WbPath 
 
1803
 */
 
1804
 
 
1805
bool WbPath::parsePathDataFast(const QString &data, QPainterPath &path)
 
1806
{
 
1807
        QString::const_iterator itr = data.constBegin();
 
1808
        qreal x0 = 0, y0 = 0;                     // starting point
 
1809
        qreal x = 0, y = 0;                             // current point
 
1810
        char lastMode = 0;
 
1811
        QChar pathElem;
 
1812
        QPointF ctrlPt;
 
1813
 
 
1814
        while (itr != data.constEnd()) {
 
1815
                while ((*itr).isSpace())
 
1816
                        ++itr;
 
1817
                pathElem = *itr;
 
1818
                ++itr;
 
1819
                QList<qreal> arg = WbItem::parseNumbersList(itr);
 
1820
                if (pathElem == 'z' || pathElem == 'Z')
 
1821
                        arg.append(0);//dummy
 
1822
                while (!arg.isEmpty()) {
 
1823
                        qreal offsetX = x;              // correction offsets
 
1824
                        qreal offsetY = y;              // for relative commands
 
1825
                        switch (pathElem.toAscii()) {
 
1826
                        case 'm': {
 
1827
                                x = x0 = arg[0] + offsetX;
 
1828
                                y = y0 = arg[1] + offsetY;
 
1829
                                path.moveTo(x0, y0);
 
1830
                                arg.pop_front(); arg.pop_front();
 
1831
                        }
 
1832
                                break;
 
1833
                        case 'M': {
 
1834
                                x = x0 = arg[0];
 
1835
                                y = y0 = arg[1];
 
1836
                                path.moveTo(x0, y0);
 
1837
                                arg.pop_front(); arg.pop_front();
 
1838
                        }
 
1839
                                break;
 
1840
                        case 'z':
 
1841
                        case 'Z': {
 
1842
                                x = x0;
 
1843
                                y = y0;
 
1844
                                path.closeSubpath();
 
1845
                                arg.pop_front();//pop dummy
 
1846
                        }
 
1847
                                break;
 
1848
                        case 'l': {
 
1849
                                x = arg.front() + offsetX;
 
1850
                                arg.pop_front();
 
1851
                                y = arg.front() + offsetY;
 
1852
                                arg.pop_front();
 
1853
                                path.lineTo(x, y);
 
1854
 
 
1855
                        }
 
1856
                                break;
 
1857
                        case 'L': {
 
1858
                                x = arg.front(); arg.pop_front();
 
1859
                                y = arg.front(); arg.pop_front();
 
1860
                                path.lineTo(x, y);
 
1861
                        }
 
1862
                                break;
 
1863
                        case 'h': {
 
1864
                                x = arg.front() + offsetX; arg.pop_front();
 
1865
                                path.lineTo(x, y);
 
1866
                        }
 
1867
                                break;
 
1868
                        case 'H': {
 
1869
                                x = arg[0];
 
1870
                                path.lineTo(x, y);
 
1871
                                arg.pop_front();
 
1872
                        }
 
1873
                                break;
 
1874
                        case 'v': {
 
1875
                                y = arg[0] + offsetY;
 
1876
                                path.lineTo(x, y);
 
1877
                                arg.pop_front();
 
1878
                        }
 
1879
                                break;
 
1880
                        case 'V': {
 
1881
                                y = arg[0];
 
1882
                                path.lineTo(x, y);
 
1883
                                arg.pop_front();
 
1884
                        }
 
1885
                                break;
 
1886
                        case 'c': {
 
1887
                                QPointF c1(arg[0]+offsetX, arg[1]+offsetY);
 
1888
                                QPointF c2(arg[2]+offsetX, arg[3]+offsetY);
 
1889
                                QPointF e(arg[4]+offsetX, arg[5]+offsetY);
 
1890
                                path.cubicTo(c1, c2, e);
 
1891
                                ctrlPt = c2;
 
1892
                                x = e.x();
 
1893
                                y = e.y();
 
1894
                                arg.pop_front(); arg.pop_front();
 
1895
                                arg.pop_front(); arg.pop_front();
 
1896
                                arg.pop_front(); arg.pop_front();
 
1897
                                break;
 
1898
                        }
 
1899
                        case 'C': {
 
1900
                                QPointF c1(arg[0], arg[1]);
 
1901
                                QPointF c2(arg[2], arg[3]);
 
1902
                                QPointF e(arg[4], arg[5]);
 
1903
                                path.cubicTo(c1, c2, e);
 
1904
                                ctrlPt = c2;
 
1905
                                x = e.x();
 
1906
                                y = e.y();
 
1907
                                arg.pop_front(); arg.pop_front();
 
1908
                                arg.pop_front(); arg.pop_front();
 
1909
                                arg.pop_front(); arg.pop_front();
 
1910
                                break;
 
1911
                        }
 
1912
                        case 's': {
 
1913
                                QPointF c1;
 
1914
                                if (lastMode == 'c' || lastMode == 'C' ||
 
1915
                                        lastMode == 's' || lastMode == 'S')
 
1916
                                        c1 = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y());
 
1917
                                else
 
1918
                                        c1 = QPointF(x, y);
 
1919
                                QPointF c2(arg[0]+offsetX, arg[1]+offsetY);
 
1920
                                QPointF e(arg[2]+offsetX, arg[3]+offsetY);
 
1921
                                path.cubicTo(c1, c2, e);
 
1922
                                ctrlPt = c2;
 
1923
                                x = e.x();
 
1924
                                y = e.y();
 
1925
                                arg.pop_front(); arg.pop_front();
 
1926
                                arg.pop_front(); arg.pop_front();
 
1927
                                break;
 
1928
                        }
 
1929
                        case 'S': {
 
1930
                                QPointF c1;
 
1931
                                if (lastMode == 'c' || lastMode == 'C' ||
 
1932
                                        lastMode == 's' || lastMode == 'S')
 
1933
                                        c1 = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y());
 
1934
                                else
 
1935
                                        c1 = QPointF(x, y);
 
1936
                                QPointF c2(arg[0], arg[1]);
 
1937
                                QPointF e(arg[2], arg[3]);
 
1938
                                path.cubicTo(c1, c2, e);
 
1939
                                ctrlPt = c2;
 
1940
                                x = e.x();
 
1941
                                y = e.y();
 
1942
                                arg.pop_front(); arg.pop_front();
 
1943
                                arg.pop_front(); arg.pop_front();
 
1944
                                break;
 
1945
                        }
 
1946
                        case 'q': {
 
1947
                                QPointF c(arg[0]+offsetX, arg[1]+offsetY);
 
1948
                                QPointF e(arg[2]+offsetX, arg[3]+offsetY);
 
1949
                                path.quadTo(c, e);
 
1950
                                ctrlPt = c;
 
1951
                                x = e.x();
 
1952
                                y = e.y();
 
1953
                                arg.pop_front(); arg.pop_front();
 
1954
                                arg.pop_front(); arg.pop_front();
 
1955
                                break;
 
1956
                        }
 
1957
                        case 'Q': {
 
1958
                                QPointF c(arg[0], arg[1]);
 
1959
                                QPointF e(arg[2], arg[3]);
 
1960
                                path.quadTo(c, e);
 
1961
                                ctrlPt = c;
 
1962
                                x = e.x();
 
1963
                                y = e.y();
 
1964
                                arg.pop_front(); arg.pop_front();
 
1965
                                arg.pop_front(); arg.pop_front();
 
1966
                                break;
 
1967
                        }
 
1968
                        case 't': {
 
1969
                                QPointF e(arg[0]+offsetX, arg[1]+offsetY);
 
1970
                                QPointF c;
 
1971
                                if (lastMode == 'q' || lastMode == 'Q' ||
 
1972
                                        lastMode == 't' || lastMode == 'T')
 
1973
                                        c = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y());
 
1974
                                else
 
1975
                                        c = QPointF(x, y);
 
1976
                                path.quadTo(c, e);
 
1977
                                ctrlPt = c;
 
1978
                                x = e.x();
 
1979
                                y = e.y();
 
1980
                                arg.pop_front(); arg.pop_front();
 
1981
                                break;
 
1982
                        }
 
1983
                        case 'T': {
 
1984
                                QPointF e(arg[0], arg[1]);
 
1985
                                QPointF c;
 
1986
                                if (lastMode == 'q' || lastMode == 'Q' ||
 
1987
                                        lastMode == 't' || lastMode == 'T')
 
1988
                                        c = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y());
 
1989
                                else
 
1990
                                        c = QPointF(x, y);
 
1991
                                path.quadTo(c, e);
 
1992
                                ctrlPt = c;
 
1993
                                x = e.x();
 
1994
                                y = e.y();
 
1995
                                arg.pop_front(); arg.pop_front();
 
1996
                                break;
 
1997
                        }
 
1998
                        case 'a': {
 
1999
                                qreal rx = arg[0];
 
2000
                                qreal ry = arg[1];
 
2001
                                qreal xAxisRotation = arg[2];
 
2002
                                qreal largeArcFlag  = arg[3];
 
2003
                                qreal sweepFlag = arg[4];
 
2004
                                qreal ex = arg[5] + offsetX;
 
2005
                                qreal ey = arg[6] + offsetY;
 
2006
                                qreal curx = x;
 
2007
                                qreal cury = y;
 
2008
                                pathArc(path, rx, ry, xAxisRotation, int(largeArcFlag),
 
2009
                                                int(sweepFlag), ex, ey, curx, cury);
 
2010
 
 
2011
                                x = ex;
 
2012
                                y = ey;
 
2013
 
 
2014
                                arg.pop_front(); arg.pop_front();
 
2015
                                arg.pop_front(); arg.pop_front();
 
2016
                                arg.pop_front(); arg.pop_front();
 
2017
                                arg.pop_front();
 
2018
                        }
 
2019
                                break;
 
2020
                        case 'A': {
 
2021
                                qreal rx = arg[0];
 
2022
                                qreal ry = arg[1];
 
2023
                                qreal xAxisRotation = arg[2];
 
2024
                                qreal largeArcFlag  = arg[3];
 
2025
                                qreal sweepFlag = arg[4];
 
2026
                                qreal ex = arg[5];
 
2027
                                qreal ey = arg[6];
 
2028
                                qreal curx = x;
 
2029
                                qreal cury = y;
 
2030
                                pathArc(path, rx, ry, xAxisRotation, int(largeArcFlag),
 
2031
                                                int(sweepFlag), ex, ey, curx, cury);
 
2032
                                x = ex;
 
2033
                                y = ey;
 
2034
                                arg.pop_front(); arg.pop_front();
 
2035
                                arg.pop_front(); arg.pop_front();
 
2036
                                arg.pop_front(); arg.pop_front();
 
2037
                                arg.pop_front();
 
2038
                        }
 
2039
                                break;
 
2040
                        default:
 
2041
                                qDebug() << QString("path data is ") << pathElem;
 
2042
                                Q_ASSERT(!"invalid path data");
 
2043
                                break;
 
2044
                        }
 
2045
                        lastMode = pathElem.toAscii();
 
2046
                }
 
2047
        }
 
2048
        return true;
 
2049
}
 
2050
 
 
2051
void WbPath::pathArcSegment(QPainterPath &path, qreal xc, qreal yc, qreal th0, qreal th1, qreal rx, qreal ry, qreal xAxisRotation)
 
2052
{
 
2053
        qreal sinTh, cosTh;
 
2054
        qreal a00, a01, a10, a11;
 
2055
        qreal x1, y1, x2, y2, x3, y3;
 
2056
        qreal t;
 
2057
        qreal thHalf;
 
2058
 
 
2059
        sinTh = sin(xAxisRotation * (M_PI / 180.0));
 
2060
        cosTh = cos(xAxisRotation * (M_PI / 180.0));
 
2061
 
 
2062
        a00 =  cosTh * rx;
 
2063
        a01 = -sinTh * ry;
 
2064
        a10 =  sinTh * rx;
 
2065
        a11 =  cosTh * ry;
 
2066
 
 
2067
        thHalf = 0.5 * (th1 - th0);
 
2068
        t = (8.0 / 3.0) * sin(thHalf * 0.5) * sin(thHalf * 0.5) / sin(thHalf);
 
2069
        x1 = xc + cos(th0) - t * sin(th0);
 
2070
        y1 = yc + sin(th0) + t * cos(th0);
 
2071
        x3 = xc + cos(th1);
 
2072
        y3 = yc + sin(th1);
 
2073
        x2 = x3 + t * sin(th1);
 
2074
        y2 = y3 - t * cos(th1);
 
2075
 
 
2076
        path.cubicTo(a00 * x1 + a01 * y1, a10 * x1 + a11 * y1,
 
2077
                                 a00 * x2 + a01 * y2, a10 * x2 + a11 * y2,
 
2078
                                 a00 * x3 + a01 * y3, a10 * x3 + a11 * y3);
 
2079
}
 
2080
 
 
2081
// the arc handling code underneath is from XSVG (BSD license)
 
2082
/*
 
2083
 * Copyright  2002 USC/Information Sciences Institute
 
2084
 *
 
2085
 * Permission to use, copy, modify, distribute, and sell this software
 
2086
 * and its documentation for any purpose is hereby granted without
 
2087
 * fee, provided that the above copyright notice appear in all copies
 
2088
 * and that both that copyright notice and this permission notice
 
2089
 * appear in supporting documentation, and that the name of
 
2090
 * Information Sciences Institute not be used in advertising or
 
2091
 * publicity pertaining to distribution of the software without
 
2092
 * specific, written prior permission.  Information Sciences Institute
 
2093
 * makes no representations about the suitability of this software for
 
2094
 * any purpose.  It is provided "as is" without express or implied
 
2095
 * warranty.
 
2096
 *
 
2097
 * INFORMATION SCIENCES INSTITUTE DISCLAIMS ALL WARRANTIES WITH REGARD
 
2098
 * TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
 
2099
 * MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL INFORMATION SCIENCES
 
2100
 * INSTITUTE BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
 
2101
 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
 
2102
 * OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 
2103
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 
2104
 * PERFORMANCE OF THIS SOFTWARE.
 
2105
 *
 
2106
 */
 
2107
void WbPath::pathArc(QPainterPath &path,
 
2108
                                        qreal   rx,
 
2109
                                        qreal   ry,
 
2110
                                        qreal   x_axis_rotation,
 
2111
                                        int             large_arc_flag,
 
2112
                                        int             sweep_flag,
 
2113
                                        qreal   x,
 
2114
                                        qreal   y,
 
2115
                                        qreal curx, qreal cury)
 
2116
{
 
2117
        qreal sin_th, cos_th;
 
2118
        qreal a00, a01, a10, a11;
 
2119
        qreal x0, y0, x1, y1, xc, yc;
 
2120
        qreal d, sfactor, sfactor_sq;
 
2121
        qreal th0, th1, th_arc;
 
2122
        int i, n_segs;
 
2123
        qreal dx, dy, dx1, dy1, Pr1, Pr2, Px, Py, check;
 
2124
 
 
2125
        rx = qAbs(rx);
 
2126
        ry = qAbs(ry);
 
2127
 
 
2128
        sin_th = sin(x_axis_rotation * (M_PI / 180.0));
 
2129
        cos_th = cos(x_axis_rotation * (M_PI / 180.0));
 
2130
 
 
2131
        dx = (curx - x) / 2.0;
 
2132
        dy = (cury - y) / 2.0;
 
2133
        dx1 =  cos_th * dx + sin_th * dy;
 
2134
        dy1 = -sin_th * dx + cos_th * dy;
 
2135
        Pr1 = rx * rx;
 
2136
        Pr2 = ry * ry;
 
2137
        Px = dx1 * dx1;
 
2138
        Py = dy1 * dy1;
 
2139
        /* Spec : check if radii are large enough */
 
2140
        check = Px / Pr1 + Py / Pr2;
 
2141
        if (check > 1) {
 
2142
                rx = rx * sqrt(check);
 
2143
                ry = ry * sqrt(check);
 
2144
        }
 
2145
 
 
2146
        a00 =  cos_th / rx;
 
2147
        a01 =  sin_th / rx;
 
2148
        a10 = -sin_th / ry;
 
2149
        a11 =  cos_th / ry;
 
2150
        x0 = a00 * curx + a01 * cury;
 
2151
        y0 = a10 * curx + a11 * cury;
 
2152
        x1 = a00 * x + a01 * y;
 
2153
        y1 = a10 * x + a11 * y;
 
2154
        /* (x0, y0) is current point in transformed coordinate space.
 
2155
           (x1, y1) is new point in transformed coordinate space.
 
2156
 
 
2157
           The arc fits a unit-radius circle in this space.
 
2158
        */
 
2159
        d = (x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0);
 
2160
        sfactor_sq = 1.0 / d - 0.25;
 
2161
        if (sfactor_sq < 0) sfactor_sq = 0;
 
2162
        sfactor = sqrt(sfactor_sq);
 
2163
        if (sweep_flag == large_arc_flag) sfactor = -sfactor;
 
2164
        xc = 0.5 * (x0 + x1) - sfactor * (y1 - y0);
 
2165
        yc = 0.5 * (y0 + y1) + sfactor * (x1 - x0);
 
2166
        /* (xc, yc) is center of the circle. */
 
2167
 
 
2168
        th0 = atan2(y0 - yc, x0 - xc);
 
2169
        th1 = atan2(y1 - yc, x1 - xc);
 
2170
 
 
2171
        th_arc = th1 - th0;
 
2172
        if (th_arc < 0 && sweep_flag)
 
2173
                th_arc += 2 * M_PI;
 
2174
        else if (th_arc > 0 && !sweep_flag)
 
2175
                th_arc -= 2 * M_PI;
 
2176
 
 
2177
        n_segs = int(ceil(qAbs(th_arc / (M_PI * 0.5 + 0.001))));
 
2178
 
 
2179
        for (i = 0; i < n_segs; i++) {
 
2180
                pathArcSegment(path, xc, yc,
 
2181
                                           th0 + i * th_arc / n_segs,
 
2182
                                           th0 + (i + 1) * th_arc / n_segs,
 
2183
                                           rx, ry, x_axis_rotation);
 
2184
        }
 
2185
}