File indexing completed on 2024-05-12 15:27:33

0001 /***************************************************************************
0002     File                 : TextLabel.cpp
0003     Project              : LabPlot
0004     Description          : Text label supporting reach text and latex formatting
0005     --------------------------------------------------------------------
0006     Copyright            : (C) 2009 Tilman Benkert (thzs@gmx.net)
0007     Copyright            : (C) 2012-2018 Alexander Semke (alexander.semke@web.de)
0008     Copyright            : (C) 2019 by Stefan Gerlach (stefan.gerlach@uni.kn)
0009  ***************************************************************************/
0010 
0011 /***************************************************************************
0012  *                                                                         *
0013  *  This program is free software; you can redistribute it and/or modify   *
0014  *  it under the terms of the GNU General Public License as published by   *
0015  *  the Free Software Foundation; either version 2 of the License, or      *
0016  *  (at your option) any later version.                                    *
0017  *                                                                         *
0018  *  This program is distributed in the hope that it will be useful,        *
0019  *  but WITHOUT ANY WARRANTY; without even the implied warranty of         *
0020  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
0021  *  GNU General Public License for more details.                           *
0022  *                                                                         *
0023  *   You should have received a copy of the GNU General Public License     *
0024  *   along with this program; if not, write to the Free Software           *
0025  *   Foundation, Inc., 51 Franklin Street, Fifth Floor,                    *
0026  *   Boston, MA  02110-1301  USA                                           *
0027  *                                                                         *
0028  ***************************************************************************/
0029 
0030 #include "TextLabel.h"
0031 #include "Worksheet.h"
0032 #include "TextLabelPrivate.h"
0033 #include "backend/lib/commandtemplates.h"
0034 #include "backend/lib/XmlStreamReader.h"
0035 
0036 #include <QApplication>
0037 #include <QBuffer>
0038 #include <QDesktopWidget>
0039 #include <QGraphicsScene>
0040 #include <QGraphicsSceneMouseEvent>
0041 #include <QKeyEvent>
0042 #include <QMenu>
0043 #include <QPainter>
0044 #include <QTextCharFormat>
0045 #include <QTextDocument>
0046 #include <QTextCursor>
0047 #include <QtConcurrent/QtConcurrentRun>
0048 
0049 #include <QIcon>
0050 #include <KConfig>
0051 #include <KConfigGroup>
0052 #include <KLocalizedString>
0053 
0054 /**
0055  * \class TextLabel
0056  * \brief A label supporting rendering of html- and tex-formatted texts.
0057  *
0058  * The label is aligned relative to the specified position.
0059  * The position can be either specified by providing the x- and y- coordinates
0060  * in parent's coordinate system, or by specifying one of the predefined position
0061  * flags (\c HorizontalPosition, \c VerticalPosition).
0062  */
0063 
0064 TextLabel::TextLabel(const QString& name, Type type)
0065     : WorksheetElement(name, AspectType::TextLabel), d_ptr(new TextLabelPrivate(this)), m_type(type) {
0066 
0067     init();
0068 }
0069 
0070 TextLabel::TextLabel(const QString &name, TextLabelPrivate *dd, Type type)
0071     : WorksheetElement(name, AspectType::TextLabel), d_ptr(dd), m_type(type) {
0072 
0073     init();
0074 }
0075 
0076 TextLabel::Type TextLabel::type() const {
0077     return m_type;
0078 }
0079 
0080 void TextLabel::init() {
0081     Q_D(TextLabel);
0082 
0083     QString groupName;
0084     switch (m_type) {
0085     case Type::General:
0086         groupName = "TextLabel";
0087         break;
0088     case Type::PlotTitle:
0089         groupName = "PlotTitle";
0090         break;
0091     case Type::AxisTitle:
0092         groupName = "AxisTitle";
0093         break;
0094     case Type::PlotLegendTitle:
0095         groupName = "PlotLegendTitle";
0096         break;
0097     }
0098 
0099     const KConfig config;
0100     DEBUG(" config has group \"" << STDSTRING(groupName) << "\": " << config.hasGroup(groupName));
0101     // group is always valid if you call config.group(..;
0102     KConfigGroup group;
0103     if (config.hasGroup(groupName))
0104         group = config.group(groupName);
0105 
0106     // non-default settings
0107     d->staticText.setTextFormat(Qt::RichText);
0108     // explicitly set no wrap mode for text label to avoid unnecessary line breaks
0109     QTextOption textOption;
0110     textOption.setWrapMode(QTextOption::NoWrap);
0111     d->staticText.setTextOption(textOption);
0112 
0113     if (m_type == Type::PlotTitle || m_type == Type::PlotLegendTitle) {
0114         d->position.verticalPosition = WorksheetElement::VerticalPosition::Top;
0115         d->verticalAlignment = WorksheetElement::VerticalAlignment::Bottom;
0116     } else if (m_type == Type::AxisTitle) {
0117         d->position.horizontalPosition = WorksheetElement::HorizontalPosition::Custom;
0118         d->position.verticalPosition = WorksheetElement::VerticalPosition::Custom;
0119     }
0120 
0121     // read settings from config if group exists
0122     if (group.isValid()) {
0123         //properties common to all types
0124         d->textWrapper.teXUsed = group.readEntry("TeXUsed", d->textWrapper.teXUsed);
0125         d->teXFont.setFamily(group.readEntry("TeXFontFamily", d->teXFont.family()));
0126         d->teXFont.setPointSize(group.readEntry("TeXFontSize", d->teXFont.pointSize()));
0127         d->fontColor = group.readEntry("TeXFontColor", d->fontColor);
0128         d->backgroundColor = group.readEntry("TeXBackgroundColor", d->backgroundColor);
0129         d->rotationAngle = group.readEntry("Rotation", d->rotationAngle);
0130 
0131         //border
0132         d->borderShape = (TextLabel::BorderShape)group.readEntry("BorderShape", (int)d->borderShape);
0133         d->borderPen = QPen(group.readEntry("BorderColor", d->borderPen.color()),
0134                             group.readEntry("BorderWidth", d->borderPen.width()),
0135                             (Qt::PenStyle) group.readEntry("BorderStyle", (int)(d->borderPen.style())));
0136         d->borderOpacity = group.readEntry("BorderOpacity", d->borderOpacity);
0137 
0138         //position and alignment relevant properties
0139         d->position.point.setX( group.readEntry("PositionXValue", d->position.point.x()) );
0140         d->position.point.setY( group.readEntry("PositionYValue", d->position.point.y()) );
0141         d->position.horizontalPosition = (HorizontalPosition) group.readEntry("PositionX", (int)d->position.horizontalPosition);
0142         d->position.verticalPosition = (VerticalPosition) group.readEntry("PositionY", (int)d->position.verticalPosition);
0143         d->horizontalAlignment = (WorksheetElement::HorizontalAlignment) group.readEntry("HorizontalAlignment", static_cast<int>(d->horizontalAlignment));
0144         d->verticalAlignment = (WorksheetElement::VerticalAlignment) group.readEntry("VerticalAlignment", static_cast<int>(d->verticalAlignment));
0145     }
0146 
0147     DEBUG("CHECK: default/run time image resolution: " << d->teXImageResolution << '/' << QApplication::desktop()->physicalDpiX());
0148 
0149     connect(&d->teXImageFutureWatcher, &QFutureWatcher<QImage>::finished, this, &TextLabel::updateTeXImage);
0150 }
0151 
0152 //no need to delete the d-pointer here - it inherits from QGraphicsItem
0153 //and is deleted during the cleanup in QGraphicsScene
0154 TextLabel::~TextLabel() = default;
0155 
0156 QGraphicsItem* TextLabel::graphicsItem() const {
0157     return d_ptr;
0158 }
0159 
0160 void TextLabel::setParentGraphicsItem(QGraphicsItem* item) {
0161     Q_D(TextLabel);
0162     d->setParentItem(item);
0163     d->updatePosition();
0164 }
0165 
0166 void TextLabel::retransform() {
0167     Q_D(TextLabel);
0168     d->retransform();
0169 }
0170 
0171 void TextLabel::handleResize(double horizontalRatio, double verticalRatio, bool pageResize) {
0172     DEBUG("TextLabel::handleResize()");
0173     Q_UNUSED(pageResize);
0174     Q_D(TextLabel);
0175 
0176     double ratio = 0;
0177     if (horizontalRatio > 1.0 || verticalRatio > 1.0)
0178         ratio = qMax(horizontalRatio, verticalRatio);
0179     else
0180         ratio = qMin(horizontalRatio, verticalRatio);
0181 
0182     d->teXFont.setPointSizeF(d->teXFont.pointSizeF() * ratio);
0183     d->updateText();
0184 
0185     //TODO: doesn't seem to work
0186     QTextDocument doc;
0187     doc.setHtml(d->textWrapper.text);
0188     QTextCursor cursor(&doc);
0189     cursor.select(QTextCursor::Document);
0190     QTextCharFormat fmt = cursor.charFormat();
0191     QFont font = fmt.font();
0192     font.setPointSizeF(font.pointSizeF() * ratio);
0193     fmt.setFont(font);
0194     cursor.setCharFormat(fmt);
0195 }
0196 
0197 /*!
0198     Returns an icon to be used in the project explorer.
0199 */
0200 QIcon TextLabel::icon() const{
0201     return QIcon::fromTheme("draw-text");
0202 }
0203 
0204 QMenu* TextLabel::createContextMenu() {
0205     QMenu* menu = WorksheetElement::createContextMenu();
0206     QAction* firstAction = menu->actions().at(1); //skip the first action because of the "title-action"
0207 
0208     if (!visibilityAction) {
0209         visibilityAction = new QAction(i18n("Visible"), this);
0210         visibilityAction->setCheckable(true);
0211         connect(visibilityAction, &QAction::triggered, this, &TextLabel::visibilityChanged);
0212     }
0213 
0214     visibilityAction->setChecked(isVisible());
0215     menu->insertAction(firstAction, visibilityAction);
0216     menu->insertSeparator(firstAction);
0217 
0218     return menu;
0219 }
0220 
0221 /* ============================ getter methods ================= */
0222 CLASS_SHARED_D_READER_IMPL(TextLabel, TextLabel::TextWrapper, text, textWrapper)
0223 CLASS_SHARED_D_READER_IMPL(TextLabel, QColor, fontColor, fontColor);
0224 CLASS_SHARED_D_READER_IMPL(TextLabel, QColor, backgroundColor, backgroundColor);
0225 CLASS_SHARED_D_READER_IMPL(TextLabel, QFont, teXFont, teXFont);
0226 CLASS_SHARED_D_READER_IMPL(TextLabel, TextLabel::PositionWrapper, position, position);
0227 BASIC_SHARED_D_READER_IMPL(TextLabel, WorksheetElement::HorizontalAlignment, horizontalAlignment, horizontalAlignment);
0228 BASIC_SHARED_D_READER_IMPL(TextLabel, WorksheetElement::VerticalAlignment, verticalAlignment, verticalAlignment);
0229 BASIC_SHARED_D_READER_IMPL(TextLabel, qreal, rotationAngle, rotationAngle);
0230 
0231 BASIC_SHARED_D_READER_IMPL(TextLabel, TextLabel::BorderShape, borderShape, borderShape)
0232 CLASS_SHARED_D_READER_IMPL(TextLabel, QPen, borderPen, borderPen)
0233 BASIC_SHARED_D_READER_IMPL(TextLabel, qreal, borderOpacity, borderOpacity)
0234 
0235 /* ============================ setter methods and undo commands ================= */
0236 STD_SETTER_CMD_IMPL_F_S(TextLabel, SetText, TextLabel::TextWrapper, textWrapper, updateText);
0237 void TextLabel::setText(const TextWrapper &textWrapper) {
0238     Q_D(TextLabel);
0239     if ( (textWrapper.text != d->textWrapper.text) || (textWrapper.teXUsed != d->textWrapper.teXUsed) )
0240         exec(new TextLabelSetTextCmd(d, textWrapper, ki18n("%1: set label text")));
0241 }
0242 
0243 STD_SETTER_CMD_IMPL_F_S(TextLabel, SetTeXFont, QFont, teXFont, updateText);
0244 void TextLabel::setTeXFont(const QFont& font) {
0245     Q_D(TextLabel);
0246     if (font != d->teXFont)
0247         exec(new TextLabelSetTeXFontCmd(d, font, ki18n("%1: set TeX main font")));
0248 }
0249 
0250 STD_SETTER_CMD_IMPL_F_S(TextLabel, SetTeXFontColor, QColor, fontColor, updateText);
0251 void TextLabel::setFontColor(const QColor color) {
0252     Q_D(TextLabel);
0253     if (color != d->fontColor)
0254         exec(new TextLabelSetTeXFontColorCmd(d, color, ki18n("%1: set font color")));
0255 }
0256 
0257 STD_SETTER_CMD_IMPL_F_S(TextLabel, SetTeXBackgroundColor, QColor, backgroundColor, updateText);
0258 void TextLabel::setBackgroundColor(const QColor color) {
0259     Q_D(TextLabel);
0260     if (color != d->backgroundColor)
0261         exec(new TextLabelSetTeXBackgroundColorCmd(d, color, ki18n("%1: set background color")));
0262 }
0263 
0264 STD_SETTER_CMD_IMPL_F_S(TextLabel, SetPosition, TextLabel::PositionWrapper, position, retransform);
0265 void TextLabel::setPosition(const PositionWrapper& pos) {
0266     Q_D(TextLabel);
0267     if (pos.point != d->position.point || pos.horizontalPosition != d->position.horizontalPosition || pos.verticalPosition != d->position.verticalPosition)
0268         exec(new TextLabelSetPositionCmd(d, pos, ki18n("%1: set position")));
0269 
0270 }
0271 
0272 /*!
0273     sets the position without undo/redo-stuff
0274 */
0275 void TextLabel::setPosition(QPointF point) {
0276     Q_D(TextLabel);
0277     if (point != d->position.point) {
0278         d->position.point = point;
0279         retransform();
0280     }
0281 }
0282 
0283 /*!
0284  * position is set to invalid if the parent item is not drawn on the scene
0285  * (e.g. axis is not drawn because it's outside plot ranges -> don't draw axis' title label)
0286  */
0287 void TextLabel::setPositionInvalid(bool invalid) {
0288     Q_D(TextLabel);
0289     if (invalid != d->positionInvalid)
0290         d->positionInvalid = invalid;
0291 }
0292 
0293 STD_SETTER_CMD_IMPL_F_S(TextLabel, SetRotationAngle, qreal, rotationAngle, recalcShapeAndBoundingRect);
0294 void TextLabel::setRotationAngle(qreal angle) {
0295     Q_D(TextLabel);
0296     if (angle != d->rotationAngle)
0297         exec(new TextLabelSetRotationAngleCmd(d, angle, ki18n("%1: set rotation angle")));
0298 }
0299 
0300 STD_SETTER_CMD_IMPL_F_S(TextLabel, SetHorizontalAlignment, TextLabel::HorizontalAlignment, horizontalAlignment, retransform);
0301 void TextLabel::setHorizontalAlignment(const WorksheetElement::HorizontalAlignment hAlign) {
0302     Q_D(TextLabel);
0303     if (hAlign != d->horizontalAlignment)
0304         exec(new TextLabelSetHorizontalAlignmentCmd(d, hAlign, ki18n("%1: set horizontal alignment")));
0305 }
0306 
0307 STD_SETTER_CMD_IMPL_F_S(TextLabel, SetVerticalAlignment, WorksheetElement::VerticalAlignment, verticalAlignment, retransform);
0308 void TextLabel::setVerticalAlignment(const TextLabel::VerticalAlignment vAlign) {
0309     Q_D(TextLabel);
0310     if (vAlign != d->verticalAlignment)
0311         exec(new TextLabelSetVerticalAlignmentCmd(d, vAlign, ki18n("%1: set vertical alignment")));
0312 }
0313 
0314 //Border
0315 STD_SETTER_CMD_IMPL_F_S(TextLabel, SetBorderShape, TextLabel::BorderShape, borderShape, updateBorder)
0316 void TextLabel::setBorderShape(TextLabel::BorderShape shape) {
0317     Q_D(TextLabel);
0318     if (shape != d->borderShape)
0319         exec(new TextLabelSetBorderShapeCmd(d, shape, ki18n("%1: set border shape")));
0320 }
0321 
0322 STD_SETTER_CMD_IMPL_F_S(TextLabel, SetBorderPen, QPen, borderPen, update)
0323 void TextLabel::setBorderPen(const QPen &pen) {
0324     Q_D(TextLabel);
0325     if (pen != d->borderPen)
0326         exec(new TextLabelSetBorderPenCmd(d, pen, ki18n("%1: set border")));
0327 }
0328 
0329 STD_SETTER_CMD_IMPL_F_S(TextLabel, SetBorderOpacity, qreal, borderOpacity, update)
0330 void TextLabel::setBorderOpacity(qreal opacity) {
0331     Q_D(TextLabel);
0332     if (opacity != d->borderOpacity)
0333         exec(new TextLabelSetBorderOpacityCmd(d, opacity, ki18n("%1: set border opacity")));
0334 }
0335 
0336 //misc
0337 STD_SWAP_METHOD_SETTER_CMD_IMPL_F(TextLabel, SetVisible, bool, swapVisible, retransform);
0338 void TextLabel::setVisible(bool on) {
0339     Q_D(TextLabel);
0340     exec(new TextLabelSetVisibleCmd(d, on, on ? ki18n("%1: set visible") : ki18n("%1: set invisible")));
0341 }
0342 
0343 bool TextLabel::isVisible() const {
0344     Q_D(const TextLabel);
0345     return d->isVisible();
0346 }
0347 
0348 void TextLabel::setPrinting(bool on) {
0349     Q_D(TextLabel);
0350     d->m_printing = on;
0351 }
0352 
0353 void TextLabel::updateTeXImage() {
0354     Q_D(TextLabel);
0355     d->updateTeXImage();
0356 }
0357 
0358 //##############################################################################
0359 //######  SLOTs for changes triggered via QActions in the context menu  ########
0360 //##############################################################################
0361 void TextLabel::visibilityChanged() {
0362     Q_D(const TextLabel);
0363     this->setVisible(!d->isVisible());
0364 }
0365 
0366 //##############################################################################
0367 //####################### Private implementation ###############################
0368 //##############################################################################
0369 TextLabelPrivate::TextLabelPrivate(TextLabel* owner) : q(owner) {
0370     setFlag(QGraphicsItem::ItemIsSelectable);
0371     setFlag(QGraphicsItem::ItemIsMovable);
0372     setFlag(QGraphicsItem::ItemSendsGeometryChanges);
0373     setFlag(QGraphicsItem::ItemIsFocusable);
0374     setAcceptHoverEvents(true);
0375 }
0376 
0377 QString TextLabelPrivate::name() const {
0378     return q->name();
0379 }
0380 
0381 /*!
0382     calculates the position and the bounding box of the label. Called on geometry or text changes.
0383  */
0384 void TextLabelPrivate::retransform() {
0385     if (suppressRetransform)
0386         return;
0387 
0388     if (position.horizontalPosition != WorksheetElement::HorizontalPosition::Custom
0389             || position.verticalPosition != WorksheetElement::VerticalPosition::Custom)
0390         updatePosition();
0391 
0392     double x = position.point.x();
0393     double y = position.point.y();
0394 
0395     //determine the size of the label in scene units.
0396     double w, h;
0397     if (textWrapper.teXUsed) {
0398         //image size is in pixel, convert to scene units
0399         w = teXImage.width()*teXImageScaleFactor;
0400         h = teXImage.height()*teXImageScaleFactor;
0401     }
0402     else {
0403         //size is in points, convert to scene units
0404         w = staticText.size().width()*scaleFactor;
0405         h = staticText.size().height()*scaleFactor;
0406     }
0407 
0408     //depending on the alignment, calculate the new GraphicsItem's position in parent's coordinate system
0409     QPointF itemPos;
0410     switch (horizontalAlignment) {
0411     case WorksheetElement::HorizontalAlignment::Left:
0412         itemPos.setX(x - w/2);
0413         break;
0414     case WorksheetElement::HorizontalAlignment::Center:
0415         itemPos.setX(x);
0416         break;
0417     case WorksheetElement::HorizontalAlignment::Right:
0418         itemPos.setX(x + w/2);
0419         break;
0420     }
0421 
0422     switch (verticalAlignment) {
0423     case WorksheetElement::VerticalAlignment::Top:
0424         itemPos.setY(y - h/2);
0425         break;
0426     case WorksheetElement::VerticalAlignment::Center:
0427         itemPos.setY(y);
0428         break;
0429     case WorksheetElement::VerticalAlignment::Bottom:
0430         itemPos.setY(y + h/2);
0431         break;
0432     }
0433 
0434     suppressItemChangeEvent = true;
0435     setPos(itemPos);
0436     suppressItemChangeEvent = false;
0437 
0438     boundingRectangle.setX(-w/2);
0439     boundingRectangle.setY(-h/2);
0440     boundingRectangle.setWidth(w);
0441     boundingRectangle.setHeight(h);
0442 
0443     updateBorder();
0444 
0445     emit q->changed();
0446 }
0447 
0448 /*!
0449     calculates the position of the label, when the position relative to the parent was specified (left, right, etc.)
0450 */
0451 void TextLabelPrivate::updatePosition() {
0452     //determine the parent item
0453     QRectF parentRect;
0454     QGraphicsItem* parent = parentItem();
0455     if (parent) {
0456         parentRect = parent->boundingRect();
0457     } else {
0458         if (!scene())
0459             return;
0460 
0461         parentRect = scene()->sceneRect();
0462     }
0463 
0464     if (position.horizontalPosition != WorksheetElement::HorizontalPosition::Custom) {
0465         if (position.horizontalPosition == WorksheetElement::HorizontalPosition::Left)
0466             position.point.setX( parentRect.x() );
0467         else if (position.horizontalPosition == WorksheetElement::HorizontalPosition::Center)
0468             position.point.setX( parentRect.x() + parentRect.width()/2 );
0469         else if (position.horizontalPosition == WorksheetElement::HorizontalPosition::Right)
0470             position.point.setX( parentRect.x() + parentRect.width() );
0471     }
0472 
0473     if (position.verticalPosition != WorksheetElement::VerticalPosition::Custom) {
0474         if (position.verticalPosition == WorksheetElement::VerticalPosition::Top)
0475             position.point.setY( parentRect.y() );
0476         else if (position.verticalPosition == WorksheetElement::VerticalPosition::Center)
0477             position.point.setY( parentRect.y() + parentRect.height()/2 );
0478         else if (position.verticalPosition == WorksheetElement::VerticalPosition::Bottom)
0479             position.point.setY( parentRect.y() + parentRect.height() );
0480     }
0481 
0482     emit q->positionChanged(position);
0483 }
0484 
0485 /*!
0486     updates the static text.
0487  */
0488 void TextLabelPrivate::updateText() {
0489     if (suppressRetransform)
0490         return;
0491 
0492     if (textWrapper.teXUsed) {
0493         TeXRenderer::Formatting format;
0494         format.fontColor = fontColor;
0495         format.backgroundColor = backgroundColor;
0496         format.fontSize = teXFont.pointSize();
0497         format.fontFamily = teXFont.family();
0498         format.dpi = teXImageResolution;
0499         QFuture<QImage> future = QtConcurrent::run(TeXRenderer::renderImageLaTeX, textWrapper.text, &teXRenderSuccessful, format);
0500         teXImageFutureWatcher.setFuture(future);
0501 
0502         //don't need to call retransorm() here since it is done in updateTeXImage
0503         //when the asynchronous rendering of the image is finished.
0504     } else {
0505         staticText.setText(textWrapper.text);
0506 
0507         //the size of the label was most probably changed.
0508         //call retransform() to recalculate the position and the bounding box of the label
0509         retransform();
0510     }
0511 }
0512 
0513 void TextLabelPrivate::updateTeXImage() {
0514     teXImage = teXImageFutureWatcher.result();
0515     retransform();
0516     DEBUG("teXRenderSuccessful =" << teXRenderSuccessful);
0517     emit q->teXImageUpdated(teXRenderSuccessful);
0518 }
0519 
0520 void TextLabelPrivate::updateBorder() {
0521     borderShapePath = QPainterPath();
0522     switch (borderShape) {
0523     case TextLabel::BorderShape::NoBorder:
0524         break;
0525     case TextLabel::BorderShape::Rect:
0526         borderShapePath.addRect(boundingRectangle);
0527         break;
0528     case TextLabel::BorderShape::Ellipse: {
0529         const double xs = boundingRectangle.x();
0530         const double ys = boundingRectangle.y();
0531         const double w = boundingRectangle.width();
0532         const double h = boundingRectangle.height();
0533         borderShapePath.addEllipse(xs  - 0.1 * w, ys - 0.1 * h, 1.2 * w,  1.2 * h);
0534         break;
0535     }
0536     case TextLabel::BorderShape::RoundSideRect: {
0537         const double xs = boundingRectangle.x();
0538         const double ys = boundingRectangle.y();
0539         const double w = boundingRectangle.width();
0540         const double h = boundingRectangle.height();
0541         borderShapePath.moveTo(xs, ys);
0542         borderShapePath.lineTo(xs + w, ys);
0543         borderShapePath.quadTo(xs + w + h/2, ys + h/2, xs + w, ys + h);
0544         borderShapePath.lineTo(xs, ys + h);
0545         borderShapePath.quadTo(xs - h/2, ys + h/2, xs, ys);
0546         break;
0547     }
0548     case TextLabel::BorderShape::RoundCornerRect: {
0549         const double xs = boundingRectangle.x();
0550         const double ys = boundingRectangle.y();
0551         const double w = boundingRectangle.width();
0552         const double h = boundingRectangle.height();
0553         borderShapePath.moveTo(xs + h * 0.2, ys);
0554         borderShapePath.lineTo(xs + w - h * 0.2, ys);
0555         borderShapePath.quadTo(xs + w, ys, xs + w, ys + h * 0.2);
0556         borderShapePath.lineTo(xs + w, ys + h - 0.2 * h);
0557         borderShapePath.quadTo(xs + w, ys + h, xs + w - 0.2 * h, ys + h);
0558         borderShapePath.lineTo(xs + 0.2 * h, ys + h);
0559         borderShapePath.quadTo(xs, ys + h, xs, ys + h - 0.2 * h);
0560         borderShapePath.lineTo(xs, ys + 0.2 * h);
0561         borderShapePath.quadTo(xs, ys, xs + 0.2 * h, ys);
0562         break;
0563     }
0564     case TextLabel::BorderShape::InwardsRoundCornerRect: {
0565         const double xs = boundingRectangle.x();
0566         const double ys = boundingRectangle.y();
0567         const double w = boundingRectangle.width();
0568         const double h = boundingRectangle.height();
0569         borderShapePath.moveTo(xs, ys - 0.3 * h);
0570         borderShapePath.lineTo(xs + w, ys - 0.3 * h);
0571         borderShapePath.quadTo(xs + w, ys, xs + w + 0.3 * h, ys);
0572         borderShapePath.lineTo(xs + w + 0.3 * h, ys + h);
0573         borderShapePath.quadTo(xs + w, ys + h, xs + w, ys + h + 0.3 * h);
0574         borderShapePath.lineTo(xs, ys + h + 0.3 * h);
0575         borderShapePath.quadTo(xs, ys + h, xs - 0.3 * h, ys + h);
0576         borderShapePath.lineTo(xs - 0.3 * h, ys);
0577         borderShapePath.quadTo(xs, ys, xs, ys - 0.3 * h);
0578         break;
0579     }
0580     case TextLabel::BorderShape::DentedBorderRect: {
0581         const double xs = boundingRectangle.x();
0582         const double ys = boundingRectangle.y();
0583         const double w = boundingRectangle.width();
0584         const double h = boundingRectangle.height();
0585         borderShapePath.moveTo(xs - 0.2 * h, ys - 0.2 * h);
0586         borderShapePath.quadTo(xs + w / 2, ys, xs + w + 0.2 * h, ys - 0.2 * h);
0587         borderShapePath.quadTo(xs + w, ys + h / 2, xs + w + 0.2 * h, ys + h + 0.2 * h);
0588         borderShapePath.quadTo(xs + w / 2, ys + h, xs - 0.2 * h, ys + h + 0.2 * h);
0589         borderShapePath.quadTo(xs, ys + h / 2, xs - 0.2 * h, ys - 0.2 * h);
0590         break;
0591     }
0592     case TextLabel::BorderShape::Cuboid: {
0593         const double xs = boundingRectangle.x();
0594         const double ys = boundingRectangle.y();
0595         const double w = boundingRectangle.width();
0596         const double h = boundingRectangle.height();
0597         borderShapePath.moveTo(xs, ys);
0598         borderShapePath.lineTo(xs + w, ys);
0599         borderShapePath.lineTo(xs + w, ys + h);
0600         borderShapePath.lineTo(xs, ys + h);
0601         borderShapePath.lineTo(xs, ys);
0602         borderShapePath.lineTo(xs + 0.3 * h, ys - 0.2 * h);
0603         borderShapePath.lineTo(xs + w + 0.3 * h, ys - 0.2 * h);
0604         borderShapePath.lineTo(xs + w, ys);
0605         borderShapePath.moveTo(xs + w, ys + h);
0606         borderShapePath.lineTo(xs + w + 0.3 * h, ys + h - 0.2 * h);
0607         borderShapePath.lineTo(xs + w + 0.3 * h, ys - 0.2 * h);
0608         break;
0609     }
0610     case TextLabel::BorderShape::UpPointingRectangle: {
0611         const double xs = boundingRectangle.x();
0612         const double ys = boundingRectangle.y();
0613         const double w = boundingRectangle.width();
0614         const double h = boundingRectangle.height();
0615         borderShapePath.moveTo(xs + h * 0.2, ys);
0616         borderShapePath.lineTo(xs + w / 2 - 0.2 * h, ys);
0617         borderShapePath.lineTo(xs + w / 2, ys - 0.2 * h);
0618         borderShapePath.lineTo(xs + w / 2 + 0.2 * h, ys);
0619         borderShapePath.lineTo(xs + w - h * 0.2, ys);
0620         borderShapePath.quadTo(xs + w, ys, xs + w, ys + h * 0.2);
0621         borderShapePath.lineTo(xs + w, ys + h - 0.2 * h);
0622         borderShapePath.quadTo(xs + w, ys + h, xs + w - 0.2 * h, ys + h);
0623         borderShapePath.lineTo(xs + 0.2 * h, ys + h);
0624         borderShapePath.quadTo(xs, ys + h, xs, ys + h - 0.2 * h);
0625         borderShapePath.lineTo(xs, ys + 0.2 * h);
0626         borderShapePath.quadTo(xs, ys, xs + 0.2 * h, ys);
0627         break;
0628     }
0629     case (TextLabel::BorderShape::DownPointingRectangle): {
0630         const double xs = boundingRectangle.x();
0631         const double ys = boundingRectangle.y();
0632         const double w = boundingRectangle.width();
0633         const double h = boundingRectangle.height();
0634         borderShapePath.moveTo(xs +h * 0.2, ys);
0635         borderShapePath.lineTo(xs + w - h * 0.2, ys);
0636         borderShapePath.quadTo(xs + w, ys, xs + w, ys + h * 0.2);
0637         borderShapePath.lineTo(xs + w, ys + h - 0.2 * h);
0638         borderShapePath.quadTo(xs + w, ys + h, xs + w - 0.2 * h, ys + h);
0639         borderShapePath.lineTo(xs + w / 2 + 0.2 * h, ys + h);
0640         borderShapePath.lineTo(xs + w / 2, ys + h + 0.2 * h);
0641         borderShapePath.lineTo(xs + w / 2 - 0.2 * h, ys + h);
0642         borderShapePath.lineTo(xs + 0.2 * h, ys + h);
0643         borderShapePath.quadTo(xs, ys + h, xs, ys + h - 0.2 * h);
0644         borderShapePath.lineTo(xs, ys + 0.2 * h);
0645         borderShapePath.quadTo(xs, ys, xs + 0.2 * h, ys);
0646         break;
0647     }
0648     case (TextLabel::BorderShape::LeftPointingRectangle): {
0649         const double xs = boundingRectangle.x();
0650         const double ys = boundingRectangle.y();
0651         const double w = boundingRectangle.width();
0652         const double h = boundingRectangle.height();
0653         borderShapePath.moveTo(xs + h*0.2, ys);
0654         borderShapePath.lineTo(xs + w - h * 0.2, ys);
0655         borderShapePath.quadTo(xs + w, ys, xs + w, ys + h * 0.2);
0656         borderShapePath.lineTo(xs + w,  ys + h - 0.2 * h);
0657         borderShapePath.quadTo(xs + w, ys + h, xs + w - 0.2 * h, ys + h);
0658         borderShapePath.lineTo(xs + 0.2 * h, ys + h);
0659         borderShapePath.quadTo(xs, ys + h, xs, ys + h - 0.2 * h);
0660         borderShapePath.lineTo(xs, ys + h / 2 + 0.2 * h);
0661         borderShapePath.lineTo(xs - 0.2 * h, ys + h / 2);
0662         borderShapePath.lineTo(xs, ys + h / 2 - 0.2 * h);
0663         borderShapePath.lineTo(xs, ys + 0.2 * h);
0664         borderShapePath.quadTo(xs, ys, xs + 0.2 * h, ys);
0665         break;
0666     }
0667     case (TextLabel::BorderShape::RightPointingRectangle): {
0668         const double xs = boundingRectangle.x();
0669         const double ys = boundingRectangle.y();
0670         const double w = boundingRectangle.width();
0671         const double h = boundingRectangle.height();
0672         borderShapePath.moveTo(xs + h * 0.2, ys);
0673         borderShapePath.lineTo(xs + w - h * 0.2, ys);
0674         borderShapePath.quadTo(xs + w, ys, xs + w, ys + h * 0.2);
0675         borderShapePath.lineTo(xs + w, ys + h / 2 - 0.2 * h);
0676         borderShapePath.lineTo(xs + w + 0.2 * h, ys + h / 2);
0677         borderShapePath.lineTo(xs + w, ys + h / 2 + 0.2 * h);
0678         borderShapePath.lineTo(xs + w, ys + h - 0.2 * h);
0679         borderShapePath.quadTo(xs + w, ys + h, xs + w - 0.2 * h, ys + h);
0680         borderShapePath.lineTo(xs + 0.2 * h, ys + h);
0681         borderShapePath.quadTo(xs, ys + h, xs, ys + h - 0.2 * h);
0682         borderShapePath.lineTo(xs, ys + 0.2 * h);
0683         borderShapePath.quadTo(xs, ys, xs + 0.2 * h, ys);
0684         break;
0685     }
0686     }
0687 
0688     recalcShapeAndBoundingRect();
0689 }
0690 
0691 bool TextLabelPrivate::swapVisible(bool on) {
0692     bool oldValue = isVisible();
0693 
0694     //When making a graphics item invisible, it gets deselected in the scene.
0695     //In this case we don't want to deselect the item in the project explorer.
0696     //We need to supress the deselection in the view.
0697     auto* worksheet = static_cast<Worksheet*>(q->parent(AspectType::Worksheet));
0698     worksheet->suppressSelectionChangedEvent(true);
0699     setVisible(on);
0700     worksheet->suppressSelectionChangedEvent(false);
0701 
0702     emit q->changed();
0703     emit q->visibleChanged(on);
0704     return oldValue;
0705 }
0706 
0707 /*!
0708     Returns the outer bounds of the item as a rectangle.
0709  */
0710 QRectF TextLabelPrivate::boundingRect() const {
0711     return transformedBoundingRectangle;
0712 }
0713 
0714 /*!
0715     Returns the shape of this item as a QPainterPath in local coordinates.
0716 */
0717 QPainterPath TextLabelPrivate::shape() const {
0718     return labelShape;
0719 }
0720 
0721 /*!
0722   recalculates the outer bounds and the shape of the label.
0723 */
0724 void TextLabelPrivate::recalcShapeAndBoundingRect() {
0725     prepareGeometryChange();
0726 
0727     QMatrix matrix;
0728     matrix.rotate(-rotationAngle);
0729     labelShape = QPainterPath();
0730     if (borderShape != TextLabel::BorderShape::NoBorder) {
0731         labelShape.addPath(WorksheetElement::shapeFromPath(borderShapePath, borderPen));
0732         transformedBoundingRectangle = matrix.mapRect(labelShape.boundingRect());
0733     } else {
0734         labelShape.addRect(boundingRectangle);
0735         transformedBoundingRectangle = matrix.mapRect(boundingRectangle);
0736     }
0737 
0738     labelShape = matrix.map(labelShape);
0739 }
0740 
0741 void TextLabelPrivate::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) {
0742     Q_UNUSED(option)
0743     Q_UNUSED(widget)
0744 
0745     if (positionInvalid || textWrapper.text.isEmpty())
0746         return;
0747 
0748     painter->save();
0749     painter->rotate(-rotationAngle);
0750 
0751     //draw the text
0752     if (textWrapper.teXUsed) {
0753         if (boundingRect().width() != 0.0 &&  boundingRect().height() != 0.0)
0754             painter->drawImage(boundingRect(), teXImage);
0755     } else {
0756         // don't set pen color, the color is already in the HTML code
0757         //painter->setPen(fontColor);
0758         painter->scale(scaleFactor, scaleFactor);
0759         qreal w = staticText.size().width();
0760         qreal h = staticText.size().height();
0761         //staticText.setPerformanceHint(QStaticText::AggressiveCaching);
0762         //QDEBUG(Q_FUNC_INFO << ", Drawing text:" << staticText.text())
0763         painter->drawStaticText(QPointF(-w/2., -h/2.), staticText);
0764     }
0765     painter->restore();
0766 
0767     //draw the border
0768     if (borderShape != TextLabel::BorderShape::NoBorder) {
0769         painter->save();
0770         painter->setPen(borderPen);
0771         painter->setOpacity(borderOpacity);
0772 
0773         painter->rotate(-rotationAngle);
0774         painter->drawPath(borderShapePath);
0775         painter->restore();
0776     }
0777 
0778     if (m_hovered && !isSelected() && !m_printing) {
0779         painter->setPen(QPen(QApplication::palette().color(QPalette::Shadow), 2, Qt::SolidLine));
0780         painter->drawPath(labelShape);
0781     }
0782 
0783     if (isSelected() && !m_printing) {
0784         painter->setPen(QPen(QApplication::palette().color(QPalette::Highlight), 2, Qt::SolidLine));
0785         painter->drawPath(labelShape);
0786     }
0787 }
0788 
0789 QVariant TextLabelPrivate::itemChange(GraphicsItemChange change, const QVariant &value) {
0790     if (suppressItemChangeEvent)
0791         return value;
0792 
0793     if (change == QGraphicsItem::ItemPositionChange) {
0794         //convert item's center point in parent's coordinates
0795         TextLabel::PositionWrapper tempPosition;
0796         tempPosition.point = positionFromItemPosition(value.toPointF());
0797         tempPosition.horizontalPosition = WorksheetElement::HorizontalPosition::Custom;
0798         tempPosition.verticalPosition = WorksheetElement::VerticalPosition::Custom;
0799 
0800         //emit the signals in order to notify the UI.
0801         //we don't set the position related member variables during the mouse movements.
0802         //this is done on mouse release events only.
0803         emit q->positionChanged(tempPosition);
0804     }
0805 
0806     return QGraphicsItem::itemChange(change, value);
0807 }
0808 
0809 void TextLabelPrivate::mouseReleaseEvent(QGraphicsSceneMouseEvent* event) {
0810     //convert position of the item in parent coordinates to label's position
0811     QPointF point = positionFromItemPosition(pos());
0812     if (point != position.point) {
0813         //position was changed -> set the position related member variables
0814         suppressRetransform = true;
0815         TextLabel::PositionWrapper tempPosition;
0816         tempPosition.point = point;
0817         tempPosition.horizontalPosition = WorksheetElement::HorizontalPosition::Custom;
0818         tempPosition.verticalPosition = WorksheetElement::VerticalPosition::Custom;
0819         q->setPosition(tempPosition);
0820         suppressRetransform = false;
0821     }
0822 
0823     QGraphicsItem::mouseReleaseEvent(event);
0824 }
0825 
0826 void TextLabelPrivate::keyPressEvent(QKeyEvent* event) {
0827     if (event->key() == Qt::Key_Left || event->key() == Qt::Key_Right
0828         || event->key() == Qt::Key_Up ||event->key() == Qt::Key_Down) {
0829         const int delta = 5;
0830         QPointF point = positionFromItemPosition(pos());
0831         WorksheetElement::PositionWrapper tempPosition;
0832 
0833         if (event->key() == Qt::Key_Left) {
0834             point.setX(point.x() - delta);
0835             tempPosition.horizontalPosition = WorksheetElement::HorizontalPosition::Custom;
0836             tempPosition.verticalPosition = position.verticalPosition;
0837         } else if (event->key() == Qt::Key_Right) {
0838             point.setX(point.x() + delta);
0839             tempPosition.horizontalPosition = WorksheetElement::HorizontalPosition::Custom;
0840             tempPosition.verticalPosition = position.verticalPosition;
0841         } else if (event->key() == Qt::Key_Up) {
0842             point.setY(point.y() - delta);
0843             tempPosition.horizontalPosition = position.horizontalPosition;
0844             tempPosition.verticalPosition = WorksheetElement::VerticalPosition::Custom;
0845         } else if (event->key() == Qt::Key_Down) {
0846             point.setY(point.y() + delta);
0847             tempPosition.horizontalPosition = position.horizontalPosition;
0848             tempPosition.verticalPosition = WorksheetElement::VerticalPosition::Custom;
0849         }
0850 
0851         tempPosition.point = point;
0852         q->setPosition(tempPosition);
0853     }
0854 
0855     QGraphicsItem::keyPressEvent(event);
0856 }
0857 
0858 /*!
0859  *  converts label's position to GraphicsItem's position.
0860  */
0861 QPointF TextLabelPrivate::positionFromItemPosition(QPointF itemPos) {
0862     double x = itemPos.x();
0863     double y = itemPos.y();
0864     double w, h;
0865     QPointF tmpPosition;
0866     if (textWrapper.teXUsed) {
0867         w = teXImage.width()*scaleFactor;
0868         h = teXImage.height()*scaleFactor;
0869     } else {
0870         w = staticText.size().width()*scaleFactor;
0871         h = staticText.size().height()*scaleFactor;
0872     }
0873 
0874     //depending on the alignment, calculate the new position
0875     switch (horizontalAlignment) {
0876     case WorksheetElement::HorizontalAlignment::Left:
0877         tmpPosition.setX(x + w/2);
0878         break;
0879     case WorksheetElement::HorizontalAlignment::Center:
0880         tmpPosition.setX(x);
0881         break;
0882     case WorksheetElement::HorizontalAlignment::Right:
0883         tmpPosition.setX(x - w/2);
0884         break;
0885     }
0886 
0887     switch (verticalAlignment) {
0888     case WorksheetElement::VerticalAlignment::Top:
0889         tmpPosition.setY(y + h/2);
0890         break;
0891     case WorksheetElement::VerticalAlignment::Center:
0892         tmpPosition.setY(y);
0893         break;
0894     case WorksheetElement::VerticalAlignment::Bottom:
0895         tmpPosition.setY(y - h/2);
0896         break;
0897     }
0898 
0899     return tmpPosition;
0900 }
0901 
0902 void TextLabelPrivate::contextMenuEvent(QGraphicsSceneContextMenuEvent* event) {
0903     q->createContextMenu()->exec(event->screenPos());
0904 }
0905 
0906 void TextLabelPrivate::hoverEnterEvent(QGraphicsSceneHoverEvent*) {
0907     if (!isSelected()) {
0908         m_hovered = true;
0909         emit q->hovered();
0910         update();
0911     }
0912 }
0913 
0914 void TextLabelPrivate::hoverLeaveEvent(QGraphicsSceneHoverEvent*) {
0915     if (m_hovered) {
0916         m_hovered = false;
0917         emit q->unhovered();
0918         update();
0919     }
0920 }
0921 
0922 //##############################################################################
0923 //##################  Serialization/Deserialization  ###########################
0924 //##############################################################################
0925 //! Save as XML
0926 void TextLabel::save(QXmlStreamWriter* writer) const {
0927     Q_D(const TextLabel);
0928 
0929     writer->writeStartElement( "textLabel" );
0930     writeBasicAttributes(writer);
0931     writeCommentElement(writer);
0932 
0933     //geometry
0934     writer->writeStartElement( "geometry" );
0935     writer->writeAttribute( "x", QString::number(d->position.point.x()) );
0936     writer->writeAttribute( "y", QString::number(d->position.point.y()) );
0937     writer->writeAttribute( "horizontalPosition", QString::number(static_cast<int>(d->position.horizontalPosition)) );
0938     writer->writeAttribute( "verticalPosition", QString::number(static_cast<int>(d->position.verticalPosition)) );
0939     writer->writeAttribute( "horizontalAlignment", QString::number(static_cast<int>(d->horizontalAlignment)) );
0940     writer->writeAttribute( "verticalAlignment", QString::number(static_cast<int>(d->verticalAlignment)) );
0941     writer->writeAttribute( "rotationAngle", QString::number(d->rotationAngle) );
0942     writer->writeAttribute( "visible", QString::number(d->isVisible()) );
0943     writer->writeEndElement();
0944 
0945     writer->writeStartElement( "text" );
0946     writer->writeCharacters( d->textWrapper.text );
0947     writer->writeEndElement();
0948 
0949     writer->writeStartElement( "format" );
0950     writer->writeAttribute( "teXUsed", QString::number(d->textWrapper.teXUsed) );
0951     WRITE_QFONT(d->teXFont);
0952     writer->writeAttribute( "fontColor_r", QString::number(d->fontColor.red()) );
0953     writer->writeAttribute( "fontColor_g", QString::number(d->fontColor.green()) );
0954     writer->writeAttribute( "fontColor_b", QString::number(d->fontColor.blue()) );
0955     writer->writeEndElement();
0956 
0957     //border
0958     writer->writeStartElement("border");
0959     writer->writeAttribute("borderShape", QString::number(static_cast<int>(d->borderShape)));
0960     WRITE_QPEN(d->borderPen);
0961     writer->writeAttribute("borderOpacity", QString::number(d->borderOpacity));
0962     writer->writeEndElement();
0963 
0964     if (d->textWrapper.teXUsed) {
0965         writer->writeStartElement("teXImage");
0966         QByteArray ba;
0967         QBuffer buffer(&ba);
0968         buffer.open(QIODevice::WriteOnly);
0969         d->teXImage.save(&buffer, "PNG");
0970         writer->writeCharacters(ba.toBase64());
0971         writer->writeEndElement();
0972     }
0973 
0974     writer->writeEndElement(); // close "textLabel" section
0975 }
0976 
0977 //! Load from XML
0978 bool TextLabel::load(XmlStreamReader* reader, bool preview) {
0979     if (!readBasicAttributes(reader))
0980         return false;
0981 
0982     Q_D(TextLabel);
0983     KLocalizedString attributeWarning = ki18n("Attribute '%1' missing or empty, default value is used");
0984     QXmlStreamAttributes attribs;
0985     QString str;
0986     bool teXImageFound = false;
0987 
0988     while (!reader->atEnd()) {
0989         reader->readNext();
0990         if (reader->isEndElement() && reader->name() == "textLabel")
0991             break;
0992 
0993         if (!reader->isStartElement())
0994             continue;
0995 
0996         if (!preview && reader->name() == "comment") {
0997             if (!readCommentElement(reader)) return false;
0998         } else if (!preview && reader->name() == "geometry") {
0999             attribs = reader->attributes();
1000 
1001             str = attribs.value("x").toString();
1002             if (str.isEmpty())
1003                 reader->raiseWarning(attributeWarning.subs("x").toString());
1004             else
1005                 d->position.point.setX(str.toDouble());
1006 
1007             str = attribs.value("y").toString();
1008             if (str.isEmpty())
1009                 reader->raiseWarning(attributeWarning.subs("y").toString());
1010             else
1011                 d->position.point.setY(str.toDouble());
1012 
1013             READ_INT_VALUE("horizontalPosition", position.horizontalPosition, WorksheetElement::HorizontalPosition);
1014             READ_INT_VALUE("verticalPosition", position.verticalPosition, WorksheetElement::VerticalPosition);
1015             READ_INT_VALUE("horizontalAlignment", horizontalAlignment, WorksheetElement::HorizontalAlignment);
1016             READ_INT_VALUE("verticalAlignment", verticalAlignment, WorksheetElement::VerticalAlignment);
1017             READ_DOUBLE_VALUE("rotationAngle", rotationAngle);
1018 
1019             str = attribs.value("visible").toString();
1020             if (str.isEmpty())
1021                 reader->raiseWarning(attributeWarning.subs("visible").toString());
1022             else
1023                 d->setVisible(str.toInt());
1024         } else if (!preview && reader->name() == "text") {
1025             d->textWrapper.text = reader->readElementText();
1026         } else if (!preview && reader->name() == "format") {
1027             attribs = reader->attributes();
1028 
1029             READ_INT_VALUE("teXUsed", textWrapper.teXUsed, bool);
1030             READ_QFONT(d->teXFont);
1031 
1032             str = attribs.value("fontColor_r").toString();
1033             if (str.isEmpty())
1034                 reader->raiseWarning(attributeWarning.subs("fontColor_r").toString());
1035             else
1036                 d->fontColor.setRed( str.toInt() );
1037 
1038             str = attribs.value("fontColor_g").toString();
1039             if (str.isEmpty())
1040                 reader->raiseWarning(attributeWarning.subs("fontColor_g").toString());
1041             else
1042                 d->fontColor.setGreen( str.toInt() );
1043 
1044             str = attribs.value("fontColor_b").toString();
1045             if (str.isEmpty())
1046                 reader->raiseWarning(attributeWarning.subs("fontColor_b").toString());
1047             else
1048                 d->fontColor.setBlue( str.toInt() );
1049         } else if (!preview && reader->name() == "border") {
1050             attribs = reader->attributes();
1051             READ_INT_VALUE("borderShape", borderShape, BorderShape);
1052             READ_QPEN(d->borderPen);
1053             READ_DOUBLE_VALUE("borderOpacity", borderOpacity);
1054         } else if (!preview && reader->name() == "teXImage") {
1055             reader->readNext();
1056             QString content = reader->text().toString().trimmed();
1057             QByteArray ba = QByteArray::fromBase64(content.toLatin1());
1058             teXImageFound = d->teXImage.loadFromData(ba);
1059         } else { // unknown element
1060             reader->raiseWarning(i18n("unknown element '%1'", reader->name().toString()));
1061             if (!reader->skipToEndElement()) return false;
1062         }
1063     }
1064 
1065     if (preview)
1066         return true;
1067 
1068     //in case we use latex and the image was stored (older versions of LabPlot didn't save the image)and loaded,
1069     //we just need to retransform.
1070     //otherwise, we set the static text and retransform in updateText()
1071     if ( !(d->textWrapper.teXUsed && teXImageFound) )
1072         d->updateText();
1073     else
1074         retransform();
1075 
1076     return true;
1077 }
1078 
1079 //##############################################################################
1080 //#########################  Theme management ##################################
1081 //##############################################################################
1082 void TextLabel::loadThemeConfig(const KConfig& config) {
1083     Q_D(TextLabel);
1084 
1085     KConfigGroup group = config.group("Label");
1086     d->fontColor = group.readEntry("FontColor", QColor(Qt::black)); // used when it's latex text
1087     d->backgroundColor = group.readEntry("BackgroundColor", QColor(Qt::white)); // used when it's latex text
1088 
1089     if (!d->textWrapper.teXUsed && !d->textWrapper.text.isEmpty()) {
1090         // TODO: Replace QTextEdit by QTextDocument, because this does not contain the graphical stuff
1091         // to set the color in a html text, a qTextEdit must be used
1092         QTextEdit te;
1093         te.setHtml(d->textWrapper.text);
1094         te.selectAll();
1095         te.setTextColor(d->fontColor);
1096         //te.setTextBackgroundColor(backgroundColor); // for plain text no background color supported, due to bug https://bugreports.qt.io/browse/QTBUG-25420
1097 
1098         // update the text. also in the Widget to which is connected
1099         TextWrapper wrapper(te.toHtml(), false, true);
1100         setText(wrapper);
1101     }
1102 
1103     // otherwise when changing theme while the textlabel dock is visible, the
1104     // color comboboxes do not change the color
1105     backgroundColorChanged(d->backgroundColor);
1106     fontColorChanged(d->fontColor);
1107 
1108     group = config.group("CartesianPlot");
1109     QPen pen = this->borderPen();
1110     pen.setColor(group.readEntry("BorderColor", pen.color()));
1111     pen.setStyle((Qt::PenStyle)(group.readEntry("BorderStyle", (int) pen.style())));
1112     pen.setWidthF(group.readEntry("BorderWidth", pen.widthF()));
1113     this->setBorderPen(pen);
1114     this->setBorderOpacity(group.readEntry("BorderOpacity", this->borderOpacity()));
1115 }
1116 
1117 void TextLabel::saveThemeConfig(const KConfig& config) {
1118     KConfigGroup group = config.group("Label");
1119     //TODO
1120 //  group.writeEntry("TeXFontColor", (QColor) this->fontColor());
1121 }