SvgTextEditor.cpp 55.6 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/* This file is part of the KDE project
 *
 * Copyright 2017 Boudewijn Rempt <boud@valdyas.org>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */

21
#include "SvgTextEditor.h"
22

23
#include <QAction>
24
#include <QApplication>
25
#include <QBuffer>
26
#include <QComboBox>
Wolthera van Hövell's avatar
Wolthera van Hövell committed
27
#include <QDialogButtonBox>
28
#include <QDoubleSpinBox>
29
30
31
#include <QFontComboBox>
#include <QFontDatabase>
#include <QFormLayout>
32
#include <QLineEdit>
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <QListView>
#include <QMenu>
#include <QMessageBox>
#include <QPainter>
#include <QPalette>
#include <QPushButton>
#include <QStandardItem>
#include <QStandardItemModel>
#include <QSvgGenerator>
#include <QTabWidget>
#include <QTextEdit>
#include <QUrl>
#include <QVBoxLayout>
#include <QWidgetAction>
47
48
#include <QDesktopWidget>
#include <QScreen>
49

50
#include <kcharselect.h>
51
#include <klocalizedstring.h>
52
53
54
55
56
57
58
59
#include <ksharedconfig.h>
#include <kconfiggroup.h>
#include <kactioncollection.h>
#include <kxmlguifactory.h>
#include <ktoolbar.h>
#include <ktoggleaction.h>
#include <kguiitem.h>

60
#include <KoDialog.h>
61
62
63
#include <KoResourcePaths.h>
#include <KoSvgTextShape.h>
#include <KoSvgTextShapeMarkupConverter.h>
64
#include <KoColorSpaceRegistry.h>
65
#include <KoColorPopupAction.h>
66
#include <svg/SvgUtil.h>
67

68
#include <KisScreenColorPicker.h>
69
#include <kis_icon.h>
70
#include <kis_config.h>
71
#include <kis_file_name_requester.h>
72
#include <kis_action_registry.h>
73

74
#include "kis_font_family_combo_box.h"
75
#include "FontSizeAction.h"
76
#include "kis_signals_blocker.h"
77

78
79
80
81
82
83
84
85
86
87
88
89

class SvgTextEditor::Private
{
public:

    Private() {}

    // collection of last-used properties
    QColor fontColor {Qt::black};
    //QColor backgroundColor {Qt::transparent};
    qreal fontSize {10.0};
    QFont font;
90
    bool kerning {true};
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129

    qreal letterSpacing {0.0};

    bool bold {false};
    bool italic {false};
    bool underline {false};

    bool strikeThrough {false};
    bool superscript {false};
    bool subscript {false};

    // unsupported properties:
    // backgroundColor - because there is no button for that
    // horizontal alignment - it seems to work without saving
    // line height - it seems to work without saving

    void saveFromWidgets(KActionCollection* actions)
    {

        FontSizeAction *fontSizeAction = qobject_cast<FontSizeAction*>(actions->action("svg_font_size"));
        fontSize = fontSizeAction->fontSize();

        KisFontComboBoxes* fontComboBox2 = qobject_cast<KisFontComboBoxes*>(qobject_cast<QWidgetAction*>(actions->action("svg_font"))->defaultWidget());
        font = fontComboBox2->currentFont(fontSize);

        KoColorPopupAction *fontColorAction = qobject_cast<KoColorPopupAction*>(actions->action("svg_format_textcolor"));
        fontColor = fontColorAction->currentColor();

        QWidgetAction *letterSpacingAction = qobject_cast<QWidgetAction*>(actions->action("svg_letter_spacing"));
        letterSpacing = qobject_cast<QDoubleSpinBox*>(letterSpacingAction->defaultWidget())->value();

        saveBoolActionFromWidget(actions, "svg_weight_bold", bold);
        saveBoolActionFromWidget(actions, "svg_format_italic", italic);
        saveBoolActionFromWidget(actions, "svg_format_underline", underline);

        saveBoolActionFromWidget(actions, "svg_format_strike_through", strikeThrough);
        saveBoolActionFromWidget(actions, "svg_format_superscript", superscript);
        saveBoolActionFromWidget(actions, "svg_format_subscript", subscript);

130
        saveBoolActionFromWidget(actions, "svg_font_kerning", kerning);
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
    }

    void setSavedToWidgets(KActionCollection* actions)
    {

        FontSizeAction *fontSizeAction = qobject_cast<FontSizeAction*>(actions->action("svg_font_size"));
        fontSizeAction->setFontSize(fontSize);

        KisFontComboBoxes* fontComboBox2 = qobject_cast<KisFontComboBoxes*>(qobject_cast<QWidgetAction*>(actions->action("svg_font"))->defaultWidget());
        fontComboBox2->setCurrentFont(font);

        KoColorPopupAction *fontColorAction = qobject_cast<KoColorPopupAction*>(actions->action("svg_format_textcolor"));
        fontColorAction->setCurrentColor(fontColor);

        QWidgetAction *letterSpacingAction = qobject_cast<QWidgetAction*>(actions->action("svg_letter_spacing"));
        qobject_cast<QDoubleSpinBox*>(letterSpacingAction->defaultWidget())->setValue(letterSpacing);

        setBoolActionToWidget(actions, "svg_weight_bold", bold);
        setBoolActionToWidget(actions, "svg_format_italic", italic);

        setSavedLineDecorationToWidgets(actions);

        setBoolActionToWidget(actions, "svg_format_superscript", superscript);
        setBoolActionToWidget(actions, "svg_format_subscript", subscript);
155
156

        setBoolActionToWidget(actions, "svg_font_kerning", kerning);
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
    }

    void setSavedToFormat(QTextCharFormat &format)
    {

        format.setFont(font);
        format.setFontPointSize(fontSize);
        format.setForeground(fontColor);

        format.setFontLetterSpacingType(QFont::AbsoluteSpacing);
        format.setFontLetterSpacing(letterSpacing);

        format.setFontUnderline(underline);
        format.setFontStrikeOut(strikeThrough);
        format.setFontOverline(false);

        if (subscript) {
            format.setVerticalAlignment(QTextCharFormat::AlignSubScript);
        } else if (superscript) {
            format.setVerticalAlignment(QTextCharFormat::AlignSuperScript);
        } else {
            format.setVerticalAlignment(QTextCharFormat::AlignMiddle);
        }

        if (bold) {
            format.setFontWeight(QFont::Bold);
        }

        format.setFontItalic(italic);
186
        format.setFontKerning(kerning);
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
    }

    void saveFontLineDecoration(KoSvgText::TextDecoration decoration)
    {
        // Krita for now cannot handle both at the same time; and there is no way to set overline
        // FIXME: Krita should support all three at the same time
        // (It does support it in SVG)
        switch (decoration) {
        case KoSvgText::DecorationUnderline:
            underline = true;
            strikeThrough = false;
            break;
        case KoSvgText::DecorationLineThrough:
            underline = false;
            strikeThrough = true;
            break;
        case KoSvgText::DecorationOverline:
            Q_FALLTHROUGH();
        case KoSvgText::DecorationNone:
            Q_FALLTHROUGH();
         default:
            underline = false;
            strikeThrough = false;
            break;
        }
    }


    void setSavedLineDecorationToWidgets(KActionCollection* actions)
    {
        setBoolActionToWidget(actions, "svg_format_underline", underline);
        setBoolActionToWidget(actions, "svg_format_strike_through", strikeThrough);
    }

private:

    void saveBoolActionFromWidget(KActionCollection* actions, QString actionName, bool &variable)
    {
        QAction *boolAction = actions->action(actionName);
        KIS_ASSERT_RECOVER_RETURN(boolAction);
        variable = boolAction->isChecked();
    }

    void setBoolActionToWidget(KActionCollection* actions, QString actionName, bool variable)
    {
        QAction *boolAction = actions->action(actionName);
        KIS_ASSERT_RECOVER_RETURN(boolAction);
        boolAction->setChecked(variable);
    }

};


240
SvgTextEditor::SvgTextEditor(QWidget *parent, Qt::WindowFlags flags)
241
    : KXmlGuiWindow(parent, flags)
242
    , m_page(new QWidget(this))
Halla Rempt's avatar
Halla Rempt committed
243
#ifndef Q_OS_WIN
244
    , m_charSelectDialog(new KoDialog(this))
Halla Rempt's avatar
Halla Rempt committed
245
#endif
246
    , d(new Private())
247
{
248
249
250
    m_textEditorWidget.setupUi(m_page);
    setCentralWidget(m_page);

251
    m_textEditorWidget.chkVertical->setVisible(false);
252
#ifndef Q_OS_WIN
253
254
255
256
257
    KCharSelect *charSelector = new KCharSelect(m_charSelectDialog, 0, KCharSelect::AllGuiElements);
    m_charSelectDialog->setMainWidget(charSelector);
    connect(charSelector, SIGNAL(currentCharChanged(QChar)), SLOT(insertCharacter(QChar)));
    m_charSelectDialog->hide();
    m_charSelectDialog->setButtons(KoDialog::Close);
258
#endif
Wolthera van Hövell's avatar
Wolthera van Hövell committed
259
    connect(m_textEditorWidget.buttons, SIGNAL(accepted()), this, SLOT(save()));
260
    connect(m_textEditorWidget.buttons, SIGNAL(rejected()), this, SLOT(slotCloseEditor()));
261
    connect(m_textEditorWidget.buttons, SIGNAL(clicked(QAbstractButton*)), this, SLOT(dialogButtonClicked(QAbstractButton*)));
Wolthera van Hövell's avatar
Wolthera van Hövell committed
262

263
264
265
266
267
268
    KConfigGroup cg(KSharedConfig::openConfig(), "SvgTextTool");
    actionCollection()->setConfigGroup("SvgTextTool");
    actionCollection()->setComponentName("svgtexttool");
    actionCollection()->setComponentDisplayName(i18n("Text Tool"));

    if (cg.hasKey("WindowState")) {
269
        QByteArray state = cg.readEntry("State", state);
270
        // One day will need to load the version number, but for now, assume 0
271
272
273
274
275
276
277
        restoreState(QByteArray::fromBase64(state));
    }
    if (cg.hasKey("Geometry")) {
        QByteArray ba = cg.readEntry("Geometry", QByteArray());
        restoreGeometry(QByteArray::fromBase64(ba));
    }
    else {
278
279
        const int scnum = QApplication::desktop()->screenNumber(parent);
        QRect desk = QGuiApplication::screens().at(scnum)->availableGeometry();
280
281
282
283
284
285
286
287
288
289
290
291
292
293

        quint32 x = desk.x();
        quint32 y = desk.y();
        quint32 w = 0;
        quint32 h = 0;
        const int deskWidth = desk.width();
        w = (deskWidth / 3) * 2;
        h = (desk.height() / 3) * 2;
        x += (desk.width() - w) / 2;
        y += (desk.height() - h) / 2;

        move(x,y);
        setGeometry(geometry().x(), geometry().y(), w, h);

294
295
296
    }

    setAcceptDrops(true);
297
    //setStandardToolBarMenuEnabled(true);
298
#ifdef Q_OS_MACOS
299
300
301
    setUnifiedTitleAndToolBarOnMac(true);
#endif
    setTabPosition(Qt::AllDockWidgetAreas, QTabWidget::North);
302

303
    m_syntaxHighlighter = new BasicXMLSyntaxHighlighter(m_textEditorWidget.svgTextEdit);
304
    m_textEditorWidget.svgTextEdit->setFont(QFontDatabase().systemFont(QFontDatabase::FixedFont));
305

306
307
308
309
310
311
312
313
314
315
316
317
    createActions();
    // If we have customized the toolbars, load that first
    setLocalXMLFile(KoResourcePaths::locateLocal("data", "svgtexttool.xmlgui"));
    setXMLFile(":/kxmlgui5/svgtexttool.xmlgui");

    guiFactory()->addClient(this);

    // Create and plug toolbar list for Settings menu
    QList<QAction *> toolbarList;
    Q_FOREACH (QWidget* it, guiFactory()->containers("ToolBar")) {
        KToolBar * toolBar = ::qobject_cast<KToolBar *>(it);
        if (toolBar) {
318
            toolBar->setToolButtonStyle(Qt::ToolButtonIconOnly);
319
320
321
322
323
324
325
326
327
            KToggleAction* act = new KToggleAction(i18n("Show %1 Toolbar", toolBar->windowTitle()), this);
            actionCollection()->addAction(toolBar->objectName().toUtf8(), act);
            act->setCheckedState(KGuiItem(i18n("Hide %1 Toolbar", toolBar->windowTitle())));
            connect(act, SIGNAL(toggled(bool)), this, SLOT(slotToolbarToggled(bool)));
            act->setChecked(!toolBar->isHidden());
            toolbarList.append(act);
        }
    }
    plugActionList("toolbarlist", toolbarList);
328
329
    connect(m_textEditorWidget.textTab, SIGNAL(currentChanged(int)), this, SLOT(switchTextEditorTab()));
    switchTextEditorTab();
330
331

    m_textEditorWidget.richTextEdit->document()->setDefaultStyleSheet("p {margin:0px;}");
332

333
    applySettings();
334

335
336
}

337
SvgTextEditor::~SvgTextEditor()
338
{
339
340
341
    KConfigGroup g(KSharedConfig::openConfig(), "SvgTextTool");
    QByteArray ba = saveState();
    g.writeEntry("windowState", ba.toBase64());
342
343
    ba = saveGeometry();
    g.writeEntry("Geometry", ba.toBase64());
344
345
}

346

347
void SvgTextEditor::setInitialShape(KoSvgTextShape *shape)
348
{
349
350
351
    m_shape = shape;
    if (m_shape) {
        KoSvgTextShapeMarkupConverter converter(m_shape);
352

353
354
        QString svg;
        QString styles;
355
        QTextDocument *doc = m_textEditorWidget.richTextEdit->document();
356

357
        if (converter.convertToSvg(&svg, &styles)) {
358
            m_textEditorWidget.svgTextEdit->setPlainText(svg);
359
            m_textEditorWidget.svgStylesEdit->setPlainText(styles);
360
            m_textEditorWidget.svgTextEdit->document()->setModified(false);
361
362
363
364

            if (shape->isRichTextPreferred() &&
                converter.convertSvgToDocument(svg, doc)) {

365
                m_textEditorWidget.richTextEdit->setDocument(doc);
366
367
                KisSignalsBlocker b(m_textEditorWidget.textTab);
                m_textEditorWidget.textTab->setCurrentIndex(Richtext);
368
                doc->clearUndoRedoStacks();
369
370
371
372
373
                switchTextEditorTab(false);
            } else {
                KisSignalsBlocker b(m_textEditorWidget.textTab);
                m_textEditorWidget.textTab->setCurrentIndex(SvgSource);
                switchTextEditorTab(false);
374
            }
375
376
        }
        else {
377
            QMessageBox::warning(this, i18n("Conversion failed"), "Could not get svg text from the shape:\n" + converter.errors().join('\n') + "\n" + converter.warnings().join('\n'));
378
379
        }
    }
380
381
    KisFontComboBoxes* fontComboBox = qobject_cast<KisFontComboBoxes*>(qobject_cast<QWidgetAction*>(actionCollection()->action("svg_font"))->defaultWidget());
    fontComboBox->setInitialized();
382
383
384
385
386
387
388
389
390
391
392
393
394

    KConfigGroup cfg(KSharedConfig::openConfig(), "SvgTextTool");

    d->saveFromWidgets(actionCollection());

    QTextCursor cursor = m_textEditorWidget.richTextEdit->textCursor();
    QTextCharFormat format = cursor.blockCharFormat();

    d->setSavedToFormat(format);

    KisSignalsBlocker b(m_textEditorWidget.richTextEdit);
    cursor.setBlockCharFormat(format);

395
    m_textEditorWidget.richTextEdit->document()->setModified(false);
396
}
397

398
399
void SvgTextEditor::save()
{
400
    if (m_shape) {
401
        if (isRichTextEditorActive()) {
402
            QString svg;
403
            QString styles = m_textEditorWidget.svgStylesEdit->document()->toPlainText();
404
405
            KoSvgTextShapeMarkupConverter converter(m_shape);

406
407
            if (!converter.convertDocumentToSvg(m_textEditorWidget.richTextEdit->document(), &svg)) {
                    qWarning()<<"new converter doesn't work!";
408
            }
409
            m_textEditorWidget.richTextEdit->document()->setModified(false);
410
            emit textUpdated(m_shape, svg, styles, true);
411
        } else if (isSvgSourceEditorActive()) {
412
            emit textUpdated(m_shape, m_textEditorWidget.svgTextEdit->document()->toPlainText(), m_textEditorWidget.svgStylesEdit->document()->toPlainText(), false);
413
            m_textEditorWidget.svgTextEdit->document()->setModified(false);
414
415
        }
    }
416

417
}
418

419
void SvgTextEditor::switchTextEditorTab(bool convertData)
420
{
421
422
423
    KoSvgTextShape shape;
    KoSvgTextShapeMarkupConverter converter(&shape);

424
425
426
427
    if (m_currentEditor) {
        disconnect(m_currentEditor->document(), SIGNAL(modificationChanged(bool)), this, SLOT(setModified(bool)));
    }

428
429
    // do not switch to the same tab again, otherwise we're losing current changes
    if (m_currentEditor != m_textEditorWidget.richTextEdit && isRichTextEditorActive()) {
430
431
        //first, make buttons checkable
        enableRichTextActions(true);
432
        enableSvgTextActions(false);
433

434
435
        //then connect the cursor change to the checkformat();
        connect(m_textEditorWidget.richTextEdit, SIGNAL(cursorPositionChanged()), this, SLOT(checkFormat()));
436
437
        connect(m_textEditorWidget.richTextEdit, SIGNAL(textChanged()), this, SLOT(slotFixUpEmptyTextBlock()));
        checkFormat();
438

439
        if (m_shape && convertData) {
440
441
442
443
444
            QTextDocument *doc = m_textEditorWidget.richTextEdit->document();
            if (!converter.convertSvgToDocument(m_textEditorWidget.svgTextEdit->document()->toPlainText(), doc)) {
                qWarning()<<"new converter svgToDoc doesn't work!";
            }
            m_textEditorWidget.richTextEdit->setDocument(doc);
445
            doc->clearUndoRedoStacks();
446
        }
447
        m_currentEditor = m_textEditorWidget.richTextEdit;
448
    } else if (m_currentEditor != m_textEditorWidget.svgTextEdit && isSvgSourceEditorActive()) {
449
450
        //first, make buttons uncheckable
        enableRichTextActions(false);
451
        enableSvgTextActions(true);
452
        disconnect(m_textEditorWidget.richTextEdit, SIGNAL(cursorPositionChanged()), this, SLOT(checkFormat()));
453

Halla Rempt's avatar
Halla Rempt committed
454
        // Convert the rich text to svg and styles strings
455
        if (m_shape && convertData) {
456
457
            QString svg;
            QString styles;
458

459
460
            if (!converter.convertDocumentToSvg(m_textEditorWidget.richTextEdit->document(), &svg)) {
                    qWarning()<<"new converter docToSVG doesn't work!";
461
            }
462
463
            m_textEditorWidget.svgTextEdit->setPlainText(svg);
        }
464
465
        m_currentEditor = m_textEditorWidget.svgTextEdit;
    }
466
467

    connect(m_currentEditor->document(), SIGNAL(modificationChanged(bool)), SLOT(setModified(bool)));
468
}
469

470
471
void SvgTextEditor::checkFormat()
{
472
    QTextCharFormat format = m_textEditorWidget.richTextEdit->textCursor().charFormat();
473
    QTextBlockFormat blockFormat = m_textEditorWidget.richTextEdit->textCursor().blockFormat();
474
475
476
477

    // checkboxes do not emit signals on manual switching, so we
    // can avoid blocking them

478
    if (format.fontWeight() > QFont::Normal) {
Halla Rempt's avatar
Halla Rempt committed
479
        actionCollection()->action("svg_weight_bold")->setChecked(true);
480
    } else {
Halla Rempt's avatar
Halla Rempt committed
481
        actionCollection()->action("svg_weight_bold")->setChecked(false);
482
    }
Halla Rempt's avatar
Halla Rempt committed
483
484
485
    actionCollection()->action("svg_format_italic")->setChecked(format.fontItalic());
    actionCollection()->action("svg_format_underline")->setChecked(format.fontUnderline());
    actionCollection()->action("svg_format_strike_through")->setChecked(format.fontStrikeOut());
486
    actionCollection()->action("svg_font_kerning")->setChecked(format.fontKerning());
487

488
489
490
491
492
    {
        FontSizeAction *fontSizeAction = qobject_cast<FontSizeAction*>(actionCollection()->action("svg_font_size"));
        KisSignalsBlocker b(fontSizeAction);
        fontSizeAction->setFontSize(format.font().pointSize());
    }
493

494

495
496
497
498
499
500
    {
        KoColor fg(format.foreground().color(), KoColorSpaceRegistry::instance()->rgb8());
        KoColorPopupAction *fgColorPopup = qobject_cast<KoColorPopupAction*>(actionCollection()->action("svg_format_textcolor"));
        KisSignalsBlocker b(fgColorPopup);
        fgColorPopup->setCurrentColor(fg);
    }
501

502
503
504
505
506
507
    {
        KoColor bg(format.foreground().color(), KoColorSpaceRegistry::instance()->rgb8());
        KoColorPopupAction *bgColorPopup = qobject_cast<KoColorPopupAction*>(actionCollection()->action("svg_background_color"));
        KisSignalsBlocker b(bgColorPopup);
        bgColorPopup->setCurrentColor(bg);
    }
508

509
510
511
512
513
    {
        KisFontComboBoxes* fontComboBox = qobject_cast<KisFontComboBoxes*>(qobject_cast<QWidgetAction*>(actionCollection()->action("svg_font"))->defaultWidget());
        KisSignalsBlocker b(fontComboBox);
        fontComboBox->setCurrentFont(format.font());
    }
514

515
516
517
    {
        QDoubleSpinBox *spnLineHeight = qobject_cast<QDoubleSpinBox*>(qobject_cast<QWidgetAction*>(actionCollection()->action("svg_line_height"))->defaultWidget());
        KisSignalsBlocker b(spnLineHeight);
518

519
520
521
522
523
        if (blockFormat.lineHeightType() == QTextBlockFormat::SingleHeight) {
            spnLineHeight->setValue(100.0);
        } else if(blockFormat.lineHeightType() == QTextBlockFormat::ProportionalHeight) {
            spnLineHeight->setValue(double(blockFormat.lineHeight()));
        }
524
    }
525
526
527
528
529
530

    {
        QDoubleSpinBox* spnLetterSpacing = qobject_cast<QDoubleSpinBox*>(qobject_cast<QWidgetAction*>(actionCollection()->action("svg_letter_spacing"))->defaultWidget());
        KisSignalsBlocker b(spnLetterSpacing);
        spnLetterSpacing->setValue(format.fontLetterSpacing());
    }
531
532
}

533
534
535
536
537
538
539
void SvgTextEditor::slotFixUpEmptyTextBlock()
{
    if (m_textEditorWidget.richTextEdit->document()->isEmpty()) {
        QTextCursor cursor = m_textEditorWidget.richTextEdit->textCursor();
        QTextCharFormat format = cursor.blockCharFormat();


540
        KisSignalsBlocker b(m_textEditorWidget.richTextEdit);
541

542
543
        d->setSavedToFormat(format);
        d->setSavedToWidgets(actionCollection());
544
545
546
547
548

        cursor.setBlockCharFormat(format);
    }
}

549
550
void SvgTextEditor::undo()
{
551
    m_currentEditor->undo();
552
553
554
555
}

void SvgTextEditor::redo()
{
556
    m_currentEditor->redo();
557
558
559
560
}

void SvgTextEditor::cut()
{
561
    m_currentEditor->cut();
562
563
564
565
}

void SvgTextEditor::copy()
{
566
    m_currentEditor->copy();
567
568
569
570
}

void SvgTextEditor::paste()
{
571
    m_currentEditor->paste();
572
573
574
575
}

void SvgTextEditor::selectAll()
{
576
    m_currentEditor->selectAll();
577
578
579
580
}

void SvgTextEditor::deselect()
{
581
582
583
    QTextCursor cursor(m_currentEditor->textCursor());
    cursor.clearSelection();
    m_currentEditor->setTextCursor(cursor);
584
585
586
587
}

void SvgTextEditor::find()
{
588
589
    QDialog *findDialog = new QDialog(this);
    findDialog->setWindowTitle(i18n("Find Text"));
590
591
    QFormLayout *layout = new QFormLayout();
    findDialog->setLayout(layout);
592
    QLineEdit *lnSearchKey = new QLineEdit();
593
    layout->addRow(i18n("Find:"), lnSearchKey);
594
595
596
597
598
599
600
601
602
603
    QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel);
    findDialog->layout()->addWidget(buttons);
    connect(buttons, SIGNAL(accepted()), findDialog, SLOT(accept()));
    connect(buttons, SIGNAL(rejected()), findDialog, SLOT(reject()));


    if (findDialog->exec()==QDialog::Accepted) {
        m_searchKey = lnSearchKey->text();
        m_currentEditor->find(m_searchKey);
    }
604
605
606
607
}

void SvgTextEditor::findNext()
{
608
    if (!m_currentEditor->find(m_searchKey)) {
609
610
611
        QTextCursor cursor(m_currentEditor->textCursor());
        cursor.movePosition(QTextCursor::Start);
        m_currentEditor->setTextCursor(cursor);
612
613
        m_currentEditor->find(m_searchKey);
    }
614
615
616
617
}

void SvgTextEditor::findPrev()
{
618
    if (!m_currentEditor->find(m_searchKey,QTextDocument::FindBackward)) {
619
620
621
        QTextCursor cursor(m_currentEditor->textCursor());
        cursor.movePosition(QTextCursor::End);
        m_currentEditor->setTextCursor(cursor);
622
623
        m_currentEditor->find(m_searchKey,QTextDocument::FindBackward);
    }
624
625
}

626
627
void SvgTextEditor::replace()
{
628
    QDialog *findDialog = new QDialog(this);
629
    findDialog->setWindowTitle(i18n("Find and Replace all"));
630
631
    QFormLayout *layout = new QFormLayout();
    findDialog->setLayout(layout);
632
633
    QLineEdit *lnSearchKey = new QLineEdit();
    QLineEdit *lnReplaceKey = new QLineEdit();
634
    layout->addRow(i18n("Find:"), lnSearchKey);
635
    QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel);
636
    layout->addRow(i18n("Replace:"), lnReplaceKey);
637
638
639
640
641
642
643
644
    findDialog->layout()->addWidget(buttons);
    connect(buttons, SIGNAL(accepted()), findDialog, SLOT(accept()));
    connect(buttons, SIGNAL(rejected()), findDialog, SLOT(reject()));


    if (findDialog->exec()==QDialog::Accepted) {
        QString search = lnSearchKey->text();
        QString replace = lnReplaceKey->text();
645
646
647
648
649
650
651
        QTextCursor cursor(m_currentEditor->textCursor());
        cursor.movePosition(QTextCursor::Start);
        m_currentEditor->setTextCursor(cursor);
        while(m_currentEditor->find(search)) {
            m_currentEditor->textCursor().removeSelectedText();
            m_currentEditor->textCursor().insertText(replace);
        }
652

653
    }
654
655
656
657
658
}


void SvgTextEditor::zoomOut()
{
Halla Rempt's avatar
Halla Rempt committed
659
    m_currentEditor->zoomOut();
660
661
662
663
}

void SvgTextEditor::zoomIn()
{
Halla Rempt's avatar
Halla Rempt committed
664
    m_currentEditor->zoomIn();
665
666
}

667
#ifndef Q_OS_WIN
668
void SvgTextEditor::showInsertSpecialCharacterDialog()
669
{
670
671
    m_charSelectDialog->setVisible(!m_charSelectDialog->isVisible());
}
672

673
674
675
void SvgTextEditor::insertCharacter(const QChar &c)
{
    m_currentEditor->textCursor().insertText(QString(c));
676
}
677
#endif
678

679
void SvgTextEditor::setTextBold(QFont::Weight weight)
680
{
681
    if (isRichTextEditorActive()) {
682
        QTextCharFormat format;
683
        QTextCursor oldCursor = setTextSelection();
684
685
686
687
688
689
        if (m_textEditorWidget.richTextEdit->textCursor().charFormat().fontWeight() > QFont::Normal && weight==QFont::Bold) {
            format.setFontWeight(QFont::Normal);
        } else {
            format.setFontWeight(weight);
        }
        m_textEditorWidget.richTextEdit->mergeCurrentCharFormat(format);
690
        m_textEditorWidget.richTextEdit->setTextCursor(oldCursor);
691
    } else if (isSvgSourceEditorActive()) {
692
693
694
695
696
697
698
        QTextCursor cursor = m_textEditorWidget.svgTextEdit->textCursor();
        if (cursor.hasSelection()) {
            QString selectionModified = "<tspan style=\"font-weight:700;\">" + cursor.selectedText() + "</tspan>";
            cursor.removeSelectedText();
            cursor.insertText(selectionModified);
        }
    }
699
    d->bold = weight == QFont::Bold;
700
701
}

702
void SvgTextEditor::setTextWeightLight()
703
{
704
705
706
707
708
    if (m_textEditorWidget.richTextEdit->textCursor().charFormat().fontWeight() < QFont::Normal) {
        setTextBold(QFont::Normal);
    } else {
        setTextBold(QFont::Light);
    }
709
710
711
712
713
714
715
716
717
}

void SvgTextEditor::setTextWeightNormal()
{
    setTextBold(QFont::Normal);
}

void SvgTextEditor::setTextWeightDemi()
{
718
    if (m_textEditorWidget.richTextEdit->textCursor().charFormat().fontWeight() != QFont::Normal) {
719
720
        setTextBold(QFont::Normal);
    } else {
721
        setTextBold(QFont::DemiBold);
722
723
724
725
726
    }
}

void SvgTextEditor::setTextWeightBlack()
{
727
728
729
730
731
    if (m_textEditorWidget.richTextEdit->textCursor().charFormat().fontWeight()>QFont::Normal) {
        setTextBold(QFont::Normal);
    } else {
        setTextBold(QFont::Black);
    }
732
733
734
735
}

void SvgTextEditor::setTextItalic(QFont::Style style)
{
736
737
738
739
740
    QTextCursor cursor = m_textEditorWidget.svgTextEdit->textCursor();
    QString fontStyle = "inherit";

    if (style == QFont::StyleItalic) {
        fontStyle = "italic";
741
742
        d->italic = true;
    } else if (style == QFont::StyleOblique) {
743
        fontStyle = "oblique";
744
745
746
        d->italic = true;
    } else {
        d->italic = false;
747
748
    }

749

750
    if (isRichTextEditorActive()) {
751
        QTextCharFormat format;
752
        QTextCursor origCursor = setTextSelection();
753
754
        format.setFontItalic(!m_textEditorWidget.richTextEdit->textCursor().charFormat().fontItalic());
        m_textEditorWidget.richTextEdit->mergeCurrentCharFormat(format);
755
        m_textEditorWidget.richTextEdit->setTextCursor(origCursor);
756
    } else if (isSvgSourceEditorActive()) {
757
758
759
760
761
762
        if (cursor.hasSelection()) {
            QString selectionModified = "<tspan style=\"font-style:"+fontStyle+";\">" + cursor.selectedText() + "</tspan>";
            cursor.removeSelectedText();
            cursor.insertText(selectionModified);
        }
    }
763
764
}

765
766
void SvgTextEditor::setTextDecoration(KoSvgText::TextDecoration decor)
{
767
    QTextCursor cursor = setTextSelection();
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
    QTextCharFormat currentFormat = m_textEditorWidget.richTextEdit->textCursor().charFormat();
    QTextCharFormat format;
    QString textDecoration = "inherit";

    if (decor == KoSvgText::DecorationUnderline) {
        textDecoration = "underline";
        if (currentFormat.fontUnderline()) {
            format.setFontUnderline(false);
        }
        else {
            format.setFontUnderline(true);
        }
        format.setFontOverline(false);
        format.setFontStrikeOut(false);
    }
    else if (decor == KoSvgText::DecorationLineThrough) {
        textDecoration = "line-through";
        format.setFontUnderline(false);
        format.setFontOverline(false);
        if (currentFormat.fontStrikeOut()) {
            format.setFontStrikeOut(false);
        }
        else {
            format.setFontStrikeOut(true);
        }
    }
    else if (decor == KoSvgText::DecorationOverline) {
        textDecoration = "overline";
        format.setFontUnderline(false);
        if (currentFormat.fontOverline()) {
            format.setFontOverline(false);
        }
        else {
            format.setFontOverline(true);
        }
        format.setFontStrikeOut(false);
    }

806
    if (isRichTextEditorActive()) {
807
        m_textEditorWidget.richTextEdit->mergeCurrentCharFormat(format);
808
    } else if (isSvgSourceEditorActive()) {
809
810
811
812
813
814
        if (cursor.hasSelection()) {
            QString selectionModified = "<tspan style=\"text-decoration:" + textDecoration + ";\">" + cursor.selectedText() + "</tspan>";
            cursor.removeSelectedText();
            cursor.insertText(selectionModified);
        }
    }
815
    m_textEditorWidget.richTextEdit->setTextCursor(cursor);
816
817
818

    d->saveFontLineDecoration(decor);
    d->setSavedLineDecorationToWidgets(actionCollection());
819
820
821
822
}

void SvgTextEditor::setTextUnderline()
{
823
    setTextDecoration(KoSvgText::DecorationUnderline);
824
825
826
827
828
829
830
831
832
833
834
835
}

void SvgTextEditor::setTextOverline()
{
    setTextDecoration(KoSvgText::DecorationOverline);
}

void SvgTextEditor::setTextStrikethrough()
{
    setTextDecoration(KoSvgText::DecorationLineThrough);
}

836
837
void SvgTextEditor::setTextSubscript()
{
Wolthera van Hövell's avatar
Wolthera van Hövell committed
838
839
840
    QTextCharFormat format = m_textEditorWidget.richTextEdit->textCursor().charFormat();
    if (format.verticalAlignment()==QTextCharFormat::AlignSubScript) {
        format.setVerticalAlignment(QTextCharFormat::AlignNormal);
841
        d->subscript = false;
Wolthera van Hövell's avatar
Wolthera van Hövell committed
842
843
    } else {
        format.setVerticalAlignment(QTextCharFormat::AlignSubScript);
844
845
        d->subscript = true;
        d->superscript = false;
Wolthera van Hövell's avatar
Wolthera van Hövell committed
846
847
    }
    m_textEditorWidget.richTextEdit->mergeCurrentCharFormat(format);
848
849
850
851
}

void SvgTextEditor::setTextSuperScript()
{
Wolthera van Hövell's avatar
Wolthera van Hövell committed
852
853
854
    QTextCharFormat format = m_textEditorWidget.richTextEdit->textCursor().charFormat();
    if (format.verticalAlignment()==QTextCharFormat::AlignSuperScript) {
        format.setVerticalAlignment(QTextCharFormat::AlignNormal);
855
        d->superscript = false;
Wolthera van Hövell's avatar
Wolthera van Hövell committed
856
857
    } else {
        format.setVerticalAlignment(QTextCharFormat::AlignSuperScript);
858
859
        d->superscript = true;
        d->subscript = false;
Wolthera van Hövell's avatar
Wolthera van Hövell committed
860
861
    }
    m_textEditorWidget.richTextEdit->mergeCurrentCharFormat(format);
862
863
864
865
}

void SvgTextEditor::increaseTextSize()
{
866
    QTextCursor oldCursor = setTextSelection();
Wolthera van Hövell's avatar
Wolthera van Hövell committed
867
868
869
870
871
872
    QTextCharFormat format;
    int pointSize = m_textEditorWidget.richTextEdit->textCursor().charFormat().font().pointSize();
    if (pointSize<0) {
        pointSize = m_textEditorWidget.richTextEdit->textCursor().charFormat().font().pixelSize();
    }
    format.setFontPointSize(pointSize+1.0);
873
    d->fontSize = format.fontPointSize();
Wolthera van Hövell's avatar
Wolthera van Hövell committed
874
    m_textEditorWidget.richTextEdit->mergeCurrentCharFormat(format);
875
    m_textEditorWidget.richTextEdit->setTextCursor(oldCursor);
876
877
878
879
}

void SvgTextEditor::decreaseTextSize()
{
880
    QTextCursor oldCursor = setTextSelection();
Wolthera van Hövell's avatar
Wolthera van Hövell committed
881
882
883
884
885
886
    QTextCharFormat format;
    int pointSize = m_textEditorWidget.richTextEdit->textCursor().charFormat().font().pointSize();
    if (pointSize<1) {
        pointSize = m_textEditorWidget.richTextEdit->textCursor().charFormat().font().pixelSize();
    }
    format.setFontPointSize(qMax(pointSize-1.0, 1.0));
887
    d->fontSize = format.fontPointSize();
Wolthera van Hövell's avatar
Wolthera van Hövell committed
888
    m_textEditorWidget.richTextEdit->mergeCurrentCharFormat(format);
889
    m_textEditorWidget.richTextEdit->setTextCursor(oldCursor);
890
891
}

892
void SvgTextEditor::setLineHeight(double lineHeightPercentage)
893
{
894
    QTextCursor oldCursor = setTextSelection();
895
    QTextBlockFormat format = m_textEditorWidget.richTextEdit->textCursor().blockFormat();
896
    format.setLineHeight(lineHeightPercentage, QTextBlockFormat::ProportionalHeight);
897
898
    m_textEditorWidget.richTextEdit->textCursor().mergeBlockFormat(format);
    m_textEditorWidget.richTextEdit->setTextCursor(oldCursor);
899
900
}

901
902
903
void SvgTextEditor::setLetterSpacing(double letterSpacing)
{
    QTextCursor cursor = setTextSelection();
904
    if (isRichTextEditorActive()) {
905
906
907
908
909
        QTextCharFormat format;
        format.setFontLetterSpacingType(QFont::AbsoluteSpacing);
        format.setFontLetterSpacing(letterSpacing);
        m_textEditorWidget.richTextEdit->mergeCurrentCharFormat(format);
        m_textEditorWidget.richTextEdit->setTextCursor(cursor);
910
    } else if (isSvgSourceEditorActive()) {
911
912
913
914
915
916
        if (cursor.hasSelection()) {
            QString selectionModified = "<tspan style=\"letter-spacing:" + QString::number(letterSpacing) + "\">" + cursor.selectedText() + "</tspan>";
            cursor.removeSelectedText();
            cursor.insertText(selectionModified);
        }
    }
917
    d->letterSpacing = letterSpacing;
918
}
919
920
921

void SvgTextEditor::alignLeft()
{
922
    QTextCursor oldCursor = setTextSelection();
Wolthera van Hövell's avatar
Wolthera van Hövell committed
923
924
925
    QTextBlockFormat format = m_textEditorWidget.richTextEdit->textCursor().blockFormat();
    format.setAlignment(Qt::AlignLeft);
    m_textEditorWidget.richTextEdit->textCursor().mergeBlockFormat(format);
926
    m_textEditorWidget.richTextEdit->setTextCursor(oldCursor);
927
928
929
930
}

void SvgTextEditor::alignRight()
{
931
    QTextCursor oldCursor = setTextSelection();
Wolthera van Hövell's avatar
Wolthera van Hövell committed
932
933
934
    QTextBlockFormat format = m_textEditorWidget.richTextEdit->textCursor().blockFormat();
    format.setAlignment(Qt::AlignRight);
    m_textEditorWidget.richTextEdit->textCursor().mergeBlockFormat(format);
935
    m_textEditorWidget.richTextEdit->setTextCursor(oldCursor);
936
937
938
939
}

void SvgTextEditor::alignCenter()
{
940
    QTextCursor oldCursor = setTextSelection();
Wolthera van Hövell's avatar
Wolthera van Hövell committed
941
942
943
    QTextBlockFormat format = m_textEditorWidget.richTextEdit->textCursor().blockFormat();
    format.setAlignment(Qt::AlignCenter);
    m_textEditorWidget.richTextEdit->textCursor().mergeBlockFormat(format);
944
    m_textEditorWidget.richTextEdit->setTextCursor(oldCursor);
945
946
947
948
}

void SvgTextEditor::alignJustified()
{
949
    QTextCursor oldCursor = setTextSelection();
Wolthera van Hövell's avatar
Wolthera van Hövell committed
950
951
952
    QTextBlockFormat format = m_textEditorWidget.richTextEdit->textCursor().blockFormat();
    format.setAlignment(Qt::AlignJustify);
    m_textEditorWidget.richTextEdit->textCursor().mergeBlockFormat(format);
953
    m_textEditorWidget.richTextEdit->setTextCursor(oldCursor);
954
955
}

956
void SvgTextEditor::setSettings()
957
{
958
    KoDialog settingsDialog(this);
Halla Rempt's avatar
Halla Rempt committed
959
960
961
962
963
964
965
966
    Ui_WdgSvgTextSettings textSettings;
    QWidget *settingsPage = new QWidget(&settingsDialog, 0);
    settingsDialog.setMainWidget(settingsPage);
    textSettings.setupUi(settingsPage);

    // get the settings and initialize the dialog
    KConfigGroup cfg(KSharedConfig::openConfig(), "SvgTextTool");

967
968
969
970
971
972
973
974
975
976
977
978
979
980
    QStringList selectedWritingSystems = cfg.readEntry("selectedWritingSystems", "").split(",");

    QList<QFontDatabase::WritingSystem> scripts = QFontDatabase().writingSystems();
    QStandardItemModel *writingSystemsModel = new QStandardItemModel(&settingsDialog);
    for (int s = 0; s < scripts.size(); s ++) {
        QString writingSystem = QFontDatabase().writingSystemName(scripts.at(s));
        QStandardItem *script = new QStandardItem(writingSystem);
        script->setCheckable(true);
        script->setCheckState(selectedWritingSystems.contains(QString::number(scripts.at(s))) ? Qt::Checked : Qt::Unchecked);
        script->setData((int)scripts.at(s));
        writingSystemsModel->appendRow(script);
    }
    textSettings.lwScripts->setModel(writingSystemsModel);

981
982
983
    m_currentEditorMode = (EditorMode)cfg.readEntry("EditorMode", (int)EditorMode::Both);
    switch (m_currentEditorMode) {
    case EditorMode::RichText:
984
985
        textSettings.radioRichText->setChecked(true);
        break;
986
    case EditorMode::SvgSource:
987
988
        textSettings.radioSvgSource->setChecked(true);
        break;
989
    case EditorMode::Both:
990
991
992
        textSettings.radioBoth->setChecked(true);
    }

993
    QColor background = cfg.readEntry("colorEditorBackground", qApp->palette().window().color());
994
995
996
997
998
999
1000
    textSettings.colorEditorBackground->setColor(background);
    textSettings.colorEditorForeground->setColor(cfg.readEntry("colorEditorForeground", qApp->palette().text().color()));

    textSettings.colorKeyword->setColor(cfg.readEntry("colorKeyword", QColor(background.value() < 100 ? Qt::cyan : Qt::blue)));
    textSettings.chkBoldKeyword->setChecked(cfg.readEntry("BoldKeyword", true));
    textSettings.chkItalicKeyword->setChecked(cfg.readEntry("ItalicKeyword", false));