1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the examples of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:BSD$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** BSD License Usage
18** Alternatively, you may use this file under the terms of the BSD license
19** as follows:
20**
21** "Redistribution and use in source and binary forms, with or without
22** modification, are permitted provided that the following conditions are
23** met:
24** * Redistributions of source code must retain the above copyright
25** notice, this list of conditions and the following disclaimer.
26** * Redistributions in binary form must reproduce the above copyright
27** notice, this list of conditions and the following disclaimer in
28** the documentation and/or other materials provided with the
29** distribution.
30** * Neither the name of The Qt Company Ltd nor the names of its
31** contributors may be used to endorse or promote products derived
32** from this software without specific prior written permission.
33**
34**
35** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
36** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
37** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
38** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
39** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
42** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
43** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
44** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
45** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
46**
47** $QT_END_LICENSE$
48**
49****************************************************************************/
50
51#include "documenthandler.h"
52
53#include <QtGui/QTextDocument>
54#include <QtGui/QTextCursor>
55#include <QtGui/QFontDatabase>
56#include <QtCore/QFileInfo>
57
58DocumentHandler::DocumentHandler()
59 : m_target(0)
60 , m_doc(0)
61 , m_cursorPosition(-1)
62 , m_selectionStart(0)
63 , m_selectionEnd(0)
64{
65}
66
67void DocumentHandler::setTarget(QQuickItem *target)
68{
69 m_doc = 0;
70 m_target = target;
71 if (!m_target)
72 return;
73
74 QVariant doc = m_target->property(name: "textDocument");
75 if (doc.canConvert<QQuickTextDocument*>()) {
76 QQuickTextDocument *qqdoc = doc.value<QQuickTextDocument*>();
77 if (qqdoc)
78 m_doc = qqdoc->textDocument();
79 }
80 emit targetChanged();
81}
82
83void DocumentHandler::setFileUrl(const QUrl &arg)
84{
85 if (m_fileUrl != arg) {
86 m_fileUrl = arg;
87 QString fileName = QQmlFile::urlToLocalFileOrQrc(arg);
88 if (QFile::exists(fileName)) {
89 QFile file(fileName);
90 if (file.open(flags: QFile::ReadOnly)) {
91 QByteArray data = file.readAll();
92 QTextCodec *codec = QTextCodec::codecForHtml(ba: data);
93 setText(codec->toUnicode(data));
94 if (m_doc)
95 m_doc->setModified(false);
96 if (fileName.isEmpty())
97 m_documentTitle = QStringLiteral("untitled.txt");
98 else
99 m_documentTitle = QFileInfo(fileName).fileName();
100
101 emit textChanged();
102 emit documentTitleChanged();
103
104 reset();
105 }
106 }
107 emit fileUrlChanged();
108 }
109}
110
111QString DocumentHandler::documentTitle() const
112{
113 return m_documentTitle;
114}
115
116void DocumentHandler::setDocumentTitle(QString arg)
117{
118 if (m_documentTitle != arg) {
119 m_documentTitle = arg;
120 emit documentTitleChanged();
121 }
122}
123
124void DocumentHandler::setText(const QString &arg)
125{
126 if (m_text != arg) {
127 m_text = arg;
128 emit textChanged();
129 }
130}
131
132void DocumentHandler::saveAs(const QUrl &arg, const QString &fileType)
133{
134 bool isHtml = fileType.contains(s: QLatin1String("htm"));
135 QLatin1String ext(isHtml ? ".html" : ".txt");
136 QString localPath = arg.toLocalFile();
137 if (!localPath.endsWith(s: ext))
138 localPath += ext;
139 QFile f(localPath);
140 if (!f.open(flags: QFile::WriteOnly | QFile::Truncate | (isHtml ? QFile::NotOpen : QFile::Text))) {
141 emit error(message: tr(s: "Cannot save: ") + f.errorString());
142 return;
143 }
144 f.write(data: (isHtml ? m_doc->toHtml() : m_doc->toPlainText()).toLocal8Bit());
145 f.close();
146 setFileUrl(QUrl::fromLocalFile(localfile: localPath));
147}
148
149QUrl DocumentHandler::fileUrl() const
150{
151 return m_fileUrl;
152}
153
154QString DocumentHandler::text() const
155{
156 return m_text;
157}
158
159void DocumentHandler::setCursorPosition(int position)
160{
161 if (position == m_cursorPosition)
162 return;
163
164 m_cursorPosition = position;
165
166 reset();
167}
168
169void DocumentHandler::reset()
170{
171 emit fontFamilyChanged();
172 emit alignmentChanged();
173 emit boldChanged();
174 emit italicChanged();
175 emit underlineChanged();
176 emit fontSizeChanged();
177 emit textColorChanged();
178}
179
180QTextCursor DocumentHandler::textCursor() const
181{
182 if (!m_doc)
183 return QTextCursor();
184
185 QTextCursor cursor = QTextCursor(m_doc);
186 if (m_selectionStart != m_selectionEnd) {
187 cursor.setPosition(pos: m_selectionStart);
188 cursor.setPosition(pos: m_selectionEnd, mode: QTextCursor::KeepAnchor);
189 } else {
190 cursor.setPosition(pos: m_cursorPosition);
191 }
192 return cursor;
193}
194
195void DocumentHandler::mergeFormatOnWordOrSelection(const QTextCharFormat &format)
196{
197 QTextCursor cursor = textCursor();
198 if (!cursor.hasSelection())
199 cursor.select(selection: QTextCursor::WordUnderCursor);
200 cursor.mergeCharFormat(modifier: format);
201}
202
203void DocumentHandler::setSelectionStart(int position)
204{
205 m_selectionStart = position;
206}
207
208void DocumentHandler::setSelectionEnd(int position)
209{
210 m_selectionEnd = position;
211}
212
213void DocumentHandler::setAlignment(Qt::Alignment a)
214{
215 if (!m_doc)
216 return;
217
218 QTextBlockFormat fmt;
219 fmt.setAlignment((Qt::Alignment) a);
220 QTextCursor cursor = QTextCursor(m_doc);
221 cursor.setPosition(pos: m_selectionStart, mode: QTextCursor::MoveAnchor);
222 cursor.setPosition(pos: m_selectionEnd, mode: QTextCursor::KeepAnchor);
223 cursor.mergeBlockFormat(modifier: fmt);
224 emit alignmentChanged();
225}
226
227Qt::Alignment DocumentHandler::alignment() const
228{
229 QTextCursor cursor = textCursor();
230 if (cursor.isNull())
231 return Qt::AlignLeft;
232 return textCursor().blockFormat().alignment();
233}
234
235bool DocumentHandler::bold() const
236{
237 QTextCursor cursor = textCursor();
238 if (cursor.isNull())
239 return false;
240 return textCursor().charFormat().fontWeight() == QFont::Bold;
241}
242
243bool DocumentHandler::italic() const
244{
245 QTextCursor cursor = textCursor();
246 if (cursor.isNull())
247 return false;
248 return textCursor().charFormat().fontItalic();
249}
250
251bool DocumentHandler::underline() const
252{
253 QTextCursor cursor = textCursor();
254 if (cursor.isNull())
255 return false;
256 return textCursor().charFormat().fontUnderline();
257}
258
259void DocumentHandler::setBold(bool arg)
260{
261 QTextCharFormat fmt;
262 fmt.setFontWeight(arg ? QFont::Bold : QFont::Normal);
263 mergeFormatOnWordOrSelection(format: fmt);
264 emit boldChanged();
265}
266
267void DocumentHandler::setItalic(bool arg)
268{
269 QTextCharFormat fmt;
270 fmt.setFontItalic(arg);
271 mergeFormatOnWordOrSelection(format: fmt);
272 emit italicChanged();
273}
274
275void DocumentHandler::setUnderline(bool arg)
276{
277 QTextCharFormat fmt;
278 fmt.setFontUnderline(arg);
279 mergeFormatOnWordOrSelection(format: fmt);
280 emit underlineChanged();
281}
282
283int DocumentHandler::fontSize() const
284{
285 QTextCursor cursor = textCursor();
286 if (cursor.isNull())
287 return 0;
288 QTextCharFormat format = cursor.charFormat();
289 return format.font().pointSize();
290}
291
292void DocumentHandler::setFontSize(int arg)
293{
294 QTextCursor cursor = textCursor();
295 if (cursor.isNull())
296 return;
297 QTextCharFormat format;
298 format.setFontPointSize(arg);
299 mergeFormatOnWordOrSelection(format);
300 emit fontSizeChanged();
301}
302
303QColor DocumentHandler::textColor() const
304{
305 QTextCursor cursor = textCursor();
306 if (cursor.isNull())
307 return QColor(Qt::black);
308 QTextCharFormat format = cursor.charFormat();
309 return format.foreground().color();
310}
311
312void DocumentHandler::setTextColor(const QColor &c)
313{
314 QTextCursor cursor = textCursor();
315 if (cursor.isNull())
316 return;
317 QTextCharFormat format;
318 format.setForeground(QBrush(c));
319 mergeFormatOnWordOrSelection(format);
320 emit textColorChanged();
321}
322
323QString DocumentHandler::fontFamily() const
324{
325 QTextCursor cursor = textCursor();
326 if (cursor.isNull())
327 return QString();
328 QTextCharFormat format = cursor.charFormat();
329 return format.font().family();
330}
331
332void DocumentHandler::setFontFamily(const QString &arg)
333{
334 QTextCursor cursor = textCursor();
335 if (cursor.isNull())
336 return;
337 QTextCharFormat format;
338 format.setFontFamily(arg);
339 mergeFormatOnWordOrSelection(format);
340 emit fontFamilyChanged();
341}
342
343QStringList DocumentHandler::defaultFontSizes() const
344{
345 // uhm... this is quite ugly
346 QStringList sizes;
347 QFontDatabase db;
348 const auto standardSizes = db.standardSizes();
349 for (int size : standardSizes)
350 sizes.append(t: QString::number(size));
351 return sizes;
352}
353

source code of qtquickcontrols/examples/quickcontrols/controls/texteditor/src/documenthandler.cpp