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 | |
58 | DocumentHandler::DocumentHandler() |
59 | : m_target(0) |
60 | , m_doc(0) |
61 | , m_cursorPosition(-1) |
62 | , m_selectionStart(0) |
63 | , m_selectionEnd(0) |
64 | { |
65 | } |
66 | |
67 | void 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 | |
83 | void 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 | |
111 | QString DocumentHandler::documentTitle() const |
112 | { |
113 | return m_documentTitle; |
114 | } |
115 | |
116 | void DocumentHandler::setDocumentTitle(QString arg) |
117 | { |
118 | if (m_documentTitle != arg) { |
119 | m_documentTitle = arg; |
120 | emit documentTitleChanged(); |
121 | } |
122 | } |
123 | |
124 | void DocumentHandler::setText(const QString &arg) |
125 | { |
126 | if (m_text != arg) { |
127 | m_text = arg; |
128 | emit textChanged(); |
129 | } |
130 | } |
131 | |
132 | void 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 | |
149 | QUrl DocumentHandler::fileUrl() const |
150 | { |
151 | return m_fileUrl; |
152 | } |
153 | |
154 | QString DocumentHandler::text() const |
155 | { |
156 | return m_text; |
157 | } |
158 | |
159 | void DocumentHandler::setCursorPosition(int position) |
160 | { |
161 | if (position == m_cursorPosition) |
162 | return; |
163 | |
164 | m_cursorPosition = position; |
165 | |
166 | reset(); |
167 | } |
168 | |
169 | void 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 | |
180 | QTextCursor 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 | |
195 | void 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 | |
203 | void DocumentHandler::setSelectionStart(int position) |
204 | { |
205 | m_selectionStart = position; |
206 | } |
207 | |
208 | void DocumentHandler::setSelectionEnd(int position) |
209 | { |
210 | m_selectionEnd = position; |
211 | } |
212 | |
213 | void 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 | |
227 | Qt::Alignment DocumentHandler::alignment() const |
228 | { |
229 | QTextCursor cursor = textCursor(); |
230 | if (cursor.isNull()) |
231 | return Qt::AlignLeft; |
232 | return textCursor().blockFormat().alignment(); |
233 | } |
234 | |
235 | bool DocumentHandler::bold() const |
236 | { |
237 | QTextCursor cursor = textCursor(); |
238 | if (cursor.isNull()) |
239 | return false; |
240 | return textCursor().charFormat().fontWeight() == QFont::Bold; |
241 | } |
242 | |
243 | bool DocumentHandler::italic() const |
244 | { |
245 | QTextCursor cursor = textCursor(); |
246 | if (cursor.isNull()) |
247 | return false; |
248 | return textCursor().charFormat().fontItalic(); |
249 | } |
250 | |
251 | bool DocumentHandler::underline() const |
252 | { |
253 | QTextCursor cursor = textCursor(); |
254 | if (cursor.isNull()) |
255 | return false; |
256 | return textCursor().charFormat().fontUnderline(); |
257 | } |
258 | |
259 | void 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 | |
267 | void DocumentHandler::setItalic(bool arg) |
268 | { |
269 | QTextCharFormat fmt; |
270 | fmt.setFontItalic(arg); |
271 | mergeFormatOnWordOrSelection(format: fmt); |
272 | emit italicChanged(); |
273 | } |
274 | |
275 | void DocumentHandler::setUnderline(bool arg) |
276 | { |
277 | QTextCharFormat fmt; |
278 | fmt.setFontUnderline(arg); |
279 | mergeFormatOnWordOrSelection(format: fmt); |
280 | emit underlineChanged(); |
281 | } |
282 | |
283 | int 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 | |
292 | void 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 | |
303 | QColor 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 | |
312 | void 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 | |
323 | QString 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 | |
332 | void 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 | |
343 | QStringList 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 | |