~ubuntu-branches/ubuntu/precise/koffice/precise

« back to all changes in this revision

Viewing changes to plugins/pathshapes/enhancedpath/EnhancedPathShape.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Jonathan Riddell
  • Date: 2010-09-21 15:36:35 UTC
  • mfrom: (1.4.1 upstream) (60.2.11 maverick)
  • Revision ID: james.westby@ubuntu.com-20100921153635-6tejqkiro2u21ydi
Tags: 1:2.2.2-0ubuntu3
Add kubuntu_03_fix-crash-on-closing-sqlite-connection-2.2.2.diff and
kubuntu_04_support-large-memo-values-for-msaccess-2.2.2.diff as
recommended by upstream http://kexi-
project.org/wiki/wikiview/index.php@Kexi2.2_Patches.html#sqlite_stab
ility

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* This file is part of the KDE project
 
2
 * Copyright (C) 2007,2010 Jan Hambrecht <jaham@gmx.net>
 
3
 * Copyright (C) 2009-2010 Thomas Zander <zander@kde.org>
 
4
 * Copyright (C) 2010 Carlos Licea <carlos@kdab.com>
 
5
 * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
 
6
 *   Contact: Suresh Chande suresh.chande@nokia.com
 
7
 *
 
8
 * This library is free software; you can redistribute it and/or
 
9
 * modify it under the terms of the GNU Library General Public
 
10
 * License as published by the Free Software Foundation; either
 
11
 * version 2 of the License, or (at your option) any later version.
 
12
 *
 
13
 * This library is distributed in the hope that it will be useful,
 
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
16
 * Library General Public License for more details.
 
17
 *
 
18
 * You should have received a copy of the GNU Library General Public License
 
19
 * along with this library; see the file COPYING.LIB.  If not, write to
 
20
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 
21
 * Boston, MA 02110-1301, USA.
 
22
 */
 
23
 
 
24
#include "EnhancedPathShape.h"
 
25
#include "EnhancedPathCommand.h"
 
26
#include "EnhancedPathParameter.h"
 
27
#include "EnhancedPathHandle.h"
 
28
#include "EnhancedPathFormula.h"
 
29
 
 
30
#include <KoXmlNS.h>
 
31
#include <KoXmlWriter.h>
 
32
#include <KoXmlReader.h>
 
33
#include <KoShapeSavingContext.h>
 
34
#include <KoUnit.h>
 
35
#include <KoOdfWorkaround.h>
 
36
#include <KoPathPoint.h>
 
37
 
 
38
EnhancedPathShape::EnhancedPathShape(const QRectF &viewBox)
 
39
: m_viewBox(viewBox), m_viewBoxOffset(0.0, 0.0), m_mirrorVertically(false), m_mirrorHorizontally(false)
 
40
{
 
41
}
 
42
 
 
43
EnhancedPathShape::~EnhancedPathShape()
 
44
{
 
45
    reset();
 
46
}
 
47
 
 
48
void EnhancedPathShape::reset()
 
49
{
 
50
    qDeleteAll(m_commands);
 
51
    m_commands.clear();
 
52
    qDeleteAll(m_enhancedHandles);
 
53
    m_enhancedHandles.clear();
 
54
    setHandles(QList<QPointF>());
 
55
    qDeleteAll(m_formulae);
 
56
    m_formulae.clear();
 
57
    qDeleteAll(m_parameters);
 
58
    m_parameters.clear();
 
59
    m_modifiers.clear();
 
60
    m_viewMatrix.reset();
 
61
    m_viewBoxOffset = QPointF();
 
62
    clear();
 
63
}
 
64
 
 
65
void EnhancedPathShape::moveHandleAction(int handleId, const QPointF & point, Qt::KeyboardModifiers modifiers)
 
66
{
 
67
    Q_UNUSED(modifiers);
 
68
    EnhancedPathHandle *handle = m_enhancedHandles[ handleId ];
 
69
    if (handle) {
 
70
        handle->changePosition(shapeToViewbox(point));
 
71
    }
 
72
}
 
73
 
 
74
void EnhancedPathShape::updatePath(const QSizeF &)
 
75
{
 
76
    clear();
 
77
 
 
78
    foreach (EnhancedPathCommand *cmd, m_commands)
 
79
        cmd->execute();
 
80
 
 
81
    m_viewBound = outline().boundingRect();
 
82
 
 
83
    QMatrix matrix;
 
84
    matrix.translate(m_viewBoxOffset.x(), m_viewBoxOffset.y());
 
85
    matrix = m_viewMatrix * matrix;
 
86
 
 
87
    KoSubpathList::const_iterator pathIt(m_subpaths.constBegin());
 
88
    for (; pathIt != m_subpaths.constEnd(); ++pathIt) {
 
89
        KoSubpath::const_iterator it((*pathIt)->constBegin());
 
90
        for (; it != (*pathIt)->constEnd(); ++it) {
 
91
            (*it)->map(matrix);
 
92
        }
 
93
    }
 
94
    const int handleCount = m_enhancedHandles.count();
 
95
    QList<QPointF> handles;
 
96
    for (int i = 0; i < handleCount; ++i)
 
97
        handles.append(matrix.map(m_enhancedHandles[i]->position()));
 
98
    setHandles(handles);
 
99
 
 
100
    normalize();
 
101
}
 
102
 
 
103
void EnhancedPathShape::setSize(const QSizeF &newSize)
 
104
{
 
105
    KoParameterShape::setSize(newSize);
 
106
 
 
107
    // calculate scaling factors from viewbox size to shape size
 
108
    qreal xScale = newSize.width()/m_viewBound.width();
 
109
    qreal yScale = newSize.height()/m_viewBound.height();
 
110
 
 
111
    // create view matrix, take mirroring into account
 
112
    m_viewMatrix.reset();
 
113
    m_viewMatrix.translate(m_viewBound.center().x(), m_viewBound.center().y());
 
114
    m_viewMatrix.scale(m_mirrorHorizontally ? -xScale : xScale, m_mirrorVertically ? -yScale : yScale);
 
115
    m_viewMatrix.translate(-m_viewBound.center().x(), -m_viewBound.center().y());
 
116
 
 
117
    updatePath(newSize);
 
118
}
 
119
 
 
120
QPointF EnhancedPathShape::normalize()
 
121
{
 
122
    QPointF offset = KoParameterShape::normalize();
 
123
 
 
124
    m_viewBoxOffset -= offset;
 
125
 
 
126
    return offset;
 
127
}
 
128
 
 
129
QPointF EnhancedPathShape::shapeToViewbox(const QPointF &point) const
 
130
{
 
131
    return m_viewMatrix.inverted().map( point-m_viewBoxOffset );
 
132
}
 
133
 
 
134
void EnhancedPathShape::evaluateHandles()
 
135
{
 
136
    const int handleCount = m_enhancedHandles.count();
 
137
    QList<QPointF> handles;
 
138
    for (int i = 0; i < handleCount; ++i)
 
139
        handles.append(m_enhancedHandles[i]->position());
 
140
    setHandles(handles);
 
141
}
 
142
 
 
143
QRectF EnhancedPathShape::viewBox() const
 
144
{
 
145
    return m_viewBox;
 
146
}
 
147
 
 
148
qreal EnhancedPathShape::evaluateReference(const QString &reference)
 
149
{
 
150
    if (reference.isEmpty())
 
151
        return 0.0;
 
152
 
 
153
    QChar c = reference[0];
 
154
 
 
155
    qreal res = 0.0;
 
156
 
 
157
    switch(c.toAscii()) {
 
158
    // referenced modifier
 
159
    case '$': {
 
160
        bool success = false;
 
161
        int modifierIndex = reference.mid(1).toInt(&success);
 
162
        res = m_modifiers.value(modifierIndex);
 
163
        break;
 
164
    }
 
165
    // referenced formula
 
166
    case '?': {
 
167
        QString fname = reference.mid(1);
 
168
        FormulaStore::const_iterator formulaIt = m_formulae.constFind(fname);
 
169
        if (formulaIt != m_formulae.constEnd())
 
170
        {
 
171
            EnhancedPathFormula * formula = formulaIt.value();
 
172
            if (formula)
 
173
                res = formula->evaluate();
 
174
        }
 
175
        break;
 
176
    }
 
177
    // maybe an identifier ?
 
178
    default:
 
179
        EnhancedPathNamedParameter p(reference, this);
 
180
        res = p.evaluate();
 
181
        break;
 
182
    }
 
183
 
 
184
    return res;
 
185
}
 
186
 
 
187
void EnhancedPathShape::modifyReference(const QString &reference, qreal value)
 
188
{
 
189
    if (reference.isEmpty())
 
190
        return;
 
191
 
 
192
    QChar c = reference[0];
 
193
 
 
194
    if (c.toAscii() == '$') {
 
195
        bool success = false;
 
196
        int modifierIndex = reference.mid(1).toInt(&success);
 
197
        if (modifierIndex >= 0 && modifierIndex < m_modifiers.count())
 
198
            m_modifiers[modifierIndex] = value;
 
199
    }
 
200
}
 
201
 
 
202
EnhancedPathParameter * EnhancedPathShape::parameter(const QString & text)
 
203
{
 
204
    Q_ASSERT(! text.isEmpty());
 
205
 
 
206
    ParameterStore::const_iterator parameterIt = m_parameters.constFind(text);
 
207
    if (parameterIt != m_parameters.constEnd()) {
 
208
        return parameterIt.value();
 
209
    } else {
 
210
        EnhancedPathParameter *parameter = 0;
 
211
        QChar c = text[0];
 
212
        if (c.toAscii() == '$' || c.toAscii() == '?') {
 
213
            parameter = new EnhancedPathReferenceParameter(text, this);
 
214
        } else {
 
215
            if (c.isDigit()) {
 
216
                bool success = false;
 
217
                qreal constant = text.toDouble(&success);
 
218
                if (success)
 
219
                    parameter = new EnhancedPathConstantParameter(constant, this);
 
220
            } else {
 
221
                Identifier identifier = EnhancedPathNamedParameter::identifierFromString(text);
 
222
                if (identifier != IdentifierUnknown)
 
223
                    parameter = new EnhancedPathNamedParameter(identifier, this);
 
224
            }
 
225
        }
 
226
 
 
227
        if (parameter)
 
228
            m_parameters[text] = parameter;
 
229
 
 
230
        return parameter;
 
231
    }
 
232
}
 
233
 
 
234
void EnhancedPathShape::addFormula(const QString &name, const QString &formula)
 
235
{
 
236
    if (name.isEmpty() || formula.isEmpty())
 
237
        return;
 
238
 
 
239
    m_formulae[name] = new EnhancedPathFormula(formula, this);
 
240
}
 
241
 
 
242
void EnhancedPathShape::addHandle(const QMap<QString,QVariant> &handle)
 
243
{
 
244
    if (handle.isEmpty())
 
245
        return;
 
246
 
 
247
    if (! handle.contains("draw:handle-position"))
 
248
        return;
 
249
    QVariant position = handle.value("draw:handle-position");
 
250
 
 
251
    QStringList tokens = position.toString().simplified().split(' ');
 
252
    if (tokens.count() < 2)
 
253
        return;
 
254
 
 
255
    EnhancedPathHandle *newHandle = new EnhancedPathHandle(this);
 
256
    newHandle->setPosition(parameter(tokens[0]), parameter(tokens[1]));
 
257
 
 
258
    // check if we have a polar handle
 
259
    if (handle.contains("draw:handle-polar")) {
 
260
        QVariant polar = handle.value("draw:handle-polar");
 
261
        QStringList tokens = polar.toString().simplified().split(' ');
 
262
        if (tokens.count() == 2) {
 
263
            newHandle->setPolarCenter(parameter(tokens[0]), parameter(tokens[1]));
 
264
 
 
265
            QVariant minRadius = handle.value("draw:handle-radius-range-minimum");
 
266
            QVariant maxRadius = handle.value("draw:handle-radius-range-maximum");
 
267
            if (minRadius.isValid() && maxRadius.isValid())
 
268
                newHandle->setRadiusRange(parameter(minRadius.toString()), parameter(maxRadius.toString()));
 
269
        }
 
270
    } else {
 
271
        QVariant minX = handle.value("draw:handle-range-x-minimum");
 
272
        QVariant maxX = handle.value("draw:handle-range-x-maximum");
 
273
        if (minX.isValid() && maxX.isValid())
 
274
            newHandle->setRangeX(parameter(minX.toString()), parameter(maxX.toString()));
 
275
 
 
276
        QVariant minY = handle.value("draw:handle-range-y-minimum");
 
277
        QVariant maxY = handle.value("draw:handle-range-y-maximum");
 
278
        if (minY.isValid() && maxY.isValid())
 
279
            newHandle->setRangeY(parameter(minY.toString()), parameter(maxY.toString()));
 
280
    }
 
281
 
 
282
    m_enhancedHandles.append(newHandle);
 
283
 
 
284
    evaluateHandles();
 
285
}
 
286
 
 
287
void EnhancedPathShape::addModifiers(const QString &modifiers)
 
288
{
 
289
    if (modifiers.isEmpty())
 
290
        return;
 
291
 
 
292
    QStringList tokens = modifiers.simplified().split(' ');
 
293
    int tokenCount = tokens.count();
 
294
    for (int i = 0; i < tokenCount; ++i)
 
295
       m_modifiers.append(tokens[i].toDouble());
 
296
}
 
297
 
 
298
void EnhancedPathShape::addCommand(const QString &command)
 
299
{
 
300
    addCommand(command, true);
 
301
}
 
302
 
 
303
void EnhancedPathShape::addCommand(const QString &command, bool triggerUpdate)
 
304
{
 
305
    QString commandStr = command.simplified();
 
306
    if (commandStr.isEmpty())
 
307
        return;
 
308
 
 
309
    // the first character is the command
 
310
    EnhancedPathCommand *cmd = new EnhancedPathCommand(commandStr[0], this);
 
311
 
 
312
    // strip command char
 
313
    commandStr = commandStr.mid(1).simplified();
 
314
 
 
315
    // now parse the command parameters
 
316
    if (!commandStr.isEmpty()) {
 
317
        QStringList tokens = commandStr.split(' ');
 
318
        for (int i = 0; i < tokens.count(); ++i)
 
319
            cmd->addParameter(parameter(tokens[i]));
 
320
    }
 
321
    m_commands.append(cmd);
 
322
 
 
323
    if (triggerUpdate)
 
324
        updatePath(size());
 
325
}
 
326
 
 
327
void EnhancedPathShape::saveOdf(KoShapeSavingContext &context) const
 
328
{
 
329
    if (isParametricShape()) {
 
330
        context.xmlWriter().startElement("draw:custom-shape");
 
331
        saveOdfAttributes(context, OdfAllAttributes&~OdfSize);
 
332
 
 
333
        // save the right size so that when loading we fit the viewbox
 
334
        // to the right size without getting any wrong scaling
 
335
        // -> calculate the right size from the current size/viewbound ratio
 
336
        const QSizeF currentSize = outline().boundingRect().size();
 
337
        context.xmlWriter().addAttributePt("svg:width", m_viewBox.width()*currentSize.width()/m_viewBound.width());
 
338
        context.xmlWriter().addAttributePt("svg:height", m_viewBox.height()*currentSize.height()/m_viewBound.height());
 
339
 
 
340
        context.xmlWriter().startElement("draw:enhanced-geometry");
 
341
        context.xmlWriter().addAttribute("svg:viewBox", QString("%1 %2 %3 %4").arg(m_viewBox.x()).arg(m_viewBox.y()).arg(m_viewBox.width()).arg(m_viewBox.height()));
 
342
 
 
343
        QString modifiers;
 
344
        foreach (qreal modifier, m_modifiers)
 
345
            modifiers += QString::number(modifier) + ' ';
 
346
        context.xmlWriter().addAttribute("draw:modifiers", modifiers.trimmed());
 
347
 
 
348
        QString path;
 
349
        foreach (EnhancedPathCommand * c, m_commands)
 
350
            path += c->toString() + ' ';
 
351
        context.xmlWriter().addAttribute("draw:enhanced-path", path.trimmed());
 
352
 
 
353
        FormulaStore::const_iterator i = m_formulae.constBegin();
 
354
        for (; i != m_formulae.constEnd(); ++i) {
 
355
            context.xmlWriter().startElement("draw:equation");
 
356
            context.xmlWriter().addAttribute("draw:name", i.key());
 
357
            context.xmlWriter().addAttribute("draw:formula", i.value()->toString());
 
358
            context.xmlWriter().endElement(); // draw:equation
 
359
        }
 
360
 
 
361
        foreach (EnhancedPathHandle * handle, m_enhancedHandles)
 
362
            handle->saveOdf(context);
 
363
 
 
364
        context.xmlWriter().endElement(); // draw:enhanced-geometry
 
365
        saveOdfCommonChildElements(context);
 
366
        context.xmlWriter().endElement(); // draw:custom-shape
 
367
 
 
368
        if (m_mirrorHorizontally) {
 
369
            context.xmlWriter().addAttribute("draw:mirror-horizontal", "true");
 
370
        }
 
371
        if (m_mirrorVertically) {
 
372
            context.xmlWriter().addAttribute("draw:mirror-vertical", "true");
 
373
        }
 
374
    } else {
 
375
        KoPathShape::saveOdf(context);
 
376
    }
 
377
}
 
378
 
 
379
bool EnhancedPathShape::loadOdf(const KoXmlElement & element, KoShapeLoadingContext &context)
 
380
{
 
381
    reset();
 
382
 
 
383
    KoXmlElement child;
 
384
    forEachElement(child, element) {
 
385
        if (child.localName() == "enhanced-geometry" && child.namespaceURI() == KoXmlNS::draw) {
 
386
            // load the viewbox
 
387
            QRectF viewBox = loadOdfViewbox(child);
 
388
            if (! viewBox.isEmpty())
 
389
                m_viewBox = viewBox;
 
390
 
 
391
            // load the modifiers
 
392
            QString modifiers = child.attributeNS(KoXmlNS::draw, "modifiers", "");
 
393
            if (! modifiers.isEmpty()) {
 
394
                addModifiers(modifiers);
 
395
            }
 
396
 
 
397
            KoXmlElement grandChild;
 
398
            forEachElement(grandChild, child) {
 
399
                if (grandChild.namespaceURI() != KoXmlNS::draw)
 
400
                    continue;
 
401
                if (grandChild.localName() == "equation") {
 
402
                    QString name = grandChild.attributeNS(KoXmlNS::draw, "name");
 
403
                    QString formula = grandChild.attributeNS(KoXmlNS::draw, "formula");
 
404
                    addFormula(name, formula);
 
405
                } else if (grandChild.localName() == "handle") {
 
406
                    EnhancedPathHandle * handle = new EnhancedPathHandle(this);
 
407
                    if (handle->loadOdf(grandChild, context)) {
 
408
                        m_enhancedHandles.append(handle);
 
409
                        evaluateHandles();
 
410
                    } else {
 
411
                        delete handle;
 
412
                    }
 
413
                }
 
414
 
 
415
            }
 
416
            // load the enhanced path data
 
417
            QString path = child.attributeNS(KoXmlNS::draw, "enhanced-path", "");
 
418
#ifndef NWORKAROUND_ODF_BUGS
 
419
            KoOdfWorkaround::fixEnhancedPath(path, child, context);
 
420
#endif
 
421
            if (!path.isEmpty()) {
 
422
                parsePathData(path);
 
423
            }
 
424
 
 
425
            setMirrorHorizontally( child.attributeNS(KoXmlNS::draw, "mirror-horizontal") == "true");
 
426
            setMirrorVertically( child.attributeNS(KoXmlNS::draw, "mirror-vertical") == "true");
 
427
        }
 
428
    }
 
429
 
 
430
    QSizeF size;
 
431
    size.setWidth(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "width", QString())));
 
432
    size.setHeight(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "height", QString())));
 
433
    // the viewbox is to be fitted into the size of the shape, so before setting
 
434
    // the size we just loaded // we set the viewbox to be the basis to calculate
 
435
    // the viewbox matrix from
 
436
    m_viewBound = m_viewBox;
 
437
    setSize(size);
 
438
 
 
439
    QPointF pos;
 
440
    pos.setX(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "x", QString())));
 
441
    pos.setY(KoUnit::parseValue(element.attributeNS(KoXmlNS::svg, "y", QString())));
 
442
    setPosition(pos);
 
443
 
 
444
    loadOdfAttributes(element, context, OdfMandatories | OdfTransformation | OdfAdditionalAttributes | OdfCommonChildElements);
 
445
 
 
446
    return true;
 
447
}
 
448
 
 
449
void EnhancedPathShape::parsePathData(const QString &data)
 
450
{
 
451
    if (data.isEmpty())
 
452
        return;
 
453
 
 
454
    int start = -1;
 
455
    bool separator = true;
 
456
    for (int i = 0; i < data.length(); ++i) {
 
457
        QChar ch = data.at(i);
 
458
        if (separator && (ch.unicode() == 'M' || ch.unicode() == 'L'
 
459
            || ch.unicode() == 'C' || ch.unicode() == 'Z'
 
460
            || ch.unicode() == 'N' || ch.unicode() == 'F'
 
461
            || ch.unicode() == 'S' || ch.unicode() == 'T'
 
462
            || ch.unicode() == 'U' || ch.unicode() == 'A'
 
463
            || ch.unicode() == 'B' || ch.unicode() == 'W'
 
464
            || ch.unicode() == 'V' || ch.unicode() == 'X'
 
465
            || ch.unicode() == 'Y' || ch.unicode() == 'Q')) {
 
466
            if (start != -1) { // process last chars
 
467
                addCommand(data.mid(start, i - start));
 
468
            }
 
469
            start = i;
 
470
        }
 
471
        separator = ch.isSpace();
 
472
    }
 
473
    if (start < data.length())
 
474
        addCommand(data.mid(start));
 
475
    if (start != -1)
 
476
        updatePath(size());
 
477
}
 
478
 
 
479
void EnhancedPathShape::setMirrorHorizontally(bool mirrorHorizontally)
 
480
{
 
481
    if( m_mirrorHorizontally != mirrorHorizontally) {
 
482
        m_mirrorHorizontally = mirrorHorizontally;
 
483
    }
 
484
}
 
485
 
 
486
void EnhancedPathShape::setMirrorVertically(bool mirrorVertically)
 
487
{
 
488
    if( m_mirrorVertically != mirrorVertically) {
 
489
        m_mirrorVertically = mirrorVertically;
 
490
    }
 
491
}