File indexing completed on 2025-09-14 03:43:30

0001 /*
0002     File                 : LabelWidget.cc
0003     Project              : LabPlot
0004     Description          : label settings widget
0005     --------------------------------------------------------------------
0006     SPDX-FileCopyrightText: 2008-2022 Alexander Semke <alexander.semke@web.de>
0007     SPDX-FileCopyrightText: 2012-2022 Stefan Gerlach <stefan.gerlach@uni-konstanz.de>
0008     SPDX-License-Identifier: GPL-2.0-or-later
0009 */
0010 
0011 #include "LabelWidget.h"
0012 #include "backend/core/Settings.h"
0013 #include "backend/worksheet/Background.h"
0014 #include "backend/worksheet/Worksheet.h"
0015 #include "backend/worksheet/plots/PlotArea.h"
0016 #include "backend/worksheet/plots/cartesian/Axis.h"
0017 #include "backend/worksheet/plots/cartesian/CartesianPlot.h"
0018 #include "backend/worksheet/plots/cartesian/CartesianPlotLegend.h"
0019 #include "kdefrontend/GuiTools.h"
0020 #include "kdefrontend/dockwidgets/BaseDock.h"
0021 #include "tools/TeXRenderer.h"
0022 
0023 #include <KCharSelect>
0024 #include <KLocalizedString>
0025 #ifdef HAVE_KF5_SYNTAX_HIGHLIGHTING
0026 #include <KSyntaxHighlighting/Definition>
0027 #include <KSyntaxHighlighting/SyntaxHighlighter>
0028 #include <KSyntaxHighlighting/Theme>
0029 #endif
0030 #include <KMessageWidget>
0031 
0032 #include <QFile>
0033 #include <QMenu>
0034 #include <QSettings>
0035 #include <QSplitter>
0036 #include <QStandardItemModel>
0037 #include <QTextDocumentFragment>
0038 #include <QWidgetAction>
0039 
0040 #include <gsl/gsl_const_cgs.h>
0041 
0042 /*!
0043  * Setting label property without changing the content. This is needed,
0044  * because the text is html formatted and therefore setting properties
0045  * is not that easy
0046  */
0047 #define SETLABELTEXTPROPERTY(TextEditFunction, TextEditArgument)                                                                                               \
0048     auto cursor = ui.teLabel->textCursor();                                                                                                                    \
0049     int cursorAnchor = cursor.anchor(), cursorPos = cursor.position();                                                                                         \
0050     /* move position with right allows only positive numbers (from left to right) */                                                                           \
0051     if (cursorAnchor > cursorPos)                                                                                                                              \
0052         qSwap(cursorAnchor, cursorPos);                                                                                                                        \
0053     bool cursorHasSelection = cursor.hasSelection();                                                                                                           \
0054     if (!cursorHasSelection)                                                                                                                                   \
0055         ui.teLabel->selectAll();                                                                                                                               \
0056                                                                                                                                                                \
0057     ui.teLabel->TextEditFunction(TextEditArgument);                                                                                                            \
0058     QTextEdit te;                                                                                                                                              \
0059     for (auto& label : m_labelsList) {                                                                                                                         \
0060         TextLabel::TextWrapper w = label->text();                                                                                                              \
0061         te.setText(w.text);                                                                                                                                    \
0062         if (!cursorHasSelection)                                                                                                                               \
0063             te.selectAll();                                                                                                                                    \
0064         else {                                                                                                                                                 \
0065             /* Set cursor as it is in the ui.teLabel*/                                                                                                         \
0066             auto c = te.textCursor();                                                                                                                          \
0067             c.setPosition(cursorAnchor);                                                                                                                       \
0068             c.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, cursorPos - cursorAnchor);                                                             \
0069             te.setTextCursor(c);                                                                                                                               \
0070         }                                                                                                                                                      \
0071         te.TextEditFunction(TextEditArgument);                                                                                                                 \
0072         w.text = te.toHtml();                                                                                                                                  \
0073         label->setText(w);                                                                                                                                     \
0074     }                                                                                                                                                          \
0075                                                                                                                                                                \
0076     if (!cursorHasSelection) {                                                                                                                                 \
0077         cursor.clearSelection();                                                                                                                               \
0078         ui.teLabel->setTextCursor(cursor);                                                                                                                     \
0079     }
0080 
0081 /*!
0082     \class LabelWidget
0083     \brief Widget for editing the properties of a TextLabel object, mostly used in an appropriate dock widget.
0084 
0085     In order the properties of the label to be shown, \c loadConfig() has to be called with the corresponding KConfigGroup
0086     (settings for a label in *Plot, Axis etc. or for an independent label on the worksheet).
0087 
0088     \ingroup kdefrontend
0089  */
0090 LabelWidget::LabelWidget(QWidget* parent)
0091     : BaseDock(parent)
0092     , m_dateTimeMenu(new QMenu(this)) {
0093     ui.setupUi(this);
0094     setBaseWidgets(ui.leName, ui.teComment);
0095     setVisibilityWidgets(ui.chbVisible);
0096 
0097     // set the minimum size of the text edit widget to one row of a QLabel
0098     ui.teLabel->setMinimumHeight(ui.lName->height());
0099 
0100     // adjust the layout margins
0101     if (auto* l = dynamic_cast<QGridLayout*>(layout())) {
0102         l->setContentsMargins(2, 2, 2, 2);
0103         l->setHorizontalSpacing(2);
0104         l->setVerticalSpacing(2);
0105     }
0106 
0107     const KConfigGroup group = Settings::group(QStringLiteral("Settings_General"));
0108     m_units = (BaseDock::Units)group.readEntry("Units", (int)BaseDock::Units::Metric);
0109     if (m_units == BaseDock::Units::Imperial)
0110         m_worksheetUnit = Worksheet::Unit::Inch;
0111 
0112     m_dateTimeMenu->setSeparatorsCollapsible(false); // we don't want the first separator to be removed
0113 
0114     QString msg = i18n("Use logical instead of absolute coordinates to specify the position on the plot");
0115     ui.chbBindLogicalPos->setToolTip(msg);
0116 
0117     ui.sbBorderWidth->setMinimum(0);
0118 
0119     // Icons
0120     ui.tbFontBold->setIcon(QIcon::fromTheme(QLatin1String("format-text-bold")));
0121     ui.tbFontItalic->setIcon(QIcon::fromTheme(QLatin1String("format-text-italic")));
0122     ui.tbFontUnderline->setIcon(QIcon::fromTheme(QLatin1String("format-text-underline")));
0123     ui.tbFontStrikeOut->setIcon(QIcon::fromTheme(QLatin1String("format-text-strikethrough")));
0124     ui.tbFontSuperScript->setIcon(QIcon::fromTheme(QLatin1String("format-text-superscript")));
0125     ui.tbFontSubScript->setIcon(QIcon::fromTheme(QLatin1String("format-text-subscript")));
0126     ui.tbSymbols->setIcon(QIcon::fromTheme(QLatin1String("labplot-format-text-symbol")));
0127     ui.tbDateTime->setIcon(QIcon::fromTheme(QLatin1String("chronometer")));
0128 
0129     ui.tbFontBold->setToolTip(i18n("Bold"));
0130     ui.tbFontItalic->setToolTip(i18n("Italic"));
0131     ui.tbFontUnderline->setToolTip(i18n("Underline"));
0132     ui.tbFontStrikeOut->setToolTip(i18n("Strike Out"));
0133     ui.tbFontSuperScript->setToolTip(i18n("Super Script"));
0134     ui.tbFontSubScript->setToolTip(i18n("Sub-Script"));
0135     ui.tbSymbols->setToolTip(i18n("Insert Symbol"));
0136     ui.tbDateTime->setToolTip(i18n("Insert Date/Time"));
0137 
0138     // Positioning and alignment
0139     ui.cbPositionX->addItem(i18n("Left"));
0140     ui.cbPositionX->addItem(i18n("Center"));
0141     ui.cbPositionX->addItem(i18n("Right"));
0142     ui.cbPositionX->addItem(i18n("Relative to plot"));
0143 
0144     ui.cbPositionY->addItem(i18n("Top"));
0145     ui.cbPositionY->addItem(i18n("Center"));
0146     ui.cbPositionY->addItem(i18n("Bottom"));
0147     ui.cbPositionY->addItem(i18n("Relative to plot"));
0148 
0149     QString suffix;
0150     if (m_units == BaseDock::Units::Metric)
0151         suffix = QLatin1String(" cm");
0152     else
0153         suffix = QLatin1String(" in");
0154 
0155     ui.sbPositionX->setSuffix(suffix);
0156     ui.sbPositionY->setSuffix(suffix);
0157 
0158     ui.cbHorizontalAlignment->addItem(i18n("Left"));
0159     ui.cbHorizontalAlignment->addItem(i18n("Center"));
0160     ui.cbHorizontalAlignment->addItem(i18n("Right"));
0161 
0162     ui.cbVerticalAlignment->addItem(i18n("Top"));
0163     ui.cbVerticalAlignment->addItem(i18n("Center"));
0164     ui.cbVerticalAlignment->addItem(i18n("Bottom"));
0165 
0166     ui.cbBorderShape->addItem(i18n("No Border"));
0167     ui.cbBorderShape->addItem(i18n("Rectangle"));
0168     ui.cbBorderShape->addItem(i18n("Ellipse"));
0169     ui.cbBorderShape->addItem(i18n("Round sided rectangle"));
0170     ui.cbBorderShape->addItem(i18n("Round corner rectangle"));
0171     ui.cbBorderShape->addItem(i18n("Inwards round corner rectangle"));
0172     ui.cbBorderShape->addItem(i18n("Dented border rectangle"));
0173     ui.cbBorderShape->addItem(i18n("Cuboid"));
0174     ui.cbBorderShape->addItem(i18n("Up Pointing rectangle"));
0175     ui.cbBorderShape->addItem(i18n("Down Pointing rectangle"));
0176     ui.cbBorderShape->addItem(i18n("Left Pointing rectangle"));
0177     ui.cbBorderShape->addItem(i18n("Right Pointing rectangle"));
0178 
0179     ui.cbBorderStyle->addItem(i18n("No line"));
0180     ui.cbBorderStyle->addItem(i18n("Solid line"));
0181     ui.cbBorderStyle->addItem(i18n("Dash line"));
0182     ui.cbBorderStyle->addItem(i18n("Dot line"));
0183     ui.cbBorderStyle->addItem(i18n("Dash dot line"));
0184     ui.cbBorderStyle->addItem(i18n("Dash dot dot line"));
0185 
0186     ui.kcbBackgroundColor->setAlphaChannelEnabled(true);
0187     ui.kcbBackgroundColor->setColor(QColor(0, 0, 0, 0)); // transparent
0188     ui.kcbFontColor->setAlphaChannelEnabled(true);
0189     ui.kcbFontColor->setColor(QColor(255, 255, 255, 255)); // black
0190     ui.kcbBorderColor->setAlphaChannelEnabled(true);
0191     ui.kcbBorderColor->setColor(QColor(255, 255, 255, 255)); // black
0192 
0193     // Text mode
0194     ui.cbMode->addItem(QIcon::fromTheme(QLatin1String("text-x-plain")), i18n("Text"));
0195     ui.cbMode->addItem(QIcon::fromTheme(QLatin1String("text-x-tex")), i18n("LaTeX"));
0196 #ifdef HAVE_DISCOUNT
0197     ui.cbMode->addItem(QIcon::fromTheme(QLatin1String("text-x-markdown")), i18n("Markdown"));
0198 #endif
0199 
0200     msg = i18n(
0201         "Text setting mode:"
0202         "<ul>"
0203         "<li>Text - text setting using rich-text formatting</li>"
0204         "<li>LaTeX - text setting using LaTeX, installation of LaTeX required</li>"
0205 #ifdef HAVE_DISCOUNT
0206         "<li>Markdown - text setting using Markdown markup language</li>"
0207 #endif
0208         "</ul>");
0209     ui.cbMode->setToolTip(msg);
0210 
0211     // check whether LaTeX is available and deactivate the item in the combobox, if not.
0212     // in case LaTeX was used to generate the text label in the stored project
0213     // and no LaTeX is available on the target system, the disabled LaTeX item is selected
0214     // in the combobox in load() and the user still can switch to the non-latex mode.
0215     m_teXEnabled = TeXRenderer::enabled();
0216     if (!m_teXEnabled) {
0217         const auto* model = qobject_cast<QStandardItemModel*>(ui.cbMode->model());
0218         model->item(1)->setEnabled(false);
0219     }
0220 
0221 #ifdef HAVE_KF5_SYNTAX_HIGHLIGHTING
0222     m_highlighter = new KSyntaxHighlighting::SyntaxHighlighter(ui.teLabel->document());
0223     m_highlighter->setTheme(GuiTools::isDarkMode() ? m_repository.defaultTheme(KSyntaxHighlighting::Repository::DarkTheme)
0224                                                    : m_repository.defaultTheme(KSyntaxHighlighting::Repository::LightTheme));
0225 #endif
0226 
0227     m_messageWidget = new KMessageWidget(this);
0228     m_messageWidget->setMessageType(KMessageWidget::Error);
0229     m_messageWidget->setWordWrap(true);
0230     auto* gridLayout = qobject_cast<QGridLayout*>(layout());
0231     gridLayout->addWidget(m_messageWidget, 2, 3);
0232     m_messageWidget->hide(); // will be shown later once there is a latex render result
0233 
0234     // SLOTS
0235 
0236     // text properties
0237     connect(ui.cbMode, QOverload<int>::of(&KComboBox::currentIndexChanged), this, &LabelWidget::modeChanged);
0238     connect(ui.teLabel, &ResizableTextEdit::textChanged, this, &LabelWidget::textChanged);
0239     connect(ui.teLabel, &ResizableTextEdit::currentCharFormatChanged, this, &LabelWidget::charFormatChanged);
0240     connect(ui.kcbFontColor, &KColorButton::changed, this, &LabelWidget::fontColorChanged);
0241     connect(ui.kcbBackgroundColor, &KColorButton::changed, this, &LabelWidget::backgroundColorChanged);
0242     connect(ui.tbFontBold, &QToolButton::clicked, this, &LabelWidget::fontBoldChanged);
0243     connect(ui.tbFontItalic, &QToolButton::clicked, this, &LabelWidget::fontItalicChanged);
0244     connect(ui.tbFontUnderline, &QToolButton::clicked, this, &LabelWidget::fontUnderlineChanged);
0245     connect(ui.tbFontStrikeOut, &QToolButton::clicked, this, &LabelWidget::fontStrikeOutChanged);
0246     connect(ui.tbFontSuperScript, &QToolButton::clicked, this, &LabelWidget::fontSuperScriptChanged);
0247     connect(ui.tbFontSubScript, &QToolButton::clicked, this, &LabelWidget::fontSubScriptChanged);
0248     connect(ui.tbSymbols, &QToolButton::clicked, this, &LabelWidget::charMenu);
0249     connect(ui.tbDateTime, &QToolButton::clicked, this, &LabelWidget::dateTimeMenu);
0250     connect(m_dateTimeMenu, &QMenu::triggered, this, &LabelWidget::insertDateTime);
0251     connect(ui.kfontRequester, &KFontRequester::fontSelected, this, &LabelWidget::fontChanged);
0252     connect(ui.kfontRequesterTeX, &KFontRequester::fontSelected, this, &LabelWidget::teXFontChanged);
0253     connect(ui.sbFontSize, QOverload<int>::of(&QSpinBox::valueChanged), this, &LabelWidget::fontSizeChanged);
0254 
0255     // geometry
0256     connect(ui.cbPositionX, QOverload<int>::of(&KComboBox::currentIndexChanged), this, &LabelWidget::positionXChanged);
0257     connect(ui.cbPositionY, QOverload<int>::of(&KComboBox::currentIndexChanged), this, &LabelWidget::positionYChanged);
0258     connect(ui.sbPositionX, QOverload<double>::of(&NumberSpinBox::valueChanged), this, &LabelWidget::customPositionXChanged);
0259     connect(ui.sbPositionY, QOverload<double>::of(&NumberSpinBox::valueChanged), this, &LabelWidget::customPositionYChanged);
0260     connect(ui.cbHorizontalAlignment, QOverload<int>::of(&KComboBox::currentIndexChanged), this, &LabelWidget::horizontalAlignmentChanged);
0261     connect(ui.cbVerticalAlignment, QOverload<int>::of(&KComboBox::currentIndexChanged), this, &LabelWidget::verticalAlignmentChanged);
0262 
0263     connect(ui.sbPositionXLogical, QOverload<double>::of(&NumberSpinBox::valueChanged), this, &LabelWidget::positionXLogicalChanged);
0264     connect(ui.dtePositionXLogical, &UTCDateTimeEdit::mSecsSinceEpochUTCChanged, this, &LabelWidget::positionXLogicalDateTimeChanged);
0265     connect(ui.sbPositionYLogical, QOverload<double>::of(&NumberSpinBox::valueChanged), this, &LabelWidget::positionYLogicalChanged);
0266     // TODO?: connect(ui.dtePositionYLogical, &UTCDateTimeEdit::mSecsSinceEpochUTCChanged, this, &LabelWidget::positionYLogicalDateTimeChanged);
0267     connect(ui.sbRotation, QOverload<int>::of(&QSpinBox::valueChanged), this, &LabelWidget::rotationChanged);
0268     connect(ui.sbOffsetX, QOverload<double>::of(&NumberSpinBox::valueChanged), this, &LabelWidget::offsetXChanged);
0269     connect(ui.sbOffsetY, QOverload<double>::of(&NumberSpinBox::valueChanged), this, &LabelWidget::offsetYChanged);
0270 
0271     connect(ui.chbLock, &QCheckBox::clicked, this, &LabelWidget::lockChanged);
0272     connect(ui.chbBindLogicalPos, &QCheckBox::clicked, this, &LabelWidget::bindingChanged);
0273     connect(ui.chbShowPlaceholderText, &QCheckBox::toggled, this, &LabelWidget::showPlaceholderTextChanged);
0274 
0275     // Border
0276     connect(ui.cbBorderShape, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &LabelWidget::borderShapeChanged);
0277     connect(ui.cbBorderStyle, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &LabelWidget::borderStyleChanged);
0278     connect(ui.kcbBorderColor, &KColorButton::changed, this, &LabelWidget::borderColorChanged);
0279     connect(ui.sbBorderWidth, QOverload<double>::of(&NumberSpinBox::valueChanged), this, &LabelWidget::borderWidthChanged);
0280     connect(ui.sbBorderOpacity, QOverload<int>::of(&QSpinBox::valueChanged), this, &LabelWidget::borderOpacityChanged);
0281 
0282     // TODO: https://bugreports.qt.io/browse/QTBUG-25420
0283     ui.tbFontUnderline->hide();
0284     ui.tbFontStrikeOut->hide();
0285 }
0286 
0287 void LabelWidget::setLabels(QList<TextLabel*> labels) {
0288     m_labelsList = labels;
0289     m_axesList.clear();
0290     m_label = labels.first();
0291     setAspects(labels);
0292 
0293     ui.lOffsetX->hide();
0294     ui.lOffsetY->hide();
0295 
0296     ui.sbOffsetX->hide();
0297     ui.sbOffsetY->hide();
0298 
0299     // show the text fields for name and comment if the label is not hidden (i.e. not a plot title, etc.)
0300     bool visible = !m_label->hidden();
0301     ui.lName->setVisible(visible);
0302     ui.leName->setVisible(visible);
0303     ui.lComment->setVisible(visible);
0304     ui.teComment->setVisible(visible);
0305 
0306     this->load();
0307     initConnections();
0308     updateBackground();
0309     updateLocale();
0310 
0311     // hide the option "Visible" if the label is child of a InfoElement,
0312     // the label is what the user identifies with the info element itself
0313     visible = (m_label->parentAspect()->type() != AspectType::InfoElement);
0314     ui.chbVisible->setVisible(visible);
0315 
0316     // hide the option "Lock" if the label is child of an axis, the label cannot be freely moved in this case
0317     visible = (m_label->parentAspect()->type() != AspectType::Axis);
0318     ui.chbLock->setVisible(visible);
0319 
0320     // resize the widget to take the minimal height
0321     layout()->activate();
0322     const auto s = QSize(this->width(), 0).expandedTo(minimumSize());
0323     if (s.height() > 0)
0324         resize(s);
0325 }
0326 
0327 void LabelWidget::setAxes(QList<Axis*> axes) {
0328     m_labelsList.clear();
0329     for (const auto* axis : axes) {
0330         DEBUG(Q_FUNC_INFO << ", axis TITLE = " << axis->title())
0331         m_labelsList.append(axis->title());
0332         connect(axis, &Axis::titleOffsetXChanged, this, &LabelWidget::labelOffsetXChanged);
0333         connect(axis, &Axis::titleOffsetYChanged, this, &LabelWidget::labelOffsetYChanged);
0334         connect(axis->title(), &TextLabel::rotationAngleChanged, this, &LabelWidget::labelRotationAngleChanged);
0335     }
0336 
0337     m_axesList = axes;
0338     m_label = m_labelsList.first();
0339     setAspects(m_labelsList);
0340 
0341     ui.lName->hide();
0342     ui.leName->hide();
0343     ui.lComment->hide();
0344     ui.teComment->hide();
0345 
0346     this->load();
0347     initConnections();
0348     updateBackground();
0349     updateLocale();
0350 
0351     // resize the widget to take the minimal height
0352     layout()->activate();
0353     const auto s = QSize(this->width(), 0).expandedTo(minimumSize());
0354     if (s.height() > 0)
0355         resize(s);
0356 }
0357 
0358 /*!
0359  * this function keeps the background color of the TextEdit in LabelWidget in sync with
0360  * the background color of the parent aspect. This is to avoid the situations where the
0361  * text wouldn't be readable anymore if the foreground color is close or equal to the
0362  * background color of TextEdit - e.g., a label with white foreground color on a dark worksheet
0363  * and with white background color in TextEdit (desktop default color).
0364  *
0365  * Called if the background color of the parent aspect has changed.
0366  */
0367 void LabelWidget::updateBackground() const {
0368     // if latex or markdown mode is used, use the default palette from the desktop theme
0369     // since we have additional highlighting for latex and markdown and we don't want it to
0370     // collide with the modified background color of QTextEdit. Modify it only for rich-text.
0371     const auto mode = static_cast<TextLabel::Mode>(ui.cbMode->currentIndex());
0372     if (mode != TextLabel::Mode::Text) {
0373         ui.teLabel->setPalette(QPalette());
0374         return;
0375     }
0376 
0377     QColor color(Qt::white);
0378     const auto type = m_label->parentAspect()->type();
0379     if (type == AspectType::Worksheet)
0380         color = static_cast<const Worksheet*>(m_label->parentAspect())->background()->firstColor();
0381     else if (type == AspectType::CartesianPlot)
0382         color = static_cast<CartesianPlot*>(m_label->parentAspect())->plotArea()->background()->firstColor();
0383     else if (type == AspectType::CartesianPlotLegend)
0384         color = static_cast<const CartesianPlotLegend*>(m_label->parentAspect())->background()->firstColor();
0385     else if (type == AspectType::InfoElement || type == AspectType::Axis)
0386         color = static_cast<CartesianPlot*>(m_label->parentAspect()->parentAspect())->plotArea()->background()->firstColor();
0387     else
0388         DEBUG(Q_FUNC_INFO << ", Not handled type:" << static_cast<int>(type));
0389 
0390     auto p = ui.teLabel->palette();
0391     // QDEBUG(Q_FUNC_INFO << ", color = " << color)
0392     p.setColor(QPalette::Base, color);
0393     ui.teLabel->setPalette(p);
0394 }
0395 
0396 void LabelWidget::initConnections() {
0397     while (!m_connections.isEmpty())
0398         disconnect(m_connections.takeFirst());
0399     m_connections << connect(m_label, &TextLabel::textWrapperChanged, this, &LabelWidget::labelTextWrapperChanged);
0400     m_connections << connect(m_label, &TextLabel::teXImageUpdated, this, &LabelWidget::labelTeXImageUpdated);
0401     m_connections << connect(m_label, &TextLabel::teXFontChanged, this, &LabelWidget::labelTeXFontChanged);
0402     m_connections << connect(m_label, &TextLabel::fontColorChanged, this, &LabelWidget::labelFontColorChanged);
0403     m_connections << connect(m_label, &TextLabel::backgroundColorChanged, this, &LabelWidget::labelBackgroundColorChanged);
0404     m_connections << connect(m_label, &TextLabel::positionChanged, this, &LabelWidget::labelPositionChanged);
0405 
0406     m_connections << connect(m_label, &TextLabel::positionLogicalChanged, this, &LabelWidget::labelPositionLogicalChanged);
0407     m_connections << connect(m_label, &TextLabel::coordinateBindingEnabledChanged, this, &LabelWidget::labelCoordinateBindingEnabledChanged);
0408     m_connections << connect(m_label, &TextLabel::horizontalAlignmentChanged, this, &LabelWidget::labelHorizontalAlignmentChanged);
0409     m_connections << connect(m_label, &TextLabel::verticalAlignmentChanged, this, &LabelWidget::labelVerticalAlignmentChanged);
0410     m_connections << connect(m_label, &TextLabel::rotationAngleChanged, this, &LabelWidget::labelRotationAngleChanged);
0411     m_connections << connect(m_label, &TextLabel::borderShapeChanged, this, &LabelWidget::labelBorderShapeChanged);
0412     m_connections << connect(m_label, &TextLabel::borderPenChanged, this, &LabelWidget::labelBorderPenChanged);
0413     m_connections << connect(m_label, &TextLabel::borderOpacityChanged, this, &LabelWidget::labelBorderOpacityChanged);
0414     m_connections << connect(m_label, &TextLabel::lockChanged, this, &LabelWidget::labelLockChanged);
0415 
0416     if (!m_label->parentAspect()) {
0417         QDEBUG(Q_FUNC_INFO << ", LABEL " << m_label << " HAS NO PARENT!")
0418         return;
0419     }
0420     AspectType type = m_label->parentAspect()->type();
0421     if (type == AspectType::Worksheet) {
0422         auto* worksheet = static_cast<const Worksheet*>(m_label->parentAspect());
0423         connect(worksheet->background(), &Background::firstColorChanged, this, &LabelWidget::updateBackground);
0424     } else if (type == AspectType::CartesianPlot) {
0425         auto* plotArea = static_cast<CartesianPlot*>(m_label->parentAspect())->plotArea();
0426         connect(plotArea->background(), &Background::firstColorChanged, this, &LabelWidget::updateBackground);
0427     } else if (type == AspectType::CartesianPlotLegend) {
0428         auto* legend = static_cast<const CartesianPlotLegend*>(m_label->parentAspect());
0429         connect(legend->background(), &Background::firstColorChanged, this, &LabelWidget::updateBackground);
0430     } else if (type == AspectType::Axis) {
0431         auto* plotArea = static_cast<CartesianPlot*>(m_label->parentAspect()->parentAspect())->plotArea();
0432         connect(plotArea->background(), &Background::firstColorChanged, this, &LabelWidget::updateBackground);
0433     }
0434 }
0435 
0436 /*!
0437  * enables/disables the "fixed label"-mode, used when displaying
0438  * the properties of axis' title label.
0439  * In this mode, in the "geometry"-part only the offset (offset to the axis)
0440  * and the rotation of the label are available.
0441  */
0442 void LabelWidget::setFixedLabelMode(const bool b) {
0443     ui.lPositionX->setVisible(!b);
0444     ui.cbPositionX->setVisible(!b);
0445     ui.sbPositionX->setVisible(!b);
0446     ui.lPositionY->setVisible(!b);
0447     ui.cbPositionY->setVisible(!b);
0448     ui.sbPositionY->setVisible(!b);
0449     ui.lHorizontalAlignment->setVisible(!b);
0450     ui.cbHorizontalAlignment->setVisible(!b);
0451     ui.lVerticalAlignment->setVisible(!b);
0452     ui.cbVerticalAlignment->setVisible(!b);
0453     ui.lOffsetX->setVisible(b);
0454     ui.lOffsetY->setVisible(b);
0455     ui.sbOffsetX->setVisible(b);
0456     ui.sbOffsetY->setVisible(b);
0457 }
0458 
0459 /*!
0460  * enables/disables all geometry relevant widgets.
0461  * Used when displaying legend's title label.
0462  */
0463 void LabelWidget::setGeometryAvailable(const bool b) {
0464     ui.lGeometry->setVisible(b);
0465     ui.lPositionX->setVisible(b);
0466     ui.cbPositionX->setVisible(b);
0467     ui.sbPositionX->setVisible(b);
0468     ui.lPositionY->setVisible(b);
0469     ui.cbPositionY->setVisible(b);
0470     ui.sbPositionY->setVisible(b);
0471     ui.lHorizontalAlignment->setVisible(b);
0472     ui.cbHorizontalAlignment->setVisible(b);
0473     ui.lVerticalAlignment->setVisible(b);
0474     ui.cbVerticalAlignment->setVisible(b);
0475     ui.lOffsetX->setVisible(b);
0476     ui.lOffsetY->setVisible(b);
0477     ui.sbOffsetX->setVisible(b);
0478     ui.sbOffsetY->setVisible(b);
0479     ui.lRotation->setVisible(b);
0480     ui.sbRotation->setVisible(b);
0481 }
0482 
0483 /*!
0484  * enables/disables all border relevant widgets.
0485  * Used when displaying legend's title label.
0486  */
0487 void LabelWidget::setBorderAvailable(bool b) {
0488     ui.lBorder->setVisible(b);
0489     ui.lBorderShape->setVisible(b);
0490     ui.cbBorderShape->setVisible(b);
0491     ui.lBorderStyle->setVisible(b);
0492     ui.cbBorderStyle->setVisible(b);
0493     ui.lBorderColor->setVisible(b);
0494     ui.kcbBorderColor->setVisible(b);
0495     ui.lBorderWidth->setVisible(b);
0496     ui.sbBorderWidth->setVisible(b);
0497     ui.lBorderOpacity->setVisible(b);
0498     ui.sbBorderOpacity->setVisible(b);
0499 }
0500 
0501 void LabelWidget::updateUnits() {
0502     const KConfigGroup group = Settings::group(QStringLiteral("Settings_General"));
0503     BaseDock::Units units = (BaseDock::Units)group.readEntry("Units", (int)BaseDock::Units::Metric);
0504     if (units == m_units)
0505         return;
0506 
0507     m_units = units;
0508     CONDITIONAL_LOCK_RETURN;
0509     QString suffix;
0510     auto xPosition = ui.cbPositionX->currentIndex();
0511     auto yPosition = ui.cbPositionY->currentIndex();
0512     if (m_units == BaseDock::Units::Metric) {
0513         // convert from imperial to metric
0514         m_worksheetUnit = Worksheet::Unit::Centimeter;
0515         suffix = QLatin1String(" cm");
0516         if (xPosition != static_cast<int>(WorksheetElement::HorizontalPosition::Relative))
0517             ui.sbPositionX->setValue(ui.sbPositionX->value() * GSL_CONST_CGS_INCH);
0518         if (yPosition != static_cast<int>(WorksheetElement::VerticalPosition::Relative))
0519             ui.sbPositionY->setValue(ui.sbPositionY->value() * GSL_CONST_CGS_INCH);
0520     } else {
0521         // convert from metric to imperial
0522         m_worksheetUnit = Worksheet::Unit::Inch;
0523         suffix = QLatin1String(" in");
0524         if (xPosition != static_cast<int>(WorksheetElement::HorizontalPosition::Relative))
0525             ui.sbPositionX->setValue(ui.sbPositionX->value() / GSL_CONST_CGS_INCH);
0526         if (yPosition != static_cast<int>(WorksheetElement::VerticalPosition::Relative))
0527             ui.sbPositionY->setValue(ui.sbPositionY->value() / GSL_CONST_CGS_INCH);
0528     }
0529 
0530     if (xPosition != static_cast<int>(WorksheetElement::HorizontalPosition::Relative))
0531         ui.sbPositionX->setSuffix(suffix);
0532     if (yPosition != static_cast<int>(WorksheetElement::VerticalPosition::Relative))
0533         ui.sbPositionY->setSuffix(suffix);
0534 }
0535 
0536 void LabelWidget::updateLocale() {
0537     const auto numberLocale = QLocale();
0538     ui.sbPositionX->setLocale(numberLocale);
0539     ui.sbPositionY->setLocale(numberLocale);
0540     ui.sbOffsetX->setLocale(numberLocale);
0541     ui.sbOffsetY->setLocale(numberLocale);
0542     ui.sbBorderWidth->setLocale(numberLocale);
0543 }
0544 
0545 //**********************************************************
0546 //****** SLOTs for changes triggered in LabelWidget ********
0547 //**********************************************************
0548 
0549 // text formatting slots
0550 
0551 void LabelWidget::textChanged() {
0552     // QDEBUG("############\n" << Q_FUNC_INFO << ", label text =" << m_label->text().text)
0553     CONDITIONAL_LOCK_RETURN;
0554 
0555     const QString plainText = ui.teLabel->toPlainText();
0556     QTextEdit te(ui.chbShowPlaceholderText->isChecked() ? m_label->text().textPlaceholder : m_label->text().text);
0557     bool plainTextChanged = plainText != te.toPlainText();
0558 
0559     auto mode = static_cast<TextLabel::Mode>(ui.cbMode->currentIndex());
0560     switch (mode) {
0561     case TextLabel::Mode::LaTeX:
0562     case TextLabel::Mode::Markdown: {
0563         QString text = ui.teLabel->toPlainText();
0564         TextLabel::TextWrapper wrapper;
0565         wrapper.mode = mode;
0566 
0567         if (!ui.chbShowPlaceholderText->isChecked()) {
0568             if (plainTextChanged) {
0569                 // set text only if the plain text change. otherwise the text is changed
0570                 // already in the setter functions
0571                 wrapper.text = text;
0572                 for (auto* label : m_labelsList) {
0573                     wrapper.textPlaceholder = label->text().textPlaceholder;
0574                     wrapper.allowPlaceholder = label->text().allowPlaceholder;
0575                     label->setText(wrapper);
0576                 }
0577             }
0578         } else {
0579             // No need to compare if plainTextChanged
0580             // Change it always.
0581             wrapper.textPlaceholder = text;
0582             for (auto* label : m_labelsList) {
0583                 wrapper.allowPlaceholder = label->text().allowPlaceholder;
0584                 wrapper.text = label->text().text;
0585                 label->setPlaceholderText(wrapper);
0586             }
0587         }
0588         break;
0589     }
0590     case TextLabel::Mode::Text: {
0591         // QDEBUG(Q_FUNC_INFO << ", color = " << m_label->fontColor())
0592         // QDEBUG(Q_FUNC_INFO << ", background color = " << m_label->backgroundColor())
0593         // QDEBUG(Q_FUNC_INFO << ", format color = " << ui.teLabel->currentCharFormat().foreground().color())
0594         // QDEBUG(Q_FUNC_INFO << ", Plain TEXT = " << ui.teLabel->toPlainText() << '\n')
0595         // QDEBUG(Q_FUNC_INFO << ", OLD TEXT =" << m_label->text().text << '\n')
0596         // save an empty string instead of html with empty body if no text is in QTextEdit
0597         QString text;
0598         if (!ui.teLabel->toPlainText().isEmpty()) {
0599             // if the current or previous label text is empty, set the color first
0600             QTextEdit pte(m_label->text().text); // te with previous text
0601             if (m_label->text().text.isEmpty() || pte.toPlainText().isEmpty()) {
0602                 // DEBUG("EMPTY TEXT")
0603                 ui.teLabel->selectAll();
0604                 ui.teLabel->setTextColor(m_label->fontColor());
0605                 ui.teLabel->setTextBackgroundColor(m_label->backgroundColor());
0606                 // clear the selection after setting the color
0607                 auto tc = ui.teLabel->textCursor();
0608                 tc.setPosition(tc.selectionEnd());
0609                 ui.teLabel->setTextCursor(tc);
0610             }
0611 
0612             text = ui.teLabel->toHtml();
0613         }
0614 
0615         QDEBUG(Q_FUNC_INFO << ", NEW TEXT = " << text << '\n')
0616         TextLabel::TextWrapper wrapper(text, TextLabel::Mode::Text, true);
0617         // Don't set font color, because it is already in the html code
0618         // of the text. The font color is used to change the color for Latex text
0619         if (!ui.chbShowPlaceholderText->isChecked()) {
0620             if (plainTextChanged) {
0621                 wrapper.text = text;
0622                 for (auto* label : m_labelsList) {
0623                     if (text.isEmpty()) {
0624                         label->setFontColor(ui.kcbFontColor->color());
0625                         label->setBackgroundColor(ui.kcbBackgroundColor->color());
0626                     }
0627                     wrapper.allowPlaceholder = label->text().allowPlaceholder;
0628                     wrapper.textPlaceholder = label->text().textPlaceholder;
0629                     label->setText(wrapper);
0630                 }
0631             }
0632         } else {
0633             wrapper.textPlaceholder = text;
0634             for (auto* label : m_labelsList) {
0635                 wrapper.allowPlaceholder = label->text().allowPlaceholder;
0636                 wrapper.text = label->text().text;
0637                 label->setPlaceholderText(wrapper);
0638             }
0639         }
0640     }
0641     }
0642 
0643     // background color gets lost on every text change...
0644     updateBackground();
0645     // DEBUG(Q_FUNC_INFO << " DONE\n#################################")
0646 }
0647 
0648 /*!
0649  * \brief LabelWidget::charFormatChanged
0650  * \param format
0651  * Used to update the colors, font,... in the color font widgets to show the style of the selected text
0652  */
0653 void LabelWidget::charFormatChanged(const QTextCharFormat& format) {
0654     auto mode = static_cast<TextLabel::Mode>(ui.cbMode->currentIndex());
0655     if (mode != TextLabel::Mode::Text)
0656         return;
0657 
0658     CONDITIONAL_LOCK_RETURN;
0659 
0660     // update button state
0661     ui.tbFontBold->setChecked(ui.teLabel->fontWeight() == QFont::Bold);
0662     ui.tbFontItalic->setChecked(ui.teLabel->fontItalic());
0663     ui.tbFontUnderline->setChecked(ui.teLabel->fontUnderline());
0664     ui.tbFontStrikeOut->setChecked(format.fontStrikeOut());
0665     ui.tbFontSuperScript->setChecked(format.verticalAlignment() == QTextCharFormat::AlignSuperScript);
0666     ui.tbFontSubScript->setChecked(format.verticalAlignment() == QTextCharFormat::AlignSubScript);
0667 
0668     // font and colors
0669     QDEBUG(Q_FUNC_INFO << ", format color = " << format.foreground().color())
0670     QDEBUG(Q_FUNC_INFO << ", label color = " << m_label->fontColor())
0671     QDEBUG(Q_FUNC_INFO << ", text = " << ui.teLabel->toPlainText())
0672     QDEBUG(Q_FUNC_INFO << ", label text = " << m_label->text().text)
0673 
0674     // TEST
0675     if (ui.teLabel->toPlainText().isEmpty())
0676         return;
0677 
0678     // when text is empty the default color of format is black instead of the theme color!
0679     if (m_label->text().isHtml() && format.foreground().color().isValid() && !ui.teLabel->toPlainText().isEmpty())
0680         ui.kcbFontColor->setColor(format.foreground().color());
0681     else
0682         ui.kcbFontColor->setColor(m_label->fontColor());
0683 
0684     if (m_label->text().isHtml() && format.background().color().isValid() && !ui.teLabel->toPlainText().isEmpty())
0685         ui.kcbBackgroundColor->setColor(format.background().color());
0686     else
0687         ui.kcbBackgroundColor->setColor(m_label->backgroundColor());
0688 
0689     ui.kfontRequester->setFont(format.font());
0690 }
0691 
0692 // called when textlabel mode is changed
0693 void LabelWidget::labelModeChanged(TextLabel::Mode mode) {
0694     CONDITIONAL_LOCK_RETURN;
0695 
0696     updateMode(mode);
0697 }
0698 
0699 // Called when the combobox changes index
0700 void LabelWidget::modeChanged(int index) {
0701     auto mode = static_cast<TextLabel::Mode>(index);
0702     bool plain = (mode != TextLabel::Mode::Text);
0703 
0704     labelModeChanged(mode);
0705 
0706     CONDITIONAL_RETURN_NO_LOCK; // No lock, because multiple things are set by the feedback
0707 
0708     QString text = plain ? ui.teLabel->toPlainText() : ui.teLabel->toHtml();
0709     TextLabel::TextWrapper wrapper(text, mode, !plain);
0710     DEBUG(Q_FUNC_INFO << ", text = " << STDSTRING(wrapper.text))
0711     for (auto* label : m_labelsList)
0712         label->setText(wrapper);
0713 }
0714 
0715 void LabelWidget::fontColorChanged(const QColor& color) {
0716     CONDITIONAL_LOCK_RETURN;
0717 
0718     auto mode = m_label->text().mode;
0719     if (mode == TextLabel::Mode::Text || (mode == TextLabel::Mode::LaTeX && !m_teXEnabled)) {
0720         SETLABELTEXTPROPERTY(setTextColor, color);
0721         if (!cursorHasSelection) {
0722             for (auto* label : m_labelsList)
0723                 label->setFontColor(color);
0724         }
0725     } else { // LaTeX (enabled) or Markup mode
0726         for (auto* label : m_labelsList)
0727             label->setFontColor(color);
0728     }
0729 }
0730 
0731 void LabelWidget::backgroundColorChanged(const QColor& color) {
0732     QDEBUG(Q_FUNC_INFO << ", color = " << color)
0733     CONDITIONAL_LOCK_RETURN;
0734 
0735     auto mode = m_label->text().mode;
0736     DEBUG(Q_FUNC_INFO << ", tex enable = " << m_teXEnabled << ", mode = " << (int)mode)
0737     if (mode == TextLabel::Mode::Text || (mode == TextLabel::Mode::LaTeX && !m_teXEnabled)) {
0738         auto newColor(color);
0739         if (color.alpha() == 0) { // remove the transparency if it was set initially before.
0740             newColor.setAlpha(255);
0741             ui.kcbBackgroundColor->setColor(newColor);
0742         }
0743         SETLABELTEXTPROPERTY(setTextBackgroundColor, newColor)
0744     } else { // LaTeX (enabled) or Markup mode
0745         // Latex text does not support html code. For this the backgroundColor variable is used
0746         // Only single color background is supported
0747         for (auto* label : m_labelsList)
0748             label->setBackgroundColor(color);
0749     }
0750 }
0751 
0752 void LabelWidget::fontSizeChanged(int value) {
0753     CONDITIONAL_LOCK_RETURN;
0754 
0755     QFont font = m_label->teXFont();
0756     font.setPointSize(value);
0757     for (auto* label : m_labelsList)
0758         label->setTeXFont(font);
0759 }
0760 
0761 void LabelWidget::fontBoldChanged(bool checked) {
0762     CONDITIONAL_LOCK_RETURN;
0763 
0764     QFont::Weight weight;
0765     if (checked)
0766         weight = QFont::Bold;
0767     else
0768         weight = QFont::Normal;
0769     SETLABELTEXTPROPERTY(setFontWeight, weight);
0770 }
0771 
0772 void LabelWidget::fontItalicChanged(bool checked) {
0773     CONDITIONAL_LOCK_RETURN;
0774 
0775     SETLABELTEXTPROPERTY(setFontItalic, checked);
0776 }
0777 
0778 void LabelWidget::fontUnderlineChanged(bool checked) {
0779     CONDITIONAL_LOCK_RETURN;
0780 
0781     SETLABELTEXTPROPERTY(setFontUnderline, checked);
0782 }
0783 
0784 void LabelWidget::fontStrikeOutChanged(bool checked) {
0785     CONDITIONAL_LOCK_RETURN;
0786 
0787     QTextCharFormat format = ui.teLabel->currentCharFormat();
0788     format.setFontStrikeOut(checked);
0789     SETLABELTEXTPROPERTY(setCurrentCharFormat, format);
0790 }
0791 
0792 void LabelWidget::fontSuperScriptChanged(bool checked) {
0793     CONDITIONAL_LOCK_RETURN;
0794 
0795     QTextCharFormat format = ui.teLabel->currentCharFormat();
0796     if (checked)
0797         format.setVerticalAlignment(QTextCharFormat::AlignSuperScript);
0798     else
0799         format.setVerticalAlignment(QTextCharFormat::AlignNormal);
0800 
0801     SETLABELTEXTPROPERTY(setCurrentCharFormat, format);
0802 }
0803 
0804 void LabelWidget::fontSubScriptChanged(bool checked) {
0805     CONDITIONAL_LOCK_RETURN;
0806 
0807     QTextCharFormat format = ui.teLabel->currentCharFormat();
0808     if (checked)
0809         format.setVerticalAlignment(QTextCharFormat::AlignSubScript);
0810     else
0811         format.setVerticalAlignment(QTextCharFormat::AlignNormal);
0812 
0813     SETLABELTEXTPROPERTY(setCurrentCharFormat, format);
0814 }
0815 
0816 void LabelWidget::fontChanged(const QFont& font) {
0817     CONDITIONAL_LOCK_RETURN;
0818 
0819     // use mergeCurrentCharFormat(QTextCharFormat) instead of setFontFamily(font.family()), etc.
0820     // because this avoids textChanged() after every command
0821     QTextCharFormat format;
0822     format.setFontFamily(font.family());
0823 #if (QT_VERSION >= QT_VERSION_CHECK(5, 13, 0))
0824     format.setFontFamilies({font.family()}); // see QTBUG-80475
0825 #endif
0826     format.setFontPointSize(font.pointSize());
0827     format.setFontItalic(font.italic());
0828     format.setFontWeight(font.weight());
0829     if (font.underline())
0830         format.setUnderlineStyle(QTextCharFormat::UnderlineStyle::SingleUnderline);
0831     if (font.strikeOut())
0832         format.setFontStrikeOut(font.strikeOut());
0833 
0834     // QDEBUG(Q_FUNC_INFO << ", BEFORE:" << ui.teLabel->toHtml())
0835     SETLABELTEXTPROPERTY(mergeCurrentCharFormat, format);
0836     // QDEBUG(Q_FUNC_INFO << ", AFTER :" << ui.teLabel->toHtml())
0837 }
0838 
0839 void LabelWidget::teXFontChanged(const QFont& font) {
0840     CONDITIONAL_LOCK_RETURN;
0841 
0842     for (auto* label : m_labelsList)
0843         label->setTeXFont(font);
0844 }
0845 
0846 void LabelWidget::charMenu() {
0847     QMenu menu;
0848     KCharSelect selection(this, nullptr, KCharSelect::SearchLine | KCharSelect::CharacterTable | KCharSelect::BlockCombos | KCharSelect::HistoryButtons);
0849     QFont font = ui.teLabel->currentFont();
0850     // use the system default size, otherwise the symbols might be hard to read
0851     // if the current label font size is too small
0852     font.setPointSize(QFont().pointSize());
0853     selection.setCurrentFont(font);
0854     connect(&selection, &KCharSelect::charSelected, this, &LabelWidget::insertChar);
0855     connect(&selection, &KCharSelect::charSelected, &menu, &LabelWidget::close);
0856 
0857     auto* widgetAction = new QWidgetAction(this);
0858     widgetAction->setDefaultWidget(&selection);
0859     menu.addAction(widgetAction);
0860 
0861     QPoint pos(-menu.sizeHint().width() + ui.tbSymbols->width(), -menu.sizeHint().height());
0862     menu.exec(ui.tbSymbols->mapToGlobal(pos));
0863 }
0864 
0865 void LabelWidget::insertChar(QChar c) {
0866     ui.teLabel->insertPlainText(QString(c));
0867 }
0868 
0869 void LabelWidget::dateTimeMenu() {
0870     m_dateTimeMenu->clear();
0871 
0872     const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation);
0873     const QString configFile = configPath + QLatin1String("/klanguageoverridesrc");
0874     if (!QFile::exists(configFile)) {
0875         QDate date = QDate::currentDate();
0876         m_dateTimeMenu->addSeparator()->setText(i18n("Date"));
0877         m_dateTimeMenu->addAction(date.toString(Qt::TextDate));
0878         m_dateTimeMenu->addAction(date.toString(Qt::ISODate));
0879         m_dateTimeMenu->addAction(QLocale::system().toString(date, QLocale::ShortFormat));
0880         m_dateTimeMenu->addAction(QLocale::system().toString(date, QLocale::LongFormat));
0881         m_dateTimeMenu->addAction(date.toString(Qt::RFC2822Date));
0882 
0883         QDateTime time = QDateTime::currentDateTime();
0884         m_dateTimeMenu->addSeparator()->setText(i18n("Date and Time"));
0885         m_dateTimeMenu->addAction(time.toString(Qt::TextDate));
0886         m_dateTimeMenu->addAction(time.toString(Qt::ISODate));
0887         m_dateTimeMenu->addAction(QLocale::system().toString(time, QLocale::ShortFormat));
0888         m_dateTimeMenu->addAction(QLocale::system().toString(time, QLocale::LongFormat));
0889         m_dateTimeMenu->addAction(time.toString(Qt::RFC2822Date));
0890     } else {
0891         // application language was changed:
0892         // determine the currently used language and use QLocale::toString()
0893         // to get the strings translated into the currently used language
0894         // TODO: why not use QLocale() ?
0895         QSettings settings(configFile, QSettings::IniFormat);
0896         settings.beginGroup(QLatin1String("Language"));
0897         QByteArray languageCode;
0898         languageCode = settings.value(qAppName(), languageCode).toByteArray();
0899         QLocale locale(QString::fromLatin1(languageCode.data()));
0900 
0901         QDate date = QDate::currentDate();
0902         m_dateTimeMenu->addSeparator()->setText(i18n("Date"));
0903         m_dateTimeMenu->addAction(locale.toString(date, QLatin1String("ddd MMM d yyyy"))); // Qt::TextDate
0904         m_dateTimeMenu->addAction(locale.toString(date, QLatin1String("yyyy-MM-dd"))); // Qt::ISODate
0905         m_dateTimeMenu->addAction(locale.system().toString(date, QLocale::ShortFormat)); // Qt::SystemLocaleShortDate
0906         // no LongFormat here since it would contain strings in system's language which (potentially) is not the current application language
0907         m_dateTimeMenu->addAction(locale.toString(date, QLatin1String("dd MMM yyyy"))); // Qt::RFC2822Date
0908 
0909         QDateTime time = QDateTime::currentDateTime();
0910         m_dateTimeMenu->addSeparator()->setText(i18n("Date and Time"));
0911         m_dateTimeMenu->addAction(locale.toString(time, QLatin1String("ddd MMM d hh:mm:ss yyyy"))); // Qt::TextDate
0912         m_dateTimeMenu->addAction(locale.toString(time, QLatin1String("yyyy-MM-ddTHH:mm:ss"))); // Qt::ISODate
0913         m_dateTimeMenu->addAction(locale.system().toString(time, QLocale::ShortFormat)); // Qt::SystemLocaleShortDate
0914         // no LongFormat here since it would contain strings in system's language which (potentially) is not the current application language
0915 
0916         // TODO: RFC2822 requires time zone but Qt QLocale::toString() seems to ignore TZD (time zone designator) completely,
0917         // which works correctly with QDateTime::toString()
0918         m_dateTimeMenu->addAction(locale.toString(time, QLatin1String("dd MMM yyyy hh:mm:ss"))); // Qt::RFC2822Date
0919     }
0920 
0921     m_dateTimeMenu->exec(mapToGlobal(ui.tbDateTime->rect().bottomLeft()));
0922 }
0923 
0924 void LabelWidget::insertDateTime(QAction* action) {
0925     ui.teLabel->insertPlainText(action->text().remove(QLatin1Char('&')));
0926 }
0927 
0928 // positioning using absolute coordinates
0929 /*!
0930     called when label's current horizontal position relative to its parent (left, center, right, relative) is changed.
0931 */
0932 void LabelWidget::positionXChanged(int index) {
0933     CONDITIONAL_LOCK_RETURN;
0934 
0935     auto position = m_label->position();
0936     auto oldHorizontalPosition = position.horizontalPosition;
0937     position.horizontalPosition = TextLabel::HorizontalPosition(index);
0938     double x = 0.;
0939     if (position.horizontalPosition == WorksheetElement::HorizontalPosition::Relative) {
0940         switch (oldHorizontalPosition) {
0941         case WorksheetElement::HorizontalPosition::Left:
0942         case WorksheetElement::HorizontalPosition::Relative:
0943             break;
0944         case WorksheetElement::HorizontalPosition::Center:
0945             x = 0.5;
0946             break;
0947         case WorksheetElement::HorizontalPosition::Right:
0948             x = 1.0;
0949         }
0950         ui.sbPositionX->setSuffix(QStringLiteral(" %"));
0951     } else {
0952         if (m_units == Units::Metric)
0953             ui.sbPositionX->setSuffix(QStringLiteral(" cm"));
0954         else
0955             ui.sbPositionX->setSuffix(QStringLiteral(" in"));
0956     }
0957 
0958     position.point.setX(x);
0959     ui.sbPositionX->setValue(100. * x);
0960 
0961     for (auto* label : m_labelsList)
0962         label->setPosition(position);
0963 }
0964 
0965 /*!
0966     called when label's current horizontal position relative to its parent (top, center, bottom, relative) is changed.
0967 */
0968 void LabelWidget::positionYChanged(int index) {
0969     CONDITIONAL_LOCK_RETURN;
0970 
0971     auto position = m_label->position();
0972     auto oldVerticalPosition = position.verticalPosition;
0973     position.verticalPosition = TextLabel::VerticalPosition(index);
0974     double y = 0.;
0975     if (position.verticalPosition == WorksheetElement::VerticalPosition::Relative) {
0976         switch (oldVerticalPosition) {
0977         case WorksheetElement::VerticalPosition::Top:
0978         case WorksheetElement::VerticalPosition::Relative:
0979             break;
0980         case WorksheetElement::VerticalPosition::Center:
0981             y = 0.5;
0982             break;
0983         case WorksheetElement::VerticalPosition::Bottom:
0984             y = 1.0;
0985         }
0986         ui.sbPositionY->setSuffix(QStringLiteral(" %"));
0987     } else {
0988         if (m_units == Units::Metric)
0989             ui.sbPositionY->setSuffix(QStringLiteral(" cm"));
0990         else
0991             ui.sbPositionY->setSuffix(QStringLiteral(" in"));
0992     }
0993 
0994     position.point.setY(y);
0995     ui.sbPositionY->setValue(100. * y);
0996 
0997     for (auto* label : m_labelsList)
0998         label->setPosition(position);
0999 }
1000 
1001 void LabelWidget::horizontalAlignmentChanged(int index) {
1002     CONDITIONAL_LOCK_RETURN;
1003 
1004     for (auto* label : m_labelsList)
1005         label->setHorizontalAlignment(TextLabel::HorizontalAlignment(index));
1006 }
1007 
1008 void LabelWidget::verticalAlignmentChanged(int index) {
1009     CONDITIONAL_LOCK_RETURN;
1010 
1011     for (auto* label : m_labelsList)
1012         label->setVerticalAlignment(TextLabel::VerticalAlignment(index));
1013 }
1014 
1015 void LabelWidget::customPositionXChanged(double value) {
1016     CONDITIONAL_RETURN_NO_LOCK;
1017 
1018     for (auto* label : m_labelsList) {
1019         auto position = label->position();
1020         if (position.horizontalPosition == WorksheetElement::HorizontalPosition::Relative)
1021             position.point.setX(value / 100.);
1022         else
1023             position.point.setX(Worksheet::convertToSceneUnits(value, m_worksheetUnit));
1024         label->setPosition(position);
1025     }
1026 }
1027 
1028 void LabelWidget::customPositionYChanged(double value) {
1029     CONDITIONAL_RETURN_NO_LOCK;
1030 
1031     for (auto* label : m_labelsList) {
1032         auto position = label->position();
1033         if (position.verticalPosition == WorksheetElement::VerticalPosition::Relative)
1034             position.point.setY(value / 100.);
1035         else
1036             position.point.setY(Worksheet::convertToSceneUnits(value, m_worksheetUnit));
1037         label->setPosition(position);
1038     }
1039 }
1040 
1041 // positioning using logical plot coordinates
1042 void LabelWidget::positionXLogicalChanged(double value) {
1043     CONDITIONAL_RETURN_NO_LOCK;
1044 
1045     for (auto* label : m_labelsList) {
1046         auto pos = label->positionLogical();
1047         pos.setX(value);
1048         label->setPositionLogical(pos);
1049     }
1050 }
1051 
1052 void LabelWidget::positionXLogicalDateTimeChanged(qint64 value) {
1053     CONDITIONAL_LOCK_RETURN;
1054 
1055     for (auto* label : m_labelsList) {
1056         auto pos = label->positionLogical();
1057         pos.setX(value);
1058         label->setPositionLogical(pos);
1059     }
1060 }
1061 
1062 void LabelWidget::positionYLogicalChanged(double value) {
1063     CONDITIONAL_RETURN_NO_LOCK;
1064 
1065     for (auto* label : m_labelsList) {
1066         auto pos = label->positionLogical();
1067         pos.setY(value);
1068         label->setPositionLogical(pos);
1069     }
1070 }
1071 
1072 void LabelWidget::rotationChanged(int value) {
1073     CONDITIONAL_LOCK_RETURN;
1074 
1075     for (auto* label : m_labelsList)
1076         label->setRotationAngle(value);
1077 }
1078 
1079 void LabelWidget::offsetXChanged(double value) {
1080     CONDITIONAL_RETURN_NO_LOCK;
1081 
1082     for (auto* axis : m_axesList)
1083         axis->setTitleOffsetX(Worksheet::convertToSceneUnits(value, Worksheet::Unit::Point));
1084 }
1085 
1086 void LabelWidget::offsetYChanged(double value) {
1087     CONDITIONAL_RETURN_NO_LOCK;
1088 
1089     for (auto* axis : m_axesList)
1090         axis->setTitleOffsetY(Worksheet::convertToSceneUnits(value, Worksheet::Unit::Point));
1091 }
1092 
1093 void LabelWidget::lockChanged(bool locked) {
1094     CONDITIONAL_LOCK_RETURN;
1095     for (auto* label : m_labelsList)
1096         label->setLock(locked);
1097 }
1098 
1099 // border
1100 void LabelWidget::borderShapeChanged(int index) {
1101     auto shape = (TextLabel::BorderShape)index;
1102     bool b = (shape != TextLabel::BorderShape::NoBorder);
1103     ui.lBorderStyle->setVisible(b);
1104     ui.cbBorderStyle->setVisible(b);
1105     ui.lBorderWidth->setVisible(b);
1106     ui.sbBorderWidth->setVisible(b);
1107     ui.lBorderColor->setVisible(b);
1108     ui.kcbBorderColor->setVisible(b);
1109     ui.lBorderOpacity->setVisible(b);
1110     ui.sbBorderOpacity->setVisible(b);
1111 
1112     CONDITIONAL_LOCK_RETURN;
1113 
1114     for (auto* label : m_labelsList)
1115         label->setBorderShape(shape);
1116 }
1117 
1118 void LabelWidget::borderStyleChanged(int index) {
1119     CONDITIONAL_LOCK_RETURN;
1120 
1121     auto penStyle = Qt::PenStyle(index);
1122     QPen pen;
1123     for (auto* label : m_labelsList) {
1124         pen = label->borderPen();
1125         pen.setStyle(penStyle);
1126         label->setBorderPen(pen);
1127     }
1128 }
1129 
1130 void LabelWidget::borderColorChanged(const QColor& color) {
1131     CONDITIONAL_LOCK_RETURN;
1132 
1133     QPen pen;
1134     for (auto* label : m_labelsList) {
1135         pen = label->borderPen();
1136         pen.setColor(color);
1137         label->setBorderPen(pen);
1138     }
1139     GuiTools::updatePenStyles(ui.cbBorderStyle, color);
1140 }
1141 
1142 void LabelWidget::borderWidthChanged(double value) {
1143     CONDITIONAL_RETURN_NO_LOCK;
1144 
1145     QPen pen;
1146     for (auto* label : m_labelsList) {
1147         pen = label->borderPen();
1148         pen.setWidthF(Worksheet::convertToSceneUnits(value, Worksheet::Unit::Point));
1149         label->setBorderPen(pen);
1150     }
1151 }
1152 
1153 void LabelWidget::borderOpacityChanged(int value) {
1154     CONDITIONAL_LOCK_RETURN;
1155 
1156     qreal opacity = (float)value / 100.;
1157     for (auto* label : m_labelsList)
1158         label->setBorderOpacity(opacity);
1159 }
1160 
1161 /*!
1162  * \brief LabelWidget::bindingChanged
1163  * Bind TextLabel to the cartesian plot coords or not
1164  * \param checked
1165  */
1166 void LabelWidget::bindingChanged(bool checked) {
1167     // widgets for positioning using absolute plot distances
1168     ui.lPositionX->setVisible(!checked);
1169     ui.cbPositionX->setVisible(!checked);
1170     ui.sbPositionX->setVisible(!checked);
1171 
1172     ui.lPositionY->setVisible(!checked);
1173     ui.cbPositionY->setVisible(!checked);
1174     ui.sbPositionY->setVisible(!checked);
1175 
1176     // widgets for positioning using logical plot coordinates
1177     const auto* plot = static_cast<const CartesianPlot*>(m_label->parent(AspectType::CartesianPlot));
1178     if (plot && plot->xRangeFormatDefault() == RangeT::Format::DateTime) {
1179         ui.lPositionXLogicalDateTime->setVisible(checked);
1180         ui.dtePositionXLogical->setVisible(checked);
1181 
1182         ui.lPositionXLogical->setVisible(false);
1183         ui.sbPositionXLogical->setVisible(false);
1184     } else {
1185         ui.lPositionXLogicalDateTime->setVisible(false);
1186         ui.dtePositionXLogical->setVisible(false);
1187 
1188         ui.lPositionXLogical->setVisible(checked);
1189         ui.sbPositionXLogical->setVisible(checked);
1190     }
1191 
1192     ui.lPositionYLogical->setVisible(checked);
1193     ui.sbPositionYLogical->setVisible(checked);
1194 
1195     CONDITIONAL_LOCK_RETURN;
1196 
1197     ui.chbBindLogicalPos->setChecked(checked);
1198 
1199     for (auto* label : m_labelsList)
1200         label->setCoordinateBindingEnabled(checked);
1201 }
1202 
1203 void LabelWidget::showPlaceholderTextChanged(bool checked) {
1204     CONDITIONAL_LOCK_RETURN;
1205 
1206     if (!checked) {
1207         ui.teLabel->setEnabled(false);
1208         if (m_label->text().mode != TextLabel::Mode::Text)
1209             ui.teLabel->setText(m_label->text().text);
1210         else
1211             ui.teLabel->setHtml(m_label->text().text);
1212     } else {
1213         ui.teLabel->setEnabled(true);
1214         if (m_label->text().mode != TextLabel::Mode::Text)
1215             ui.teLabel->setText(m_label->text().textPlaceholder);
1216         else
1217             ui.teLabel->setHtml(m_label->text().textPlaceholder);
1218     }
1219 }
1220 
1221 //*********************************************************
1222 //****** SLOTs for changes triggered in TextLabel *********
1223 //*********************************************************
1224 void LabelWidget::labelTextWrapperChanged(const TextLabel::TextWrapper& text) {
1225     CONDITIONAL_LOCK_RETURN;
1226 
1227     // save and restore the current cursor position after changing the text
1228     auto cursor = ui.teLabel->textCursor();
1229     int position = cursor.position();
1230     if (!ui.chbShowPlaceholderText->isChecked()) {
1231         if (text.mode != TextLabel::Mode::Text)
1232             ui.teLabel->setText(text.text);
1233         else
1234             ui.teLabel->setHtml(text.text);
1235     } else {
1236         if (text.mode != TextLabel::Mode::Text)
1237             ui.teLabel->setText(text.textPlaceholder);
1238         else
1239             ui.teLabel->setHtml(text.textPlaceholder);
1240     }
1241     cursor.movePosition(QTextCursor::Start);
1242     cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, position);
1243     ui.teLabel->setTextCursor(cursor);
1244 
1245     const int index = static_cast<int>(text.mode);
1246     ui.cbMode->setCurrentIndex(index);
1247     this->labelModeChanged(text.mode);
1248 }
1249 
1250 /*!
1251  * \brief Highlights the text field if wrong latex syntax was used (null image was produced)
1252  * or something else went wrong during rendering (\sa ExpressionTextEdit::validateExpression())
1253  */
1254 void LabelWidget::labelTeXImageUpdated(const TeXRenderer::Result& result) {
1255     if (!result.successful) {
1256         if (ui.teLabel->styleSheet().isEmpty())
1257             SET_WARNING_STYLE(ui.teLabel)
1258         m_messageWidget->setText(result.errorMessage);
1259         m_messageWidget->setMaximumWidth(ui.teLabel->width());
1260     } else
1261         ui.teLabel->setStyleSheet(QString());
1262 
1263     m_messageWidget->setVisible(!result.successful);
1264 }
1265 
1266 void LabelWidget::labelTeXFontChanged(const QFont& font) {
1267     CONDITIONAL_LOCK_RETURN;
1268     ui.kfontRequesterTeX->setFont(font);
1269     ui.sbFontSize->setValue(font.pointSize());
1270 }
1271 
1272 // this function is only called when the theme is changed. Otherwise the color is coded in the html text.
1273 // when the theme changes, the whole text should change color regardless of the color it has
1274 void LabelWidget::labelFontColorChanged(const QColor& color) {
1275     Q_EMIT labelFontColorChangedSignal(color);
1276 
1277     CONDITIONAL_LOCK_RETURN;
1278     ui.kcbFontColor->setColor(color);
1279     ui.teLabel->selectAll();
1280     ui.teLabel->setTextColor(color);
1281 }
1282 
1283 void LabelWidget::labelPositionChanged(const TextLabel::PositionWrapper& position) {
1284     CONDITIONAL_LOCK_RETURN;
1285 
1286     ui.cbPositionX->setCurrentIndex(static_cast<int>(position.horizontalPosition));
1287     ui.cbPositionY->setCurrentIndex(static_cast<int>(position.verticalPosition));
1288     if (position.horizontalPosition == WorksheetElement::HorizontalPosition::Relative) {
1289         ui.sbPositionX->setValue(position.point.x() * 100.);
1290         ui.sbPositionX->setSuffix(QStringLiteral(" %"));
1291     } else
1292         ui.sbPositionX->setValue(Worksheet::convertFromSceneUnits(position.point.x(), m_worksheetUnit));
1293 
1294     if (position.verticalPosition == WorksheetElement::VerticalPosition::Relative) {
1295         ui.sbPositionY->setValue(position.point.y() * 100.);
1296         ui.sbPositionY->setSuffix(QStringLiteral(" %"));
1297     } else
1298         ui.sbPositionY->setValue(Worksheet::convertFromSceneUnits(position.point.y(), m_worksheetUnit));
1299 }
1300 
1301 void LabelWidget::labelHorizontalAlignmentChanged(TextLabel::HorizontalAlignment index) {
1302     CONDITIONAL_LOCK_RETURN;
1303     ui.cbHorizontalAlignment->setCurrentIndex(static_cast<int>(index));
1304 }
1305 
1306 void LabelWidget::labelVerticalAlignmentChanged(TextLabel::VerticalAlignment index) {
1307     CONDITIONAL_LOCK_RETURN;
1308     ui.cbVerticalAlignment->setCurrentIndex(static_cast<int>(index));
1309 }
1310 
1311 void LabelWidget::labelCoordinateBindingEnabledChanged(bool enabled) {
1312     CONDITIONAL_LOCK_RETURN;
1313     bindingChanged(enabled);
1314 }
1315 
1316 void LabelWidget::labelPositionLogicalChanged(QPointF pos) {
1317     CONDITIONAL_LOCK_RETURN;
1318     ui.sbPositionXLogical->setValue(pos.x());
1319     ui.dtePositionXLogical->setMSecsSinceEpochUTC(pos.x());
1320     ui.sbPositionYLogical->setValue(pos.y());
1321     // TODO: why not ui.dtePositionYLogical->setMSecsSinceEpochUTC(pos.y());
1322 }
1323 
1324 // this function is only called when the theme is changed. Otherwise the color is coded in the html text.
1325 // when the theme changes, the whole text should change color regardless of the color it has
1326 void LabelWidget::labelBackgroundColorChanged(const QColor& color) {
1327     QDEBUG(Q_FUNC_INFO << ", color =" << color)
1328     CONDITIONAL_LOCK_RETURN;
1329     ui.kcbBackgroundColor->setColor(color);
1330 
1331     auto mode = static_cast<TextLabel::Mode>(ui.cbMode->currentIndex());
1332     if (mode != TextLabel::Mode::Text)
1333         return;
1334 
1335     ui.teLabel->selectAll();
1336     ui.teLabel->setTextBackgroundColor(color);
1337 }
1338 
1339 void LabelWidget::labelOffsetXChanged(qreal offset) {
1340     CONDITIONAL_LOCK_RETURN;
1341     ui.sbOffsetX->setValue(Worksheet::convertFromSceneUnits(offset, Worksheet::Unit::Point));
1342 }
1343 
1344 void LabelWidget::labelOffsetYChanged(qreal offset) {
1345     CONDITIONAL_LOCK_RETURN;
1346     ui.sbOffsetY->setValue(Worksheet::convertFromSceneUnits(offset, Worksheet::Unit::Point));
1347 }
1348 
1349 void LabelWidget::labelRotationAngleChanged(qreal angle) {
1350     CONDITIONAL_LOCK_RETURN;
1351     ui.sbRotation->setValue(angle);
1352 }
1353 
1354 void LabelWidget::labelLockChanged(bool on) {
1355     CONDITIONAL_LOCK_RETURN;
1356     ui.chbLock->setChecked(on);
1357 }
1358 
1359 // border
1360 void LabelWidget::labelBorderShapeChanged(TextLabel::BorderShape shape) {
1361     CONDITIONAL_LOCK_RETURN;
1362     ui.cbBorderShape->setCurrentIndex(static_cast<int>(shape));
1363 }
1364 
1365 void LabelWidget::labelBorderPenChanged(const QPen& pen) {
1366     CONDITIONAL_LOCK_RETURN;
1367     if (ui.cbBorderStyle->currentIndex() != pen.style())
1368         ui.cbBorderStyle->setCurrentIndex(pen.style());
1369     if (ui.kcbBorderColor->color() != pen.color())
1370         ui.kcbBorderColor->setColor(pen.color());
1371     // Feedback needed therefore no condition
1372     ui.sbBorderWidth->setValue(Worksheet::convertFromSceneUnits(pen.widthF(), Worksheet::Unit::Point));
1373 }
1374 
1375 void LabelWidget::labelBorderOpacityChanged(float value) {
1376     CONDITIONAL_LOCK_RETURN;
1377     float v = (float)value * 100.;
1378     ui.sbBorderOpacity->setValue(v);
1379 }
1380 
1381 void LabelWidget::labelCartesianPlotParent(bool on) {
1382     CONDITIONAL_LOCK_RETURN;
1383     ui.chbBindLogicalPos->setVisible(on);
1384     if (!on)
1385         ui.chbBindLogicalPos->setChecked(false);
1386 }
1387 
1388 //**********************************************************
1389 //******************** SETTINGS ****************************
1390 //**********************************************************
1391 void LabelWidget::load() {
1392     if (!m_label)
1393         return;
1394 
1395     CONDITIONAL_LOCK_RETURN;
1396 
1397     ui.chbVisible->setChecked(m_label->isVisible());
1398     ui.chbLock->setChecked(m_label->isLocked());
1399 
1400     // don't show checkbox if Placeholder feature not used
1401     bool allowPlaceholder = m_label->text().allowPlaceholder;
1402     ui.chbShowPlaceholderText->setVisible(allowPlaceholder);
1403     ui.chbShowPlaceholderText->setEnabled(allowPlaceholder);
1404     ui.chbShowPlaceholderText->setChecked(allowPlaceholder);
1405 
1406     // Text
1407     const auto mode = m_label->text().mode;
1408     ui.cbMode->setCurrentIndex(static_cast<int>(mode));
1409     this->updateMode(mode);
1410 
1411     if (!allowPlaceholder) {
1412         if (mode == TextLabel::Mode::Text) {
1413             ui.teLabel->setHtml(m_label->text().text);
1414             ui.teLabel->selectAll(); // must be done to retrieve font
1415             ui.kfontRequester->setFont(ui.teLabel->currentFont());
1416         } else
1417             ui.teLabel->setText(m_label->text().text);
1418 
1419     } else {
1420         if (mode == TextLabel::Mode::Text) {
1421             ui.teLabel->setHtml(m_label->text().textPlaceholder);
1422             ui.teLabel->selectAll(); // must be done to retrieve font
1423             ui.kfontRequester->setFont(ui.teLabel->currentFont());
1424         } else
1425             ui.teLabel->setText(m_label->text().textPlaceholder);
1426     }
1427 
1428     auto format = ui.teLabel->currentCharFormat();
1429 
1430     // when text is empty the default color of format is black instead of the theme color!
1431     if (m_label->text().isHtml() && format.foreground().color().isValid() && !ui.teLabel->toPlainText().isEmpty())
1432         ui.kcbFontColor->setColor(format.foreground().color());
1433     else
1434         ui.kcbFontColor->setColor(m_label->fontColor());
1435 
1436     if (m_label->text().isHtml() && format.background().color().isValid() && !ui.teLabel->toPlainText().isEmpty()) {
1437         // The html below does not contain any information about the background color. So qt uses black which is not correct
1438         // "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n<html><head><meta name=\"qrichtext\"
1439         // content=\"1\" /><style type=\"text/css\">\np, li { white-space: pre-wrap; }\n</style></head><body style=\" font-family:'Noto Sans'; font-size:10pt;
1440         // font-weight:400; font-style:normal;\">\n<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0;
1441         // text-indent:0px;\"><span style=\" color:#000000;\">1</span></p></body></html>"
1442         if (m_label->text().text.contains(QStringLiteral("background-color")))
1443             ui.kcbBackgroundColor->setColor(format.background().color());
1444         else
1445             ui.kcbBackgroundColor->setColor(QColor(Qt::GlobalColor::transparent));
1446     } else
1447         ui.kcbBackgroundColor->setColor(m_label->backgroundColor());
1448 
1449     ui.kfontRequesterTeX->setFont(m_label->teXFont());
1450     ui.sbFontSize->setValue(m_label->teXFont().pointSize());
1451 
1452     ui.tbFontBold->setChecked(ui.teLabel->fontWeight() == QFont::Bold);
1453     ui.tbFontItalic->setChecked(ui.teLabel->fontItalic());
1454     ui.tbFontUnderline->setChecked(ui.teLabel->fontUnderline());
1455     ui.tbFontStrikeOut->setChecked(format.fontStrikeOut());
1456     ui.tbFontSuperScript->setChecked(format.verticalAlignment() == QTextCharFormat::AlignSuperScript);
1457     ui.tbFontSubScript->setChecked(format.verticalAlignment() == QTextCharFormat::AlignSubScript);
1458 
1459     // move the cursor to the end
1460     QTextCursor cursor = ui.teLabel->textCursor();
1461     cursor.movePosition(QTextCursor::End);
1462     ui.teLabel->setTextCursor(cursor);
1463     // ui.teLabel->setFocus(); // Do not set focus, otherwise the WorksheetView is not able to catch key input events!
1464 
1465     // Geometry
1466     // widgets for positioning using absolute plot distances
1467     ui.cbPositionX->setCurrentIndex((int)m_label->position().horizontalPosition);
1468     // positionXChanged(ui.cbPositionX->currentIndex());
1469     if (m_label->position().horizontalPosition == WorksheetElement::HorizontalPosition::Relative) {
1470         ui.sbPositionX->setValue(m_label->position().point.x() * 100);
1471         ui.sbPositionX->setSuffix(QStringLiteral(" %"));
1472     } else
1473         ui.sbPositionX->setValue(Worksheet::convertFromSceneUnits(m_label->position().point.x(), m_worksheetUnit));
1474     ui.cbPositionY->setCurrentIndex((int)m_label->position().verticalPosition);
1475     // positionYChanged(ui.cbPositionY->currentIndex());
1476     if (m_label->position().verticalPosition == WorksheetElement::VerticalPosition::Relative) {
1477         ui.sbPositionY->setValue(m_label->position().point.y() * 100);
1478         ui.sbPositionY->setSuffix(QStringLiteral(" %"));
1479     } else
1480         ui.sbPositionY->setValue(Worksheet::convertFromSceneUnits(m_label->position().point.y(), m_worksheetUnit));
1481 
1482     ui.cbHorizontalAlignment->setCurrentIndex((int)m_label->horizontalAlignment());
1483     ui.cbVerticalAlignment->setCurrentIndex((int)m_label->verticalAlignment());
1484 
1485     // widgets for positioning using logical plot coordinates
1486     bool allowLogicalCoordinates = (m_label->plot() != nullptr);
1487     ui.chbBindLogicalPos->setVisible(allowLogicalCoordinates);
1488 
1489     if (allowLogicalCoordinates) {
1490         const auto* plot = static_cast<const CartesianPlot*>(m_label->plot());
1491         if (plot->xRangeFormatDefault() == RangeT::Format::Numeric) {
1492             ui.lPositionXLogical->show();
1493             ui.sbPositionXLogical->show();
1494             ui.lPositionXLogicalDateTime->hide();
1495             ui.dtePositionXLogical->hide();
1496 
1497             ui.sbPositionXLogical->setValue(m_label->positionLogical().x());
1498             ui.sbPositionYLogical->setValue(m_label->positionLogical().y());
1499         } else { // DateTime
1500             ui.lPositionXLogical->hide();
1501             ui.sbPositionXLogical->hide();
1502             ui.lPositionXLogicalDateTime->show();
1503             ui.dtePositionXLogical->show();
1504 
1505             ui.dtePositionXLogical->setDisplayFormat(plot->rangeDateTimeFormat(Dimension::X));
1506             ui.dtePositionXLogical->setMSecsSinceEpochUTC(m_label->positionLogical().x());
1507         }
1508 
1509         ui.chbBindLogicalPos->setChecked(m_label->coordinateBindingEnabled());
1510         bindingChanged(m_label->coordinateBindingEnabled());
1511     } else {
1512         ui.lPositionXLogical->hide();
1513         ui.sbPositionXLogical->hide();
1514         ui.lPositionYLogical->hide();
1515         ui.sbPositionYLogical->hide();
1516         ui.lPositionXLogicalDateTime->hide();
1517         ui.dtePositionXLogical->hide();
1518     }
1519 
1520     // offsets, available for axis label only
1521     if (!m_axesList.isEmpty()) {
1522         ui.sbOffsetX->setValue(Worksheet::convertFromSceneUnits(m_axesList.first()->titleOffsetX(), Worksheet::Unit::Point));
1523         ui.sbOffsetY->setValue(Worksheet::convertFromSceneUnits(m_axesList.first()->titleOffsetY(), Worksheet::Unit::Point));
1524     }
1525     ui.sbRotation->setValue(m_label->rotationAngle());
1526 
1527     // don't show if binding not enabled. example: axis titles
1528     // Border
1529     ui.cbBorderShape->setCurrentIndex(static_cast<int>(m_label->borderShape()));
1530     borderShapeChanged(ui.cbBorderShape->currentIndex());
1531     ui.kcbBorderColor->setColor(m_label->borderPen().color());
1532     ui.cbBorderStyle->setCurrentIndex((int)m_label->borderPen().style());
1533     ui.sbBorderWidth->setValue(Worksheet::convertFromSceneUnits(m_label->borderPen().widthF(), Worksheet::Unit::Point));
1534     ui.sbBorderOpacity->setValue(round(m_label->borderOpacity() * 100));
1535     GuiTools::updatePenStyles(ui.cbBorderStyle, ui.kcbBorderColor->color());
1536 }
1537 
1538 // General updater function to update the dock (used also in the load method)
1539 void LabelWidget::updateMode(TextLabel::Mode mode) {
1540     bool plain = (mode != TextLabel::Mode::Text);
1541 
1542     // hide text editing elements if TeX-option is used
1543     ui.tbFontBold->setVisible(!plain);
1544     ui.tbFontItalic->setVisible(!plain);
1545 
1546     // TODO: https://bugreports.qt.io/browse/QTBUG-25420
1547     //  ui.tbFontUnderline->setVisible(!plain);
1548     //  ui.tbFontStrikeOut->setVisible(!plain);
1549 
1550     ui.tbFontSubScript->setVisible(!plain);
1551     ui.tbFontSuperScript->setVisible(!plain);
1552 
1553     ui.lFont->setVisible(!plain);
1554     ui.kfontRequester->setVisible(!plain);
1555 
1556     if (plain) {
1557         // reset all applied formattings when switching from html to tex mode
1558         QTextCursor cursor = ui.teLabel->textCursor();
1559         int position = cursor.position();
1560         ui.teLabel->selectAll();
1561         QTextCharFormat format;
1562         ui.teLabel->setCurrentCharFormat(format);
1563         cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, position);
1564         ui.teLabel->setTextCursor(cursor);
1565 
1566 #ifdef HAVE_KF5_SYNTAX_HIGHLIGHTING
1567         m_highlighter->setDocument(ui.teLabel->document());
1568         if (mode == TextLabel::Mode::LaTeX)
1569             m_highlighter->setDefinition(m_repository.definitionForName(QLatin1String("LaTeX")));
1570         else
1571             m_highlighter->setDefinition(m_repository.definitionForName(QLatin1String("Markdown")));
1572 #endif
1573         KConfigGroup conf = Settings::group(QLatin1String("Settings_Worksheet"));
1574         QString engine = conf.readEntry(QLatin1String("LaTeXEngine"), "");
1575         if (engine == QLatin1String("xelatex") || engine == QLatin1String("lualatex")) {
1576             ui.lFontTeX->setVisible(true);
1577             ui.kfontRequesterTeX->setVisible(true);
1578             ui.lFontSize->setVisible(false);
1579             ui.sbFontSize->setVisible(false);
1580         } else {
1581             ui.lFontTeX->setVisible(false);
1582             ui.kfontRequesterTeX->setVisible(false);
1583             ui.lFontSize->setVisible(true);
1584             ui.sbFontSize->setVisible(true);
1585         }
1586 
1587         // update colors
1588         ui.kcbFontColor->setColor(m_label->fontColor());
1589         ui.kcbBackgroundColor->setColor(m_label->backgroundColor());
1590     } else {
1591 #ifdef HAVE_KF5_SYNTAX_HIGHLIGHTING
1592         m_highlighter->setDocument(nullptr);
1593 #endif
1594         ui.lFontTeX->setVisible(false);
1595         ui.kfontRequesterTeX->setVisible(false);
1596         ui.lFontSize->setVisible(false);
1597         ui.sbFontSize->setVisible(false);
1598     }
1599 
1600     updateBackground();
1601 
1602     // when switching to non-LaTeX mode, set the background color to white just for the case the latex code provided by the user
1603     // in the TeX-mode is not valid and the background was set to red (s.a. LabelWidget::labelTeXImageUpdated())
1604     if (mode != TextLabel::Mode::LaTeX) {
1605         ui.teLabel->setStyleSheet(QString());
1606         m_messageWidget->setVisible(false);
1607     }
1608 }
1609 
1610 void LabelWidget::loadConfig(KConfigGroup& group) {
1611     if (!m_label)
1612         return;
1613 
1614     // Text
1615     ui.cbMode->setCurrentIndex(group.readEntry("Mode", static_cast<int>(m_label->text().mode)));
1616     this->modeChanged(ui.cbMode->currentIndex());
1617     ui.sbFontSize->setValue(group.readEntry("TeXFontSize", m_label->teXFont().pointSize()));
1618     ui.kcbFontColor->setColor(group.readEntry("FontColor", m_label->fontColor()));
1619     ui.kcbBackgroundColor->setColor(group.readEntry("BackgroundColor", m_label->backgroundColor()));
1620     ui.kfontRequesterTeX->setFont(group.readEntry("TeXFont", m_label->teXFont()));
1621 
1622     // Geometry
1623     ui.cbPositionX->setCurrentIndex(group.readEntry("PositionX", (int)m_label->position().horizontalPosition));
1624     ui.sbPositionX->setValue(Worksheet::convertFromSceneUnits(group.readEntry("PositionXValue", m_label->position().point.x()), m_worksheetUnit));
1625     ui.cbPositionY->setCurrentIndex(group.readEntry("PositionY", (int)m_label->position().verticalPosition));
1626     ui.sbPositionY->setValue(Worksheet::convertFromSceneUnits(group.readEntry("PositionYValue", m_label->position().point.y()), m_worksheetUnit));
1627 
1628     if (!m_axesList.isEmpty()) {
1629         ui.sbOffsetX->setValue(Worksheet::convertFromSceneUnits(group.readEntry("OffsetX", m_axesList.first()->titleOffsetX()), Worksheet::Unit::Point));
1630         ui.sbOffsetY->setValue(Worksheet::convertFromSceneUnits(group.readEntry("OffsetY", m_axesList.first()->titleOffsetY()), Worksheet::Unit::Point));
1631     }
1632     ui.cbHorizontalAlignment->setCurrentIndex(group.readEntry("HorizontalAlignment", (int)m_label->horizontalAlignment()));
1633     ui.cbVerticalAlignment->setCurrentIndex(group.readEntry("VerticalAlignment", (int)m_label->verticalAlignment()));
1634     ui.sbRotation->setValue(group.readEntry("Rotation", m_label->rotationAngle()));
1635 
1636     // Border
1637     ui.cbBorderShape->setCurrentIndex(group.readEntry("BorderShape").toInt());
1638     ui.kcbBorderColor->setColor(group.readEntry("BorderColor", m_label->borderPen().color()));
1639     ui.cbBorderStyle->setCurrentIndex(group.readEntry("BorderStyle", (int)m_label->borderPen().style()));
1640     ui.sbBorderWidth->setValue(Worksheet::convertFromSceneUnits(group.readEntry("BorderWidth", m_label->borderPen().widthF()), Worksheet::Unit::Point));
1641     ui.sbBorderOpacity->setValue(group.readEntry("BorderOpacity", m_label->borderOpacity()) * 100);
1642 }
1643 
1644 void LabelWidget::saveConfig(KConfigGroup& group) {
1645     // Text
1646     group.writeEntry("Mode", ui.cbMode->currentIndex());
1647     group.writeEntry("FontColor", ui.kcbFontColor->color());
1648     group.writeEntry("BackgroundColor", ui.kcbBackgroundColor->color());
1649     group.writeEntry("TeXFont", ui.kfontRequesterTeX->font());
1650 
1651     // Geometry
1652     group.writeEntry("PositionX", ui.cbPositionX->currentIndex());
1653     group.writeEntry("PositionXValue", Worksheet::convertToSceneUnits(ui.sbPositionX->value(), m_worksheetUnit));
1654     group.writeEntry("PositionY", ui.cbPositionY->currentIndex());
1655     group.writeEntry("PositionYValue", Worksheet::convertToSceneUnits(ui.sbPositionY->value(), m_worksheetUnit));
1656 
1657     if (!m_axesList.isEmpty()) {
1658         group.writeEntry("OffsetX", Worksheet::convertToSceneUnits(ui.sbOffsetX->value(), Worksheet::Unit::Point));
1659         group.writeEntry("OffsetY", Worksheet::convertToSceneUnits(ui.sbOffsetY->value(), Worksheet::Unit::Point));
1660     }
1661     group.writeEntry("HorizontalAlignment", ui.cbHorizontalAlignment->currentIndex());
1662     group.writeEntry("VerticalAlignment", ui.cbVerticalAlignment->currentIndex());
1663     group.writeEntry("Rotation", ui.sbRotation->value());
1664 
1665     // Border
1666     group.writeEntry("BorderShape", ui.cbBorderShape->currentIndex());
1667     group.writeEntry("BorderStyle", ui.cbBorderStyle->currentIndex());
1668     group.writeEntry("BorderColor", ui.kcbBorderColor->color());
1669     group.writeEntry("BorderWidth", Worksheet::convertToSceneUnits(ui.sbBorderWidth->value(), Worksheet::Unit::Point));
1670     group.writeEntry("BorderOpacity", ui.sbBorderOpacity->value() / 100.0);
1671 }