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#ifdef Q_OS_MACOS
127 setUnifiedTitleAndToolBarOnMac(true);
128#endif
129 setTabPosition(Qt::AllDockWidgetAreas, QTabWidget::North);
130
132 m_textEditorWidget.svgTextEdit->setFont(QFontDatabase().systemFont(QFontDatabase::FixedFont));
133
135 // If we have customized the toolbars, load that first
136 setLocalXMLFile(KoResourcePaths::locateLocal("data", "svgtexttool.xmlgui"));
137 setXMLFile(":/kxmlgui5/svgtexttool.xmlgui");
138
139 guiFactory()->addClient(this);
140
142
143}
144
146{
147 KConfigGroup g(KSharedConfig::openConfig(), "SvgTextTool");
148 QByteArray ba = saveState();
149 g.writeEntry("windowState", ba.toBase64());
150 ba = saveGeometry();
151 g.writeEntry("Geometry", ba.toBase64());
152}
153
154
156{
157 m_shape = shape;
158 if (m_shape) {
160
161 QString svg;
162 QString styles;
163
164 if (converter.convertToSvg(&svg, &styles)) {
165 m_textEditorWidget.svgTextEdit->setPlainText(svg);
166 m_textEditorWidget.svgStylesEdit->setPlainText(styles);
167 m_textEditorWidget.svgTextEdit->document()->setModified(false);
168 }
169 else {
170 QMessageBox::warning(this, i18n("Conversion failed"), "Could not get svg text from the shape:\n" + converter.errors().join('\n') + "\n" + converter.warnings().join('\n'));
171 }
172 }
173}
174
176{
177 if (m_shape) {
178 Q_EMIT textUpdated(m_shape, m_textEditorWidget.svgTextEdit->document()->toPlainText(), m_textEditorWidget.svgStylesEdit->document()->toPlainText());
179 m_textEditorWidget.svgTextEdit->document()->setModified(false);
180 }
181}
182
184{
185 m_currentEditor->undo();
186}
187
189{
190 m_currentEditor->redo();
191}
192
194{
195 m_currentEditor->cut();
196}
197
199{
200 m_currentEditor->copy();
201}
202
204{
205 m_currentEditor->paste();
206}
207
209{
210 m_currentEditor->selectAll();
211}
212
214{
215 QTextCursor cursor(m_currentEditor->textCursor());
216 cursor.clearSelection();
217 m_currentEditor->setTextCursor(cursor);
218}
219
221{
222 QDialog findDialog;
223 findDialog.setWindowTitle(i18n("Find Text"));
224 QFormLayout *layout = new QFormLayout(&findDialog);
225 QLineEdit *lnSearchKey = new QLineEdit();
226 layout->addRow(i18n("Find:"), lnSearchKey);
227 QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel);
228 layout->addWidget(buttons);
229
230 KGuiItem::assign(buttons->button(QDialogButtonBox::Ok), KStandardGuiItem::ok());
231 KGuiItem::assign(buttons->button(QDialogButtonBox::Cancel), KStandardGuiItem::cancel());
232
233 connect(buttons, SIGNAL(accepted()), &findDialog, SLOT(accept()));
234 connect(buttons, SIGNAL(rejected()), &findDialog, SLOT(reject()));
235
236 if (findDialog.exec() == QDialog::Accepted) {
237 m_searchKey = lnSearchKey->text();
239 }
240}
241
243{
244 if (!m_currentEditor->find(m_searchKey)) {
245 QTextCursor cursor(m_currentEditor->textCursor());
246 cursor.movePosition(QTextCursor::Start);
247 m_currentEditor->setTextCursor(cursor);
249 }
250}
251
253{
254 if (!m_currentEditor->find(m_searchKey,QTextDocument::FindBackward)) {
255 QTextCursor cursor(m_currentEditor->textCursor());
256 cursor.movePosition(QTextCursor::End);
257 m_currentEditor->setTextCursor(cursor);
258 m_currentEditor->find(m_searchKey,QTextDocument::FindBackward);
259 }
260}
261
263{
264 QDialog findDialog;
265 findDialog.setWindowTitle(i18n("Find and Replace all"));
266 QFormLayout *layout = new QFormLayout(&findDialog);
267 QLineEdit *lnSearchKey = new QLineEdit();
268 QLineEdit *lnReplaceKey = new QLineEdit();
269 layout->addRow(i18n("Find:"), lnSearchKey);
270 QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel);
271 layout->addRow(i18n("Replace:"), lnReplaceKey);
272 layout->addWidget(buttons);
273
274 KGuiItem::assign(buttons->button(QDialogButtonBox::Ok), KStandardGuiItem::ok());
275 KGuiItem::assign(buttons->button(QDialogButtonBox::Cancel), KStandardGuiItem::cancel());
276
277 connect(buttons, SIGNAL(accepted()), &findDialog, SLOT(accept()));
278 connect(buttons, SIGNAL(rejected()), &findDialog, SLOT(reject()));
279
280 if (findDialog.exec() == QDialog::Accepted) {
281 QString search = lnSearchKey->text();
282 QString replace = lnReplaceKey->text();
283 QTextCursor cursor(m_currentEditor->textCursor());
284 cursor.movePosition(QTextCursor::Start);
285 m_currentEditor->setTextCursor(cursor);
286 while(m_currentEditor->find(search)) {
287 m_currentEditor->textCursor().removeSelectedText();
288 m_currentEditor->textCursor().insertText(replace);
289 }
290
291 }
292}
293
294
296{
297 m_currentEditor->zoomOut();
298}
299
301{
302 m_currentEditor->zoomIn();
303}
304
306{
307 KoDialog settingsDialog(this);
308 Ui_WdgSvgTextSettings textSettings;
309 QWidget *settingsPage = new QWidget(&settingsDialog);
310 settingsDialog.setMainWidget(settingsPage);
311 textSettings.setupUi(settingsPage);
312
313 // get the settings and initialize the dialog
314 KConfigGroup cfg(KSharedConfig::openConfig(), "SvgTextTool");
315
316 QColor background = cfg.readEntry("colorEditorBackground", qApp->palette().window().color());
317 textSettings.colorEditorBackground->setColor(background);
318 textSettings.colorEditorForeground->setColor(cfg.readEntry("colorEditorForeground", qApp->palette().text().color()));
319
320 textSettings.colorKeyword->setColor(cfg.readEntry("colorKeyword", QColor(background.value() < 100 ? Qt::cyan : Qt::blue)));
321 textSettings.chkBoldKeyword->setChecked(cfg.readEntry("BoldKeyword", true));
322 textSettings.chkItalicKeyword->setChecked(cfg.readEntry("ItalicKeyword", false));
323
324 textSettings.colorElement->setColor(cfg.readEntry("colorElement", QColor(background.value() < 100 ? Qt::magenta : Qt::darkMagenta)));
325 textSettings.chkBoldElement->setChecked(cfg.readEntry("BoldElement", true));
326 textSettings.chkItalicElement->setChecked(cfg.readEntry("ItalicElement", false));
327
328 textSettings.colorAttribute->setColor(cfg.readEntry("colorAttribute", QColor(background.value() < 100 ? Qt::green : Qt::darkGreen)));
329 textSettings.chkBoldAttribute->setChecked(cfg.readEntry("BoldAttribute", true));
330 textSettings.chkItalicAttribute->setChecked(cfg.readEntry("ItalicAttribute", true));
331
332 textSettings.colorValue->setColor(cfg.readEntry("colorValue", QColor(background.value() < 100 ? Qt::red: Qt::darkRed)));
333 textSettings.chkBoldValue->setChecked(cfg.readEntry("BoldValue", true));
334 textSettings.chkItalicValue->setChecked(cfg.readEntry("ItalicValue", false));
335
336 textSettings.colorComment->setColor(cfg.readEntry("colorComment", QColor(background.value() < 100 ? Qt::lightGray : Qt::gray)));
337 textSettings.chkBoldComment->setChecked(cfg.readEntry("BoldComment", false));
338 textSettings.chkItalicComment->setChecked(cfg.readEntry("ItalicComment", false));
339
340 settingsDialog.setButtons(KoDialog::Ok | KoDialog::Cancel);
341 if (settingsDialog.exec() == QDialog::Accepted) {
342
343 cfg.writeEntry("colorEditorBackground", textSettings.colorEditorBackground->color());
344 cfg.writeEntry("colorEditorForeground", textSettings.colorEditorForeground->color());
345
346 cfg.writeEntry("colorKeyword", textSettings.colorKeyword->color());
347 cfg.writeEntry("BoldKeyword", textSettings.chkBoldKeyword->isChecked());
348 cfg.writeEntry("ItalicKeyWord", textSettings.chkItalicKeyword->isChecked());
349
350 cfg.writeEntry("colorElement", textSettings.colorElement->color());
351 cfg.writeEntry("BoldElement", textSettings.chkBoldElement->isChecked());
352 cfg.writeEntry("ItalicElement", textSettings.chkItalicElement->isChecked());
353
354 cfg.writeEntry("colorAttribute", textSettings.colorAttribute->color());
355 cfg.writeEntry("BoldAttribute", textSettings.chkBoldAttribute->isChecked());
356 cfg.writeEntry("ItalicAttribute", textSettings.chkItalicAttribute->isChecked());
357
358 cfg.writeEntry("colorValue", textSettings.colorValue->color());
359 cfg.writeEntry("BoldValue", textSettings.chkBoldValue->isChecked());
360 cfg.writeEntry("ItalicValue", textSettings.chkItalicValue->isChecked());
361
362 cfg.writeEntry("colorComment", textSettings.colorComment->color());
363 cfg.writeEntry("BoldComment", textSettings.chkBoldComment->isChecked());
364 cfg.writeEntry("ItalicComment", textSettings.chkItalicComment->isChecked());
365
367 }
368}
369
370
371void SvgTextEditor::setModified(bool modified)
372{
373 if (modified) {
374 m_textEditorWidget.buttons->setStandardButtons(QDialogButtonBox::Save | QDialogButtonBox::Discard);
375 KGuiItem::assign(m_textEditorWidget.buttons->button(QDialogButtonBox::Save), KStandardGuiItem::save());
376 KGuiItem::assign(m_textEditorWidget.buttons->button(QDialogButtonBox::Discard), KStandardGuiItem::discard());
377 }
378 else {
379 m_textEditorWidget.buttons->setStandardButtons(QDialogButtonBox::Save | QDialogButtonBox::Close);
380 KGuiItem::assign(m_textEditorWidget.buttons->button(QDialogButtonBox::Save), KStandardGuiItem::save());
381 KGuiItem::assign(m_textEditorWidget.buttons->button(QDialogButtonBox::Close), KStandardGuiItem::close());
382 }
383}
384
386{
387 if (m_textEditorWidget.buttons->standardButton(button) == QDialogButtonBox::Discard) {
388 if (QMessageBox::warning(this, i18nc("@title:window", "Krita"), i18n("You have modified the text. Discard changes?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
389 close();
390 }
391 }
392}
393
394void SvgTextEditor::wheelEvent(QWheelEvent *event)
395{
396 if (event->modifiers() & Qt::ControlModifier) {
397 int numDegrees = event->angleDelta().y() / 8;
398 int numSteps = numDegrees / 7;
399 m_textEditorWidget.svgTextEdit->zoomOut(numSteps);
400 event->accept();
401 }
402}
403
405{
406 KConfigGroup cfg(KSharedConfig::openConfig(), "SvgTextTool");
407
408 m_page->setUpdatesEnabled(false);
409
411
412 QPalette palette = m_textEditorWidget.svgTextEdit->palette();
413
414 QColor background = cfg.readEntry("colorEditorBackground", qApp->palette().window().color());
415 palette.setBrush(QPalette::Active, QPalette::Window, QBrush(background));
416 m_textEditorWidget.svgStylesEdit->setStyleSheet(QString("background-color:%1").arg(background.name()));
417 m_textEditorWidget.svgTextEdit->setStyleSheet(QString("background-color:%1").arg(background.name()));
418
419 QColor foreground = cfg.readEntry("colorEditorForeground", qApp->palette().text().color());
420 palette.setBrush(QPalette::Active, QPalette::Text, QBrush(foreground));
421
422 m_page->setUpdatesEnabled(true);
423}
424
425QAction *SvgTextEditor::createAction(const QString &name, const char *member)
426{
427 QAction *action = new QAction(this);
429 actionRegistry->propertizeAction(name, action);
430
432 QObject::connect(action, SIGNAL(triggered(bool)), this, member);
433 return action;
434}
435
436
438{
439 // File: new, open, save, save as, close
442
443 // Edit
446 KStandardAction::cut(this, SLOT(cut()), actionCollection());
455
456 // View
457 // WISH: we cannot zoom-in/out in rech-text mode
460
461 Q_FOREACH(QAction *action, m_svgTextActions) {
462 action->setEnabled(true);
463 }
464
465 // Settings
466 // do not add settings action to m_richTextActions list,
467 // it should always be active, regardless of which editor mode is used.
468 // otherwise we can lock the user out of being able to change
469 // editor mode, if user changes to SVG only mode.
470 createAction("svg_settings", SLOT(setSettings()));
471}
473{
474 close();
475 Q_EMIT textEditorClosed();
476}
477
478bool SvgTextEditor::eventFilter(QObject *const watched, QEvent *const event)
479{
480 if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) {
481 QKeyEvent *const keyEvent = static_cast<QKeyEvent *>(event);
482 if ((keyEvent->key() == Qt::Key_Enter || keyEvent->key() == Qt::Key_Return)
483 && (keyEvent->modifiers() & Qt::ShiftModifier)) {
484 // Disable soft line breaks
485 return true;
486 }
487 }
488 return false;
489 return KXmlGuiWindow::eventFilter(watched, event);
490}
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
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