1 /*
2 SPDX-License-Identifier: GPL-2.0-or-later
3 SPDX-FileCopyrightText: 2020 Sirgienko Nikita <warquark@gmail.com>
4 */
5
6 #include "horizontalruleentry.h"
7
8 #include <QApplication>
9 #include <QPainter>
10 #include <QPropertyAnimation>
11 #include <QJsonObject>
12
13 #include <KLocalizedString>
14
15 #include <jupyterutils.h>
16
17 const qreal HorizontalRuleEntry::LineVerticalMargin = 10;
18
19 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")};
20 const Qt::PenStyle HorizontalRuleEntry::styles[] = {Qt::SolidLine, Qt::DashLine, Qt::DotLine, Qt::DashDotLine, Qt::DashDotDotLine};
21
HorizontalRuleEntry(Worksheet * worksheet)22 HorizontalRuleEntry::HorizontalRuleEntry(Worksheet* worksheet)
23 : 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),
24 m_menusInitialized(false), m_lineTypeActionGroup(nullptr), m_lineTypeMenu(nullptr), m_lineColorCustom(false), m_lineColorActionGroup(nullptr), m_lineColorMenu(nullptr),
25 m_lineStyleActionGroup(nullptr), m_lineStyleMenu(nullptr)
26 {
27 }
28
~HorizontalRuleEntry()29 HorizontalRuleEntry::~HorizontalRuleEntry()
30 {
31 if (m_menusInitialized)
32 {
33 m_lineColorActionGroup->deleteLater();
34 m_lineColorMenu->deleteLater();
35 m_lineTypeActionGroup->deleteLater();
36 m_lineTypeMenu->deleteLater();
37 m_lineStyleActionGroup->deleteLater();
38 m_lineStyleMenu->deleteLater();
39 }
40 }
41
type() const42 int HorizontalRuleEntry::type() const
43 {
44 return Type;
45 }
46
setLineType(HorizontalRuleEntry::LineType type)47 void HorizontalRuleEntry::setLineType(HorizontalRuleEntry::LineType type)
48 {
49 m_type = type;
50
51 setSize(QSizeF(m_width, lineWidth(m_type) + 2*LineVerticalMargin));
52 }
53
lineWidth(HorizontalRuleEntry::LineType type)54 int HorizontalRuleEntry::lineWidth(HorizontalRuleEntry::LineType type)
55 {
56 return type == LineType::Thick ? 4 : ((int)type) + 1;
57 }
58
paint(QPainter * painter,const QStyleOptionGraphicsItem * option,QWidget * widget)59 void HorizontalRuleEntry::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
60 {
61 Q_UNUSED(option);
62 Q_UNUSED(widget);
63
64 painter->setPen(QPen(m_color, lineWidth(m_type), m_style));
65
66 const qreal margin = worksheet()->isPrinting() ? 0 : RightMargin;
67
68 painter->drawLine(m_entry_zone_offset_x, LineVerticalMargin, m_width - margin, LineVerticalMargin);
69 }
70
isEmpty()71 bool HorizontalRuleEntry::isEmpty()
72 {
73 return true;
74 }
75
acceptRichText()76 bool HorizontalRuleEntry::acceptRichText()
77 {
78 return false;
79 }
80
setContent(const QString &)81 void HorizontalRuleEntry::setContent(const QString&)
82 {
83 }
84
setContent(const QDomElement & content,const KZip & archive)85 void HorizontalRuleEntry::setContent(const QDomElement& content, const KZip& archive)
86 {
87 Q_UNUSED(archive);
88
89 m_type = (LineType)(content.attribute(QLatin1String("thickness"), QString::number((int)LineType::Medium)).toInt());
90 m_style = (Qt::PenStyle)(content.attribute(QLatin1String("style"), QString::number((int)Qt::SolidLine)).toInt());
91
92 QDomElement backgroundElem = content.firstChildElement(QLatin1String("lineColor"));
93 if (!backgroundElem.isNull())
94 {
95 m_color.setRed(backgroundElem.attribute(QLatin1String("red")).toInt());
96 m_color.setGreen(backgroundElem.attribute(QLatin1String("green")).toInt());
97 m_color.setBlue(backgroundElem.attribute(QLatin1String("blue")).toInt());
98 m_lineColorCustom = true;
99 }
100 }
101
setContentFromJupyter(const QJsonObject & cell)102 void HorizontalRuleEntry::setContentFromJupyter(const QJsonObject& cell)
103 {
104 QJsonObject cantorMetadata = Cantor::JupyterUtils::getCantorMetadata(cell);
105 QJsonValue typeValue = cantorMetadata.value(QLatin1String("type"));
106 if (typeValue.isDouble())
107 setLineType(static_cast<LineType>(static_cast<int>(typeValue.toDouble())));
108
109 QJsonValue styleValue = cantorMetadata.value(QLatin1String("style"));
110 if (styleValue.isDouble())
111 m_style = static_cast<Qt::PenStyle>(static_cast<int>(styleValue.toDouble()));
112
113 QJsonValue colorValue = cantorMetadata.value(QLatin1String("lineColor"));
114 if (colorValue.isObject())
115 {
116 m_color.setRed(colorValue.toObject().value(QLatin1String("red")).toInt());
117 m_color.setGreen(colorValue.toObject().value(QLatin1String("green")).toInt());
118 m_color.setBlue(colorValue.toObject().value(QLatin1String("blue")).toInt());
119 m_lineColorCustom = true;
120 }
121
122 setJupyterMetadata(Cantor::JupyterUtils::getMetadata(cell));
123 }
124
toJupyterJson()125 QJsonValue HorizontalRuleEntry::toJupyterJson()
126 {
127 QJsonObject entry;
128
129 entry.insert(QLatin1String("cell_type"), QLatin1String("markdown"));
130 QJsonObject metadata(jupyterMetadata());
131
132 QJsonObject cantor;
133 cantor.insert(QLatin1String("type"), m_type);
134 cantor.insert(QLatin1String("style"), m_style);
135
136 if (m_lineColorCustom)
137 {
138 QJsonObject color;
139 color.insert(QLatin1String("red"), m_color.red());
140 color.insert(QLatin1String("green"), m_color.green());
141 color.insert(QLatin1String("blue"), m_color.blue());
142 cantor.insert(QLatin1String("lineColor"), color);
143 }
144
145 metadata.insert(Cantor::JupyterUtils::cantorMetadataKey, cantor);
146
147 entry.insert(Cantor::JupyterUtils::metadataKey, metadata);
148
149 Cantor::JupyterUtils::setSource(entry, QLatin1String("----"));
150 return entry;
151 }
152
153
toXml(QDomDocument & doc,KZip * archive)154 QDomElement HorizontalRuleEntry::toXml(QDomDocument& doc, KZip* archive)
155 {
156 Q_UNUSED(archive);
157
158 QDomElement el = doc.createElement(QLatin1String("HorizontalRule"));
159 el.setAttribute(QLatin1String("thickness"), (int)m_type);
160 el.setAttribute(QLatin1String("style"), (int)m_style);
161
162 if (m_lineColorCustom)
163 {
164 QColor backgroundColor = m_color;
165 QDomElement colorElem = doc.createElement( QLatin1String("lineColor") );
166 colorElem.setAttribute(QLatin1String("red"), QString::number(backgroundColor.red()));
167 colorElem.setAttribute(QLatin1String("green"), QString::number(backgroundColor.green()));
168 colorElem.setAttribute(QLatin1String("blue"), QString::number(backgroundColor.blue()));
169 el.appendChild(colorElem);
170 }
171
172 return el;
173 }
174
toPlain(const QString &,const QString &,const QString &)175 QString HorizontalRuleEntry::toPlain(const QString&, const QString&, const QString&){
176 return QString();
177 }
178
layOutForWidth(qreal entry_zone_x,qreal w,bool force)179 void HorizontalRuleEntry::layOutForWidth(qreal entry_zone_x, qreal w, bool force)
180 {
181 Q_UNUSED(force);
182
183 m_entry_zone_offset_x = entry_zone_x;
184 m_width = w;
185
186 setSize(QSizeF(w, lineWidth(m_type) + 2*LineVerticalMargin));
187 }
188
evaluate(EvaluationOption evalOp)189 bool HorizontalRuleEntry::evaluate(EvaluationOption evalOp)
190 {
191 evaluateNext(evalOp);
192 return true;
193 }
194
updateEntry()195 void HorizontalRuleEntry::updateEntry()
196 {
197 }
198
wantToEvaluate()199 bool HorizontalRuleEntry::wantToEvaluate()
200 {
201 return false;
202 }
203
changeSize(QSizeF s)204 void HorizontalRuleEntry::changeSize(QSizeF s)
205 {
206 if (!worksheet()->animationsEnabled()) {
207 setSize(s);
208 worksheet()->updateEntrySize(this);
209 return;
210 }
211 if (aboutToBeRemoved())
212 return;
213
214 if (animationActive())
215 endAnimation();
216
217 QPropertyAnimation* sizeAn = sizeChangeAnimation(s);
218
219 sizeAn->setEasingCurve(QEasingCurve::InOutQuad);
220 sizeAn->start(QAbstractAnimation::DeleteWhenStopped);
221 }
222
populateMenu(QMenu * menu,QPointF pos)223 void HorizontalRuleEntry::populateMenu(QMenu* menu, QPointF pos)
224 {
225 if (!m_menusInitialized)
226 {
227 initMenus();
228 m_menusInitialized = true;
229 }
230
231 menu->addMenu(m_lineTypeMenu);
232 menu->addMenu(m_lineColorMenu);
233 menu->addMenu(m_lineStyleMenu);
234 WorksheetEntry::populateMenu(menu, pos);
235 }
236
lineTypeChanged(QAction * action)237 void HorizontalRuleEntry::lineTypeChanged(QAction* action)
238 {
239 int index = m_lineTypeActionGroup->actions().indexOf(action);
240 setLineType((LineType)(index % LineType::Count));
241 }
242
isConvertableToHorizontalRuleEntry(const QJsonObject & cell)243 bool HorizontalRuleEntry::isConvertableToHorizontalRuleEntry(const QJsonObject& cell)
244 {
245 if (!Cantor::JupyterUtils::isMarkdownCell(cell))
246 return false;
247
248 const QString& trimmedSource = Cantor::JupyterUtils::getSource(cell).trimmed();
249
250 int sourceLength = trimmedSource.length();
251 if (sourceLength < 3)
252 return false;
253
254 int hyphensCount = trimmedSource.count(QLatin1Char('-'));
255 int asteriksCount = trimmedSource.count(QLatin1Char('*'));
256 int underscoreCount = trimmedSource.count(QLatin1Char('_'));
257
258 return sourceLength == hyphensCount || sourceLength == asteriksCount || sourceLength == underscoreCount;
259 }
260
lineColorChanged(QAction * action)261 void HorizontalRuleEntry::lineColorChanged(QAction* action) {
262 int index = m_lineColorActionGroup->actions().indexOf(action);
263 if (index == -1 || index>=colorsCount)
264 index = 0;
265
266 if (index == 0)
267 {
268 m_color = QApplication::palette().color(QPalette::Text);
269 m_lineColorCustom = false;
270 }
271 else
272 {
273 m_color = colors[index-1];
274 m_lineColorCustom = true;
275 }
276 update();
277 }
278
lineStyleChanged(QAction * action)279 void HorizontalRuleEntry::lineStyleChanged(QAction* action)
280 {
281 unsigned int index = static_cast<unsigned int>(m_lineStyleActionGroup->actions().indexOf(action));
282 if (index > 0 && index < styleCount)
283 {
284 m_style = styles[index];
285 update();
286 }
287 }
288
289
initMenus()290 void HorizontalRuleEntry::initMenus()
291 {
292 m_lineTypeActionGroup = new QActionGroup(this);
293 m_lineTypeActionGroup->setExclusive(true);
294 connect(m_lineTypeActionGroup, &QActionGroup::triggered, this, &HorizontalRuleEntry::lineTypeChanged);
295
296 m_lineTypeMenu = new QMenu(i18n("Line Thickness"));
297
298 QAction* action = new QAction(i18n("Thin"), m_lineTypeActionGroup);
299 action->setCheckable(true);
300 m_lineTypeMenu->addAction(action);
301
302 action = new QAction(i18n("Medium"), m_lineTypeActionGroup);
303 action->setCheckable(true);
304 m_lineTypeMenu->addAction(action);
305
306 action = new QAction(i18n("Thick"), m_lineTypeActionGroup);
307 action->setCheckable(true);
308 m_lineTypeMenu->addAction(action);
309
310 // Set default menu value
311 m_lineTypeActionGroup->actions()[(int)m_type]->setChecked(true);
312
313
314
315 m_lineColorActionGroup = new QActionGroup(this);
316 m_lineColorActionGroup->setExclusive(true);
317 connect(m_lineColorActionGroup, &QActionGroup::triggered, this, &HorizontalRuleEntry::lineColorChanged);
318
319 m_lineColorMenu = new QMenu(i18n("Line Color"));
320 m_lineColorMenu->setIcon(QIcon::fromTheme(QLatin1String("format-fill-color")));
321
322 QPixmap pix(16,16);
323 QPainter p(&pix);
324
325 // Create default action
326 p.fillRect(pix.rect(), QApplication::palette().color(QPalette::Text));
327 action = new QAction(QIcon(pix), i18n("Default"), m_lineColorActionGroup);
328 action->setCheckable(true);
329 m_lineColorMenu->addAction(action);
330 if (!m_lineColorCustom)
331 action->setChecked(true);
332
333
334 for (int i=0; i<colorsCount; ++i) {
335 p.fillRect(pix.rect(), colors[i]);
336 action = new QAction(QIcon(pix), colorNames[i], m_lineColorActionGroup);
337 action->setCheckable(true);
338 m_lineColorMenu->addAction(action);
339
340 if (m_lineColorCustom && m_color == colors[i])
341 action->setChecked(true);
342 }
343
344
345
346 m_lineStyleActionGroup = new QActionGroup(this);
347 m_lineStyleActionGroup->setExclusive(true);
348 connect(m_lineStyleActionGroup, &QActionGroup::triggered, this, &HorizontalRuleEntry::lineStyleChanged);
349
350 m_lineStyleMenu = new QMenu(i18n("Line Style"));
351
352 for (unsigned int i = 0; i < styleCount; i++)
353 {
354 action = new QAction(styleNames[i], m_lineStyleActionGroup);
355 action->setCheckable(true);
356 m_lineStyleMenu->addAction(action);
357 if (styles[i] == m_style)
358 action->setChecked(true);
359 }
360 }
361