1 /* This file is part of the KDE project
2  * Copyright (C) 2006-2009 Thomas Zander <zander@kde.org>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public License
15  * along with this library; see the file COPYING.LIB.  If not, write to
16  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19 #include "KoVariable.h"
20 
21 #include "KoInlineObject_p.h"
22 
23 #include <KoShape.h>
24 
25 #include <QPainter>
26 #include <QFontMetricsF>
27 #include <QTextDocument>
28 #include <QTextInlineObject>
29 #include "TextDebug.h"
30 
31 class KoVariablePrivate : public KoInlineObjectPrivate
32 {
33 public:
KoVariablePrivate()34     KoVariablePrivate()
35             : modified(true),
36             document(0),
37             lastPositionInDocument(-1)
38     {
39     }
40 
printDebug(QDebug dbg) const41     QDebug printDebug(QDebug dbg) const override
42     {
43         dbg.nospace() << "KoVariable value=" << value;
44         return dbg.space();
45     }
46 
47     QString value;
48     bool modified;
49     const QTextDocument *document;
50     int lastPositionInDocument;
51 };
52 
KoVariable(bool propertyChangeListener)53 KoVariable::KoVariable(bool propertyChangeListener)
54         : KoInlineObject(*(new KoVariablePrivate()), propertyChangeListener)
55 {
56 }
57 
~KoVariable()58 KoVariable::~KoVariable()
59 {
60 }
61 
setValue(const QString & value)62 void KoVariable::setValue(const QString &value)
63 {
64     Q_D(KoVariable);
65     if (d->value == value)
66         return;
67     d->value = value;
68     d->modified = true;
69     if (d->document) {
70         const_cast<QTextDocument *>(d->document)->markContentsDirty(d->lastPositionInDocument, 0);
71     }
72 }
73 
updatePosition(const QTextDocument * document,int posInDocument,const QTextCharFormat & format)74 void KoVariable::updatePosition(const QTextDocument *document, int posInDocument, const QTextCharFormat & format)
75 {
76     Q_D(KoVariable);
77     if (d->document) {
78         disconnect(d->document, SIGNAL(destroyed()), this, SLOT(documentDestroyed()));
79     }
80     d->document = document;
81     connect(d->document, SIGNAL(destroyed()), this, SLOT(documentDestroyed()));
82     d->lastPositionInDocument = posInDocument;
83     Q_UNUSED(format);
84     // Variables are always 'in place' so the position is 100% defined by the text layout.
85     variableMoved(d->document, posInDocument);
86 }
87 
resize(const QTextDocument * document,QTextInlineObject & object,int posInDocument,const QTextCharFormat & format,QPaintDevice * pd)88 void KoVariable::resize(const QTextDocument *document, QTextInlineObject &object, int posInDocument, const QTextCharFormat &format, QPaintDevice *pd)
89 {
90     Q_D(KoVariable);
91     Q_UNUSED(document);
92     Q_UNUSED(posInDocument);
93     if (d->modified == false)
94         return;
95     if (object.isValid() == false)
96         return;
97     d->modified = true;
98     Q_ASSERT(format.isCharFormat());
99     QFontMetricsF fm(format.font(), pd);
100 
101     qreal width = qMax(qreal(0.0), fm.width(d->value));
102     qreal ascent = fm.ascent();
103     qreal descent = fm.descent();
104     if (object.width() != width) {
105         object.setWidth(width);
106     }
107     if (object.ascent() != ascent) {
108         object.setAscent(ascent);
109     }
110     if (object.descent() != descent) {
111         object.setDescent(descent);
112     }
113 }
114 
paint(QPainter & painter,QPaintDevice * pd,const QTextDocument * document,const QRectF & rect,const QTextInlineObject & object,int posInDocument,const QTextCharFormat & format)115 void KoVariable::paint(QPainter &painter, QPaintDevice *pd, const QTextDocument *document, const QRectF &rect, const QTextInlineObject &object, int posInDocument, const QTextCharFormat &format)
116 {
117     Q_D(KoVariable);
118     Q_UNUSED(document);
119     Q_UNUSED(posInDocument);
120 
121     // TODO set all the font properties from the format (color etc)
122     QFont font(format.font(), pd);
123     QTextLayout layout(d->value, font, pd);
124     layout.setCacheEnabled(true);
125     QList<QTextLayout::FormatRange> layouts;
126     QTextLayout::FormatRange range;
127     range.start = 0;
128     range.length = d->value.length();
129     range.format = format;
130     layouts.append(range);
131     layout.setAdditionalFormats(layouts);
132 
133     QTextOption option(Qt::AlignLeft | Qt::AlignAbsolute);
134     if (object.isValid()) {
135         option.setTextDirection(object.textDirection());
136     }
137     layout.setTextOption(option);
138     layout.beginLayout();
139     layout.createLine();
140     layout.endLayout();
141     layout.draw(&painter, rect.topLeft());
142 }
143 
variableMoved(const QTextDocument * document,int posInDocument)144 void KoVariable::variableMoved(const QTextDocument *document, int posInDocument)
145 {
146     Q_UNUSED(document);
147     Q_UNUSED(posInDocument);
148 }
149 
value() const150 QString KoVariable::value() const
151 {
152     Q_D(const KoVariable);
153     return d->value;
154 }
155 
positionInDocument() const156 int KoVariable::positionInDocument() const
157 {
158     Q_D(const KoVariable);
159     return d->lastPositionInDocument;
160 }
161 
documentDestroyed()162 void KoVariable::documentDestroyed()
163 {
164     // deleteLater(); does not work when closing a document as the inline object manager is deleted before the control is given back to the event loop
165     // therefore commit suicide.
166     // See http://www.parashift.com/c++-faq-lite/delete-this.html
167     delete(this);
168 }
169