1 /* This file is part of the KDE project
2  * Copyright (C) 2007-2008 by Adam Pigg (adam@piggz.co.uk)
3  * Copyright (C) 2016 by Dag Andersen <danders@get2net.dk>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library 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 GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 // clazy:excludeall=qstring-arg
20 #include "PlanReportItemText.h"
21 #include "KReportRenderObjects.h"
22 
23 #include <KPropertySet>
24 
25 #include <QPrinter>
26 #include <QApplication>
27 #include <QPalette>
28 #include <QFontMetrics>
29 #include <QDomNodeList>
30 #include <QTextEdit>
31 #include <QDebug>
32 #include <QLoggingCategory>
33 
REPORTTEXT_LOG()34 const QLoggingCategory &REPORTTEXT_LOG()
35 {
36     static const QLoggingCategory category("calligra.plan.report.text");
37     return category;
38 }
39 #define debugText qCDebug(REPORTTEXT_LOG)<<QString("%1:").arg(__func__)
40 
PlanReportItemText()41 PlanReportItemText::PlanReportItemText()
42 {
43     createProperties();
44 }
45 
PlanReportItemText(const QDomNode & element)46 PlanReportItemText::PlanReportItemText(const QDomNode & element) : m_bottomPadding(0.0)
47 {
48     QDomNodeList nl = element.childNodes();
49     QString n;
50     QDomNode node;
51 
52     createProperties();
53     m_name->setValue(element.toElement().attribute(QLatin1String("report:name")));
54     m_controlSource->setValue(element.toElement().attribute(QLatin1String("report:item-data-source")));
55     m_controlSource->setOption("extraValueAllowed", QLatin1String("true"));
56     m_itemValue->setValue(element.toElement().attribute(QLatin1String("report:value")));
57     Z = element.toElement().attribute(QLatin1String("report:z-index")).toDouble();
58     m_horizontalAlignment->setValue(element.toElement().attribute(QLatin1String("report:horizontal-align")));
59     m_verticalAlignment->setValue(element.toElement().attribute(QLatin1String("report:vertical-align")));
60     m_bottomPadding = element.toElement().attribute(QLatin1String("report:bottom-padding")).toDouble();
61 
62     parseReportRect(element.toElement(), &m_pos, &m_size);
63 
64     for (int i = 0; i < nl.count(); i++) {
65         node = nl.item(i);
66         n = node.nodeName();
67 
68         if (n == QLatin1String("report:text-style")) {
69             KRTextStyleData ts;
70             if (parseReportTextStyleData(node.toElement(), &ts)) {
71                 m_backgroundColor->setValue(ts.backgroundColor);
72                 m_foregroundColor->setValue(ts.foregroundColor);
73                 m_backgroundOpacity->setValue(ts.backgroundOpacity);
74                 m_font->setValue(ts.font);
75 
76             }
77         } else if (n == QLatin1String("report:line-style")) {
78             KReportLineStyle ls;
79             if (parseReportLineStyleData(node.toElement(), &ls)) {
80                 m_lineWeight->setValue(ls.width());
81                 m_lineColor->setValue(ls.color());
82                 m_lineStyle->setValue(QPen(ls.penStyle()));
83             }
84         } else {
85             qDebug() << "while parsing field element encountered unknown element: " << n;
86         }
87     }
88 
89 }
90 
~PlanReportItemText()91 PlanReportItemText::~PlanReportItemText()
92 {
93     delete m_set;
94 }
95 
textFlags() const96 Qt::Alignment PlanReportItemText::textFlags() const
97 {
98     Qt::Alignment align;
99     QString t;
100     t = m_horizontalAlignment->value().toString();
101     if (t == QLatin1String("center"))
102         align = Qt::AlignHCenter;
103     else if (t == QLatin1String("right"))
104         align = Qt::AlignRight;
105     else
106         align = Qt::AlignLeft;
107 
108     t = m_verticalAlignment->value().toString();
109     if (t == QLatin1String("center"))
110         align |= Qt::AlignVCenter;
111     else if (t == QLatin1String("bottom"))
112         align |= Qt::AlignBottom;
113     else
114         align |= Qt::AlignTop;
115 
116     return align;
117 }
118 
createProperties()119 void PlanReportItemText::createProperties()
120 {
121     m_set = new KPropertySet;
122 
123     //connect (set, SIGNAL (propertyChanged (KPropertySet &, KProperty &)), this, SLOT (propertyChanged (KPropertySet &, KProperty &)));
124 
125     QStringList keys, strings;
126 
127     //_query = new KProperty ("Query", QStringList(), QStringList(), "Data Source", "Query");
128     m_controlSource = new KProperty("item-data-source", QStringList(), QStringList(), QString(), tr("Data Source"));
129 
130     m_itemValue = new KProperty("value", QString(), tr("Value"), tr("Value used if not bound to a field"));
131 
132     keys << QLatin1String("left") << QLatin1String("center") << QLatin1String("right");
133     strings << tr("Left") << tr("Center") << tr("Right");
134     m_horizontalAlignment = new KProperty("horizontal-align", keys, strings, QLatin1String("left"), tr("Horizontal Alignment"));
135 
136     keys.clear();
137     strings.clear();
138     keys << QLatin1String("top") << QLatin1String("center") << QLatin1String("bottom");
139     strings << tr("Top") << tr("Center") << tr("Bottom");
140     m_verticalAlignment = new KProperty("vertical-align", keys, strings, QLatin1String("center"), tr("Vertical Alignment"));
141 
142     m_font = new KProperty("font", QApplication::font(), tr("Font"));
143 
144     m_backgroundColor = new KProperty("background-color", QColor(Qt::white), tr("Background Color"));
145     m_foregroundColor = new KProperty("foreground-color", QPalette().color(QPalette::Foreground), tr("Foreground Color"));
146 
147     m_lineWeight = new KProperty("line-weight", 1, tr("Line Weight"));
148     m_lineColor = new KProperty("line-color", QColor(Qt::black), tr("Line Color"));
149     m_lineStyle = new KProperty("line-style", QPen(Qt::NoPen), tr("Line Style"), tr("Line Style"), KProperty::LineStyle);
150     m_backgroundOpacity = new KProperty("background-opacity", QVariant(0), tr("Background Opacity"));
151     m_backgroundOpacity->setOption("max", 100);
152     m_backgroundOpacity->setOption("min", 0);
153     m_backgroundOpacity->setOption("unit", QLatin1String("%"));
154 
155     addDefaultProperties();
156     m_set->addProperty(m_controlSource);
157     m_set->addProperty(m_itemValue);
158     m_set->addProperty(m_horizontalAlignment);
159     m_set->addProperty(m_verticalAlignment);
160     m_set->addProperty(m_font);
161     m_set->addProperty(m_backgroundColor);
162     m_set->addProperty(m_foregroundColor);
163     m_set->addProperty(m_backgroundOpacity);
164     m_set->addProperty(m_lineWeight);
165     m_set->addProperty(m_lineColor);
166     m_set->addProperty(m_lineStyle);
167 
168 }
169 
itemDataSource() const170 QString PlanReportItemText::itemDataSource() const
171 {
172     return m_controlSource->value().toString();
173 }
174 
bottomPadding() const175 qreal PlanReportItemText::bottomPadding() const
176 {
177     return m_bottomPadding;
178 }
179 
setBottomPadding(qreal bp)180 void PlanReportItemText::setBottomPadding(qreal bp)
181 {
182     if (m_bottomPadding != bp) {
183         m_bottomPadding = bp;
184     }
185 }
186 
textStyle() const187 KRTextStyleData PlanReportItemText::textStyle() const
188 {
189     KRTextStyleData d;
190     d.backgroundColor = m_backgroundColor->value().value<QColor>();
191     d.foregroundColor = m_foregroundColor->value().value<QColor>();
192     d.font = m_font->value().value<QFont>();
193     d.backgroundOpacity = m_backgroundOpacity->value().toInt();
194     return d;
195 }
196 
lineStyle() const197 KReportLineStyle PlanReportItemText::lineStyle() const
198 {
199     KReportLineStyle ls;
200     ls.setWidth(m_lineWeight->value().toInt());
201     ls.setColor(m_lineColor->value().value<QColor>());
202     ls.setPenStyle((Qt::PenStyle)m_lineStyle->value().toInt());
203     return ls;
204 }
205 
206 // RTTI
typeName() const207 QString PlanReportItemText::typeName() const
208 {
209     return QLatin1String("plan.text");
210 }
211 
renderSimpleData(OROPage * page,OROSection * section,const QPointF & offset,const QVariant & data,KReportScriptHandler * script)212 int PlanReportItemText::renderSimpleData(OROPage *page, OROSection *section, const QPointF &offset,
213                                        const QVariant &data, KReportScriptHandler *script)
214 
215 {
216     Q_UNUSED(script);
217 
218     QString qstrValue;
219 
220     QString cs = itemDataSource();
221 
222     if (!cs.isEmpty()) {
223         // just convert rich text to plain text for now
224         QTextEdit te;
225         te.setText(data.toString());
226         qstrValue = te.toPlainText();
227         debugText<<qstrValue;
228     } else {
229         qstrValue = m_itemValue->value().toString();
230         debugText<<"No data source:"<<qstrValue;
231     }
232 
233     QPointF pos = m_pos.toScene();
234     QSizeF size = m_size.toScene();
235     pos += offset;
236 
237     QRectF trf(pos, size);
238     qreal intStretch = trf.top() - offset.y();
239 
240     if (qstrValue.length()) {
241         QRectF rect = trf;
242 
243         int pos = 0;
244         QChar separator;
245         QRegExp re(QLatin1String("\\s"));
246         QPrinter prnt(QPrinter::HighResolution);
247         QFontMetrics fm(font(), &prnt);
248 
249         // int   intRectWidth    = (int)(trf.width() * prnt.resolution()) - 10;
250         int     intRectWidth    = (int)((m_size.toPoint().width() / 72) * prnt.resolution());
251         int     intLineCounter  = 0;
252         qreal   intBaseTop      = trf.top();
253         qreal   intRectHeight   = trf.height();
254 
255         while (qstrValue.length()) {
256             int idx = re.indexIn(qstrValue, pos);
257             if (idx == -1) {
258                 idx = qstrValue.length();
259                 separator = QLatin1Char('\n');
260             } else
261                 separator = qstrValue.at(idx);
262 
263             if (fm.boundingRect(qstrValue.left(idx)).width() < intRectWidth || pos == 0) {
264                 pos = idx + 1;
265                 if (separator == QLatin1Char('\n')) {
266                     QString line = qstrValue.left(idx);
267 
268                     qstrValue.remove(0, idx + 1);
269 
270                     pos = 0;
271 
272                     rect.setTop(intBaseTop + (intLineCounter * intRectHeight));
273                     rect.setBottom(rect.top() + intRectHeight);
274 
275                     OROTextBox * tb = new OROTextBox();
276                     tb->setPosition(rect.topLeft());
277                     tb->setSize(rect.size());
278                     tb->setFont(font());
279                     tb->setText(line);
280                     tb->setFlags(textFlags());
281                     tb->setTextStyle(textStyle());
282                     tb->setLineStyle(lineStyle());
283 
284                     if (page) {
285                         page->addPrimitive(tb);
286                     }
287 
288                     if (section) {
289                         OROTextBox *tb2 = dynamic_cast<OROTextBox*>(tb->clone());
290                         tb2->setPosition(m_pos.toPoint());
291                         section->addPrimitive(tb2);
292                     }
293 
294                     if (!page) {
295                         delete tb;
296                     }
297 
298                     intStretch += intRectHeight;
299                     intLineCounter++;
300                 }
301             } else {
302                 QString line = qstrValue.left(pos - 1);
303                 qstrValue.remove(0, pos);
304                 pos = 0;
305 
306                 rect.setTop(intBaseTop + (intLineCounter * intRectHeight));
307                 rect.setBottom(rect.top() + intRectHeight);
308 
309                 OROTextBox * tb = new OROTextBox();
310                 tb->setPosition(rect.topLeft());
311                 tb->setSize(rect.size());
312                 tb->setFont(font());
313                 tb->setText(line);
314                 tb->setFlags(textFlags());
315                 tb->setTextStyle(textStyle());
316                 tb->setLineStyle(lineStyle());
317                 if (page) {
318                     page->addPrimitive(tb);
319                 } else {
320                     delete tb;
321                 }
322 
323                 intStretch += intRectHeight;
324                 intLineCounter++;
325             }
326         }
327 
328         intStretch += (m_bottomPadding / 100.0);
329     }
330 
331     return intStretch; //Item returns its required section height
332 }
333