diff --git a/src/backend/worksheet/TextLabel.cpp b/src/backend/worksheet/TextLabel.cpp index 3e465d9aa..423fee09c 100644 --- a/src/backend/worksheet/TextLabel.cpp +++ b/src/backend/worksheet/TextLabel.cpp @@ -1,1081 +1,1061 @@ /*************************************************************************** File : TextLabel.cpp Project : LabPlot Description : Text label supporting reach text and latex formatting -------------------------------------------------------------------- Copyright : (C) 2009 Tilman Benkert (thzs@gmx.net) Copyright : (C) 2012-2018 Alexander Semke (alexander.semke@web.de) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "TextLabel.h" #include "Worksheet.h" #include "TextLabelPrivate.h" #include "backend/lib/commandtemplates.h" #include "backend/lib/XmlStreamReader.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /** * \class TextLabel * \brief A label supporting rendering of html- and tex-formatted texts. * * The label is aligned relative to the specified position. * The position can be either specified by providing the x- and y- coordinates * in parent's coordinate system, or by specifying one of the predefined position * flags (\ca HorizontalPosition, \ca VerticalPosition). */ TextLabel::TextLabel(const QString& name, Type type):WorksheetElement(name), d_ptr(new TextLabelPrivate(this)), m_type(type), visibilityAction(nullptr) { init(); } TextLabel::TextLabel(const QString &name, TextLabelPrivate *dd, Type type):WorksheetElement(name), d_ptr(dd), m_type(type), visibilityAction(nullptr) { init(); } TextLabel::Type TextLabel::type() const { return m_type; } void TextLabel::init() { Q_D(TextLabel); KConfig config; KConfigGroup group; if (m_type == AxisTitle) group = config.group("AxisTitle"); else if (m_type == PlotTitle) group = config.group("PlotTitle"); else if (m_type == PlotLegendTitle) group = config.group("PlotLegendTitle"); else group = config.group("TextLabel"); //properties common to all types d->textWrapper.teXUsed = group.readEntry("TeXUsed", false); d->teXFont.setFamily(group.readEntry("TeXFontFamily", "Computer Modern")); d->teXFont.setPointSize(group.readEntry("TeXFontSize", 12)); - d->teXFontColor = group.readEntry("TeXFontColor", QColor(Qt::black)); - d->teXBackgroundColor = group.readEntry("TeXBackgroundColor", QColor(Qt::white)); + d->fontColor = group.readEntry("TeXFontColor", QColor(Qt::black)); + d->backgroundColor = group.readEntry("TeXBackgroundColor", QColor(Qt::white)); d->rotationAngle = group.readEntry("Rotation", 0.0); d->staticText.setTextFormat(Qt::RichText); // explicitly set no wrap mode for text label to avoid unnecessary line breaks QTextOption textOption; textOption.setWrapMode(QTextOption::NoWrap); d->staticText.setTextOption(textOption); //position and alignment relevant properties, dependent on the actual type if (m_type == PlotTitle || m_type == PlotLegendTitle) { d->position.horizontalPosition = (HorizontalPosition) group.readEntry("PositionX", (int)TextLabel::hPositionCenter); d->position.verticalPosition = (VerticalPosition) group.readEntry("PositionY", (int) TextLabel::vPositionTop); d->position.point.setX( group.readEntry("PositionXValue", Worksheet::convertToSceneUnits(1, Worksheet::Centimeter)) ); d->position.point.setY( group.readEntry("PositionYValue", Worksheet::convertToSceneUnits(1, Worksheet::Centimeter)) ); d->horizontalAlignment= (TextLabel::HorizontalAlignment) group.readEntry("HorizontalAlignment", (int)TextLabel::hAlignCenter); d->verticalAlignment= (TextLabel::VerticalAlignment) group.readEntry("VerticalAlignment", (int)TextLabel::vAlignBottom); } else { d->position.horizontalPosition = (HorizontalPosition) group.readEntry("PositionX", (int)TextLabel::hPositionCustom); d->position.verticalPosition = (VerticalPosition) group.readEntry("PositionY", (int) TextLabel::vPositionCustom); d->position.point.setX( group.readEntry("PositionXValue", Worksheet::convertToSceneUnits(1, Worksheet::Centimeter)) ); d->position.point.setY( group.readEntry("PositionYValue", Worksheet::convertToSceneUnits(1, Worksheet::Centimeter)) ); d->horizontalAlignment= (TextLabel::HorizontalAlignment) group.readEntry("HorizontalAlignment", (int)TextLabel::hAlignCenter); d->verticalAlignment= (TextLabel::VerticalAlignment) group.readEntry("VerticalAlignment", (int)TextLabel::vAlignCenter); } //border d->borderShape = (TextLabel::BorderShape)group.readEntry("BorderShape", (int)TextLabel::NoBorder); d->borderPen = QPen(group.readEntry("BorderColor", QColor(Qt::black)), group.readEntry("BorderWidth", Worksheet::convertToSceneUnits(1.0, Worksheet::Point)), (Qt::PenStyle) group.readEntry("BorderStyle", (int)Qt::SolidLine)); d->borderOpacity = group.readEntry("BorderOpacity", 1.0); //scaling: //we need to scale from the font size specified in points to scene units. //furhermore, we create the tex-image in a higher resolution then usual desktop resolution // -> take this into account d->scaleFactor = Worksheet::convertToSceneUnits(1, Worksheet::Point); d->teXImageResolution = QApplication::desktop()->physicalDpiX(); d->teXImageScaleFactor = Worksheet::convertToSceneUnits(2.54/QApplication::desktop()->physicalDpiX(), Worksheet::Centimeter); connect(&d->teXImageFutureWatcher, &QFutureWatcher::finished, this, &TextLabel::updateTeXImage); } //no need to delete the d-pointer here - it inherits from QGraphicsItem //and is deleted during the cleanup in QGraphicsScene TextLabel::~TextLabel() = default; QGraphicsItem* TextLabel::graphicsItem() const { return d_ptr; } void TextLabel::setParentGraphicsItem(QGraphicsItem* item) { Q_D(TextLabel); d->setParentItem(item); d->updatePosition(); } void TextLabel::retransform() { Q_D(TextLabel); d->retransform(); } void TextLabel::handleResize(double horizontalRatio, double verticalRatio, bool pageResize) { DEBUG("TextLabel::handleResize()"); Q_UNUSED(pageResize); Q_D(TextLabel); double ratio = 0; if (horizontalRatio > 1.0 || verticalRatio > 1.0) ratio = qMax(horizontalRatio, verticalRatio); else ratio = qMin(horizontalRatio, verticalRatio); d->teXFont.setPointSizeF(d->teXFont.pointSizeF() * ratio); d->updateText(); //TODO: doesn't seem to work QTextDocument doc; doc.setHtml(d->textWrapper.text); QTextCursor cursor(&doc); cursor.select(QTextCursor::Document); QTextCharFormat fmt = cursor.charFormat(); QFont font = fmt.font(); font.setPointSizeF(font.pointSizeF() * ratio); fmt.setFont(font); cursor.setCharFormat(fmt); } /*! Returns an icon to be used in the project explorer. */ QIcon TextLabel::icon() const{ return QIcon::fromTheme("draw-text"); } QMenu* TextLabel::createContextMenu() { QMenu *menu = WorksheetElement::createContextMenu(); QAction* firstAction = menu->actions().at(1); //skip the first action because of the "title-action" if (!visibilityAction) { visibilityAction = new QAction(i18n("Visible"), this); visibilityAction->setCheckable(true); connect(visibilityAction, &QAction::triggered, this, &TextLabel::visibilityChanged); } visibilityAction->setChecked(isVisible()); menu->insertAction(firstAction, visibilityAction); menu->insertSeparator(firstAction); return menu; } /* ============================ getter methods ================= */ CLASS_SHARED_D_READER_IMPL(TextLabel, TextLabel::TextWrapper, text, textWrapper) -CLASS_SHARED_D_READER_IMPL(TextLabel, QColor, teXFontColor, teXFontColor); -CLASS_SHARED_D_READER_IMPL(TextLabel, QColor, teXBackgroundColor, teXBackgroundColor); +CLASS_SHARED_D_READER_IMPL(TextLabel, QColor, fontColor, fontColor); +CLASS_SHARED_D_READER_IMPL(TextLabel, QColor, backgroundColor, backgroundColor); CLASS_SHARED_D_READER_IMPL(TextLabel, QFont, teXFont, teXFont); CLASS_SHARED_D_READER_IMPL(TextLabel, TextLabel::PositionWrapper, position, position); BASIC_SHARED_D_READER_IMPL(TextLabel, TextLabel::HorizontalAlignment, horizontalAlignment, horizontalAlignment); BASIC_SHARED_D_READER_IMPL(TextLabel, TextLabel::VerticalAlignment, verticalAlignment, verticalAlignment); BASIC_SHARED_D_READER_IMPL(TextLabel, qreal, rotationAngle, rotationAngle); BASIC_SHARED_D_READER_IMPL(TextLabel, TextLabel::BorderShape, borderShape, borderShape) CLASS_SHARED_D_READER_IMPL(TextLabel, QPen, borderPen, borderPen) BASIC_SHARED_D_READER_IMPL(TextLabel, qreal, borderOpacity, borderOpacity) /* ============================ setter methods and undo commands ================= */ STD_SETTER_CMD_IMPL_F_S(TextLabel, SetText, TextLabel::TextWrapper, textWrapper, updateText); void TextLabel::setText(const TextWrapper &textWrapper) { Q_D(TextLabel); if ( (textWrapper.text != d->textWrapper.text) || (textWrapper.teXUsed != d->textWrapper.teXUsed) ) exec(new TextLabelSetTextCmd(d, textWrapper, ki18n("%1: set label text"))); } STD_SETTER_CMD_IMPL_F_S(TextLabel, SetTeXFont, QFont, teXFont, updateText); void TextLabel::setTeXFont(const QFont& font) { Q_D(TextLabel); if (font != d->teXFont) exec(new TextLabelSetTeXFontCmd(d, font, ki18n("%1: set TeX main font"))); } -STD_SETTER_CMD_IMPL_F_S(TextLabel, SetTeXFontColor, QColor, teXFontColor, updateText); -void TextLabel::setTeXFontColor(const QColor color) { +STD_SETTER_CMD_IMPL_F_S(TextLabel, SetTeXFontColor, QColor, fontColor, updateText); +void TextLabel::setFontColor(const QColor color) { Q_D(TextLabel); - if (color != d->teXFontColor) - exec(new TextLabelSetTeXFontColorCmd(d, color, ki18n("%1: set TeX font color"))); + if (color != d->fontColor) + exec(new TextLabelSetTeXFontColorCmd(d, color, ki18n("%1: set font color"))); } -STD_SETTER_CMD_IMPL_F_S(TextLabel, SetTeXBackgroundColor, QColor, teXBackgroundColor, updateText); -void TextLabel::setTeXBackgroundColor(const QColor color) { +STD_SETTER_CMD_IMPL_F_S(TextLabel, SetTeXBackgroundColor, QColor, backgroundColor, updateText); +void TextLabel::setBackgroundColor(const QColor color) { Q_D(TextLabel); - if (color != d->teXBackgroundColor) - exec(new TextLabelSetTeXBackgroundColorCmd(d, color, ki18n("%1: set TeX background color"))); + if (color != d->backgroundColor) + exec(new TextLabelSetTeXBackgroundColorCmd(d, color, ki18n("%1: set background color"))); } STD_SETTER_CMD_IMPL_F_S(TextLabel, SetPosition, TextLabel::PositionWrapper, position, retransform); void TextLabel::setPosition(const PositionWrapper& pos) { Q_D(TextLabel); if (pos.point!=d->position.point || pos.horizontalPosition!=d->position.horizontalPosition || pos.verticalPosition!=d->position.verticalPosition) exec(new TextLabelSetPositionCmd(d, pos, ki18n("%1: set position"))); } /*! sets the position without undo/redo-stuff */ void TextLabel::setPosition(QPointF point) { Q_D(TextLabel); if (point != d->position.point) { d->position.point = point; retransform(); } } /*! * position is set to invalid if the parent item is not drawn on the scene * (e.g. axis is not drawn because it's outside plot ranges -> don't draw axis' title label) */ void TextLabel::setPositionInvalid(bool invalid) { Q_D(TextLabel); if (invalid != d->positionInvalid) { d->positionInvalid = invalid; } } STD_SETTER_CMD_IMPL_F_S(TextLabel, SetRotationAngle, qreal, rotationAngle, recalcShapeAndBoundingRect); void TextLabel::setRotationAngle(qreal angle) { Q_D(TextLabel); if (angle != d->rotationAngle) exec(new TextLabelSetRotationAngleCmd(d, angle, ki18n("%1: set rotation angle"))); } STD_SETTER_CMD_IMPL_F_S(TextLabel, SetHorizontalAlignment, TextLabel::HorizontalAlignment, horizontalAlignment, retransform); void TextLabel::setHorizontalAlignment(const TextLabel::HorizontalAlignment hAlign) { Q_D(TextLabel); if (hAlign != d->horizontalAlignment) exec(new TextLabelSetHorizontalAlignmentCmd(d, hAlign, ki18n("%1: set horizontal alignment"))); } STD_SETTER_CMD_IMPL_F_S(TextLabel, SetVerticalAlignment, TextLabel::VerticalAlignment, verticalAlignment, retransform); void TextLabel::setVerticalAlignment(const TextLabel::VerticalAlignment vAlign) { Q_D(TextLabel); if (vAlign != d->verticalAlignment) exec(new TextLabelSetVerticalAlignmentCmd(d, vAlign, ki18n("%1: set vertical alignment"))); } //Border STD_SETTER_CMD_IMPL_F_S(TextLabel, SetBorderShape, TextLabel::BorderShape, borderShape, updateBorder) void TextLabel::setBorderShape(TextLabel::BorderShape shape) { Q_D(TextLabel); if (shape != d->borderShape) exec(new TextLabelSetBorderShapeCmd(d, shape, ki18n("%1: set border shape"))); } STD_SETTER_CMD_IMPL_F_S(TextLabel, SetBorderPen, QPen, borderPen, update) void TextLabel::setBorderPen(const QPen &pen) { Q_D(TextLabel); if (pen != d->borderPen) exec(new TextLabelSetBorderPenCmd(d, pen, ki18n("%1: set border"))); } STD_SETTER_CMD_IMPL_F_S(TextLabel, SetBorderOpacity, qreal, borderOpacity, update) void TextLabel::setBorderOpacity(qreal opacity) { Q_D(TextLabel); if (opacity != d->borderOpacity) exec(new TextLabelSetBorderOpacityCmd(d, opacity, ki18n("%1: set border opacity"))); } //misc STD_SWAP_METHOD_SETTER_CMD_IMPL_F(TextLabel, SetVisible, bool, swapVisible, retransform); void TextLabel::setVisible(bool on) { Q_D(TextLabel); exec(new TextLabelSetVisibleCmd(d, on, on ? ki18n("%1: set visible") : ki18n("%1: set invisible"))); } bool TextLabel::isVisible() const { Q_D(const TextLabel); return d->isVisible(); } void TextLabel::setPrinting(bool on) { Q_D(TextLabel); d->m_printing = on; } void TextLabel::updateTeXImage() { Q_D(TextLabel); d->updateTeXImage(); } //############################################################################## //###### SLOTs for changes triggered via QActions in the context menu ######## //############################################################################## void TextLabel::visibilityChanged() { Q_D(const TextLabel); this->setVisible(!d->isVisible()); } //############################################################################## //####################### Private implementation ############################### //############################################################################## TextLabelPrivate::TextLabelPrivate(TextLabel* owner) : teXRenderSuccessful(false), positionInvalid(false), suppressItemChangeEvent(false), suppressRetransform(false), m_printing(false), m_hovered(false), q(owner) { setFlag(QGraphicsItem::ItemIsSelectable); setFlag(QGraphicsItem::ItemIsMovable); setFlag(QGraphicsItem::ItemSendsGeometryChanges); setAcceptHoverEvents(true); } QString TextLabelPrivate::name() const { return q->name(); } /*! calculates the position and the bounding box of the label. Called on geometry or text changes. */ void TextLabelPrivate::retransform() { if (suppressRetransform) return; if (position.horizontalPosition != TextLabel::hPositionCustom || position.verticalPosition != TextLabel::vPositionCustom) updatePosition(); float x = position.point.x(); float y = position.point.y(); //determine the size of the label in scene units. float w, h; if (textWrapper.teXUsed) { //image size is in pixel, convert to scene units w = teXImage.width()*teXImageScaleFactor; h = teXImage.height()*teXImageScaleFactor; } else { //size is in points, convert to scene units w = staticText.size().width()*scaleFactor; h = staticText.size().height()*scaleFactor; } //depending on the alignment, calculate the new GraphicsItem's position in parent's coordinate system QPointF itemPos; switch (horizontalAlignment) { case TextLabel::hAlignLeft: itemPos.setX(x - w/2); break; case TextLabel::hAlignCenter: itemPos.setX(x); break; case TextLabel::hAlignRight: itemPos.setX(x + w/2); break; } switch (verticalAlignment) { case TextLabel::vAlignTop: itemPos.setY(y - h/2); break; case TextLabel::vAlignCenter: itemPos.setY(y); break; case TextLabel::vAlignBottom: itemPos.setY(y + h/2); break; } suppressItemChangeEvent = true; setPos(itemPos); suppressItemChangeEvent = false; boundingRectangle.setX(-w/2); boundingRectangle.setY(-h/2); boundingRectangle.setWidth(w); boundingRectangle.setHeight(h); updateBorder(); } /*! calculates the position of the label, when the position relative to the parent was specified (left, right, etc.) */ void TextLabelPrivate::updatePosition() { //determine the parent item QRectF parentRect; QGraphicsItem* parent = parentItem(); if (parent) { parentRect = parent->boundingRect(); } else { if (!scene()) return; parentRect = scene()->sceneRect(); } if (position.horizontalPosition != TextLabel::hPositionCustom) { if (position.horizontalPosition == TextLabel::hPositionLeft) position.point.setX( parentRect.x() ); else if (position.horizontalPosition == TextLabel::hPositionCenter) position.point.setX( parentRect.x() + parentRect.width()/2 ); else if (position.horizontalPosition == TextLabel::hPositionRight) position.point.setX( parentRect.x() + parentRect.width() ); } if (position.verticalPosition != TextLabel::vPositionCustom) { if (position.verticalPosition == TextLabel::vPositionTop) position.point.setY( parentRect.y() ); else if (position.verticalPosition == TextLabel::vPositionCenter) position.point.setY( parentRect.y() + parentRect.height()/2 ); else if (position.verticalPosition == TextLabel::vPositionBottom) position.point.setY( parentRect.y() + parentRect.height() ); } emit q->positionChanged(position); } /*! updates the static text. */ void TextLabelPrivate::updateText() { if (suppressRetransform) return; if (textWrapper.teXUsed) { TeXRenderer::Formatting format; - format.fontColor = teXFontColor; - format.backgroundColor = teXBackgroundColor; + format.fontColor = fontColor; + format.backgroundColor = backgroundColor; format.fontSize = teXFont.pointSize(); format.fontFamily = teXFont.family(); format.dpi = teXImageResolution; QFuture future = QtConcurrent::run(TeXRenderer::renderImageLaTeX, textWrapper.text, &teXRenderSuccessful, format); teXImageFutureWatcher.setFuture(future); //don't need to call retransorm() here since it is done in updateTeXImage //when the asynchronous rendering of the image is finished. } else { staticText.setText(textWrapper.text); //the size of the label was most probably changed. //call retransform() to recalculate the position and the bounding box of the label retransform(); } } void TextLabelPrivate::updateTeXImage() { teXImage = teXImageFutureWatcher.result(); retransform(); DEBUG("teXRenderSuccessful =" << teXRenderSuccessful); emit q->teXImageUpdated(teXRenderSuccessful); } void TextLabelPrivate::updateBorder() { borderShapePath = QPainterPath(); switch (borderShape) { case (TextLabel::NoBorder): break; case (TextLabel::BorderShape::Rect): { borderShapePath.addRect(boundingRectangle); break; } case (TextLabel::BorderShape::Ellipse): { const double xs = boundingRectangle.x(); const double ys = boundingRectangle.y(); const double w = boundingRectangle.width(); const double h = boundingRectangle.height(); borderShapePath.addEllipse(xs - 0.1 * w, ys - 0.1 * h, 1.2 * w, 1.2 * h); break; } case (TextLabel::BorderShape::RoundSideRect): { const double xs = boundingRectangle.x(); const double ys = boundingRectangle.y(); const double w = boundingRectangle.width(); const double h = boundingRectangle.height(); borderShapePath.moveTo(xs, ys); borderShapePath.lineTo(xs + w, ys); borderShapePath.quadTo(xs + w + h/2, ys + h/2, xs + w, ys + h); borderShapePath.lineTo(xs, ys + h); borderShapePath.quadTo(xs - h/2, ys + h/2, xs, ys); break; } case (TextLabel::BorderShape::RoundCornerRect): { const double xs = boundingRectangle.x(); const double ys = boundingRectangle.y(); const double w = boundingRectangle.width(); const double h = boundingRectangle.height(); borderShapePath.moveTo(xs + h * 0.2, ys); borderShapePath.lineTo(xs + w - h * 0.2, ys); borderShapePath.quadTo(xs + w, ys, xs + w, ys + h * 0.2); borderShapePath.lineTo(xs + w, ys + h - 0.2 * h); borderShapePath.quadTo(xs + w, ys + h, xs + w - 0.2 * h, ys + h); borderShapePath.lineTo(xs + 0.2 * h, ys + h); borderShapePath.quadTo(xs, ys + h, xs, ys + h - 0.2 * h); borderShapePath.lineTo(xs, ys + 0.2 * h); borderShapePath.quadTo(xs, ys, xs + 0.2 * h, ys); break; } case (TextLabel::BorderShape::InwardsRoundCornerRect): { const double xs = boundingRectangle.x(); const double ys = boundingRectangle.y(); const double w = boundingRectangle.width(); const double h = boundingRectangle.height(); borderShapePath.moveTo(xs, ys - 0.3 * h); borderShapePath.lineTo(xs + w, ys - 0.3 * h); borderShapePath.quadTo(xs + w, ys, xs + w + 0.3 * h, ys); borderShapePath.lineTo(xs + w + 0.3 * h, ys + h); borderShapePath.quadTo(xs + w, ys + h, xs + w, ys + h + 0.3 * h); borderShapePath.lineTo(xs, ys + h + 0.3 * h); borderShapePath.quadTo(xs, ys + h, xs - 0.3 * h, ys + h); borderShapePath.lineTo(xs - 0.3 * h, ys); borderShapePath.quadTo(xs, ys, xs, ys - 0.3 * h); break; } case (TextLabel::BorderShape::DentedBorderRect): { const double xs = boundingRectangle.x(); const double ys = boundingRectangle.y(); const double w = boundingRectangle.width(); const double h = boundingRectangle.height(); borderShapePath.moveTo(xs - 0.2 * h, ys - 0.2 * h); borderShapePath.quadTo(xs + w / 2, ys, xs + w + 0.2 * h, ys - 0.2 * h); borderShapePath.quadTo(xs + w, ys + h / 2, xs + w + 0.2 * h, ys + h + 0.2 * h); borderShapePath.quadTo(xs + w / 2, ys + h, xs - 0.2 * h, ys + h + 0.2 * h); borderShapePath.quadTo(xs, ys + h / 2, xs - 0.2 * h, ys - 0.2 * h); break; } case (TextLabel::BorderShape::Cuboid): { const double xs = boundingRectangle.x(); const double ys = boundingRectangle.y(); const double w = boundingRectangle.width(); const double h = boundingRectangle.height(); borderShapePath.moveTo(xs, ys); borderShapePath.lineTo(xs + w, ys); borderShapePath.lineTo(xs + w, ys + h); borderShapePath.lineTo(xs, ys + h); borderShapePath.lineTo(xs, ys); borderShapePath.lineTo(xs + 0.3 * h, ys - 0.2 * h); borderShapePath.lineTo(xs + w + 0.3 * h, ys - 0.2 * h); borderShapePath.lineTo(xs + w, ys); borderShapePath.moveTo(xs + w, ys + h); borderShapePath.lineTo(xs + w + 0.3 * h, ys + h - 0.2 * h); borderShapePath.lineTo(xs + w + 0.3 * h, ys - 0.2 * h); break; } case (TextLabel::BorderShape::UpPointingRectangle): { const double xs = boundingRectangle.x(); const double ys = boundingRectangle.y(); const double w = boundingRectangle.width(); const double h = boundingRectangle.height(); borderShapePath.moveTo(xs + h * 0.2, ys); borderShapePath.lineTo(xs + w / 2 - 0.2 * h, ys); borderShapePath.lineTo(xs + w / 2, ys - 0.2 * h); borderShapePath.lineTo(xs + w / 2 + 0.2 * h, ys); borderShapePath.lineTo(xs + w - h * 0.2, ys); borderShapePath.quadTo(xs + w, ys, xs + w, ys + h * 0.2); borderShapePath.lineTo(xs + w, ys + h - 0.2 * h); borderShapePath.quadTo(xs + w, ys + h, xs + w - 0.2 * h, ys + h); borderShapePath.lineTo(xs + 0.2 * h, ys + h); borderShapePath.quadTo(xs, ys + h, xs, ys + h - 0.2 * h); borderShapePath.lineTo(xs, ys + 0.2 * h); borderShapePath.quadTo(xs, ys, xs + 0.2 * h, ys); break; } case (TextLabel::BorderShape::DownPointingRectangle): { const double xs = boundingRectangle.x(); const double ys = boundingRectangle.y(); const double w = boundingRectangle.width(); const double h = boundingRectangle.height(); borderShapePath.moveTo(xs +h * 0.2, ys); borderShapePath.lineTo(xs + w - h * 0.2, ys); borderShapePath.quadTo(xs + w, ys, xs + w, ys + h * 0.2); borderShapePath.lineTo(xs + w, ys + h - 0.2 * h); borderShapePath.quadTo(xs + w, ys + h, xs + w - 0.2 * h, ys + h); borderShapePath.lineTo(xs + w / 2 + 0.2 * h, ys + h); borderShapePath.lineTo(xs + w / 2, ys + h + 0.2 * h); borderShapePath.lineTo(xs + w / 2 - 0.2 * h, ys + h); borderShapePath.lineTo(xs + 0.2 * h, ys + h); borderShapePath.quadTo(xs, ys + h, xs, ys + h - 0.2 * h); borderShapePath.lineTo(xs, ys + 0.2 * h); borderShapePath.quadTo(xs, ys, xs + 0.2 * h, ys); break; } case (TextLabel::BorderShape::LeftPointingRectangle): { const double xs = boundingRectangle.x(); const double ys = boundingRectangle.y(); const double w = boundingRectangle.width(); const double h = boundingRectangle.height(); borderShapePath.moveTo(xs + h*0.2, ys); borderShapePath.lineTo(xs + w - h * 0.2, ys); borderShapePath.quadTo(xs + w, ys, xs + w, ys + h * 0.2); borderShapePath.lineTo(xs + w, ys + h - 0.2 * h); borderShapePath.quadTo(xs + w, ys + h, xs + w - 0.2 * h, ys + h); borderShapePath.lineTo(xs + 0.2 * h, ys + h); borderShapePath.quadTo(xs, ys + h, xs, ys + h - 0.2 * h); borderShapePath.lineTo(xs, ys + h / 2 + 0.2 * h); borderShapePath.lineTo(xs - 0.2 * h, ys + h / 2); borderShapePath.lineTo(xs, ys + h / 2 - 0.2 * h); borderShapePath.lineTo(xs, ys + 0.2 * h); borderShapePath.quadTo(xs, ys, xs + 0.2 * h, ys); break; } case (TextLabel::BorderShape::RightPointingRectangle): { const double xs = boundingRectangle.x(); const double ys = boundingRectangle.y(); const double w = boundingRectangle.width(); const double h = boundingRectangle.height(); borderShapePath.moveTo(xs + h * 0.2, ys); borderShapePath.lineTo(xs + w - h * 0.2, ys); borderShapePath.quadTo(xs + w, ys, xs + w, ys + h * 0.2); borderShapePath.lineTo(xs + w, ys + h / 2 - 0.2 * h); borderShapePath.lineTo(xs + w + 0.2 * h, ys + h / 2); borderShapePath.lineTo(xs + w, ys + h / 2 + 0.2 * h); borderShapePath.lineTo(xs + w, ys + h - 0.2 * h); borderShapePath.quadTo(xs + w, ys + h, xs + w - 0.2 * h, ys + h); borderShapePath.lineTo(xs + 0.2 * h, ys + h); borderShapePath.quadTo(xs, ys + h, xs, ys + h - 0.2 * h); borderShapePath.lineTo(xs, ys + 0.2 * h); borderShapePath.quadTo(xs, ys, xs + 0.2 * h, ys); break; } } recalcShapeAndBoundingRect(); } bool TextLabelPrivate::swapVisible(bool on) { bool oldValue = isVisible(); setVisible(on); emit q->changed(); emit q->visibleChanged(on); return oldValue; } /*! Returns the outer bounds of the item as a rectangle. */ QRectF TextLabelPrivate::boundingRect() const { return transformedBoundingRectangle; } /*! Returns the shape of this item as a QPainterPath in local coordinates. */ QPainterPath TextLabelPrivate::shape() const { return labelShape; } /*! recalculates the outer bounds and the shape of the label. */ void TextLabelPrivate::recalcShapeAndBoundingRect() { prepareGeometryChange(); QMatrix matrix; matrix.rotate(-rotationAngle); labelShape = QPainterPath(); if (borderShape != TextLabel::NoBorder) { labelShape.addPath(WorksheetElement::shapeFromPath(borderShapePath, borderPen)); transformedBoundingRectangle = matrix.mapRect(labelShape.boundingRect()); } else { labelShape.addRect(boundingRectangle); transformedBoundingRectangle = matrix.mapRect(boundingRectangle); } labelShape = matrix.map(labelShape); emit q->changed(); } void TextLabelPrivate::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) { Q_UNUSED(option) Q_UNUSED(widget) if (positionInvalid) return; if (textWrapper.text.isEmpty()) return; painter->save(); //draw the text painter->rotate(-rotationAngle); if (textWrapper.teXUsed) { if (boundingRect().width() != 0.0 && boundingRect().height() != 0.0) painter->drawImage(boundingRect(), teXImage); } else { + painter->setPen(fontColor); painter->scale(scaleFactor, scaleFactor); float w = staticText.size().width(); float h = staticText.size().height(); painter->drawStaticText(QPoint(-w/2,-h/2), staticText); } painter->restore(); //draw the border if (borderShape != TextLabel::NoBorder) { painter->rotate(-rotationAngle); painter->setPen(borderPen); painter->setOpacity(borderOpacity); painter->drawPath(borderShapePath); painter->restore(); } if (m_hovered && !isSelected() && !m_printing){ painter->setPen(QPen(QApplication::palette().color(QPalette::Shadow), 2, Qt::SolidLine)); painter->drawPath(labelShape); } if (isSelected() && !m_printing){ painter->setPen(QPen(QApplication::palette().color(QPalette::Highlight), 2, Qt::SolidLine)); painter->drawPath(labelShape); } } QVariant TextLabelPrivate::itemChange(GraphicsItemChange change, const QVariant &value) { if (suppressItemChangeEvent) return value; if (change == QGraphicsItem::ItemPositionChange) { //convert item's center point in parent's coordinates TextLabel::PositionWrapper tempPosition; tempPosition.point = positionFromItemPosition(value.toPointF()); tempPosition.horizontalPosition = TextLabel::hPositionCustom; tempPosition.verticalPosition = TextLabel::vPositionCustom; //emit the signals in order to notify the UI. //we don't set the position related member variables during the mouse movements. //this is done on mouse release events only. emit q->positionChanged(tempPosition); } return QGraphicsItem::itemChange(change, value); } void TextLabelPrivate::mouseReleaseEvent(QGraphicsSceneMouseEvent* event) { //convert position of the item in parent coordinates to label's position QPointF point = positionFromItemPosition(pos()); if (qAbs(point.x()-position.point.x())>20 && qAbs(point.y()-position.point.y())>20 ) { //position was changed -> set the position related member variables suppressRetransform = true; TextLabel::PositionWrapper tempPosition; tempPosition.point = point; tempPosition.horizontalPosition = TextLabel::hPositionCustom; tempPosition.verticalPosition = TextLabel::vPositionCustom; q->setPosition(tempPosition); suppressRetransform = false; } QGraphicsItem::mouseReleaseEvent(event); } /*! * converts label's position to GraphicsItem's position. */ QPointF TextLabelPrivate::positionFromItemPosition(QPointF itemPos) { float x = itemPos.x(); float y = itemPos.y(); float w, h; QPointF tmpPosition; if (textWrapper.teXUsed) { w = teXImage.width()*scaleFactor; h = teXImage.height()*scaleFactor; } else { w = staticText.size().width()*scaleFactor; h = staticText.size().height()*scaleFactor; } //depending on the alignment, calculate the new position switch (horizontalAlignment) { case TextLabel::hAlignLeft: tmpPosition.setX(x + w/2); break; case TextLabel::hAlignCenter: tmpPosition.setX(x); break; case TextLabel::hAlignRight: tmpPosition.setX(x - w/2); break; } switch (verticalAlignment) { case TextLabel::vAlignTop: tmpPosition.setY(y + h/2); break; case TextLabel::vAlignCenter: tmpPosition.setY(y); break; case TextLabel::vAlignBottom: tmpPosition.setY(y - h/2); break; } return tmpPosition; } void TextLabelPrivate::contextMenuEvent(QGraphicsSceneContextMenuEvent* event) { q->createContextMenu()->exec(event->screenPos()); } void TextLabelPrivate::hoverEnterEvent(QGraphicsSceneHoverEvent*) { if (!isSelected()) { m_hovered = true; emit q->hovered(); update(); } } void TextLabelPrivate::hoverLeaveEvent(QGraphicsSceneHoverEvent*) { if (m_hovered) { m_hovered = false; emit q->unhovered(); update(); } } //############################################################################## //################## Serialization/Deserialization ########################### //############################################################################## //! Save as XML void TextLabel::save(QXmlStreamWriter* writer) const { Q_D(const TextLabel); writer->writeStartElement( "textLabel" ); writeBasicAttributes(writer); writeCommentElement(writer); //geometry writer->writeStartElement( "geometry" ); writer->writeAttribute( "x", QString::number(d->position.point.x()) ); writer->writeAttribute( "y", QString::number(d->position.point.y()) ); writer->writeAttribute( "horizontalPosition", QString::number(d->position.horizontalPosition) ); writer->writeAttribute( "verticalPosition", QString::number(d->position.verticalPosition) ); writer->writeAttribute( "horizontalAlignment", QString::number(d->horizontalAlignment) ); writer->writeAttribute( "verticalAlignment", QString::number(d->verticalAlignment) ); writer->writeAttribute( "rotationAngle", QString::number(d->rotationAngle) ); writer->writeAttribute( "visible", QString::number(d->isVisible()) ); writer->writeEndElement(); writer->writeStartElement( "text" ); writer->writeCharacters( d->textWrapper.text ); writer->writeEndElement(); writer->writeStartElement( "format" ); writer->writeAttribute( "teXUsed", QString::number(d->textWrapper.teXUsed) ); WRITE_QFONT(d->teXFont); - writer->writeAttribute( "teXFontColor_r", QString::number(d->teXFontColor.red()) ); - writer->writeAttribute( "teXFontColor_g", QString::number(d->teXFontColor.green()) ); - writer->writeAttribute( "teXFontColor_b", QString::number(d->teXFontColor.blue()) ); + writer->writeAttribute( "fontColor_r", QString::number(d->fontColor.red()) ); + writer->writeAttribute( "fontColor_g", QString::number(d->fontColor.green()) ); + writer->writeAttribute( "fontColor_b", QString::number(d->fontColor.blue()) ); writer->writeEndElement(); //border writer->writeStartElement("border"); writer->writeAttribute("borderShape", QString::number(d->borderShape)); WRITE_QPEN(d->borderPen); writer->writeAttribute("borderOpacity", QString::number(d->borderOpacity)); writer->writeEndElement(); if (d->textWrapper.teXUsed) { writer->writeStartElement("teXImage"); QByteArray ba; QBuffer buffer(&ba); buffer.open(QIODevice::WriteOnly); d->teXImage.save(&buffer, "PNG"); writer->writeCharacters(ba.toBase64()); writer->writeEndElement(); } writer->writeEndElement(); // close "textLabel" section } //! Load from XML bool TextLabel::load(XmlStreamReader* reader, bool preview) { if (!readBasicAttributes(reader)) return false; Q_D(TextLabel); KLocalizedString attributeWarning = ki18n("Attribute '%1' missing or empty, default value is used"); QXmlStreamAttributes attribs; QString str; bool teXImageFound = false; while (!reader->atEnd()) { reader->readNext(); if (reader->isEndElement() && reader->name() == "textLabel") break; if (!reader->isStartElement()) continue; if (!preview && reader->name() == "comment") { if (!readCommentElement(reader)) return false; } else if (!preview && reader->name() == "geometry") { attribs = reader->attributes(); str = attribs.value("x").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.subs("x").toString()); else d->position.point.setX(str.toDouble()); str = attribs.value("y").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.subs("y").toString()); else d->position.point.setY(str.toDouble()); READ_INT_VALUE("horizontalPosition", position.horizontalPosition, TextLabel::HorizontalPosition); READ_INT_VALUE("verticalPosition", position.verticalPosition, TextLabel::VerticalPosition); READ_INT_VALUE("horizontalAlignment", horizontalAlignment, TextLabel::HorizontalAlignment); READ_INT_VALUE("verticalAlignment", verticalAlignment, TextLabel::VerticalAlignment); READ_INT_VALUE("rotationAngle", rotationAngle, int); str = attribs.value("visible").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.subs("visible").toString()); else d->setVisible(str.toInt()); } else if (!preview && reader->name() == "text") { d->textWrapper.text = reader->readElementText(); } else if (!preview && reader->name() == "format") { attribs = reader->attributes(); READ_INT_VALUE("teXUsed", textWrapper.teXUsed, bool); READ_QFONT(d->teXFont); - str = attribs.value("teXFontColor_r").toString(); + str = attribs.value("fontColor_r").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.subs("teXFontColor_r").toString()); + reader->raiseWarning(attributeWarning.subs("fontColor_r").toString()); else - d->teXFontColor.setRed( str.toInt() ); + d->fontColor.setRed( str.toInt() ); - str = attribs.value("teXFontColor_g").toString(); + str = attribs.value("fontColor_g").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.subs("teXFontColor_g").toString()); + reader->raiseWarning(attributeWarning.subs("fontColor_g").toString()); else - d->teXFontColor.setGreen( str.toInt() ); + d->fontColor.setGreen( str.toInt() ); - str = attribs.value("teXFontColor_b").toString(); + str = attribs.value("fontColor_b").toString(); if(str.isEmpty()) - reader->raiseWarning(attributeWarning.subs("teXFontColor_b").toString()); + reader->raiseWarning(attributeWarning.subs("fontColor_b").toString()); else - d->teXFontColor.setBlue( str.toInt() ); + d->fontColor.setBlue( str.toInt() ); } else if (!preview && reader->name() == "border") { attribs = reader->attributes(); READ_INT_VALUE("borderShape", borderShape, BorderShape); READ_QPEN(d->borderPen); READ_DOUBLE_VALUE("borderOpacity", borderOpacity); } else if (!preview && reader->name() == "teXImage") { reader->readNext(); QString content = reader->text().toString().trimmed(); QByteArray ba = QByteArray::fromBase64(content.toLatin1()); teXImageFound = d->teXImage.loadFromData(ba); } else { // unknown element reader->raiseWarning(i18n("unknown element '%1'", reader->name().toString())); if (!reader->skipToEndElement()) return false; } } if (preview) return true; //in case we use latex and the image was stored (older versions of LabPlot didn't save the image)and loaded, //we just need to retransform. //otherwise, we set the static text and retransform in updateText() if ( !(d->textWrapper.teXUsed && teXImageFound) ) d->updateText(); else retransform(); return true; } //############################################################################## //######################### Theme management ################################## //############################################################################## void TextLabel::loadThemeConfig(const KConfig& config) { Q_D(TextLabel); KConfigGroup group = config.group("Label"); - const QColor fontColor = group.readEntry("FontColor", QColor(Qt::white)); - const QColor backgroundColor = group.readEntry("BackgroundColor", QColor(Qt::black)); - - d->suppressRetransform = true; - if (!d->textWrapper.teXUsed && !d->textWrapper.text.isEmpty()) { - //replace colors in the html-formatted string - QTextDocument doc; - doc.setHtml(d->textWrapper.text); - QTextCharFormat fmt; - fmt.setForeground(QBrush(fontColor)); - fmt.setBackground(QBrush(backgroundColor)); - QTextCursor cursor(&doc); - cursor.select(QTextCursor::Document); - cursor.setCharFormat(fmt); - - TextLabel::TextWrapper wrapper(doc.toHtml(), d->textWrapper.teXUsed); - this->setText(wrapper); - } else { - //replace colors in the TeX-string - this->setTeXFontColor(fontColor); - this->setTeXBackgroundColor(backgroundColor); - } + d->fontColor = group.readEntry("FontColor", QColor(Qt::white)); + d->backgroundColor = group.readEntry("BackgroundColor", QColor(Qt::black)); group = config.group("CartesianPlot"); QPen pen = this->borderPen(); pen.setColor(group.readEntry("BorderColor", pen.color())); pen.setStyle((Qt::PenStyle)(group.readEntry("BorderStyle", (int) pen.style()))); pen.setWidthF(group.readEntry("BorderWidth", pen.widthF())); this->setBorderPen(pen); this->setBorderOpacity(group.readEntry("BorderOpacity", this->borderOpacity())); - d->suppressRetransform = false; d->updateText(); } void TextLabel::saveThemeConfig(const KConfig& config) { KConfigGroup group = config.group("Label"); //TODO -// group.writeEntry("TeXFontColor", (QColor) this->teXFontColor()); +// group.writeEntry("TeXFontColor", (QColor) this->fontColor()); } diff --git a/src/backend/worksheet/TextLabel.h b/src/backend/worksheet/TextLabel.h index e47d0f722..71e764d85 100644 --- a/src/backend/worksheet/TextLabel.h +++ b/src/backend/worksheet/TextLabel.h @@ -1,147 +1,147 @@ /*************************************************************************** File : TextLabel.h Project : LabPlot Description : Text label supporting reach text and latex formatting -------------------------------------------------------------------- Copyright : (C) 2009 Tilman Benkert (thzs@gmx.net) Copyright : (C) 2012-2014 Alexander Semke (alexander.semke@web.de) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #ifndef TEXTLABEL_H #define TEXTLABEL_H #include "backend/lib/macros.h" #include "tools/TeXRenderer.h" #include "backend/worksheet/WorksheetElement.h" #include #include #include class TextLabelPrivate; class TextLabel : public WorksheetElement { Q_OBJECT public: enum Type {General, PlotTitle, AxisTitle, PlotLegendTitle}; enum HorizontalPosition {hPositionLeft, hPositionCenter, hPositionRight, hPositionCustom}; enum VerticalPosition {vPositionTop, vPositionCenter, vPositionBottom, vPositionCustom}; enum HorizontalAlignment {hAlignLeft, hAlignCenter, hAlignRight}; enum VerticalAlignment {vAlignTop, vAlignCenter, vAlignBottom}; enum BorderShape {NoBorder, Rect, Ellipse, RoundSideRect, RoundCornerRect, InwardsRoundCornerRect, DentedBorderRect, Cuboid, UpPointingRectangle, DownPointingRectangle, LeftPointingRectangle, RightPointingRectangle}; struct TextWrapper { TextWrapper() : teXUsed(false) {} TextWrapper(const QString& t, bool b) : text(t), teXUsed(b) {} TextWrapper(const QString& t) : text(t), teXUsed(false) {} QString text; bool teXUsed; }; struct PositionWrapper { QPointF point; HorizontalPosition horizontalPosition; VerticalPosition verticalPosition; }; explicit TextLabel(const QString& name, Type type = General); ~TextLabel() override; Type type() const; QIcon icon() const override; QMenu* createContextMenu() override; QGraphicsItem* graphicsItem() const override; void setParentGraphicsItem(QGraphicsItem*); void save(QXmlStreamWriter*) const override; bool load(XmlStreamReader*, bool preview) override; - void loadThemeConfig(const KConfig& config) override; - void saveThemeConfig(const KConfig& config) override; + void loadThemeConfig(const KConfig&) override; + void saveThemeConfig(const KConfig&) override; CLASS_D_ACCESSOR_DECL(TextWrapper, text, Text) - BASIC_D_ACCESSOR_DECL(QColor, teXFontColor, TeXFontColor) - BASIC_D_ACCESSOR_DECL(QColor, teXBackgroundColor, TeXBackgroundColor) + BASIC_D_ACCESSOR_DECL(QColor, fontColor, FontColor) + BASIC_D_ACCESSOR_DECL(QColor, backgroundColor, BackgroundColor) CLASS_D_ACCESSOR_DECL(QFont, teXFont, TeXFont) CLASS_D_ACCESSOR_DECL(PositionWrapper, position, Position) void setPosition(QPointF); void setPositionInvalid(bool); BASIC_D_ACCESSOR_DECL(HorizontalAlignment, horizontalAlignment, HorizontalAlignment) BASIC_D_ACCESSOR_DECL(VerticalAlignment, verticalAlignment, VerticalAlignment) BASIC_D_ACCESSOR_DECL(qreal, rotationAngle, RotationAngle) BASIC_D_ACCESSOR_DECL(BorderShape, borderShape, BorderShape); CLASS_D_ACCESSOR_DECL(QPen, borderPen, BorderPen) BASIC_D_ACCESSOR_DECL(qreal, borderOpacity, BorderOpacity) void setVisible(bool on) override; bool isVisible() const override; void setPrinting(bool) override; void retransform() override; void handleResize(double horizontalRatio, double verticalRatio, bool pageResize) override; typedef TextLabelPrivate Private; private slots: void updateTeXImage(); //SLOTs for changes triggered via QActions in the context menu void visibilityChanged(); protected: TextLabelPrivate* const d_ptr; TextLabel(const QString& name, TextLabelPrivate* dd, Type type = General); private: Q_DECLARE_PRIVATE(TextLabel) void init(); Type m_type; QAction* visibilityAction; signals: void textWrapperChanged(const TextLabel::TextWrapper&); void teXFontSizeChanged(const int); void teXFontChanged(const QFont); - void teXFontColorChanged(const QColor); - void teXBackgroundColorChanged(const QColor); + void fontColorChanged(const QColor); + void backgroundColorChanged(const QColor); void positionChanged(const TextLabel::PositionWrapper&); void horizontalAlignmentChanged(TextLabel::HorizontalAlignment); void verticalAlignmentChanged(TextLabel::VerticalAlignment); void rotationAngleChanged(qreal); void visibleChanged(bool); void borderShapeChanged(TextLabel::BorderShape); void borderPenChanged(QPen&); void borderOpacityChanged(float); void teXImageUpdated(bool); void changed(); }; #endif diff --git a/src/backend/worksheet/TextLabelPrivate.h b/src/backend/worksheet/TextLabelPrivate.h index ee55206cd..e9242205b 100644 --- a/src/backend/worksheet/TextLabelPrivate.h +++ b/src/backend/worksheet/TextLabelPrivate.h @@ -1,100 +1,100 @@ /*************************************************************************** File : TextLabelPrivate.h Project : LabPlot Description : Private members of TextLabel -------------------------------------------------------------------- Copyright : (C) 2012-2014 by Alexander Semke (alexander.semke@web.de) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #ifndef TEXTLABELPRIVATE_H #define TEXTLABELPRIVATE_H #include #include #include class QGraphicsSceneHoverEvent; class TextLabelPrivate: public QGraphicsItem { public: explicit TextLabelPrivate(TextLabel*); qreal rotationAngle; float scaleFactor; int teXImageResolution; float teXImageScaleFactor; TextLabel::TextWrapper textWrapper; QFont teXFont; - QColor teXFontColor; - QColor teXBackgroundColor; + QColor fontColor; + QColor backgroundColor; QImage teXImage; QFutureWatcher teXImageFutureWatcher; bool teXRenderSuccessful; TextLabel::PositionWrapper position; //position in parent's coordinate system, the label gets aligned around this point. bool positionInvalid; TextLabel::HorizontalAlignment horizontalAlignment; TextLabel::VerticalAlignment verticalAlignment; TextLabel::BorderShape borderShape; QPen borderPen; qreal borderOpacity; QString name() const; void retransform(); bool swapVisible(bool on); virtual void recalcShapeAndBoundingRect(); void updatePosition(); QPointF positionFromItemPosition(QPointF); void updateText(); void updateTeXImage(); void updateBorder(); QStaticText staticText; bool suppressItemChangeEvent; bool suppressRetransform; bool m_printing; bool m_hovered; QRectF boundingRectangle; //bounding rectangle of the text QRectF transformedBoundingRectangle; //bounding rectangle of transformed (rotated etc.) text QPainterPath borderShapePath; QPainterPath labelShape; //reimplemented from QGraphicsItem QRectF boundingRect() const override; QPainterPath shape() const override; void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget* widget = nullptr) override; QVariant itemChange(GraphicsItemChange change, const QVariant &value) override; TextLabel* const q; private: void contextMenuEvent(QGraphicsSceneContextMenuEvent*) override; void mouseReleaseEvent(QGraphicsSceneMouseEvent*) override; void hoverEnterEvent(QGraphicsSceneHoverEvent*) override; void hoverLeaveEvent(QGraphicsSceneHoverEvent*) override; }; #endif diff --git a/src/kdefrontend/widgets/LabelWidget.cpp b/src/kdefrontend/widgets/LabelWidget.cpp index 5114015e2..7aa01e6d1 100644 --- a/src/kdefrontend/widgets/LabelWidget.cpp +++ b/src/kdefrontend/widgets/LabelWidget.cpp @@ -1,974 +1,992 @@ /*************************************************************************** File : LabelWidget.cc Project : LabPlot -------------------------------------------------------------------- Copyright : (C) 2008-2017 Alexander Semke (alexander.semke@web.de) Copyright : (C) 2012-2017 Stefan Gerlach (stefan.gerlach@uni-konstanz.de) Description : label settings widget ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "LabelWidget.h" #include "backend/worksheet/Worksheet.h" #include "backend/worksheet/plots/cartesian/Axis.h" #include "kdefrontend/GuiTools.h" #include "tools/TeXRenderer.h" #include -#include #include +#include +#include #include #include #include #include #ifdef HAVE_KF5_SYNTAX_HIGHLIGHTING #include #include #include #endif #include /*! \class LabelWidget \brief Widget for editing the properties of a TextLabel object, mostly used in an appropriate dock widget. In order the properties of the label to be shown, \c loadConfig() has to be called with the corresponding KConfigGroup (settings for a label in *Plot, Axis etc. or for an independent label on the worksheet). \ingroup kdefrontend */ LabelWidget::LabelWidget(QWidget* parent) : QWidget(parent), m_label(nullptr), m_initializing(false), m_dateTimeMenu(new QMenu(this)), m_teXEnabled(false) { ui.setupUi(this); m_dateTimeMenu->setSeparatorsCollapsible(false); //we don't want the first separator to be removed ui.kcbFontColor->setColor(Qt::black); // default color //Icons ui.tbFontBold->setIcon( QIcon::fromTheme(QLatin1String("format-text-bold")) ); ui.tbFontItalic->setIcon( QIcon::fromTheme(QLatin1String("format-text-italic")) ); ui.tbFontUnderline->setIcon( QIcon::fromTheme(QLatin1String("format-text-underline")) ); ui.tbFontStrikeOut->setIcon( QIcon::fromTheme(QLatin1String("format-text-strikethrough")) ); ui.tbFontSuperScript->setIcon( QIcon::fromTheme(QLatin1String("format-text-superscript")) ); ui.tbFontSubScript->setIcon( QIcon::fromTheme(QLatin1String("format-text-subscript")) ); ui.tbSymbols->setIcon( QIcon::fromTheme(QLatin1String("labplot-format-text-symbol")) ); ui.tbDateTime->setIcon( QIcon::fromTheme(QLatin1String("chronometer")) ); ui.tbTexUsed->setIcon( QIcon::fromTheme(QLatin1String("labplot-TeX-logo")) ); //Positioning and alignment ui.cbPositionX->addItem(i18n("Left")); ui.cbPositionX->addItem(i18n("Center")); ui.cbPositionX->addItem(i18n("Right")); ui.cbPositionX->addItem(i18n("Custom")); ui.cbPositionY->addItem(i18n("Top")); ui.cbPositionY->addItem(i18n("Center")); ui.cbPositionY->addItem(i18n("Bottom")); ui.cbPositionY->addItem(i18n("Custom")); ui.cbHorizontalAlignment->addItem(i18n("Left")); ui.cbHorizontalAlignment->addItem(i18n("Center")); ui.cbHorizontalAlignment->addItem(i18n("Right")); ui.cbVerticalAlignment->addItem(i18n("Top")); ui.cbVerticalAlignment->addItem(i18n("Center")); ui.cbVerticalAlignment->addItem(i18n("Bottom")); ui.cbBorderShape->addItem(i18n("No Border")); ui.cbBorderShape->addItem(i18n("Rectangle")); ui.cbBorderShape->addItem(i18n("Ellipse")); ui.cbBorderShape->addItem(i18n("Round sided rectangle")); ui.cbBorderShape->addItem(i18n("Round corner rectangle")); ui.cbBorderShape->addItem(i18n("Inwards round corner rectangle")); ui.cbBorderShape->addItem(i18n("Dented border rectangle")); ui.cbBorderShape->addItem(i18n("Cuboid")); ui.cbBorderShape->addItem(i18n("Up Pointing rectangle")); ui.cbBorderShape->addItem(i18n("Down Pointing rectangle")); ui.cbBorderShape->addItem(i18n("Left Pointing rectangle")); ui.cbBorderShape->addItem(i18n("Right Pointing rectangle")); ui.cbBorderStyle->addItem(i18n("No line")); ui.cbBorderStyle->addItem(i18n("Solid line")); ui.cbBorderStyle->addItem(i18n("Dash line")); ui.cbBorderStyle->addItem(i18n("Dot line")); ui.cbBorderStyle->addItem(i18n("Dash dot line")); ui.cbBorderStyle->addItem(i18n("Dash dot dot line")); //check whether the used latex compiler is available. //Following logic is implemented (s.a. LabelWidget::teXUsedChanged()): //1. in case latex was used to generate the text label in the stored project //and no latex is available on the target system, latex button is toggled and //the user still can switch to the non-latex mode. //2. in case the label was in the non-latex mode and no latex is available, //deactivate the latex button so the user cannot switch to this mode. m_teXEnabled = TeXRenderer::enabled(); #ifdef HAVE_KF5_SYNTAX_HIGHLIGHTING m_highlighter = new KSyntaxHighlighting::SyntaxHighlighter(ui.teLabel->document()); m_highlighter->setDefinition(m_repository.definitionForName(QLatin1String("LaTeX"))); m_highlighter->setTheme( (palette().color(QPalette::Base).lightness() < 128) ? m_repository.defaultTheme(KSyntaxHighlighting::Repository::DarkTheme) : m_repository.defaultTheme(KSyntaxHighlighting::Repository::LightTheme) ); #endif //SLOTS // text properties connect(ui.tbTexUsed, SIGNAL(clicked(bool)), this, SLOT(teXUsedChanged(bool)) ); connect(ui.teLabel, SIGNAL(textChanged()), this, SLOT(textChanged())); connect(ui.teLabel, SIGNAL(currentCharFormatChanged(QTextCharFormat)), this, SLOT(charFormatChanged(QTextCharFormat))); connect(ui.kcbFontColor, SIGNAL(changed(QColor)), this, SLOT(fontColorChanged(QColor))); connect(ui.kcbBackgroundColor, SIGNAL(changed(QColor)), this, SLOT(backgroundColorChanged(QColor))); connect(ui.tbFontBold, SIGNAL(clicked(bool)), this, SLOT(fontBoldChanged(bool))); connect(ui.tbFontItalic, SIGNAL(clicked(bool)), this, SLOT(fontItalicChanged(bool))); connect(ui.tbFontUnderline, SIGNAL(clicked(bool)), this, SLOT(fontUnderlineChanged(bool))); connect(ui.tbFontStrikeOut, SIGNAL(clicked(bool)), this, SLOT(fontStrikeOutChanged(bool))); connect(ui.tbFontSuperScript, SIGNAL(clicked(bool)), this, SLOT(fontSuperScriptChanged(bool))); connect(ui.tbFontSubScript, SIGNAL(clicked(bool)), this, SLOT(fontSubScriptChanged(bool))); connect(ui.tbSymbols, SIGNAL(clicked(bool)), this, SLOT(charMenu())); connect(ui.tbDateTime, SIGNAL(clicked(bool)), this, SLOT(dateTimeMenu())); connect(m_dateTimeMenu, SIGNAL(triggered(QAction*)), this, SLOT(insertDateTime(QAction*)) ); connect(ui.kfontRequester, SIGNAL(fontSelected(QFont)), this, SLOT(fontChanged(QFont))); connect(ui.kfontRequesterTeX, SIGNAL(fontSelected(QFont)), this, SLOT(teXFontChanged(QFont))); connect(ui.sbFontSize, SIGNAL(valueChanged(int)), this, SLOT(fontSizeChanged(int)) ); // geometry connect( ui.cbPositionX, SIGNAL(currentIndexChanged(int)), this, SLOT(positionXChanged(int)) ); connect( ui.cbPositionY, SIGNAL(currentIndexChanged(int)), this, SLOT(positionYChanged(int)) ); connect( ui.sbPositionX, SIGNAL(valueChanged(double)), this, SLOT(customPositionXChanged(double)) ); connect( ui.sbPositionY, SIGNAL(valueChanged(double)), this, SLOT(customPositionYChanged(double)) ); connect( ui.cbHorizontalAlignment, SIGNAL(currentIndexChanged(int)), this, SLOT(horizontalAlignmentChanged(int)) ); connect( ui.cbVerticalAlignment, SIGNAL(currentIndexChanged(int)), this, SLOT(verticalAlignmentChanged(int)) ); connect( ui.sbRotation, SIGNAL(valueChanged(int)), this, SLOT(rotationChanged(int)) ); connect( ui.sbOffsetX, SIGNAL(valueChanged(double)), this, SLOT(offsetXChanged(double)) ); connect( ui.sbOffsetY, SIGNAL(valueChanged(double)), this, SLOT(offsetYChanged(double)) ); connect( ui.chbVisible, SIGNAL(clicked(bool)), this, SLOT(visibilityChanged(bool)) ); //Border connect(ui.cbBorderShape, static_cast(&QComboBox::currentIndexChanged), this, &LabelWidget::borderShapeChanged); connect(ui.cbBorderStyle, static_cast(&QComboBox::currentIndexChanged), this, &LabelWidget::borderStyleChanged); connect(ui.kcbBorderColor, &KColorButton::changed, this, &LabelWidget::borderColorChanged); connect(ui.sbBorderWidth, static_cast(&QDoubleSpinBox::valueChanged), this, &LabelWidget::borderWidthChanged); connect(ui.sbBorderOpacity, static_cast(&QSpinBox::valueChanged), this, &LabelWidget::borderOpacityChanged); //TODO: https://bugreports.qt.io/browse/QTBUG-25420 ui.tbFontUnderline->hide(); ui.tbFontStrikeOut->hide(); } void LabelWidget::setLabels(QList labels) { m_labelsList = labels; m_label = labels.first(); ui.lOffsetX->hide(); ui.lOffsetY->hide(); ui.sbOffsetX->hide(); ui.sbOffsetY->hide(); this->load(); borderShapeChanged(ui.cbBorderShape->currentIndex()); initConnections(); } void LabelWidget::setAxes(QList axes) { m_labelsList.clear(); for (auto* axis : axes) { m_labelsList.append(axis->title()); connect(axis, SIGNAL(titleOffsetXChanged(qreal)), this, SLOT(labelOffsetxChanged(qreal)) ); connect(axis, SIGNAL(titleOffsetYChanged(qreal)), this, SLOT(labelOffsetyChanged(qreal)) ); connect(axis->title(), SIGNAL(rotationAngleChanged(qreal)), this, SLOT(labelRotationAngleChanged(qreal)) ); } m_axesList = axes; m_label = m_labelsList.first(); this->load(); initConnections(); } void LabelWidget::initConnections() const { connect( m_label, SIGNAL(textWrapperChanged(TextLabel::TextWrapper)), this, SLOT(labelTextWrapperChanged(TextLabel::TextWrapper)) ); connect( m_label, SIGNAL(teXImageUpdated(bool)), this, SLOT(labelTeXImageUpdated(bool)) ); connect( m_label, SIGNAL(teXFontChanged(QFont)), this, SLOT(labelTeXFontChanged(QFont)) ); - connect( m_label, SIGNAL(teXFontColorChanged(QColor)), - this, SLOT(labelTeXFontColorChanged(QColor)) ); + connect( m_label, SIGNAL(fontColorChanged(QColor)), + this, SLOT(labelFontColorChanged(QColor)) ); connect( m_label, SIGNAL(positionChanged(TextLabel::PositionWrapper)), this, SLOT(labelPositionChanged(TextLabel::PositionWrapper)) ); connect( m_label, SIGNAL(horizontalAlignmentChanged(TextLabel::HorizontalAlignment)), this, SLOT(labelHorizontalAlignmentChanged(TextLabel::HorizontalAlignment)) ); connect( m_label, SIGNAL(verticalAlignmentChanged(TextLabel::VerticalAlignment)), this, SLOT(labelVerticalAlignmentChanged(TextLabel::VerticalAlignment)) ); connect( m_label, SIGNAL(rotationAngleChanged(qreal)), this, SLOT(labelRotationAngleChanged(qreal)) ); connect(m_label, &TextLabel::borderShapeChanged, this, &LabelWidget::labelBorderShapeChanged); connect(m_label, &TextLabel::borderPenChanged, this, &LabelWidget::labelBorderPenChanged); connect(m_label, &TextLabel::borderOpacityChanged, this, &LabelWidget::labelBorderOpacityChanged); connect( m_label, SIGNAL(visibleChanged(bool)), this, SLOT(labelVisibleChanged(bool)) ); } /*! * enables/disables the "fixed label"-mode, used when displaying * the properties of axis' title label. * In this mode, in the "geometry"-part only the offset (offset to the axis) * and the rotation of the label are available. */ void LabelWidget::setFixedLabelMode(const bool b) { ui.lPositionX->setVisible(!b); ui.cbPositionX->setVisible(!b); ui.sbPositionX->setVisible(!b); ui.lPositionY->setVisible(!b); ui.cbPositionY->setVisible(!b); ui.sbPositionY->setVisible(!b); ui.lHorizontalAlignment->setVisible(!b); ui.cbHorizontalAlignment->setVisible(!b); ui.lVerticalAlignment->setVisible(!b); ui.cbVerticalAlignment->setVisible(!b); ui.lOffsetX->setVisible(b); ui.lOffsetY->setVisible(b); ui.sbOffsetX->setVisible(b); ui.sbOffsetY->setVisible(b); } /*! * enables/disables all geometry relevant widgets. * Used when displaying legend's title label. */ void LabelWidget::setNoGeometryMode(const bool b) { ui.lGeometry->setVisible(!b); ui.lPositionX->setVisible(!b); ui.cbPositionX->setVisible(!b); ui.sbPositionX->setVisible(!b); ui.lPositionY->setVisible(!b); ui.cbPositionY->setVisible(!b); ui.sbPositionY->setVisible(!b); ui.lHorizontalAlignment->setVisible(!b); ui.cbHorizontalAlignment->setVisible(!b); ui.lVerticalAlignment->setVisible(!b); ui.cbVerticalAlignment->setVisible(!b); ui.lOffsetX->setVisible(!b); ui.lOffsetY->setVisible(!b); ui.sbOffsetX->setVisible(!b); ui.sbOffsetY->setVisible(!b); ui.lRotation->setVisible(!b); ui.sbRotation->setVisible(!b); } //********************************************************** //****** SLOTs for changes triggered in LabelWidget ******** //********************************************************** // text formatting slots void LabelWidget::textChanged() { if (m_initializing) return; if (ui.tbTexUsed->isChecked()) { QString text=ui.teLabel->toPlainText(); TextLabel::TextWrapper wrapper(text, true); for (auto* label : m_labelsList) label->setText(wrapper); } else { //save an empty string instead of a html-string with empty body, if no text available in QTextEdit QString text; if (ui.teLabel->toPlainText() == "") text = ""; else text = ui.teLabel->toHtml(); TextLabel::TextWrapper wrapper(text, false); for (auto* label : m_labelsList) label->setText(wrapper); } } void LabelWidget::charFormatChanged(const QTextCharFormat& format) { + if (m_initializing) + return; + if(ui.tbTexUsed->isChecked()) return; + m_initializing = true; + // update button state ui.tbFontBold->setChecked(ui.teLabel->fontWeight()==QFont::Bold); ui.tbFontItalic->setChecked(ui.teLabel->fontItalic()); ui.tbFontUnderline->setChecked(ui.teLabel->fontUnderline()); ui.tbFontStrikeOut->setChecked(format.fontStrikeOut()); ui.tbFontSuperScript->setChecked(format.verticalAlignment() == QTextCharFormat::AlignSuperScript); ui.tbFontSubScript->setChecked(format.verticalAlignment() == QTextCharFormat::AlignSubScript); //font and colors - ui.kcbFontColor->setColor(format.foreground().color()); + if (format.foreground().color().isValid()) + ui.kcbFontColor->setColor(format.foreground().color()); + else + ui.kcbFontColor->setColor(m_label->fontColor()); ui.kcbBackgroundColor->setColor(format.background().color()); ui.kfontRequester->setFont(format.font()); + + m_initializing = false; } void LabelWidget::teXUsedChanged(bool checked) { //hide text editing elements if TeX-option is used ui.tbFontBold->setVisible(!checked); ui.tbFontItalic->setVisible(!checked); //TODO: https://bugreports.qt.io/browse/QTBUG-25420 // ui.tbFontUnderline->setVisible(!checked); // ui.tbFontStrikeOut->setVisible(!checked); ui.tbFontSubScript->setVisible(!checked); ui.tbFontSuperScript->setVisible(!checked); ui.tbSymbols->setVisible(!checked); ui.lFont->setVisible(!checked); ui.kfontRequester->setVisible(!checked); if (checked) { //reset all applied formattings when switching from html to tex mode QTextCursor cursor = ui.teLabel->textCursor(); int position = cursor.position(); ui.teLabel->selectAll(); QTextCharFormat format; ui.teLabel->setCurrentCharFormat(format); cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, position); ui.teLabel->setTextCursor(cursor); #ifdef HAVE_KF5_SYNTAX_HIGHLIGHTING m_highlighter->setDocument(ui.teLabel->document()); #endif KConfigGroup conf(KSharedConfig::openConfig(), QLatin1String("Settings_Worksheet")); QString engine = conf.readEntry(QLatin1String("LaTeXEngine"), ""); if (engine == QLatin1String("xelatex") || engine == QLatin1String("lualatex")) { ui.lFontTeX->setVisible(true); ui.kfontRequesterTeX->setVisible(true); ui.lFontSize->setVisible(false); ui.sbFontSize->setVisible(false); } else { ui.lFontTeX->setVisible(false); ui.kfontRequesterTeX->setVisible(false); ui.lFontSize->setVisible(true); ui.sbFontSize->setVisible(true); } //update TeX colors - ui.kcbFontColor->setColor(m_label->teXFontColor()); - ui.kcbBackgroundColor->setColor(m_label->teXBackgroundColor()); + ui.kcbFontColor->setColor(m_label->fontColor()); + ui.kcbBackgroundColor->setColor(m_label->backgroundColor()); } else { #ifdef HAVE_KF5_SYNTAX_HIGHLIGHTING m_highlighter->setDocument(nullptr); #endif ui.lFontTeX->setVisible(false); ui.kfontRequesterTeX->setVisible(false); ui.lFontSize->setVisible(false); ui.sbFontSize->setVisible(false); //when switching to the text mode, set the background color to white just for the case the latex code provided by the user //in the TeX-mode is not valid and the background was set to red (s.a. LabelWidget::labelTeXImageUpdated()) ui.teLabel->setStyleSheet(QLatin1String("")); } //no latex is available and the user switched to the text mode, //deactivate the button since it shouldn't be possible anymore to switch to the TeX-mode if (!m_teXEnabled && !checked) { ui.tbTexUsed->setEnabled(false); ui.tbTexUsed->setToolTip(i18n("LaTeX typesetting not possible. Please check the settings.")); } else { ui.tbTexUsed->setEnabled(true); ui.tbTexUsed->setToolTip(QLatin1String("")); } if (m_initializing) return; QString text = checked ? ui.teLabel->toPlainText() : ui.teLabel->toHtml(); TextLabel::TextWrapper wrapper(text, checked); for (auto* label : m_labelsList) label->setText(wrapper); } void LabelWidget::fontColorChanged(const QColor& color) { if (m_initializing) return; - ui.teLabel->setTextColor(color); - for (auto* label : m_labelsList) - label->setTeXFontColor(color); + //if no selection is done, apply the new color for the whole label, + //apply to the currently selected part of the text only otherwise + QTextCursor cursor = ui.teLabel->textCursor(); + if (cursor.selection().isEmpty()) + for (auto* label : m_labelsList) + label->setFontColor(color); + else + ui.teLabel->setTextColor(color); } void LabelWidget::backgroundColorChanged(const QColor& color) { if (m_initializing) return; ui.teLabel->setTextBackgroundColor(color); for (auto* label : m_labelsList) - label->setTeXBackgroundColor(color); + label->setBackgroundColor(color); } void LabelWidget::fontSizeChanged(int value) { if (m_initializing) return; QFont font = m_label->teXFont(); font.setPointSize(value); for (auto* label : m_labelsList) label->setTeXFont(font); } void LabelWidget::fontBoldChanged(bool checked) { if (m_initializing) return; if (checked) ui.teLabel->setFontWeight(QFont::Bold); else ui.teLabel->setFontWeight(QFont::Normal); } void LabelWidget::fontItalicChanged(bool checked) { if (m_initializing) return; ui.teLabel->setFontItalic(checked); } void LabelWidget::fontUnderlineChanged(bool checked) { if (m_initializing) return; ui.teLabel->setFontUnderline(checked); } void LabelWidget::fontStrikeOutChanged(bool checked) { if (m_initializing) return; QTextCharFormat format = ui.teLabel->currentCharFormat(); format.setFontStrikeOut(checked); ui.teLabel->setCurrentCharFormat(format); } void LabelWidget::fontSuperScriptChanged(bool checked) { if (m_initializing) return; QTextCharFormat format = ui.teLabel->currentCharFormat(); if (checked) format.setVerticalAlignment(QTextCharFormat::AlignSuperScript); else format.setVerticalAlignment(QTextCharFormat::AlignNormal); ui.teLabel->setCurrentCharFormat(format); } void LabelWidget::fontSubScriptChanged(bool checked) { if (m_initializing) return; QTextCharFormat format = ui.teLabel->currentCharFormat(); if (checked) format.setVerticalAlignment(QTextCharFormat::AlignSubScript); else format.setVerticalAlignment(QTextCharFormat::AlignNormal); ui.teLabel->setCurrentCharFormat(format); } void LabelWidget::fontChanged(const QFont& font) { if (m_initializing) return; // underline and strike-out not included ui.teLabel->setFontFamily(font.family()); ui.teLabel->setFontPointSize(font.pointSize()); ui.teLabel->setFontItalic(font.italic()); ui.teLabel->setFontWeight(font.weight()); } void LabelWidget::teXFontChanged(const QFont& font) { if (m_initializing) return; for (auto* label : m_labelsList) label->setTeXFont(font); } void LabelWidget::charMenu() { QMenu menu; KCharSelect selection(this, nullptr, KCharSelect::SearchLine | KCharSelect::CharacterTable | KCharSelect::BlockCombos | KCharSelect::HistoryButtons); selection.setCurrentFont(ui.teLabel->currentFont()); connect(&selection, SIGNAL(charSelected(QChar)), this, SLOT(insertChar(QChar))); connect(&selection, SIGNAL(charSelected(QChar)), &menu, SLOT(close())); auto* widgetAction = new QWidgetAction(this); widgetAction->setDefaultWidget(&selection); menu.addAction(widgetAction); QPoint pos(-menu.sizeHint().width()+ui.tbSymbols->width(),-menu.sizeHint().height()); menu.exec(ui.tbSymbols->mapToGlobal(pos)); } void LabelWidget::insertChar(QChar c) { ui.teLabel->insertPlainText(QString(c)); } void LabelWidget::dateTimeMenu() { m_dateTimeMenu->clear(); QDate date = QDate::currentDate(); m_dateTimeMenu->addSeparator()->setText(i18n("Date")); m_dateTimeMenu->addAction( date.toString(Qt::TextDate) ); m_dateTimeMenu->addAction( date.toString(Qt::ISODate) ); m_dateTimeMenu->addAction( date.toString(Qt::TextDate) ); m_dateTimeMenu->addAction( date.toString(Qt::SystemLocaleShortDate) ); m_dateTimeMenu->addAction( date.toString(Qt::SystemLocaleLongDate) ); QDateTime time = QDateTime::currentDateTime(); m_dateTimeMenu->addSeparator()->setText(i18n("Date and Time")); m_dateTimeMenu->addAction( time.toString(Qt::TextDate) ); m_dateTimeMenu->addAction( time.toString(Qt::ISODate) ); m_dateTimeMenu->addAction( time.toString(Qt::TextDate) ); m_dateTimeMenu->addAction( time.toString(Qt::SystemLocaleShortDate) ); m_dateTimeMenu->addAction( time.toString(Qt::SystemLocaleLongDate) ); m_dateTimeMenu->exec( mapToGlobal(ui.tbDateTime->rect().bottomLeft())); } void LabelWidget::insertDateTime(QAction* action) { ui.teLabel->insertPlainText( action->text().remove('&') ); } // geometry slots /*! called when label's current horizontal position relative to its parent (left, center, right, custom ) is changed. */ void LabelWidget::positionXChanged(int index) { //Enable/disable the spinbox for the x- oordinates if the "custom position"-item is selected/deselected if (index == ui.cbPositionX->count()-1 ) ui.sbPositionX->setEnabled(true); else ui.sbPositionX->setEnabled(false); if (m_initializing) return; TextLabel::PositionWrapper position = m_label->position(); position.horizontalPosition = TextLabel::HorizontalPosition(index); for (auto* label : m_labelsList) label->setPosition(position); } /*! called when label's current horizontal position relative to its parent (top, center, bottom, custom ) is changed. */ void LabelWidget::positionYChanged(int index) { //Enable/disable the spinbox for the y-coordinates if the "custom position"-item is selected/deselected if (index == ui.cbPositionY->count()-1 ) ui.sbPositionY->setEnabled(true); else ui.sbPositionY->setEnabled(false); if (m_initializing) return; TextLabel::PositionWrapper position = m_label->position(); position.verticalPosition = TextLabel::VerticalPosition(index); for (auto* label : m_labelsList) label->setPosition(position); } void LabelWidget::customPositionXChanged(double value) { if (m_initializing) return; TextLabel::PositionWrapper position = m_label->position(); position.point.setX(Worksheet::convertToSceneUnits(value, Worksheet::Centimeter)); for (auto* label : m_labelsList) label->setPosition(position); } void LabelWidget::customPositionYChanged(double value) { if (m_initializing) return; TextLabel::PositionWrapper position = m_label->position(); position.point.setY(Worksheet::convertToSceneUnits(value, Worksheet::Centimeter)); for (auto* label : m_labelsList) label->setPosition(position); } void LabelWidget::horizontalAlignmentChanged(int index) { if (m_initializing) return; for (auto* label : m_labelsList) label->setHorizontalAlignment(TextLabel::HorizontalAlignment(index)); } void LabelWidget::verticalAlignmentChanged(int index) { if (m_initializing) return; for (auto* label : m_labelsList) label->setVerticalAlignment(TextLabel::VerticalAlignment(index)); } void LabelWidget::rotationChanged(int value) { if (m_initializing) return; for (auto* label : m_labelsList) label->setRotationAngle(value); } void LabelWidget::offsetXChanged(double value) { if (m_initializing) return; for (auto* axis : m_axesList) axis->setTitleOffsetX( Worksheet::convertToSceneUnits(value, Worksheet::Point) ); } void LabelWidget::offsetYChanged(double value) { if (m_initializing) return; for (auto* axis : m_axesList) axis->setTitleOffsetY( Worksheet::convertToSceneUnits(value, Worksheet::Point) ); } void LabelWidget::visibilityChanged(bool state) { if (m_initializing) return; for (auto* label : m_labelsList) label->setVisible(state); } //border void LabelWidget::borderShapeChanged(int index) { auto shape = (TextLabel::BorderShape)index; bool b = (shape != TextLabel::NoBorder); ui.lBorderStyle->setVisible(b); ui.cbBorderStyle->setVisible(b); ui.lBorderWidth->setVisible(b); ui.sbBorderWidth->setVisible(b); ui.lBorderColor->setVisible(b); ui.kcbBorderColor->setVisible(b); ui.lBorderOpacity->setVisible(b); ui.sbBorderOpacity->setVisible(b); if (m_initializing) return; for (auto* label : m_labelsList) label->setBorderShape(shape); } void LabelWidget::borderStyleChanged(int index) { if (m_initializing) return; auto penStyle = Qt::PenStyle(index); QPen pen; for (auto* label : m_labelsList) { pen = label->borderPen(); pen.setStyle(penStyle); label->setBorderPen(pen); } } void LabelWidget::borderColorChanged(const QColor& color) { if (m_initializing) return; QPen pen; for (auto* label : m_labelsList) { pen = label->borderPen(); pen.setColor(color); label->setBorderPen(pen); } m_initializing = true; GuiTools::updatePenStyles(ui.cbBorderStyle, color); m_initializing = false; } void LabelWidget::borderWidthChanged(double value) { if (m_initializing) return; QPen pen; for (auto* label : m_labelsList) { pen = label->borderPen(); pen.setWidthF( Worksheet::convertToSceneUnits(value, Worksheet::Point) ); label->setBorderPen(pen); } } void LabelWidget::borderOpacityChanged(int value) { if (m_initializing) return; qreal opacity = (float)value/100.; for (auto* label : m_labelsList) label->setBorderOpacity(opacity); } //********************************************************* //****** SLOTs for changes triggered in TextLabel ********* //********************************************************* void LabelWidget::labelTextWrapperChanged(const TextLabel::TextWrapper& text) { m_initializing = true; //save and restore the current cursor position after changing the text QTextCursor cursor = ui.teLabel->textCursor(); int position = cursor.position(); if (text.teXUsed) ui.teLabel->setText(text.text); else ui.teLabel->setHtml(text.text); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, position); ui.teLabel->setTextCursor(cursor); ui.tbTexUsed->setChecked(text.teXUsed); this->teXUsedChanged(text.teXUsed); m_initializing = false; } /*! * \brief Highlights the text field red if wrong latex syntax was used (null image was produced) * or something else went wrong during rendering (\sa ExpressionTextEdit::validateExpression()) */ void LabelWidget::labelTeXImageUpdated(bool valid) { if (!valid) ui.teLabel->setStyleSheet(QLatin1String("QTextEdit{background: red;}")); else ui.teLabel->setStyleSheet(QLatin1String("")); } void LabelWidget::labelTeXFontChanged(const QFont& font) { m_initializing = true; ui.kfontRequesterTeX->setFont(font); ui.sbFontSize->setValue(font.pointSize()); m_initializing = false; } -void LabelWidget::labelTeXFontColorChanged(const QColor color) { +void LabelWidget::labelFontColorChanged(const QColor color) { m_initializing = true; ui.kcbFontColor->setColor(color); m_initializing = false; } void LabelWidget::labelPositionChanged(const TextLabel::PositionWrapper& position) { m_initializing = true; ui.sbPositionX->setValue( Worksheet::convertFromSceneUnits(position.point.x(), Worksheet::Centimeter) ); ui.sbPositionY->setValue( Worksheet::convertFromSceneUnits(position.point.y(), Worksheet::Centimeter) ); ui.cbPositionX->setCurrentIndex( position.horizontalPosition ); ui.cbPositionY->setCurrentIndex( position.verticalPosition ); m_initializing = false; } void LabelWidget::labelHorizontalAlignmentChanged(TextLabel::HorizontalAlignment index) { m_initializing = true; ui.cbHorizontalAlignment->setCurrentIndex(index); m_initializing = false; } void LabelWidget::labelVerticalAlignmentChanged(TextLabel::VerticalAlignment index) { m_initializing = true; ui.cbVerticalAlignment->setCurrentIndex(index); m_initializing = false; } void LabelWidget::labelOffsetxChanged(qreal offset) { m_initializing = true; ui.sbOffsetX->setValue(Worksheet::convertFromSceneUnits(offset, Worksheet::Point)); m_initializing = false; } void LabelWidget::labelOffsetyChanged(qreal offset) { m_initializing = true; ui.sbOffsetY->setValue(Worksheet::convertFromSceneUnits(offset, Worksheet::Point)); m_initializing = false; } void LabelWidget::labelRotationAngleChanged(qreal angle) { m_initializing = true; ui.sbRotation->setValue(angle); m_initializing = false; } void LabelWidget::labelVisibleChanged(bool on) { m_initializing = true; ui.chbVisible->setChecked(on); m_initializing = false; } //border void LabelWidget::labelBorderShapeChanged(TextLabel::BorderShape shape) { m_initializing = true; ui.cbBorderShape->setCurrentIndex(shape); m_initializing = false; } void LabelWidget::labelBorderPenChanged(const QPen& pen) { m_initializing = true; if (ui.cbBorderStyle->currentIndex() != pen.style()) ui.cbBorderStyle->setCurrentIndex(pen.style()); if (ui.kcbBorderColor->color() != pen.color()) ui.kcbBorderColor->setColor(pen.color()); if (ui.sbBorderWidth->value() != pen.widthF()) ui.sbBorderWidth->setValue(Worksheet::convertFromSceneUnits(pen.widthF(),Worksheet::Point)); m_initializing = false; } void LabelWidget::labelBorderOpacityChanged(float value) { m_initializing = true; float v = (float)value*100.; ui.sbBorderOpacity->setValue(v); m_initializing = false; } //********************************************************** //******************** SETTINGS **************************** //********************************************************** void LabelWidget::load() { if(!m_label) return; m_initializing = true; ui.chbVisible->setChecked(m_label->isVisible()); //Text/TeX ui.tbTexUsed->setChecked( (bool) m_label->text().teXUsed ); if (m_label->text().teXUsed) ui.teLabel->setText(m_label->text().text); else ui.teLabel->setHtml(m_label->text().text); + ui.kcbFontColor->setColor(m_label->fontColor()); + ui.kcbBackgroundColor->setColor(m_label->backgroundColor()); this->teXUsedChanged(m_label->text().teXUsed); ui.kfontRequesterTeX->setFont(m_label->teXFont()); ui.sbFontSize->setValue( m_label->teXFont().pointSize() ); //move the cursor to the end and set the focus to the text editor QTextCursor cursor = ui.teLabel->textCursor(); cursor.movePosition(QTextCursor::End); ui.teLabel->setTextCursor(cursor); ui.teLabel->setFocus(); // Geometry ui.cbPositionX->setCurrentIndex( (int) m_label->position().horizontalPosition ); positionXChanged(ui.cbPositionX->currentIndex()); ui.sbPositionX->setValue( Worksheet::convertFromSceneUnits(m_label->position().point.x(),Worksheet::Centimeter) ); ui.cbPositionY->setCurrentIndex( (int) m_label->position().verticalPosition ); positionYChanged(ui.cbPositionY->currentIndex()); ui.sbPositionY->setValue( Worksheet::convertFromSceneUnits(m_label->position().point.y(),Worksheet::Centimeter) ); if (!m_axesList.isEmpty()) { ui.sbOffsetX->setValue( Worksheet::convertFromSceneUnits(m_axesList.first()->titleOffsetX(), Worksheet::Point) ); ui.sbOffsetY->setValue( Worksheet::convertFromSceneUnits(m_axesList.first()->titleOffsetY(), Worksheet::Point) ); } ui.cbHorizontalAlignment->setCurrentIndex( (int) m_label->horizontalAlignment() ); ui.cbVerticalAlignment->setCurrentIndex( (int) m_label->verticalAlignment() ); ui.sbRotation->setValue( m_label->rotationAngle() ); //Border ui.cbBorderShape->setCurrentIndex( m_label->borderShape() ); ui.kcbBorderColor->setColor( m_label->borderPen().color() ); ui.cbBorderStyle->setCurrentIndex( (int) m_label->borderPen().style() ); ui.sbBorderWidth->setValue( Worksheet::convertFromSceneUnits(m_label->borderPen().widthF(), Worksheet::Point) ); ui.sbBorderOpacity->setValue( round(m_label->borderOpacity()*100) ); GuiTools::updatePenStyles(ui.cbBorderStyle, ui.kcbBorderColor->color()); m_initializing = false; } void LabelWidget::loadConfig(KConfigGroup& group) { if(!m_label) return; m_initializing = true; //TeX ui.tbTexUsed->setChecked(group.readEntry("TeXUsed", (bool) m_label->text().teXUsed)); this->teXUsedChanged(m_label->text().teXUsed); ui.sbFontSize->setValue( group.readEntry("TeXFontSize", m_label->teXFont().pointSize()) ); ui.kfontRequesterTeX->setFont(group.readEntry("TeXFont", m_label->teXFont())); // Geometry ui.cbPositionX->setCurrentIndex( group.readEntry("PositionX", (int) m_label->position().horizontalPosition ) ); ui.sbPositionX->setValue( Worksheet::convertFromSceneUnits(group.readEntry("PositionXValue", m_label->position().point.x()),Worksheet::Centimeter) ); ui.cbPositionY->setCurrentIndex( group.readEntry("PositionY", (int) m_label->position().verticalPosition ) ); ui.sbPositionY->setValue( Worksheet::convertFromSceneUnits(group.readEntry("PositionYValue", m_label->position().point.y()),Worksheet::Centimeter) ); if (!m_axesList.isEmpty()) { ui.sbOffsetX->setValue( Worksheet::convertFromSceneUnits(group.readEntry("OffsetX", m_axesList.first()->titleOffsetX()), Worksheet::Point) ); ui.sbOffsetY->setValue( Worksheet::convertFromSceneUnits(group.readEntry("OffsetY", m_axesList.first()->titleOffsetY()), Worksheet::Point) ); } ui.cbHorizontalAlignment->setCurrentIndex( group.readEntry("HorizontalAlignment", (int) m_label->horizontalAlignment()) ); ui.cbVerticalAlignment->setCurrentIndex( group.readEntry("VerticalAlignment", (int) m_label->verticalAlignment()) ); ui.sbRotation->setValue( group.readEntry("Rotation", m_label->rotationAngle()) ); //Border ui.cbBorderShape->setCurrentIndex(group.readEntry("BorderShape").toInt()); ui.kcbBorderColor->setColor( group.readEntry("BorderColor", m_label->borderPen().color()) ); ui.cbBorderStyle->setCurrentIndex( group.readEntry("BorderStyle", (int)m_label->borderPen().style()) ); ui.sbBorderWidth->setValue( Worksheet::convertFromSceneUnits(group.readEntry("BorderWidth", m_label->borderPen().widthF()), Worksheet::Point) ); ui.sbBorderOpacity->setValue( group.readEntry("BorderOpacity", m_label->borderOpacity())*100 ); m_initializing = false; } void LabelWidget::saveConfig(KConfigGroup& group) { //TeX group.writeEntry("TeXUsed", ui.tbTexUsed->isChecked()); group.writeEntry("TeXFontColor", ui.kcbFontColor->color()); group.writeEntry("TeXBackgroundColor", ui.kcbBackgroundColor->color()); group.writeEntry("TeXFont", ui.kfontRequesterTeX->font()); // Geometry group.writeEntry("PositionX", ui.cbPositionX->currentIndex()); group.writeEntry("PositionXValue", Worksheet::convertToSceneUnits(ui.sbPositionX->value(),Worksheet::Centimeter) ); group.writeEntry("PositionY", ui.cbPositionY->currentIndex()); group.writeEntry("PositionYValue", Worksheet::convertToSceneUnits(ui.sbPositionY->value(),Worksheet::Centimeter) ); if (!m_axesList.isEmpty()) { group.writeEntry("OffsetX", Worksheet::convertToSceneUnits(ui.sbOffsetX->value(), Worksheet::Point) ); group.writeEntry("OffsetY", Worksheet::convertToSceneUnits(ui.sbOffsetY->value(), Worksheet::Point) ); } group.writeEntry("HorizontalAlignment", ui.cbHorizontalAlignment->currentIndex()); group.writeEntry("VerticalAlignment", ui.cbVerticalAlignment->currentIndex()); group.writeEntry("Rotation", ui.sbRotation->value()); //Border group.writeEntry("BorderShape", ui.cbBorderShape->currentIndex()); group.writeEntry("BorderStyle", ui.cbBorderStyle->currentIndex()); group.writeEntry("BorderColor", ui.kcbBorderColor->color()); group.writeEntry("BorderWidth", Worksheet::convertToSceneUnits(ui.sbBorderWidth->value(), Worksheet::Point)); group.writeEntry("BorderOpacity", ui.sbBorderOpacity->value()/100.0); } diff --git a/src/kdefrontend/widgets/LabelWidget.h b/src/kdefrontend/widgets/LabelWidget.h index b735ec551..bafbc603b 100644 --- a/src/kdefrontend/widgets/LabelWidget.h +++ b/src/kdefrontend/widgets/LabelWidget.h @@ -1,139 +1,139 @@ /*************************************************************************** File : LabelWidget.h Project : LabPlot -------------------------------------------------------------------- Copyright : (C) 2008-2016 Alexander Semke (alexander.semke@web.de) Copyright : (C) 2012-2014 Stefan Gerlach (stefan.gerlach@uni-konstanz.de) Description : label settings widget ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #ifndef LABELWIDGET_H #define LABELWIDGET_H #include "ui_labelwidget.h" #include "backend/worksheet/TextLabel.h" #include #ifdef HAVE_KF5_SYNTAX_HIGHLIGHTING #include namespace KSyntaxHighlighting { class SyntaxHighlighter; } #endif class Label; class Axis; class QMenu; class LabelWidget : public QWidget { Q_OBJECT public: explicit LabelWidget(QWidget*); void setLabels(QList); void setAxes(QList); void load(); void loadConfig(KConfigGroup&); void saveConfig(KConfigGroup&); void setNoGeometryMode(const bool); void setFixedLabelMode(const bool); private: Ui::LabelWidget ui; TextLabel* m_label; QList m_labelsList; QList m_axesList; bool m_initializing; QMenu* m_dateTimeMenu; bool m_teXEnabled; #ifdef HAVE_KF5_SYNTAX_HIGHLIGHTING KSyntaxHighlighting::SyntaxHighlighter* m_highlighter; KSyntaxHighlighting::Repository m_repository; #endif void initConnections() const; signals: void dataChanged(bool); private slots: //SLOTs for changes triggered in LabelWidget void textChanged(); void charFormatChanged(const QTextCharFormat&); void teXUsedChanged(bool); void fontColorChanged(const QColor&); void backgroundColorChanged(const QColor&); void fontBoldChanged(bool); void fontItalicChanged(bool); void fontUnderlineChanged(bool); void fontStrikeOutChanged(bool); void fontSuperScriptChanged(bool); void fontSubScriptChanged(bool); void charMenu(); void insertChar(QChar); void fontChanged(const QFont&); void teXFontChanged(const QFont&); void fontSizeChanged(int); void dateTimeMenu(); void insertDateTime(QAction*); void positionXChanged(int); void positionYChanged(int); void customPositionXChanged(double); void customPositionYChanged(double); void horizontalAlignmentChanged(int); void verticalAlignmentChanged(int); void rotationChanged(int); void offsetXChanged(double); void offsetYChanged(double); void borderShapeChanged(int); void borderStyleChanged(int); void borderColorChanged(const QColor&); void borderWidthChanged(double); void borderOpacityChanged(int); void visibilityChanged(bool); //SLOTs for changes triggered in TextLabel void labelTextWrapperChanged(const TextLabel::TextWrapper&); void labelTeXImageUpdated(bool); void labelTeXFontChanged(const QFont&); - void labelTeXFontColorChanged(const QColor); + void labelFontColorChanged(const QColor); void labelPositionChanged(const TextLabel::PositionWrapper&); void labelHorizontalAlignmentChanged(TextLabel::HorizontalAlignment); void labelVerticalAlignmentChanged(TextLabel::VerticalAlignment); void labelOffsetxChanged(qreal); void labelOffsetyChanged(qreal); void labelRotationAngleChanged(qreal); void labelBorderShapeChanged(TextLabel::BorderShape); void labelBorderPenChanged(const QPen&); void labelBorderOpacityChanged(float); void labelVisibleChanged(bool); }; #endif //LABELWIDGET_H