1 /***************************************************************************
2  *   This file is part of the Lime Report project                          *
3  *   Copyright (C) 2015 by Alexander Arin                                  *
4  *   arin_a@bk.ru                                                          *
5  *                                                                         *
6  **                   GNU General Public License Usage                    **
7  *                                                                         *
8  *   This library is free software: you can redistribute it and/or modify  *
9  *   it under the terms of the GNU General Public License as published by  *
10  *   the Free Software Foundation, either version 3 of the License, or     *
11  *   (at your option) any later version.                                   *
12  *   You should have received a copy of the GNU General Public License     *
13  *   along with this program.  If not, see <http://www.gnu.org/licenses/>. *
14  *                                                                         *
15  **                  GNU Lesser General Public License                    **
16  *                                                                         *
17  *   This library is free software: you can redistribute it and/or modify  *
18  *   it under the terms of the GNU Lesser General Public License as        *
19  *   published by the Free Software Foundation, either version 3 of the    *
20  *   License, or (at your option) any later version.                       *
21  *   You should have received a copy of the GNU Lesser General Public      *
22  *   License along with this library.                                      *
23  *   If not, see <http://www.gnu.org/licenses/>.                           *
24  *                                                                         *
25  *   This library is distributed in the hope that it will be useful,       *
26  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
27  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
28  *   GNU General Public License for more details.                          *
29  ****************************************************************************/
30 #include <QtGui>
31 #include <QTextLayout>
32 #include <QLocale>
33 #include <QMessageBox>
34 #include <math.h>
35 
36 #include "lrpagedesignintf.h"
37 #include "lrtextitem.h"
38 #include "lrdesignelementsfactory.h"
39 #include "lrglobal.h"
40 #include "lrdatasourcemanager.h"
41 #include "lrsimpletagparser.h"
42 #include "lrtextitemeditor.h"
43 #include "lrreportengine_p.h"
44 #include <QMenu>
45 
46 namespace{
47 
48 const QString xmlTag = "TextItem";
49 
createTextItem(QObject * owner,LimeReport::BaseDesignIntf * parent)50 LimeReport::BaseDesignIntf * createTextItem(QObject* owner, LimeReport::BaseDesignIntf*  parent){
51     return new LimeReport::TextItem(owner,parent);
52 }
53 bool VARIABLE_IS_NOT_USED registred = LimeReport::DesignElementsFactory::instance().registerCreator(xmlTag, LimeReport::ItemAttribs(QObject::tr("Text Item"),"TextItem"), createTextItem);
54 
55 }
56 
57 namespace LimeReport{
58 
TextItem(QObject * owner,QGraphicsItem * parent)59 TextItem::TextItem(QObject *owner, QGraphicsItem *parent)
60     : ContentItemDesignIntf(xmlTag,owner,parent), m_angle(Angle0), m_trimValue(true), m_allowHTML(false),
61       m_allowHTMLInFields(false), m_replaceCarriageReturns(false), m_followTo(""), m_follower(0), m_textIndent(0),
62       m_textLayoutDirection(Qt::LayoutDirectionAuto), m_hideIfEmpty(false), m_fontLetterSpacing(0)
63 {
64     PageItemDesignIntf* pageItem = dynamic_cast<PageItemDesignIntf*>(parent);
65     BaseDesignIntf* parentItem = dynamic_cast<BaseDesignIntf*>(parent);
66     while (!pageItem && parentItem){
67         parentItem = dynamic_cast<BaseDesignIntf*>(parentItem->parentItem());
68         pageItem = dynamic_cast<PageItemDesignIntf*>(parentItem);
69     }
70 
71     if (pageItem){
72         QFont defaultFont = pageItem->font();
73         setFont(defaultFont);
74     }
75     Init();
76 }
77 
~TextItem()78 TextItem::~TextItem(){}
79 
fakeMarginSize() const80 int TextItem::fakeMarginSize() const{
81     return marginSize()+5;
82 }
83 
preparePopUpMenu(QMenu & menu)84 void TextItem::preparePopUpMenu(QMenu &menu)
85 {
86     QAction* editAction = menu.addAction(QIcon(":/report/images/edit_pecil2.png"),tr("Edit"));
87     menu.insertAction(menu.actions().at(0),editAction);
88     menu.insertSeparator(menu.actions().at(1));
89 
90     menu.addSeparator();
91 
92     QAction* action = menu.addAction(tr("Auto height"));
93     action->setCheckable(true);
94     action->setChecked(autoHeight());
95 
96     action = menu.addAction(tr("Allow HTML"));
97     action->setCheckable(true);
98     action->setChecked(allowHTML());
99 
100     action = menu.addAction(tr("Allow HTML in fields"));
101     action->setCheckable(true);
102     action->setChecked(allowHTMLInFields());
103 
104     action = menu.addAction(tr("Stretch to max height"));
105     action->setCheckable(true);
106     action->setChecked(stretchToMaxHeight());
107 
108     action = menu.addAction(tr("Transparent"));
109     action->setCheckable(true);
110     action->setChecked(backgroundMode() == TransparentMode);
111 
112     action = menu.addAction(tr("Watermark"));
113     action->setCheckable(true);
114     action->setChecked(isWatermark());
115 
116     action = menu.addAction(tr("Hide if empty"));
117     action->setCheckable(true);
118     action->setChecked(hideIfEmpty());
119 
120 }
121 
processPopUpAction(QAction * action)122 void TextItem::processPopUpAction(QAction *action)
123 {
124     if (action->text().compare(tr("Edit")) == 0){
125         this->showEditorDialog();
126     }
127     if (page()){
128         if (action->text().compare(tr("Auto height")) == 0){
129             page()->setPropertyToSelectedItems("autoHeight",action->isChecked());
130         }
131         if (action->text().compare(tr("Allow HTML")) == 0){
132             page()->setPropertyToSelectedItems("allowHTML",action->isChecked());
133         }
134         if (action->text().compare(tr("Allow HTML in fields")) == 0){
135             page()->setPropertyToSelectedItems("allowHTMLInFields",action->isChecked());
136         }
137         if (action->text().compare(tr("Stretch to max height")) == 0){
138             page()->setPropertyToSelectedItems("stretchToMaxHeight",action->isChecked());
139         }
140     }
141     if (action->text().compare(tr("Transparent")) == 0){
142         if (action->isChecked()){
143             setProperty("backgroundMode",TransparentMode);
144         } else {
145             setProperty("backgroundMode",OpaqueMode);
146         }
147     }
148     if (action->text().compare(tr("Watermark")) == 0){
149         page()->setPropertyToSelectedItems("watermark",action->isChecked());
150     }
151 
152     if (action->text().compare(tr("Hide if empty")) == 0){
153         page()->setPropertyToSelectedItems("hideIfEmpty",action->isChecked());
154     }
155 
156     ContentItemDesignIntf::processPopUpAction(action);
157 }
158 
paint(QPainter * painter,const QStyleOptionGraphicsItem * style,QWidget * widget)159 void TextItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* style, QWidget* widget) {
160     Q_UNUSED(widget);
161     Q_UNUSED(style);
162 
163 
164     TextPtr text = textDocument();
165 
166     painter->save();
167 
168     setupPainter(painter);
169     prepareRect(painter,style,widget);
170 
171     QSizeF tmpSize = rect().size()-text->size();
172 
173     if (!painter->clipRegion().isEmpty()){
174         QRegion clipReg=painter->clipRegion().xored(painter->clipRegion().subtracted(rect().toRect()));
175         painter->setClipRegion(clipReg);
176     } else {
177         painter->setClipRect(rect());
178     }
179 
180     qreal hOffset = 0, vOffset = 0;
181     switch (m_angle){
182         case Angle0:
183             hOffset = fakeMarginSize();
184             if ((tmpSize.height() > 0) && (m_alignment & Qt::AlignVCenter)){
185                 vOffset = tmpSize.height() / 2;
186             }
187             if ((tmpSize.height() > 0) && (m_alignment & Qt::AlignBottom)) // allow html
188                 vOffset = tmpSize.height();
189             painter->translate(hOffset,vOffset);
190         break;
191         case Angle90:
192             hOffset = width() - fakeMarginSize();
193             vOffset = fakeMarginSize();
194             if (m_alignment & Qt::AlignVCenter){
195                 hOffset = (width() - text->size().height()) / 2 + text->size().height();
196             }
197 
198             if (m_alignment & Qt::AlignBottom){
199                 hOffset = (text->size().height());
200             }
201             painter->translate(hOffset,vOffset);
202             painter->rotate(90);
203         break;
204         case Angle180:
205             hOffset = width() - fakeMarginSize();
206             vOffset = height() - fakeMarginSize();
207             if ((tmpSize.width()>0) && (m_alignment & Qt::AlignVCenter)){
208                 vOffset = tmpSize.height() / 2+ text->size().height();
209             }
210             if ((tmpSize.height()>0) && (m_alignment & Qt::AlignBottom)){
211                 vOffset = (text->size().height());
212             }
213             painter->translate(hOffset,vOffset);
214             painter->rotate(180);
215         break;
216         case Angle270:
217             hOffset = fakeMarginSize();
218             vOffset = height()-fakeMarginSize();
219             if (m_alignment & Qt::AlignVCenter){
220                 hOffset = (width() - text->size().height())/2;
221             }
222 
223             if (m_alignment & Qt::AlignBottom){
224                 hOffset = (width() - text->size().height());
225             }
226             painter->translate(hOffset,vOffset);
227             painter->rotate(270);
228         break;
229         case Angle45:
230             painter->translate(width()/2,0);
231             painter->rotate(45);
232             text->setTextWidth(sqrt(2*(pow(width()/2,2))));
233         break;
234         case Angle315:
235             painter->translate(0,height()/2);
236             painter->rotate(315);
237             text->setTextWidth(sqrt(2*(pow(height()/2,2))));
238         break;
239     }
240 
241     int lineHeight = painter->fontMetrics().height();
242     qreal curpos = 0;
243 
244     if (m_underlines){
245         QPen pen = painter->pen();
246         pen.setWidth(m_underlineLineSize);
247         painter->setPen(pen);
248     }
249 
250     painter->setOpacity(qreal(foregroundOpacity())/100);
251     QAbstractTextDocumentLayout::PaintContext ctx;
252     ctx.palette.setColor(QPalette::Text, fontColor());
253 
254     for(QTextBlock it = text->begin(); it != text->end(); it=it.next()){
255         it.blockFormat().setLineHeight(m_lineSpacing,QTextBlockFormat::LineDistanceHeight);
256         for (int i=0;i<it.layout()->lineCount();i++){
257             QTextLine line = it.layout()->lineAt(i);
258             if (m_underlines){
259                 painter->drawLine(QPointF(0,line.rect().bottomLeft().y()),QPoint(rect().width(),line.rect().bottomRight().y()));
260                 lineHeight = line.height()+m_lineSpacing;
261                 curpos = line.rect().bottom();
262             }
263         }
264     }
265 
266     text->documentLayout()->draw(painter,ctx);
267 
268     if (m_underlines){
269         if (lineHeight<0) lineHeight = painter->fontMetrics().height();
270         for (curpos+=lineHeight; curpos<rect().height();curpos+=lineHeight){
271             painter->drawLine(QPointF(0,curpos),QPoint(rect().width(),curpos));
272         }
273     }
274 
275     painter->restore();
276     BaseDesignIntf::paint(painter, style, widget);
277 }
278 
content() const279 QString TextItem::content() const{
280     return m_strText;
281 }
282 
Init()283 void TextItem::Init()
284 {
285     m_autoWidth = NoneAutoWidth;
286     m_alignment = Qt::AlignLeft|Qt::AlignTop;
287     m_autoHeight = false;
288     m_textSize = QSizeF();
289     m_firstLineSize = 0;
290     m_foregroundOpacity = 100;
291     m_underlines = false;
292     m_adaptFontToSize = false;
293     m_underlineLineSize = 1;
294     m_lineSpacing = 1;
295     m_valueType = Default;
296 }
297 
setContent(const QString & value)298 void TextItem::setContent(const QString &value)
299 {
300     if (m_strText.compare(value)!=0){
301         QString oldValue = m_strText;
302         if (m_trimValue)
303             m_strText=value.trimmed();
304         else
305             m_strText=value;
306 
307 //        if (itemMode() == DesignMode && (autoHeight())){
308 //            initTextSizes();
309 //        }
310 
311         if (!isLoading()){
312             if (autoHeight() || autoWidth() || hasFollower())
313                 initTextSizes();
314             update(rect());
315             notify("content",oldValue,value);
316         }
317     }
318 }
319 
updateItemSize(DataSourceManager * dataManager,RenderPass pass,int maxHeight)320 void TextItem::updateItemSize(DataSourceManager* dataManager, RenderPass pass, int maxHeight)
321 {
322 
323     if (isNeedExpandContent())
324         expandContent(dataManager, pass);
325 
326     if (!isLoading() && (autoHeight() || autoWidth() || hasFollower()) )
327         initTextSizes();
328 
329     if (m_textSize.width()>width() && ((m_autoWidth==MaxWordLength)||(m_autoWidth==MaxStringLength))){
330         setWidth(m_textSize.width() + fakeMarginSize()*2);
331     }
332 
333     if (m_textSize.height()>height()) {
334         if (m_autoHeight)
335             setHeight(m_textSize.height()+borderLineSize()*2);
336         else if (hasFollower() && !content().isEmpty()){
337             follower()->setContent(getTextPart(0,height()));
338             setContent(getTextPart(height(),0));
339         }
340     }
341     BaseDesignIntf::updateItemSize(dataManager, pass, maxHeight);
342     if (isEmpty() && hideIfEmpty()) setVisible(false);
343 }
344 
updateLayout()345 void TextItem::updateLayout()
346 {
347 //    m_layout.setFont(transformToSceneFont(font()));
348 //    m_layout.setText(content());
349 //    qreal linePos = 0;
350 //    m_layout.beginLayout();
351 //    while(true){
352 //        QTextLine line = m_layout.createLine();
353 //        if (!line.isValid()) break;
354 //        line.setLineWidth(width()-marginSize()*2);
355 //        line.setPosition(QPoint(marginSize(),linePos));
356 //        linePos+=line.height();
357 //    }
358 //    m_layout.endLayout();
359 }
360 
isNeedExpandContent() const361 bool TextItem::isNeedExpandContent() const
362 {
363     QRegExp rx("$*\\{[^{]*\\}");
364     return content().contains(rx) || isContentBackedUp();
365 }
366 
replaceBR(QString text) const367 QString TextItem::replaceBR(QString text) const
368 {
369     return text.replace("<br/>","\n");
370 }
371 
replaceReturns(QString text) const372 QString TextItem::replaceReturns(QString text) const
373 {
374     QString result = text.replace("\r\n","<br/>");
375     result = result.replace("\n","<br/>");
376     return result;
377 }
378 
setTextFont(TextPtr text,const QFont & value) const379 void TextItem::setTextFont(TextPtr text, const QFont& value) const {
380     text->setDefaultFont(value);
381     if ((m_angle==Angle0)||(m_angle==Angle180)){
382         text->setTextWidth(rect().width()-fakeMarginSize()*2);
383     } else {
384         text->setTextWidth(rect().height()-fakeMarginSize()*2);
385     }
386 }
387 
adaptFontSize(TextPtr text) const388 void TextItem::adaptFontSize(TextPtr text) const{
389     QFont _font = transformToSceneFont(font());
390     do{
391         setTextFont(text,_font);
392         if (_font.pixelSize()>2)
393             _font.setPixelSize(_font.pixelSize()-1);
394         else break;
395     } while(text->size().height()>this->height() || text->size().width()>(this->width()) - fakeMarginSize() * 2);
396 }
397 
underlineLineSize() const398 int TextItem::underlineLineSize() const
399 {
400     return m_underlineLineSize;
401 }
402 
setUnderlineLineSize(int value)403 void TextItem::setUnderlineLineSize(int value)
404 {
405     int oldValue = m_underlineLineSize;
406     m_underlineLineSize = value;
407     update();
408     notify("underlineLineSize",oldValue,value);
409 }
410 
lineSpacing() const411 int TextItem::lineSpacing() const
412 {
413     return m_lineSpacing;
414 }
415 
setLineSpacing(int value)416 void TextItem::setLineSpacing(int value)
417 {
418     int oldValue = m_lineSpacing;
419     m_lineSpacing = value;
420 //    if (autoHeight())
421 //        initTextSizes();
422     update();
423     notify("lineSpacing",oldValue,value);
424 }
425 
426 
initTextSizes() const427 void TextItem::initTextSizes() const
428 {
429     TextPtr text = textDocument();
430     m_textSize= text->size();
431     if (text->begin().isValid() && text->begin().layout()->lineAt(0).isValid())
432         m_firstLineSize = text->begin().layout()->lineAt(0).height();
433 }
434 
formatDateTime(const QDateTime & value)435 QString TextItem::formatDateTime(const QDateTime &value)
436 {
437     if (m_format.isEmpty())
438     {
439         return value.toString();
440     }
441 
442     return value.toString(m_format);
443 }
444 
formatNumber(const double value)445 QString TextItem::formatNumber(const double value)
446 {
447     QString str = QString::number(value);
448 
449     if (m_format.contains("%"))
450     {
451         str.sprintf(m_format.toStdString().c_str(), value);
452         str = str.replace(",", QLocale::system().groupSeparator());
453         str = str.replace(".", QLocale::system().decimalPoint());
454     }
455 
456     return str;
457 }
458 
formatFieldValue()459 QString TextItem::formatFieldValue()
460 {
461     if (m_format.isEmpty()) {
462         return m_varValue.toString();
463     }
464 
465     QVariant value = m_varValue;
466 
467     if (m_valueType != Default) {
468         switch (m_valueType) {
469         case DateTime:
470             {
471                 QDateTime dt = QDateTime::fromString(value.toString(), Qt::ISODate);
472                 value = (dt.isValid() ? QVariant(dt) : m_varValue);
473                 break;
474             }
475         case Double:
476             {
477                 bool bOk = false;
478                 double dbl = value.toDouble(&bOk);
479                 value = (bOk ? QVariant(dbl) : m_varValue);
480             }
481         default: break;
482         }
483     }
484 
485     switch (value.type()) {
486     case QVariant::Date:
487     case QVariant::DateTime:
488         return formatDateTime(value.toDateTime());
489     case QVariant::Double:
490         return formatNumber(value.toDouble());
491     default:
492         return value.toString();
493     }
494 }
495 
textDocument() const496 TextItem::TextPtr TextItem::textDocument() const
497 {
498     TextPtr text(new QTextDocument);
499 
500     if (allowHTML())
501         if (isReplaceCarriageReturns()){
502             text->setHtml(replaceReturns(m_strText));
503         } else {
504             text->setHtml(m_strText);
505         }
506     else
507         text->setPlainText(m_strText);
508 
509     QTextOption to;
510     to.setAlignment(m_alignment);
511     to.setTextDirection(m_textLayoutDirection);
512 
513     if (m_autoWidth!=MaxStringLength)
514         if (m_adaptFontToSize && (!(m_autoHeight || m_autoWidth)))
515             to.setWrapMode(QTextOption::WordWrap);
516         else
517             to.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
518     else to.setWrapMode(QTextOption::NoWrap);
519 
520     text->setDocumentMargin(0);
521     text->setDefaultTextOption(to);
522 
523     QFont _font = transformToSceneFont(font());
524     if (m_adaptFontToSize && (!(m_autoHeight || m_autoWidth))){
525         adaptFontSize(text);
526     } else {
527         setTextFont(text,_font);
528     }
529 
530     if (follower())
531         text->documentLayout();
532 
533     if (m_lineSpacing != 1 || m_textIndent !=0 ){
534 
535         for ( QTextBlock block = text->begin(); block.isValid(); block = block.next())
536         {
537             QTextCursor tc = QTextCursor(block);
538             QTextBlockFormat fmt = block.blockFormat();
539             fmt.setTextIndent(m_textIndent);
540             if (fmt.lineHeight() != m_lineSpacing) {
541                 fmt.setLineHeight(m_lineSpacing,QTextBlockFormat::LineDistanceHeight);
542             }
543             tc.setBlockFormat( fmt );
544         }
545 
546     }
547 
548     return text;
549 
550 }
551 
fontLetterSpacing() const552 int TextItem::fontLetterSpacing() const
553 {
554     return m_fontLetterSpacing;
555 }
556 
setFontLetterSpacing(int value)557 void TextItem::setFontLetterSpacing(int value)
558 {
559     if (m_fontLetterSpacing != value){
560         int oldValue = m_fontLetterSpacing;
561         m_fontLetterSpacing = value;
562         QFont curFont = font();
563         curFont.setLetterSpacing(QFont::AbsoluteSpacing, m_fontLetterSpacing);
564         setFont(curFont);
565         notify("fontLetterSpacing", oldValue, value);
566     }
567 }
568 
hideIfEmpty() const569 bool TextItem::hideIfEmpty() const
570 {
571     return m_hideIfEmpty;
572 }
573 
setHideIfEmpty(bool hideEmpty)574 void TextItem::setHideIfEmpty(bool hideEmpty)
575 {
576     if (m_hideIfEmpty != hideEmpty){
577         m_hideIfEmpty = hideEmpty;
578         notify("hideIfEmpty",!m_hideIfEmpty, m_hideIfEmpty);
579     }
580 }
581 
isReplaceCarriageReturns() const582 bool TextItem::isReplaceCarriageReturns() const
583 {
584     return m_replaceCarriageReturns;
585 }
586 
setReplaceCarriageReturns(bool replaceCarriageReturns)587 void TextItem::setReplaceCarriageReturns(bool replaceCarriageReturns)
588 {
589     if (replaceCarriageReturns != m_replaceCarriageReturns){
590         m_replaceCarriageReturns = replaceCarriageReturns;
591         update();
592         notify("replaceCRwithBR",!replaceCarriageReturns, replaceCarriageReturns);
593     }
594 
595 }
596 
textIndent() const597 qreal TextItem::textIndent() const
598 {
599     return m_textIndent;
600 }
601 
setTextIndent(const qreal & textIndent)602 void TextItem::setTextIndent(const qreal &textIndent)
603 {
604     if (m_textIndent != textIndent){
605         qreal oldValue = m_textIndent;
606         m_textIndent = textIndent;
607         update();
608         notify("textIndent", oldValue, textIndent);
609     }
610 }
611 
textLayoutDirection() const612 Qt::LayoutDirection TextItem::textLayoutDirection() const
613 {
614     return m_textLayoutDirection;
615 }
616 
setTextLayoutDirection(const Qt::LayoutDirection & textLayoutDirection)617 void TextItem::setTextLayoutDirection(const Qt::LayoutDirection &textLayoutDirection)
618 {
619     if (m_textLayoutDirection != textLayoutDirection){
620         int oldValue = int(m_textLayoutDirection);
621         m_textLayoutDirection = textLayoutDirection;
622         update();
623         notify("textLayoutDirection",oldValue,int(textLayoutDirection));
624     }
625 }
626 
setWatermark(bool watermark)627 void TextItem::setWatermark(bool watermark)
628 {
629     if (watermark){
630         setBackgroundMode(TransparentMode);
631     }
632     BaseDesignIntf::setWatermark(watermark);
633 
634 }
635 
636 
followTo() const637 QString TextItem::followTo() const
638 {
639     return m_followTo;
640 }
641 
setFollowTo(const QString & followTo)642 void TextItem::setFollowTo(const QString &followTo)
643 {
644     if (m_followTo != followTo){
645         QString oldValue = m_followTo;
646         m_followTo = followTo;
647         if (!isLoading()){
648             TextItem* fi = scene()->findChild<TextItem*>(oldValue);
649             if (fi) fi->clearFollower();
650             fi = scene()->findChild<TextItem*>(followTo);
651             if (fi && fi != this){
652                 if (initFollower(followTo)){
653                     notify("followTo",oldValue,followTo);
654                 } else {
655                     m_followTo = "";
656                     QMessageBox::critical(
657                         0,
658                         tr("Error"),
659                         tr("TextItem \" %1 \" already has folower \" %2 \" ")
660                             .arg(fi->objectName())
661                             .arg(fi->follower()->objectName())
662                     );
663                     notify("followTo",followTo,"");
664                 }
665             } else if (m_followTo != ""){
666                 QMessageBox::critical(
667                     0,
668                     tr("Error"),
669                     tr("TextItem \" %1 \" not found!")
670                         .arg(m_followTo)
671                 );
672                 notify("followTo",followTo,"");
673             }
674         }
675     }
676 }
677 
setFollower(TextItem * follower)678 void TextItem::setFollower(TextItem *follower)
679 {
680     if (!m_follower){
681         m_follower = follower;
682     }
683 }
684 
clearFollower()685 void TextItem::clearFollower()
686 {
687     m_follower = 0;
688 }
689 
hasFollower() const690 bool TextItem::hasFollower() const
691 {
692     return m_follower != 0;
693 }
694 
initFollower(QString follower)695 bool TextItem::initFollower(QString follower)
696 {
697     TextItem* fi = scene()->findChild<TextItem*>(follower);
698     if (fi){
699         if (!fi->hasFollower()){
700             fi->setFollower(this);
701             return true;
702         }
703     }
704     return false;
705 }
706 
pageObjectHasBeenLoaded()707 void TextItem::pageObjectHasBeenLoaded()
708 {
709     if (!m_followTo.isEmpty()){
710         initFollower(m_followTo);
711     }
712 }
713 
valueType() const714 TextItem::ValueType TextItem::valueType() const
715 {
716     return m_valueType;
717 }
718 
setValueType(const ValueType valueType)719 void TextItem::setValueType(const ValueType valueType)
720 {
721     m_valueType = valueType;
722 }
723 
format() const724 QString TextItem::format() const
725 {
726     return m_format;
727 }
728 
setFormat(const QString & format)729 void TextItem::setFormat(const QString &format)
730 {
731     m_format = format;
732 }
733 
allowHTMLInFields() const734 bool TextItem::allowHTMLInFields() const
735 {
736     return m_allowHTMLInFields;
737 }
738 
setAllowHTMLInFields(bool allowHTMLInFields)739 void TextItem::setAllowHTMLInFields(bool allowHTMLInFields)
740 {
741     if (m_allowHTMLInFields != allowHTMLInFields){
742         m_allowHTMLInFields = allowHTMLInFields;
743         notify("allowHTMLInFields",!m_allowHTMLInFields,allowHTMLInFields);
744         update();
745     }
746 }
747 
allowHTML() const748 bool TextItem::allowHTML() const
749 {
750     return m_allowHTML;
751 }
752 
setAllowHTML(bool allowHTML)753 void TextItem::setAllowHTML(bool allowHTML)
754 {
755     if (m_allowHTML!=allowHTML){
756         m_allowHTML = allowHTML;
757 //        if (m_text){
758 //            if (allowHTML)
759 //                m_text->setHtml(m_strText);
760 //            else
761 //                m_text->setPlainText(m_strText);
762 //            update();
763 //        }
764         update();
765         notify("allowHTML",!m_allowHTML,allowHTML);
766     }
767 }
trimValue() const768 bool TextItem::trimValue() const
769 {
770     return m_trimValue;
771 }
772 
setTrimValue(bool value)773 void TextItem::setTrimValue(bool value)
774 {
775     bool oldValue = m_trimValue;
776     m_trimValue = value;
777     notify("trimValue",oldValue,value);
778 }
779 
780 
geometryChangedEvent(QRectF,QRectF)781 void TextItem::geometryChangedEvent(QRectF , QRectF)
782 {}
783 
isNeedUpdateSize(RenderPass pass) const784 bool TextItem::isNeedUpdateSize(RenderPass pass) const
785 {
786     Q_UNUSED(pass)
787 
788     if ((autoHeight() || autoWidth()) || hasFollower()){
789         initTextSizes();
790     }
791 
792     bool res =  (m_textSize.height()>geometry().height()&&autoHeight()) ||
793                 (m_textSize.width()>geometry().width()&&autoWidth()) ||
794                  m_follower ||
795                 isNeedExpandContent();
796     return res;
797 }
798 
setAlignment(Qt::Alignment value)799 void TextItem::setAlignment(Qt::Alignment value)
800 {
801     if (m_alignment!=value){
802         Qt::Alignment oldValue = m_alignment;
803         m_alignment=value;
804         //m_layout.setTextOption(QTextOption(m_alignment));
805         if (!isLoading()){
806             update(rect());
807             notify("alignment",QVariant(oldValue),QVariant(value));
808         }
809     }
810 }
811 
expandContent(DataSourceManager * dataManager,RenderPass pass)812 void TextItem::expandContent(DataSourceManager* dataManager, RenderPass pass)
813 {
814     QString context=content();
815     foreach (QString variableName, dataManager->variableNamesByRenderPass(SecondPass)) {
816         QRegExp rx(QString(Const::NAMED_VARIABLE_RX).arg(variableName));
817         if (context.contains(rx) && pass == FirstPass){
818             backupContent();
819             break;
820         }
821     }
822 
823     ExpandType expandType = (allowHTML() && !allowHTMLInFields()) ? ReplaceHTMLSymbols : NoEscapeSymbols;
824     switch(pass){
825     case FirstPass:
826         if (!fillInSecondPass()){
827             context=expandUserVariables(context, pass, expandType, dataManager);
828             context=expandScripts(context, dataManager);
829             context=expandDataFields(context, expandType, dataManager);
830         } else {
831             context=expandDataFields(context, expandType, dataManager);
832         }
833         break;
834     case SecondPass:
835         if (isContentBackedUp()) {
836             restoreContent();
837             context = content();
838         }
839         context=expandUserVariables(context, pass, expandType, dataManager);
840         context=expandScripts(context, dataManager);
841     }
842 
843     if (expandType == NoEscapeSymbols && !m_varValue.isNull() &&m_valueType != Default) {
844         setContent(formatFieldValue());
845     } else {
846         setContent(context);
847     }
848 
849 }
850 
setAutoHeight(bool value)851 void TextItem::setAutoHeight(bool value)
852 {
853     if (m_autoHeight!=value){
854         bool oldValue = m_autoHeight;
855         m_autoHeight=value;
856         notify("autoHeight",oldValue,value);
857     }
858 }
859 
setAutoWidth(TextItem::AutoWidth value)860 void TextItem::setAutoWidth(TextItem::AutoWidth value)
861 {
862     if (m_autoWidth!=value){
863         TextItem::AutoWidth oldValue = m_autoWidth;
864         m_autoWidth=value;
865         notify("autoWidth",oldValue,value);
866     }
867 }
868 
setAdaptFontToSize(bool value)869 void TextItem::setAdaptFontToSize(bool value)
870 {
871     if (m_adaptFontToSize!=value){
872         bool oldValue = m_adaptFontToSize;
873         m_adaptFontToSize=value;
874 //        initText();
875         invalidateRect(rect());
876         notify("updateFontToSize",oldValue,value);
877     }
878 }
879 
canBeSplitted(int height) const880 bool TextItem::canBeSplitted(int height) const
881 {
882     QFontMetrics fm(font());
883     return height > m_firstLineSize;
884 }
885 
extractText(QTextBlock & curBlock,int height)886 QString TextItem::extractText(QTextBlock& curBlock, int height){
887     int curLine = 0;
888     int linesHeight = 0;
889     QString resultText;
890     for (;curBlock != curBlock.document()->end() || curLine<=curBlock.lineCount(); curBlock = curBlock.next(), curLine = 0, resultText += '\n' ){
891         linesHeight+=curBlock.blockFormat().topMargin();
892         for (;curLine < curBlock.layout()->lineCount(); curLine++){
893             linesHeight += curBlock.layout()->lineAt(curLine).height() + lineSpacing();
894             if (height > 0 && linesHeight > (height-borderLineSize() * 2)) {goto loop_exit;}
895             resultText += curBlock.text().mid(curBlock.layout()->lineAt(curLine).textStart(),
896                                                    curBlock.layout()->lineAt(curLine).textLength());
897         }
898     }
899     loop_exit: return resultText;
900 }
901 
getTextPart(int height,int skipHeight)902 QString TextItem::getTextPart(int height, int skipHeight){
903 
904     QString resultText = "";
905     TextPtr text = textDocument();
906     text->size().height();
907     QTextBlock curBlock = text->begin();
908     QTextCursor cursor(text.data());
909     cursor.movePosition(QTextCursor::Start);
910 
911     if (skipHeight > 0){
912         resultText = extractText(curBlock, skipHeight);
913         cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, resultText.length());
914     }
915 
916     resultText = extractText(curBlock, height);
917     cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, resultText.length());
918 
919     if (allowHTML()){
920         resultText = cursor.selection().toHtml();
921         resultText.remove("<!--StartFragment-->");
922         resultText.remove("<!--EndFragment-->");
923     } else {
924         resultText = cursor.selection().toPlainText();
925     }
926 
927     return resultText;
928 }
929 
restoreLinksEvent()930 void TextItem::restoreLinksEvent()
931 {
932     if (!followTo().isEmpty()){
933         BaseDesignIntf* pi = dynamic_cast<BaseDesignIntf*>(parentItem());
934         if (pi){
935             foreach (BaseDesignIntf* bi, pi->childBaseItems()) {
936                 if (bi->patternName().compare(followTo())==0){
937                     TextItem* ti = dynamic_cast<TextItem*>(bi);
938                     if (ti){
939                         ti->setFollower(this);
940                     }
941                 }
942             }
943         }
944     }
945 }
946 
cloneUpperPart(int height,QObject * owner,QGraphicsItem * parent)947 BaseDesignIntf *TextItem::cloneUpperPart(int height, QObject *owner, QGraphicsItem *parent)
948 {
949     TextItem* upperPart = dynamic_cast<TextItem*>(cloneItem(itemMode(),owner,parent));
950     upperPart->setContent(getTextPart(height,0));
951     upperPart->initTextSizes();
952     upperPart->setHeight(upperPart->textSize().height()+borderLineSize()*2);
953     return upperPart;
954 }
955 
cloneBottomPart(int height,QObject * owner,QGraphicsItem * parent)956 BaseDesignIntf *TextItem::cloneBottomPart(int height, QObject *owner, QGraphicsItem *parent)
957 {
958     TextItem* bottomPart = dynamic_cast<TextItem*>(cloneItem(itemMode(),owner,parent));
959     bottomPart->setContent(getTextPart(0,height));
960     bottomPart->initTextSizes();
961     bottomPart->setHeight(bottomPart->textSize().height()+borderLineSize()*2);
962     return bottomPart;
963 }
964 
createSameTypeItem(QObject * owner,QGraphicsItem * parent)965 BaseDesignIntf *TextItem::createSameTypeItem(QObject *owner, QGraphicsItem *parent)
966 {
967     return new TextItem(owner,parent);
968 }
969 
cloneEmpty(int height,QObject * owner,QGraphicsItem * parent)970 BaseDesignIntf *TextItem::cloneEmpty(int height, QObject *owner, QGraphicsItem *parent)
971 {
972     TextItem* empty=dynamic_cast<TextItem*>(cloneItem(itemMode(),owner,parent));
973     empty->setContent("");
974     empty->setHeight(height);
975     return empty;
976 }
977 
objectLoadFinished()978 void TextItem::objectLoadFinished()
979 {
980     ItemDesignIntf::objectLoadFinished();
981 //    if (itemMode() == DesignMode || !isNeedExpandContent()){
982 //        if (autoHeight() && autoWidth())
983 //            initTextSizes();
984 //    }
985 }
986 
setTextItemFont(QFont value)987 void TextItem::setTextItemFont(QFont value)
988 {
989     if (font()!=value){
990         QFont oldValue = font();
991         value.setLetterSpacing(QFont::AbsoluteSpacing, m_fontLetterSpacing);
992         setFont(value);
993         if (!isLoading()) update();
994         notify("font",oldValue,value);
995     }
996 }
997 
defaultEditor()998 QWidget *TextItem::defaultEditor()
999 {
1000     QSettings* l_settings = (page()->settings() != 0) ?
1001                                  page()->settings() :
1002                                  (page()->reportEditor()!=0) ? page()->reportEditor()->settings() : 0;
1003     QWidget* editor = new TextItemEditor(this,page(),l_settings);
1004     editor->setAttribute(Qt::WA_DeleteOnClose);
1005     return editor;
1006 }
1007 
setBackgroundOpacity(int value)1008 void TextItem::setBackgroundOpacity(int value)
1009 {
1010     if (opacity()!=value){
1011         int oldValue = opacity();
1012         setOpacity(value);
1013         notify("backgroundOpacity",oldValue,value);
1014     }
1015 }
1016 
setBackgroundModeProperty(BaseDesignIntf::BGMode value)1017 void TextItem::setBackgroundModeProperty(BaseDesignIntf::BGMode value)
1018 {
1019     if (value!=backgroundMode()){
1020         BaseDesignIntf::BGMode oldValue = backgroundMode();
1021         setBackgroundMode(value);
1022         notify("backgroundMode",oldValue,value);
1023     }
1024 }
1025 
setBackgroundColorProperty(QColor value)1026 void TextItem::setBackgroundColorProperty(QColor value)
1027 {
1028     if(value!=backgroundColor()){
1029         QColor oldValue = backgroundColor();
1030         setBackgroundColor(value);
1031         notify("backgroundColor",oldValue,value);
1032     }
1033 }
1034 
setFontColorProperty(QColor value)1035 void TextItem::setFontColorProperty(QColor value)
1036 {
1037     if(value!=fontColor()){
1038         QColor oldValue = fontColor();
1039         setFontColor(value);
1040         notify("fontColor",oldValue,value);
1041     }
1042 }
1043 
angle() const1044 TextItem::AngleType TextItem::angle() const
1045 {
1046     return m_angle;
1047 }
1048 
setAngle(const AngleType & value)1049 void TextItem::setAngle(const AngleType& value)
1050 {
1051     if (m_angle!=value){
1052         AngleType oldValue = m_angle;
1053         m_angle = value;
1054         if (!isLoading()){
1055             update();
1056             notify("angle",oldValue,value);
1057         }
1058     }
1059 }
1060 
setForegroundOpacity(int value)1061 void TextItem::setForegroundOpacity(int value)
1062 {
1063     if (value>100){
1064         value=100;
1065     } else if(value<0){
1066         value=0;
1067     }
1068     if (m_foregroundOpacity != value){
1069         int oldValue = m_foregroundOpacity;
1070         m_foregroundOpacity = value;
1071         update();
1072         notify("foregroundOpacity",oldValue,value);
1073     }
1074 }
1075 
setUnderlines(bool value)1076 void TextItem::setUnderlines(bool value)
1077 {
1078     if (m_underlines != value){
1079         bool oldValue = m_underlines;
1080         m_underlines = value;
1081         update();
1082         notify("underlines",oldValue,value);
1083     }
1084 }
1085 
1086 } //namespace LimeReport
1087 
1088 
1089 
1090