summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Mollekopf <chrigi_1@fastmail.fm>2017-10-06 11:43:33 +0200
committerChristian Mollekopf <chrigi_1@fastmail.fm>2017-10-06 11:46:58 +0200
commit737770bfa4bb91ed96412d18d6490db3450bcb1c (patch)
tree9b4bd7a6252c687b4cb5ac3d91ebe786e7891377
parent41e3f3544f3ec7ae3c14f18010005e3e55c003e7 (diff)
downloadkube-737770bfa4bb91ed96412d18d6490db3450bcb1c.tar.gz
kube-737770bfa4bb91ed96412d18d6490db3450bcb1c.zip
TextDocumentHandler to deal with HTML formatting
-rw-r--r--framework/qml/TextEditor.qml2
-rw-r--r--framework/qml/tests/tst_texteditor.qml2
-rw-r--r--framework/src/CMakeLists.txt2
-rw-r--r--framework/src/domain/textdocumenthandler.cpp280
-rw-r--r--framework/src/domain/textdocumenthandler.h121
-rw-r--r--framework/src/frameworkplugin.cpp4
6 files changed, 407 insertions, 4 deletions
diff --git a/framework/qml/TextEditor.qml b/framework/qml/TextEditor.qml
index 609b85db..8a5c2bb9 100644
--- a/framework/qml/TextEditor.qml
+++ b/framework/qml/TextEditor.qml
@@ -52,7 +52,7 @@ FocusScope {
52 } 52 }
53 } 53 }
54 54
55 Kube.DocumentHandler { 55 Kube.TextDocumentHandler {
56 id: document 56 id: document
57 document: edit.textDocument 57 document: edit.textDocument
58 selectionStart: edit.selectionStart 58 selectionStart: edit.selectionStart
diff --git a/framework/qml/tests/tst_texteditor.qml b/framework/qml/tests/tst_texteditor.qml
index 21ebe642..e6773aaa 100644
--- a/framework/qml/tests/tst_texteditor.qml
+++ b/framework/qml/tests/tst_texteditor.qml
@@ -40,6 +40,8 @@ TestCase {
40 40
41 function test_2htmlConversion() { 41 function test_2htmlConversion() {
42 editor.htmlEnabled = true 42 editor.htmlEnabled = true
43 verify(editor.text.indexOf("<html>") !== -1)
44 verify(editor.text.indexOf(editor.initialText) !== -1)
43 editor.htmlEnabled = false 45 editor.htmlEnabled = false
44 compare(editor.text, editor.initialText) 46 compare(editor.text, editor.initialText)
45 } 47 }
diff --git a/framework/src/CMakeLists.txt b/framework/src/CMakeLists.txt
index b66f45ed..7b188a49 100644
--- a/framework/src/CMakeLists.txt
+++ b/framework/src/CMakeLists.txt
@@ -31,7 +31,7 @@ set(SRCS
31 domain/contactcontroller.cpp 31 domain/contactcontroller.cpp
32 domain/controller.cpp 32 domain/controller.cpp
33 domain/peoplemodel.cpp 33 domain/peoplemodel.cpp
34 domain/documenthandler.cpp 34 domain/textdocumenthandler.cpp
35 domain/mime/htmlutils.cpp 35 domain/mime/htmlutils.cpp
36 domain/mime/messageparser.cpp 36 domain/mime/messageparser.cpp
37 domain/mime/attachmentmodel.cpp 37 domain/mime/attachmentmodel.cpp
diff --git a/framework/src/domain/textdocumenthandler.cpp b/framework/src/domain/textdocumenthandler.cpp
new file mode 100644
index 00000000..729d39e8
--- /dev/null
+++ b/framework/src/domain/textdocumenthandler.cpp
@@ -0,0 +1,280 @@
1/*
2 Copyright (c) 2017 Christian Mollekopf <mollekopf@kolabsystems.com>
3
4 This library is free software; you can redistribute it and/or modify it
5 under the terms of the GNU Library General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or (at your
7 option) any later version.
8
9 This library is distributed in the hope that it will be useful, but WITHOUT
10 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
12 License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to the
16 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 02110-1301, USA.
18*/
19#include "textdocumenthandler.h"
20
21#include <QQuickTextDocument>
22#include <QTextCharFormat>
23#include <QTextDocument>
24#include <QDebug>
25
26TextDocumentHandler::TextDocumentHandler(QObject *parent)
27 : QObject(parent),
28 mDocument(nullptr),
29 mCursorPosition(-1),
30 mSelectionStart(0),
31 mSelectionEnd(0)
32{
33}
34
35QQuickTextDocument *TextDocumentHandler::document() const
36{
37 return mDocument;
38}
39
40void TextDocumentHandler::setDocument(QQuickTextDocument *document)
41{
42 if (document != mDocument) {
43 mDocument = document;
44 connect(mDocument->textDocument(), &QTextDocument::contentsChanged, this, [this] () {
45 emit textChanged();
46 });
47 connect(mDocument->textDocument(), &QTextDocument::contentsChange, this, &TextDocumentHandler::contentsChange);
48 emit documentChanged();
49 }
50}
51
52QString TextDocumentHandler::plainText() const
53{
54 if (mDocument) {
55 return mDocument->textDocument()->toPlainText();
56 }
57 return {};
58}
59
60QString TextDocumentHandler::htmlText() const
61{
62 if (mDocument) {
63 return mDocument->textDocument()->toHtml();
64 }
65 return {};
66}
67
68int TextDocumentHandler::cursorPosition() const
69{
70 return mCursorPosition;
71}
72
73void TextDocumentHandler::setCursorPosition(int position)
74{
75 if (position != mCursorPosition) {
76 mCursorPosition = position;
77 reset();
78 emit cursorPositionChanged();
79 }
80}
81
82int TextDocumentHandler::selectionStart() const
83{
84 return mSelectionStart;
85}
86
87void TextDocumentHandler::setSelectionStart(int position)
88{
89 if (position != mSelectionStart) {
90 mSelectionStart = position;
91 emit selectionStartChanged();
92 }
93}
94
95int TextDocumentHandler::selectionEnd() const
96{
97 return mSelectionEnd;
98}
99
100void TextDocumentHandler::setSelectionEnd(int position)
101{
102 if (position != mSelectionEnd) {
103 mSelectionEnd = position;
104 emit selectionEndChanged();
105 }
106}
107
108QTextCharFormat TextDocumentHandler::charFormat() const
109{
110 if (mCachedTextFormat) {
111 return *mCachedTextFormat;
112 }
113 auto cursor = textCursor();
114 if (cursor.isNull()) {
115 return {};
116 }
117 return cursor.charFormat();
118}
119
120QString TextDocumentHandler::fontFamily() const
121{
122 return charFormat().font().family();
123}
124
125void TextDocumentHandler::setFontFamily(const QString &family)
126{
127 QTextCharFormat format;
128 format.setFontFamily(family);
129 mergeFormatOnWordOrSelection(format);
130 emit fontFamilyChanged();
131}
132
133QColor TextDocumentHandler::textColor() const
134{
135 return charFormat().foreground().color();
136}
137
138void TextDocumentHandler::setTextColor(const QColor &color)
139{
140 QTextCharFormat format;
141 format.setForeground(QBrush(color));
142 mergeFormatOnWordOrSelection(format);
143 emit textColorChanged();
144}
145
146Qt::Alignment TextDocumentHandler::alignment() const
147{
148 auto cursor = textCursor();
149 if (cursor.isNull()) {
150 return Qt::AlignLeft;
151 }
152 return cursor.blockFormat().alignment();
153}
154
155void TextDocumentHandler::setAlignment(Qt::Alignment alignment)
156{
157 QTextBlockFormat format;
158 format.setAlignment(alignment);
159 QTextCursor cursor = textCursor();
160 cursor.mergeBlockFormat(format);
161 emit alignmentChanged();
162}
163
164bool TextDocumentHandler::bold() const
165{
166 return charFormat().fontWeight() == QFont::Bold;
167}
168
169void TextDocumentHandler::setBold(bool bold)
170{
171 QTextCharFormat format;
172 format.setFontWeight(bold ? QFont::Bold : QFont::Normal);
173 mergeFormatOnWordOrSelection(format);
174 emit boldChanged();
175}
176
177bool TextDocumentHandler::italic() const
178{
179 return charFormat().fontItalic();
180}
181
182void TextDocumentHandler::setItalic(bool italic)
183{
184 QTextCharFormat format;
185 format.setFontItalic(italic);
186 mergeFormatOnWordOrSelection(format);
187 emit italicChanged();
188}
189
190bool TextDocumentHandler::underline() const
191{
192 return charFormat().fontUnderline();
193}
194
195void TextDocumentHandler::setUnderline(bool underline)
196{
197 QTextCharFormat format;
198 format.setFontUnderline(underline);
199 mergeFormatOnWordOrSelection(format);
200 emit underlineChanged();
201}
202
203int TextDocumentHandler::fontSize() const
204{
205 return charFormat().font().pointSize();
206}
207
208void TextDocumentHandler::setFontSize(int size)
209{
210 if (size <= 0)
211 return;
212
213 if (charFormat().property(QTextFormat::FontPointSize).toInt() == size)
214 return;
215
216 QTextCharFormat format;
217 format.setFontPointSize(size);
218 mergeFormatOnWordOrSelection(format);
219 emit fontSizeChanged();
220}
221
222void TextDocumentHandler::reset()
223{
224 emit fontFamilyChanged();
225 emit alignmentChanged();
226 emit boldChanged();
227 emit italicChanged();
228 emit underlineChanged();
229 emit fontSizeChanged();
230 emit textColorChanged();
231}
232
233QTextCursor TextDocumentHandler::textCursor() const
234{
235 if (mDocument) {
236 if (QTextDocument *doc = mDocument->textDocument()) {
237 QTextCursor cursor = QTextCursor(doc);
238 if (mSelectionStart != mSelectionEnd) {
239 cursor.setPosition(mSelectionStart);
240 cursor.setPosition(mSelectionEnd, QTextCursor::KeepAnchor);
241 } else {
242 cursor.setPosition(mCursorPosition);
243 }
244 return cursor;
245 }
246 }
247 return QTextCursor();
248}
249
250void TextDocumentHandler::contentsChange(int position, int charsRemoved, int charsAdded)
251{
252 Q_UNUSED(charsRemoved)
253 if (mCachedTextFormat) {
254 if (charsAdded) {
255 //Apply cached formatting
256 QTextCursor cursor = textCursor();
257 cursor.setPosition(position + charsAdded, QTextCursor::KeepAnchor);
258 cursor.mergeCharFormat(*mCachedTextFormat);
259 //This is somehow necessary, otherwise space can break in the editor.
260 cursor.setPosition(position + charsAdded, QTextCursor::MoveAnchor);
261 }
262 mCachedTextFormat = {};
263 }
264}
265
266void TextDocumentHandler::mergeFormatOnWordOrSelection(const QTextCharFormat &format)
267{
268 QTextCursor cursor = textCursor();
269
270 if (cursor.hasSelection()) {
271 cursor.mergeCharFormat(format);
272 } else {
273 if (mCachedTextFormat) {
274 mCachedTextFormat->merge(format);
275 } else {
276 //If we have nothing to format right now we cache the result until the next char is inserted.
277 mCachedTextFormat = QSharedPointer<QTextCharFormat>::create(format);
278 }
279 }
280}
diff --git a/framework/src/domain/textdocumenthandler.h b/framework/src/domain/textdocumenthandler.h
new file mode 100644
index 00000000..b8ae5bdb
--- /dev/null
+++ b/framework/src/domain/textdocumenthandler.h
@@ -0,0 +1,121 @@
1/*
2 Copyright (c) 2017 Christian Mollekopf <mollekopf@kolabsystems.com>
3
4 This library is free software; you can redistribute it and/or modify it
5 under the terms of the GNU Library General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or (at your
7 option) any later version.
8
9 This library is distributed in the hope that it will be useful, but WITHOUT
10 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
12 License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to the
16 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 02110-1301, USA.
18*/
19#pragma once
20
21#include <QObject>
22#include <QFont>
23#include <QTextCursor>
24
25class QTextDocument;
26class QQuickTextDocument;
27
28class TextDocumentHandler : public QObject
29{
30 Q_OBJECT
31
32 Q_PROPERTY(QQuickTextDocument *document READ document WRITE setDocument NOTIFY documentChanged)
33 Q_PROPERTY(int cursorPosition READ cursorPosition WRITE setCursorPosition NOTIFY cursorPositionChanged)
34 Q_PROPERTY(int selectionStart READ selectionStart WRITE setSelectionStart NOTIFY selectionStartChanged)
35 Q_PROPERTY(int selectionEnd READ selectionEnd WRITE setSelectionEnd NOTIFY selectionEndChanged)
36
37 Q_PROPERTY(QColor textColor READ textColor WRITE setTextColor NOTIFY textColorChanged)
38 Q_PROPERTY(QString fontFamily READ fontFamily WRITE setFontFamily NOTIFY fontFamilyChanged)
39 Q_PROPERTY(Qt::Alignment alignment READ alignment WRITE setAlignment NOTIFY alignmentChanged)
40
41 Q_PROPERTY(bool bold READ bold WRITE setBold NOTIFY boldChanged)
42 Q_PROPERTY(bool italic READ italic WRITE setItalic NOTIFY italicChanged)
43 Q_PROPERTY(bool underline READ underline WRITE setUnderline NOTIFY underlineChanged)
44
45 Q_PROPERTY(int fontSize READ fontSize WRITE setFontSize NOTIFY fontSizeChanged)
46
47 Q_PROPERTY(QString plainText READ plainText NOTIFY textChanged)
48 Q_PROPERTY(QString htmlText READ htmlText NOTIFY textChanged)
49
50public:
51 explicit TextDocumentHandler(QObject *parent = nullptr);
52
53 QQuickTextDocument *document() const;
54 void setDocument(QQuickTextDocument *document);
55
56 QString plainText() const;
57 QString htmlText() const;
58
59 int cursorPosition() const;
60 void setCursorPosition(int position);
61
62 int selectionStart() const;
63 void setSelectionStart(int position);
64
65 int selectionEnd() const;
66 void setSelectionEnd(int position);
67
68 QString fontFamily() const;
69 void setFontFamily(const QString &family);
70
71 QColor textColor() const;
72 void setTextColor(const QColor &color);
73
74 Qt::Alignment alignment() const;
75 void setAlignment(Qt::Alignment alignment);
76
77 bool bold() const;
78 void setBold(bool bold);
79
80 bool italic() const;
81 void setItalic(bool italic);
82
83 bool underline() const;
84 void setUnderline(bool underline);
85
86 int fontSize() const;
87 void setFontSize(int size);
88
89Q_SIGNALS:
90 void documentChanged();
91 void cursorPositionChanged();
92 void selectionStartChanged();
93 void selectionEndChanged();
94
95 void fontFamilyChanged();
96 void textColorChanged();
97 void alignmentChanged();
98
99 void boldChanged();
100 void italicChanged();
101 void underlineChanged();
102
103 void fontSizeChanged();
104
105 void textChanged();
106
107private Q_SLOTS:
108 void contentsChange(int position, int charsRemoved, int charsAdded);
109
110private:
111 void reset();
112 QTextCharFormat charFormat() const;
113 QTextCursor textCursor() const;
114 void mergeFormatOnWordOrSelection(const QTextCharFormat &format);
115
116 QQuickTextDocument *mDocument;
117 int mCursorPosition;
118 int mSelectionStart;
119 int mSelectionEnd;
120 QSharedPointer<QTextCharFormat> mCachedTextFormat;
121};
diff --git a/framework/src/frameworkplugin.cpp b/framework/src/frameworkplugin.cpp
index c4f7e85d..9f7b03c1 100644
--- a/framework/src/frameworkplugin.cpp
+++ b/framework/src/frameworkplugin.cpp
@@ -29,7 +29,7 @@
29#include "domain/mouseproxy.h" 29#include "domain/mouseproxy.h"
30#include "domain/contactcontroller.h" 30#include "domain/contactcontroller.h"
31#include "domain/peoplemodel.h" 31#include "domain/peoplemodel.h"
32#include "domain/documenthandler.h" 32#include "domain/textdocumenthandler.h"
33#include "accounts/accountsmodel.h" 33#include "accounts/accountsmodel.h"
34#include "accounts/accountfactory.h" 34#include "accounts/accountfactory.h"
35#include "settings/settings.h" 35#include "settings/settings.h"
@@ -76,7 +76,7 @@ void FrameworkPlugin::registerTypes (const char *uri)
76 qmlRegisterType<MouseProxy>(uri, 1, 0, "MouseProxy"); 76 qmlRegisterType<MouseProxy>(uri, 1, 0, "MouseProxy");
77 qmlRegisterType<ContactController>(uri, 1, 0,"ContactController"); 77 qmlRegisterType<ContactController>(uri, 1, 0,"ContactController");
78 qmlRegisterType<PeopleModel>(uri, 1, 0,"PeopleModel"); 78 qmlRegisterType<PeopleModel>(uri, 1, 0,"PeopleModel");
79 qmlRegisterType<DocumentHandler>(uri, 1, 0, "DocumentHandler"); 79 qmlRegisterType<TextDocumentHandler>(uri, 1, 0, "TextDocumentHandler");
80 80
81 qmlRegisterType<AccountFactory>(uri, 1, 0, "AccountFactory"); 81 qmlRegisterType<AccountFactory>(uri, 1, 0, "AccountFactory");
82 qmlRegisterType<AccountsModel>(uri, 1, 0, "AccountsModel"); 82 qmlRegisterType<AccountsModel>(uri, 1, 0, "AccountsModel");