File indexing completed on 2024-04-28 11:20:56

0001 /*
0002     SPDX-License-Identifier: GPL-2.0-or-later
0003     SPDX-FileCopyrightText: 2020 Sirgienko Nikita <warquark@gmail.com>
0004 */
0005 
0006 #include "horizontalruleentry.h"
0007 
0008 #include <QApplication>
0009 #include <QPainter>
0010 #include <QPropertyAnimation>
0011 #include <QJsonObject>
0012 #include <QActionGroup>
0013 
0014 #include <KLocalizedString>
0015 
0016 #include <jupyterutils.h>
0017 
0018 const qreal HorizontalRuleEntry::LineVerticalMargin = 10;
0019 
0020 const QString HorizontalRuleEntry::styleNames[] = {i18n("Solid Line Style"), i18n("Dash Line Style"), i18n("Dot Line Style"), i18n("Dash Dot Line Style"), i18n("Dash Dot Dot Line Style")};
0021 const Qt::PenStyle HorizontalRuleEntry::styles[] = {Qt::SolidLine, Qt::DashLine, Qt::DotLine, Qt::DashDotLine, Qt::DashDotDotLine};
0022 
0023 HorizontalRuleEntry::HorizontalRuleEntry(Worksheet* worksheet)
0024     : WorksheetEntry(worksheet), m_type(LineType::Medium), m_color(QApplication::palette().color(QPalette::Text)), m_entry_zone_offset_x(0), m_width(0), m_style(Qt::SolidLine),
0025       m_menusInitialized(false), m_lineTypeActionGroup(nullptr), m_lineTypeMenu(nullptr), m_lineColorCustom(false), m_lineColorActionGroup(nullptr), m_lineColorMenu(nullptr),
0026       m_lineStyleActionGroup(nullptr), m_lineStyleMenu(nullptr)
0027 {
0028 }
0029 
0030 HorizontalRuleEntry::~HorizontalRuleEntry()
0031 {
0032     if (m_menusInitialized)
0033     {
0034         m_lineColorActionGroup->deleteLater();
0035         m_lineColorMenu->deleteLater();
0036         m_lineTypeActionGroup->deleteLater();
0037         m_lineTypeMenu->deleteLater();
0038         m_lineStyleActionGroup->deleteLater();
0039         m_lineStyleMenu->deleteLater();
0040     }
0041 }
0042 
0043 int HorizontalRuleEntry::type() const
0044 {
0045     return Type;
0046 }
0047 
0048 void HorizontalRuleEntry::setLineType(HorizontalRuleEntry::LineType type)
0049 {
0050     m_type = type;
0051 
0052     setSize(QSizeF(m_width, lineWidth(m_type) + 2*LineVerticalMargin));
0053 }
0054 
0055 int HorizontalRuleEntry::lineWidth(HorizontalRuleEntry::LineType type)
0056 {
0057     return type == LineType::Thick ? 4 : ((int)type) + 1;
0058 }
0059 
0060 void HorizontalRuleEntry::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
0061 {
0062     Q_UNUSED(option);
0063     Q_UNUSED(widget);
0064 
0065     painter->setPen(QPen(m_color, lineWidth(m_type), m_style));
0066 
0067     const qreal margin = worksheet()->isPrinting() ? 0 : RightMargin;
0068 
0069     painter->drawLine(m_entry_zone_offset_x, LineVerticalMargin, m_width - margin, LineVerticalMargin);
0070 }
0071 
0072 bool HorizontalRuleEntry::isEmpty()
0073 {
0074     return true;
0075 }
0076 
0077 bool HorizontalRuleEntry::acceptRichText()
0078 {
0079     return false;
0080 }
0081 
0082 void HorizontalRuleEntry::setContent(const QString&)
0083 {
0084 }
0085 
0086 void HorizontalRuleEntry::setContent(const QDomElement& content, const KZip& archive)
0087 {
0088     Q_UNUSED(archive);
0089 
0090     m_type = (LineType)(content.attribute(QLatin1String("thickness"), QString::number((int)LineType::Medium)).toInt());
0091     m_style = (Qt::PenStyle)(content.attribute(QLatin1String("style"), QString::number((int)Qt::SolidLine)).toInt());
0092 
0093     QDomElement backgroundElem = content.firstChildElement(QLatin1String("lineColor"));
0094     if (!backgroundElem.isNull())
0095     {
0096         m_color.setRed(backgroundElem.attribute(QLatin1String("red")).toInt());
0097         m_color.setGreen(backgroundElem.attribute(QLatin1String("green")).toInt());
0098         m_color.setBlue(backgroundElem.attribute(QLatin1String("blue")).toInt());
0099         m_lineColorCustom = true;
0100     }
0101 }
0102 
0103 void HorizontalRuleEntry::setContentFromJupyter(const QJsonObject& cell)
0104 {
0105     QJsonObject cantorMetadata = Cantor::JupyterUtils::getCantorMetadata(cell);
0106     QJsonValue typeValue = cantorMetadata.value(QLatin1String("type"));
0107     if (typeValue.isDouble())
0108         setLineType(static_cast<LineType>(static_cast<int>(typeValue.toDouble())));
0109 
0110     QJsonValue styleValue = cantorMetadata.value(QLatin1String("style"));
0111     if (styleValue.isDouble())
0112         m_style = static_cast<Qt::PenStyle>(static_cast<int>(styleValue.toDouble()));
0113 
0114     QJsonValue colorValue = cantorMetadata.value(QLatin1String("lineColor"));
0115     if (colorValue.isObject())
0116     {
0117         m_color.setRed(colorValue.toObject().value(QLatin1String("red")).toInt());
0118         m_color.setGreen(colorValue.toObject().value(QLatin1String("green")).toInt());
0119         m_color.setBlue(colorValue.toObject().value(QLatin1String("blue")).toInt());
0120         m_lineColorCustom = true;
0121     }
0122 
0123     setJupyterMetadata(Cantor::JupyterUtils::getMetadata(cell));
0124 }
0125 
0126 QJsonValue HorizontalRuleEntry::toJupyterJson()
0127 {
0128     QJsonObject entry;
0129 
0130     entry.insert(QLatin1String("cell_type"), QLatin1String("markdown"));
0131     QJsonObject metadata(jupyterMetadata());
0132 
0133     QJsonObject cantor;
0134     cantor.insert(QLatin1String("type"), m_type);
0135     cantor.insert(QLatin1String("style"), m_style);
0136 
0137     if (m_lineColorCustom)
0138     {
0139         QJsonObject color;
0140         color.insert(QLatin1String("red"), m_color.red());
0141         color.insert(QLatin1String("green"), m_color.green());
0142         color.insert(QLatin1String("blue"), m_color.blue());
0143         cantor.insert(QLatin1String("lineColor"), color);
0144     }
0145 
0146     metadata.insert(Cantor::JupyterUtils::cantorMetadataKey, cantor);
0147 
0148     entry.insert(Cantor::JupyterUtils::metadataKey, metadata);
0149 
0150     Cantor::JupyterUtils::setSource(entry, QLatin1String("----"));
0151     return entry;
0152 }
0153 
0154 
0155 QDomElement HorizontalRuleEntry::toXml(QDomDocument& doc, KZip* archive)
0156 {
0157     Q_UNUSED(archive);
0158 
0159     QDomElement el = doc.createElement(QLatin1String("HorizontalRule"));
0160     el.setAttribute(QLatin1String("thickness"), (int)m_type);
0161     el.setAttribute(QLatin1String("style"), (int)m_style);
0162 
0163     if (m_lineColorCustom)
0164     {
0165         QColor backgroundColor = m_color;
0166         QDomElement colorElem = doc.createElement( QLatin1String("lineColor") );
0167         colorElem.setAttribute(QLatin1String("red"), QString::number(backgroundColor.red()));
0168         colorElem.setAttribute(QLatin1String("green"), QString::number(backgroundColor.green()));
0169         colorElem.setAttribute(QLatin1String("blue"), QString::number(backgroundColor.blue()));
0170         el.appendChild(colorElem);
0171     }
0172 
0173     return el;
0174 }
0175 
0176 QString HorizontalRuleEntry::toPlain(const QString&, const QString&, const QString&){
0177     return QString();
0178 }
0179 
0180 void HorizontalRuleEntry::layOutForWidth(qreal entry_zone_x, qreal w, bool force)
0181 {
0182     Q_UNUSED(force);
0183 
0184     m_entry_zone_offset_x = entry_zone_x;
0185     m_width = w;
0186 
0187     setSize(QSizeF(w, lineWidth(m_type) + 2*LineVerticalMargin));
0188 }
0189 
0190 bool HorizontalRuleEntry::evaluate(EvaluationOption evalOp)
0191 {
0192     evaluateNext(evalOp);
0193     return true;
0194 }
0195 
0196 void HorizontalRuleEntry::updateEntry()
0197 {
0198 }
0199 
0200 bool HorizontalRuleEntry::wantToEvaluate()
0201 {
0202     return false;
0203 }
0204 
0205 void HorizontalRuleEntry::changeSize(QSizeF s)
0206 {
0207     if (!worksheet()->animationsEnabled()) {
0208         setSize(s);
0209         worksheet()->updateEntrySize(this);
0210         return;
0211     }
0212     if (aboutToBeRemoved())
0213         return;
0214 
0215     if (animationActive())
0216         endAnimation();
0217 
0218     QPropertyAnimation* sizeAn = sizeChangeAnimation(s);
0219 
0220     sizeAn->setEasingCurve(QEasingCurve::InOutQuad);
0221     sizeAn->start(QAbstractAnimation::DeleteWhenStopped);
0222 }
0223 
0224 void HorizontalRuleEntry::populateMenu(QMenu* menu, QPointF pos)
0225 {
0226     if (!m_menusInitialized)
0227     {
0228         initMenus();
0229         m_menusInitialized = true;
0230     }
0231 
0232     menu->addMenu(m_lineTypeMenu);
0233     menu->addMenu(m_lineColorMenu);
0234     menu->addMenu(m_lineStyleMenu);
0235     WorksheetEntry::populateMenu(menu, pos);
0236 }
0237 
0238 void HorizontalRuleEntry::lineTypeChanged(QAction* action)
0239 {
0240     int index = m_lineTypeActionGroup->actions().indexOf(action);
0241     setLineType((LineType)(index % LineType::Count));
0242 }
0243 
0244 bool HorizontalRuleEntry::isConvertableToHorizontalRuleEntry(const QJsonObject& cell)
0245 {
0246     if (!Cantor::JupyterUtils::isMarkdownCell(cell))
0247         return false;
0248 
0249     const QString& trimmedSource = Cantor::JupyterUtils::getSource(cell).trimmed();
0250 
0251     int sourceLength = trimmedSource.length();
0252     if (sourceLength < 3)
0253         return false;
0254 
0255     int hyphensCount = trimmedSource.count(QLatin1Char('-'));
0256     int asteriksCount = trimmedSource.count(QLatin1Char('*'));
0257     int underscoreCount = trimmedSource.count(QLatin1Char('_'));
0258 
0259     return sourceLength == hyphensCount || sourceLength == asteriksCount || sourceLength == underscoreCount;
0260 }
0261 
0262 void HorizontalRuleEntry::lineColorChanged(QAction* action) {
0263     int index = m_lineColorActionGroup->actions().indexOf(action);
0264     if (index == -1 || index>=colorsCount)
0265         index = 0;
0266 
0267     if (index == 0)
0268     {
0269         m_color = QApplication::palette().color(QPalette::Text);
0270         m_lineColorCustom = false;
0271     }
0272     else
0273     {
0274         m_color = colors[index-1];
0275         m_lineColorCustom = true;
0276     }
0277     update();
0278 }
0279 
0280 void HorizontalRuleEntry::lineStyleChanged(QAction* action)
0281 {
0282     unsigned int index = static_cast<unsigned int>(m_lineStyleActionGroup->actions().indexOf(action));
0283     if (index > 0 && index < styleCount)
0284     {
0285         m_style = styles[index];
0286         update();
0287     }
0288 }
0289 
0290 
0291 void HorizontalRuleEntry::initMenus()
0292 {
0293     m_lineTypeActionGroup = new QActionGroup(this);
0294     m_lineTypeActionGroup->setExclusive(true);
0295     connect(m_lineTypeActionGroup, &QActionGroup::triggered, this, &HorizontalRuleEntry::lineTypeChanged);
0296 
0297     m_lineTypeMenu = new QMenu(i18n("Line Thickness"));
0298 
0299     QAction* action = new QAction(i18n("Thin"), m_lineTypeActionGroup);
0300     action->setCheckable(true);
0301     m_lineTypeMenu->addAction(action);
0302 
0303     action = new QAction(i18n("Medium"), m_lineTypeActionGroup);
0304     action->setCheckable(true);
0305     m_lineTypeMenu->addAction(action);
0306 
0307     action = new QAction(i18n("Thick"), m_lineTypeActionGroup);
0308     action->setCheckable(true);
0309     m_lineTypeMenu->addAction(action);
0310 
0311     // Set default menu value
0312     m_lineTypeActionGroup->actions()[(int)m_type]->setChecked(true);
0313 
0314 
0315 
0316     m_lineColorActionGroup = new QActionGroup(this);
0317     m_lineColorActionGroup->setExclusive(true);
0318     connect(m_lineColorActionGroup, &QActionGroup::triggered, this, &HorizontalRuleEntry::lineColorChanged);
0319 
0320     m_lineColorMenu = new QMenu(i18n("Line Color"));
0321     m_lineColorMenu->setIcon(QIcon::fromTheme(QLatin1String("format-fill-color")));
0322 
0323     QPixmap pix(16,16);
0324     QPainter p(&pix);
0325 
0326     // Create default action
0327     p.fillRect(pix.rect(), QApplication::palette().color(QPalette::Text));
0328     action = new QAction(QIcon(pix), i18n("Default"), m_lineColorActionGroup);
0329     action->setCheckable(true);
0330     m_lineColorMenu->addAction(action);
0331     if (!m_lineColorCustom)
0332         action->setChecked(true);
0333 
0334 
0335     for (int i=0; i<colorsCount; ++i) {
0336         p.fillRect(pix.rect(), colors[i]);
0337         action = new QAction(QIcon(pix), colorNames[i], m_lineColorActionGroup);
0338         action->setCheckable(true);
0339         m_lineColorMenu->addAction(action);
0340 
0341         if (m_lineColorCustom && m_color == colors[i])
0342             action->setChecked(true);
0343     }
0344 
0345 
0346 
0347     m_lineStyleActionGroup = new QActionGroup(this);
0348     m_lineStyleActionGroup->setExclusive(true);
0349     connect(m_lineStyleActionGroup, &QActionGroup::triggered, this, &HorizontalRuleEntry::lineStyleChanged);
0350 
0351     m_lineStyleMenu = new QMenu(i18n("Line Style"));
0352 
0353     for (unsigned int i = 0; i < styleCount; i++)
0354     {
0355         action = new QAction(styleNames[i], m_lineStyleActionGroup);
0356         action->setCheckable(true);
0357         m_lineStyleMenu->addAction(action);
0358         if (styles[i] == m_style)
0359             action->setChecked(true);
0360     }
0361 }