Krita Source Code Documentation
Loading...
Searching...
No Matches
SvgTextEditor.cpp
Go to the documentation of this file.
1/* This file is part of the KDE project
2 *
3 * SPDX-FileCopyrightText: 2017 Boudewijn Rempt <boud@valdyas.org>
4 *
5 * SPDX-License-Identifier: LGPL-2.0-or-later
6 */
7
8#include "SvgTextEditor.h"
9
10#include <QAction>
11#include <QActionGroup>
12#include <QApplication>
13#include <QBuffer>
14#include <QDialogButtonBox>
15#include <QDoubleSpinBox>
16#include <QFontComboBox>
17#include <QFontDatabase>
18#include <QFormLayout>
19#include <QLineEdit>
20#include <QListView>
21#include <QMenu>
22#include <QMessageBox>
23#include <QPainter>
24#include <QPalette>
25#include <QStandardItem>
26#include <QStandardItemModel>
27#include <QSvgGenerator>
28#include <QTabWidget>
29#include <QTextEdit>
30#include <QUrl>
31#include <QVBoxLayout>
32#include <QWidgetAction>
33#include <QScreen>
34#include <QScreen>
35
36#include <klocalizedstring.h>
37#include <ksharedconfig.h>
38#include <kconfiggroup.h>
39#include <kactioncollection.h>
40#include <kxmlguifactory.h>
41#include <ktoolbar.h>
42#include <ktoggleaction.h>
43#include <kguiitem.h>
44#include <kstandardguiitem.h>
45
46#include <KoDialog.h>
47#include <KoResourcePaths.h>
48#include <KoSvgTextShape.h>
51#include <KoColorPopupAction.h>
52#include <svg/SvgUtil.h>
53#include <KisPortingUtils.h>
54
57#include <kis_icon.h>
58#include <kis_config.h>
60#include <kis_action_registry.h>
61
62#include "kis_signals_blocker.h"
63
65{
66public:
67
69 {
70 }
71
72private:
73
74};
75
76
77SvgTextEditor::SvgTextEditor(QWidget *parent, Qt::WindowFlags flags)
78 : KXmlGuiWindow(parent, flags)
79 , m_page(new QWidget(this))
80 , d(new Private())
81{
83 setCentralWidget(m_page);
84
85 KGuiItem::assign(m_textEditorWidget.buttons->button(QDialogButtonBox::Save), KStandardGuiItem::save());
86 KGuiItem::assign(m_textEditorWidget.buttons->button(QDialogButtonBox::Cancel), KStandardGuiItem::cancel());
87 connect(m_textEditorWidget.buttons, SIGNAL(accepted()), this, SLOT(save()));
88 connect(m_textEditorWidget.buttons, SIGNAL(rejected()), this, SLOT(slotCloseEditor()));
89 connect(m_textEditorWidget.buttons, SIGNAL(clicked(QAbstractButton*)), this, SLOT(dialogButtonClicked(QAbstractButton*)));
90
91 KConfigGroup cg(KSharedConfig::openConfig(), "SvgTextTool");
92 actionCollection()->setConfigGroup("SvgTextTool");
93 actionCollection()->setComponentName("svgtexttool");
94 actionCollection()->setComponentDisplayName(i18n("Text Tool"));
95
96 if (cg.hasKey("WindowState")) {
97 QByteArray state = cg.readEntry("State", QByteArray());
98 // One day will need to load the version number, but for now, assume 0
99 restoreState(QByteArray::fromBase64(state));
100 }
101 if (cg.hasKey("Geometry")) {
102 QByteArray ba = cg.readEntry("Geometry", QByteArray());
103 restoreGeometry(QByteArray::fromBase64(ba));
104 }
105 else {
106 const int scnum = KisPortingUtils::getScreenNumberForWidget(QApplication::activeWindow());
107 QRect desk = QGuiApplication::screens().at(scnum)->availableGeometry();
108
109 quint32 x = desk.x();
110 quint32 y = desk.y();
111 quint32 w = 0;
112 quint32 h = 0;
113 const int deskWidth = desk.width();
114 w = (deskWidth / 3) * 2;
115 h = (desk.height() / 3) * 2;
116 x += (desk.width() - w) / 2;
117 y += (desk.height() - h) / 2;
118
119 move(x,y);
120 setGeometry(geometry().x(), geometry().y(), w, h);
121
122 }
123
124 setAcceptDrops(true);
125 //setStandardToolBarMenuEnabled(true);
126 setTabPosition(Qt::AllDockWidgetAreas, QTabWidget::North);
127
129 m_textEditorWidget.svgTextEdit->setFont(QFontDatabase().systemFont(QFontDatabase::FixedFont));
130
132 // If we have customized the toolbars, load that first
133 setLocalXMLFile(KoResourcePaths::locateLocal("data", "svgtexttool.xmlgui"));
134 setXMLFile(":/kxmlgui5/svgtexttool.xmlgui");
135
136 guiFactory()->addClient(this);
137
139
140}
141
143{
144 KConfigGroup g(KSharedConfig::openConfig(), "SvgTextTool");
145 QByteArray ba = saveState();
146 g.writeEntry("windowState", ba.toBase64());
147 ba = saveGeometry();
148 g.writeEntry("Geometry", ba.toBase64());
149}
150
151
153{
154 m_shape = shape;
155 if (m_shape) {
157
158 QString svg;
159 QString styles;
160
161 if (converter.convertToSvg(&svg, &styles)) {
162 m_textEditorWidget.svgTextEdit->setPlainText(svg);
163 m_textEditorWidget.svgStylesEdit->setPlainText(styles);
164 m_textEditorWidget.svgTextEdit->document()->setModified(false);
165 }
166 else {
167 QMessageBox::warning(this, i18n("Conversion failed"), "Could not get svg text from the shape:\n" + converter.errors().join('\n') + "\n" + converter.warnings().join('\n'));
168 }
169 }
170}
171
173{
174 if (m_shape) {
175 Q_EMIT textUpdated(m_shape, m_textEditorWidget.svgTextEdit->document()->toPlainText(), m_textEditorWidget.svgStylesEdit->document()->toPlainText());
176 m_textEditorWidget.svgTextEdit->document()->setModified(false);
177 }
178}
179
181{
182 m_currentEditor->undo();
183}
184
186{
187 m_currentEditor->redo();
188}
189
191{
192 m_currentEditor->cut();
193}
194
196{
197 m_currentEditor->copy();
198}
199
201{
202 m_currentEditor->paste();
203}
204
206{
207 m_currentEditor->selectAll();
208}
209
211{
212 QTextCursor cursor(m_currentEditor->textCursor());
213 cursor.clearSelection();
214 m_currentEditor->setTextCursor(cursor);
215}
216
218{
219 QDialog findDialog;
220 findDialog.setWindowTitle(i18n("Find Text"));
221 QFormLayout *layout = new QFormLayout(&findDialog);
222 QLineEdit *lnSearchKey = new QLineEdit();
223 layout->addRow(i18n("Find:"), lnSearchKey);
224 QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel);
225 layout->addWidget(buttons);
226
227 KGuiItem::assign(buttons->button(QDialogButtonBox::Ok), KStandardGuiItem::ok());
228 KGuiItem::assign(buttons->button(QDialogButtonBox::Cancel), KStandardGuiItem::cancel());
229
230 connect(buttons, SIGNAL(accepted()), &findDialog, SLOT(accept()));
231 connect(buttons, SIGNAL(rejected()), &findDialog, SLOT(reject()));
232
233 if (findDialog.exec() == QDialog::Accepted) {
234 m_searchKey = lnSearchKey->text();
236 }
237}
238
240{
241 if (!m_currentEditor->find(m_searchKey)) {
242 QTextCursor cursor(m_currentEditor->textCursor());
243 cursor.movePosition(QTextCursor::Start);
244 m_currentEditor->setTextCursor(cursor);
246 }
247}
248
250{
251 if (!m_currentEditor->find(m_searchKey,QTextDocument::FindBackward)) {
252 QTextCursor cursor(m_currentEditor->textCursor());
253 cursor.movePosition(QTextCursor::End);
254 m_currentEditor->setTextCursor(cursor);
255 m_currentEditor->find(m_searchKey,QTextDocument::FindBackward);
256 }
257}
258
260{
261 QDialog findDialog;
262 findDialog.setWindowTitle(i18n("Find and Replace all"));
263 QFormLayout *layout = new QFormLayout(&findDialog);
264 QLineEdit *lnSearchKey = new QLineEdit();
265 QLineEdit *lnReplaceKey = new QLineEdit();
266 layout->addRow(i18n("Find:"), lnSearchKey);
267 QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel);
268 layout->addRow(i18n("Replace:"), lnReplaceKey);
269 layout->addWidget(buttons);
270
271 KGuiItem::assign(buttons->button(QDialogButtonBox::Ok), KStandardGuiItem::ok());
272 KGuiItem::assign(buttons->button(QDialogButtonBox::Cancel), KStandardGuiItem::cancel());
273
274 connect(buttons, SIGNAL(accepted()), &findDialog, SLOT(accept()));
275 connect(buttons, SIGNAL(rejected()), &findDialog, SLOT(reject()));
276
277 if (findDialog.exec() == QDialog::Accepted) {
278 QString search = lnSearchKey->text();
279 QString replace = lnReplaceKey->text();
280 QTextCursor cursor(m_currentEditor->textCursor());
281 cursor.movePosition(QTextCursor::Start);
282 m_currentEditor->setTextCursor(cursor);
283 while(m_currentEditor->find(search)) {
284 m_currentEditor->textCursor().removeSelectedText();
285 m_currentEditor->textCursor().insertText(replace);
286 }
287
288 }
289}
290
291
293{
294 m_currentEditor->zoomOut();
295}
296
298{
299 m_currentEditor->zoomIn();
300}
301
303{
304 KoDialog settingsDialog(this);
305 Ui_WdgSvgTextSettings textSettings;
306 QWidget *settingsPage = new QWidget(&settingsDialog);
307 settingsDialog.setMainWidget(settingsPage);
308 textSettings.setupUi(settingsPage);
309
310 // get the settings and initialize the dialog
311 KConfigGroup cfg(KSharedConfig::openConfig(), "SvgTextTool");
312
313 QColor background = cfg.readEntry("colorEditorBackground", qApp->palette().window().color());
314 textSettings.colorEditorBackground->setColor(background);
315 textSettings.colorEditorForeground->setColor(cfg.readEntry("colorEditorForeground", qApp->palette().text().color()));
316
317 textSettings.colorKeyword->setColor(cfg.readEntry("colorKeyword", QColor(background.value() < 100 ? Qt::cyan : Qt::blue)));
318 textSettings.chkBoldKeyword->setChecked(cfg.readEntry("BoldKeyword", true));
319 textSettings.chkItalicKeyword->setChecked(cfg.readEntry("ItalicKeyword", false));
320
321 textSettings.colorElement->setColor(cfg.readEntry("colorElement", QColor(background.value() < 100 ? Qt::magenta : Qt::darkMagenta)));
322 textSettings.chkBoldElement->setChecked(cfg.readEntry("BoldElement", true));
323 textSettings.chkItalicElement->setChecked(cfg.readEntry("ItalicElement", false));
324
325 textSettings.colorAttribute->setColor(cfg.readEntry("colorAttribute", QColor(background.value() < 100 ? Qt::green : Qt::darkGreen)));
326 textSettings.chkBoldAttribute->setChecked(cfg.readEntry("BoldAttribute", true));
327 textSettings.chkItalicAttribute->setChecked(cfg.readEntry("ItalicAttribute", true));
328
329 textSettings.colorValue->setColor(cfg.readEntry("colorValue", QColor(background.value() < 100 ? Qt::red: Qt::darkRed)));
330 textSettings.chkBoldValue->setChecked(cfg.readEntry("BoldValue", true));
331 textSettings.chkItalicValue->setChecked(cfg.readEntry("ItalicValue", false));
332
333 textSettings.colorComment->setColor(cfg.readEntry("colorComment", QColor(background.value() < 100 ? Qt::lightGray : Qt::gray)));
334 textSettings.chkBoldComment->setChecked(cfg.readEntry("BoldComment", false));
335 textSettings.chkItalicComment->setChecked(cfg.readEntry("ItalicComment", false));
336
337 settingsDialog.setButtons(KoDialog::Ok | KoDialog::Cancel);
338 if (settingsDialog.exec() == QDialog::Accepted) {
339
340 cfg.writeEntry("colorEditorBackground", textSettings.colorEditorBackground->color());
341 cfg.writeEntry("colorEditorForeground", textSettings.colorEditorForeground->color());
342
343 cfg.writeEntry("colorKeyword", textSettings.colorKeyword->color());
344 cfg.writeEntry("BoldKeyword", textSettings.chkBoldKeyword->isChecked());
345 cfg.writeEntry("ItalicKeyWord", textSettings.chkItalicKeyword->isChecked());
346
347 cfg.writeEntry("colorElement", textSettings.colorElement->color());
348 cfg.writeEntry("BoldElement", textSettings.chkBoldElement->isChecked());
349 cfg.writeEntry("ItalicElement", textSettings.chkItalicElement->isChecked());
350
351 cfg.writeEntry("colorAttribute", textSettings.colorAttribute->color());
352 cfg.writeEntry("BoldAttribute", textSettings.chkBoldAttribute->isChecked());
353 cfg.writeEntry("ItalicAttribute", textSettings.chkItalicAttribute->isChecked());
354
355 cfg.writeEntry("colorValue", textSettings.colorValue->color());
356 cfg.writeEntry("BoldValue", textSettings.chkBoldValue->isChecked());
357 cfg.writeEntry("ItalicValue", textSettings.chkItalicValue->isChecked());
358
359 cfg.writeEntry("colorComment", textSettings.colorComment->color());
360 cfg.writeEntry("BoldComment", textSettings.chkBoldComment->isChecked());
361 cfg.writeEntry("ItalicComment", textSettings.chkItalicComment->isChecked());
362
364 }
365}
366
367
368void SvgTextEditor::setModified(bool modified)
369{
370 if (modified) {
371 m_textEditorWidget.buttons->setStandardButtons(QDialogButtonBox::Save | QDialogButtonBox::Discard);
372 KGuiItem::assign(m_textEditorWidget.buttons->button(QDialogButtonBox::Save), KStandardGuiItem::save());
373 KGuiItem::assign(m_textEditorWidget.buttons->button(QDialogButtonBox::Discard), KStandardGuiItem::discard());
374 }
375 else {
376 m_textEditorWidget.buttons->setStandardButtons(QDialogButtonBox::Save | QDialogButtonBox::Close);
377 KGuiItem::assign(m_textEditorWidget.buttons->button(QDialogButtonBox::Save), KStandardGuiItem::save());
378 KGuiItem::assign(m_textEditorWidget.buttons->button(QDialogButtonBox::Close), KStandardGuiItem::close());
379 }
380}
381
383{
384 if (m_textEditorWidget.buttons->standardButton(button) == QDialogButtonBox::Discard) {
385 if (QMessageBox::warning(this, i18nc("@title:window", "Krita"), i18n("You have modified the text. Discard changes?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
386 close();
387 }
388 }
389}
390
391void SvgTextEditor::wheelEvent(QWheelEvent *event)
392{
393 if (event->modifiers() & Qt::ControlModifier) {
394 int numDegrees = event->angleDelta().y() / 8;
395 int numSteps = numDegrees / 7;
396 m_textEditorWidget.svgTextEdit->zoomOut(numSteps);
397 event->accept();
398 }
399}
400
402{
403 KConfigGroup cfg(KSharedConfig::openConfig(), "SvgTextTool");
404
405 m_page->setUpdatesEnabled(false);
406
408
409 QPalette palette = m_textEditorWidget.svgTextEdit->palette();
410
411 QColor background = cfg.readEntry("colorEditorBackground", qApp->palette().window().color());
412 palette.setBrush(QPalette::Active, QPalette::Window, QBrush(background));
413 m_textEditorWidget.svgStylesEdit->setStyleSheet(QString("background-color:%1").arg(background.name()));
414 m_textEditorWidget.svgTextEdit->setStyleSheet(QString("background-color:%1").arg(background.name()));
415
416 QColor foreground = cfg.readEntry("colorEditorForeground", qApp->palette().text().color());
417 palette.setBrush(QPalette::Active, QPalette::Text, QBrush(foreground));
418
419 m_page->setUpdatesEnabled(true);
420}
421
422QAction *SvgTextEditor::createAction(const QString &name, const char *member)
423{
424 QAction *action = new QAction(this);
426 actionRegistry->propertizeAction(name, action);
427
429 QObject::connect(action, SIGNAL(triggered(bool)), this, member);
430 return action;
431}
432
433
435{
436 // File: new, open, save, save as, close
439
440 // Edit
443 KStandardAction::cut(this, SLOT(cut()), actionCollection());
452
453 // View
454 // WISH: we cannot zoom-in/out in rech-text mode
457
458 Q_FOREACH(QAction *action, m_svgTextActions) {
459 action->setEnabled(true);
460 }
461
462 // Settings
463 // do not add settings action to m_richTextActions list,
464 // it should always be active, regardless of which editor mode is used.
465 // otherwise we can lock the user out of being able to change
466 // editor mode, if user changes to SVG only mode.
467 createAction("svg_settings", SLOT(setSettings()));
468}
470{
471 close();
472 Q_EMIT textEditorClosed();
473}
474
475bool SvgTextEditor::eventFilter(QObject *const watched, QEvent *const event)
476{
477 if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) {
478 QKeyEvent *const keyEvent = static_cast<QKeyEvent *>(event);
479 if ((keyEvent->key() == Qt::Key_Enter || keyEvent->key() == Qt::Key_Return)
480 && (keyEvent->modifiers() & Qt::ShiftModifier)) {
481 // Disable soft line breaks
482 return true;
483 }
484 }
485 return false;
486 return KXmlGuiWindow::eventFilter(watched, event);
487}
KDE top level main window with predefined action layout
bool event(QEvent *event) override
virtual KisKXMLGUIFactory * guiFactory()
bool propertizeAction(const QString &name, QAction *a)
static KisActionRegistry * instance()
void setComponentDisplayName(const QString &displayName)
void setConfigGroup(const QString &group)
Q_INVOKABLE QAction * addAction(const QString &name, QAction *action)
void setComponentName(const QString &componentName)
void setLocalXMLFile(const QString &file)
QAction * action(const char *name) const
void setXMLFile(const QString &file, bool merge=false, bool setXMLDoc=true)
virtual KisKActionCollection * actionCollection() const
void addClient(KisKXMLGUIClient *client)
A dialog base class with standard buttons and predefined layouts.
Definition KoDialog.h:116
void setMainWidget(QWidget *widget)
Definition KoDialog.cpp:354
void setButtons(ButtonCodes buttonMask)
Definition KoDialog.cpp:195
@ Ok
Show Ok button. (this button accept()s the dialog; result set to QDialog::Accepted)
Definition KoDialog.h:127
@ Cancel
Show Cancel-button. (this button reject()s the dialog; result set to QDialog::Rejected)
Definition KoDialog.h:130
static QString locateLocal(const QString &type, const QString &filename, bool createDir=false)
bool convertToSvg(QString *svgText, QString *stylesText)
KoSvgTextShape * m_shape
void setModified(bool modified)
QAction * createAction(const QString &name, const char *member)
SvgTextEditor(QWidget *parent=0, Qt::WindowFlags f=Qt::WindowFlags())
bool eventFilter(QObject *watched, QEvent *event) override
BasicXMLSyntaxHighlighter * m_syntaxHighlighter
void dialogButtonClicked(QAbstractButton *button)
QList< QAction * > m_svgTextActions
void textUpdated(KoSvgTextShape *shape, const QString &svg, const QString &defs)
QString m_searchKey
QTextEdit * m_currentEditor
void setInitialShape(KoSvgTextShape *shape)
void wheelEvent(QWheelEvent *event) override
QWidget * m_page
Ui_WdgSvgTextEditor m_textEditorWidget
void textEditorClosed()
QString button(const QWheelEvent &ev)
QString buttons(const T &ev)
QAction * replace(const QObject *recvr, const char *slot, QObject *parent)
QAction * deselect(const QObject *recvr, const char *slot, QObject *parent)
QAction * findNext(const QObject *recvr, const char *slot, QObject *parent)
QAction * redo(const QObject *recvr, const char *slot, QObject *parent)
QAction * undo(const QObject *recvr, const char *slot, QObject *parent)
QAction * zoomOut(const QObject *recvr, const char *slot, QObject *parent)
QAction * close(const QObject *recvr, const char *slot, QObject *parent)
QAction * copy(const QObject *recvr, const char *slot, QObject *parent)
QAction * selectAll(const QObject *recvr, const char *slot, QObject *parent)
QAction * save(const QObject *recvr, const char *slot, QObject *parent)
QAction * paste(const QObject *recvr, const char *slot, QObject *parent)
QAction * zoomIn(const QObject *recvr, const char *slot, QObject *parent)
QAction * cut(const QObject *recvr, const char *slot, QObject *parent)
QAction * find(const QObject *recvr, const char *slot, QObject *parent)
QAction * findPrev(const QObject *recvr, const char *slot, QObject *parent)
int getScreenNumberForWidget(const QWidget *w)
rgba palette[MAX_PALETTE]
Definition palette.c:35