MauiKit TextEditor

documenthandler.cpp
1/****************************************************************************
2 * *
3 ** Copyright (C) 2017 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 <KLocalizedString>
54
55#include <QAbstractTextDocumentLayout>
56#include <QDebug>
57#include <QFile>
58#include <QFileInfo>
59#include <QFileSelector>
60#include <QFileSystemWatcher>
61#include <QQmlFile>
62#include <QQmlFileSelector>
63#include <QTextCharFormat>
64#include <QTextDocument>
65#include <QUrl>
66
67#include <MauiKit4/Core/fmh.h>
68
69#include <KSyntaxHighlighting/Definition>
70#include <KSyntaxHighlighting/Repository>
71#include <KSyntaxHighlighting/SyntaxHighlighter>
72#include <KSyntaxHighlighting/Theme>
73
74#define AUTOSAVE_TIMEOUT 5000
75
76/**
77 * Global Variables
78 */
79KSyntaxHighlighting::Repository *DocumentHandler::m_repository = nullptr;
80int DocumentHandler::m_instanceCount = 0;
81
82Alerts::Alerts(QObject *parent)
83: QAbstractListModel(parent)
84{
85}
86
87Alerts::~Alerts()
88{
89 qDebug() << "REMOVING ALL DOCUMENTS ALERTS" << this->m_alerts.size();
90 for (auto *alert : std::as_const(m_alerts)) {
91 delete alert;
92 alert = nullptr;
93 }
94}
95
96QVariant Alerts::data(const QModelIndex &index, int role) const
97{
98 if (role == ROLES::ALERT)
99 return QVariant::fromValue(this->m_alerts.at(index.row()));
100
101 return QVariant();
102}
103
104int Alerts::rowCount(const QModelIndex &parent) const
105{
106 if (parent.isValid())
107 return 0;
108
109 return this->m_alerts.count();
110}
111
112QHash<int, QByteArray> Alerts::roleNames() const
113{
114 return {{ROLES::ALERT, "alert"}};
115}
116
117bool Alerts::contains(DocumentAlert *const alert)
118{
119 for (const auto &alert_ : std::as_const(m_alerts)) {
120 if (alert_->getId() == alert->getId())
121 return true;
122 }
123
124 return false;
125}
126
127void Alerts::append(DocumentAlert *alert)
128{
129 if (this->contains(alert))
130 return;
131
132 const auto index = this->rowCount();
134
135 // watch out for when the alert is done: such as when an action is triggered
136 connect(alert, &DocumentAlert::done, [this](int index) {
138 auto item = this->m_alerts.takeAt(index);
139 if (item) {
140 item->deleteLater();
141 item = nullptr;
142 }
143 this->endRemoveRows();
144 });
145
146 alert->setIndex(index);
147 this->m_alerts << alert;
149}
150
152{
153 if (FMH::fileExists(url))
154 {
155 QFile file(url.toLocalFile());
156 if (file.open(QFile::ReadOnly))
157 {
158 const auto array = file.readAll();
159 Q_EMIT this->fileReady(QString::fromStdString(array.toStdString()), url);
160 }
161 }
162}
163
164DocumentAlert *DocumentHandler::externallyModifiedAlert()
165{
166 auto alert = new DocumentAlert(i18nd("mauikittexteditor","File changed externally"), i18nd("mauikittexteditor","You can reload the file or save your changes now"), DocumentAlert::WARNING_LEVEL, Alerts::MODIFIED);
167
168 const auto reloadAction = [this]() {
169 Q_EMIT this->loadFile(this->fileUrl());
170 };
171
172 const auto autoReloadAction = [this]() {
173 this->setAutoReload(true);
174 Q_EMIT this->loadFile(this->fileUrl());
175 };
176
177 alert->setActions({{i18nd("mauikittexteditor","Reload"), reloadAction}, {i18nd("mauikittexteditor","Auto Reload"), autoReloadAction}, {i18nd("mauikittexteditor","Ignore"), []() {}}});
178 return alert;
179}
180
181DocumentAlert *DocumentHandler::canNotSaveAlert(const QString &details)
182{
183 auto alert = new DocumentAlert(i18nd("mauikittexteditor","File can not be saved"), details, DocumentAlert::DANGER_LEVEL, Alerts::SAVE_ERROR);
184
185 alert->setActions({{i18nd("mauikittexteditor","Ignore"), []() {}}});
186 return alert;
187}
188
189DocumentAlert *DocumentHandler::missingAlert()
190{
191 auto alert = new DocumentAlert(i18nd("mauikittexteditor","Your file was removed"), i18nd("mauikittexteditor","This file does not longer exist in your local storage, however you can save it again"), DocumentAlert::DANGER_LEVEL, Alerts::MISSING);
192
193 const auto saveAction = [this]() {
194 this->saveAs(this->fileUrl());
195 };
196
197 alert->setActions({{i18nd("mauikittexteditor","Save"), saveAction}});
198 return alert;
199}
200
201DocumentHandler::DocumentHandler(QObject *parent)
202: QObject(parent)
203, m_document(nullptr)
204, m_watcher(new QFileSystemWatcher(this))
205, m_cursorPosition(-1)
206, m_selectionStart(0)
207, m_selectionEnd(0)
208, m_highlighter(new KSyntaxHighlighting::SyntaxHighlighter(this))
209, m_alerts(new Alerts(this))
210{
211 ++m_instanceCount;
212
213 // start file loader thread implementation
214 {
215 FileLoader *m_loader = new FileLoader;
216 m_loader->moveToThread(&m_worker);
217 connect(&m_worker, &QThread::finished, m_loader, &QObject::deleteLater);
218 connect(this, &DocumentHandler::loadFile, m_loader, &FileLoader::loadFile);
219 connect(m_loader, &FileLoader::fileReady, [this](QString array, QUrl url) {
220 this->setText(array);
221
222 if (this->textDocument()) {
223 this->textDocument()->setModified(false);
224
225 this->isRich = Qt::mightBeRichText(this->text());
226 Q_EMIT this->isRichChanged();
227 }
228
229 Q_EMIT this->loaded(url);
230
231 reset();
232 });
233 m_worker.start();
234 }
235 // end file loader thread implementation
236
237 connect(&m_autoSaveTimer, &QTimer::timeout, [this]() {
238 if (m_autoSave && getModified() && !m_fileUrl.isEmpty()) {
239 qDebug() << "Autosaving file" << m_fileUrl;
240 saveAs(m_fileUrl);
241 m_autoSaveTimer.start(AUTOSAVE_TIMEOUT);
242 }
243 });
244
245 if (m_autoSave)
246 m_autoSaveTimer.start(AUTOSAVE_TIMEOUT);
247
248 connect(this, &DocumentHandler::cursorPositionChanged, [this]() {
249 Q_EMIT this->currentLineIndexChanged();
250 });
251
252 connect(this->m_watcher, &QFileSystemWatcher::fileChanged, [this](QString url) {
253 if (this->fileUrl() == QUrl::fromLocalFile(url)) {
254 // THE FILE WAS REMOVED
255 if (!FMH::fileExists(this->fileUrl())) {
256 this->m_alerts->append(DocumentHandler::missingAlert());
257 return;
258 }
259
260 // THE FILE CHANGED BUT STILL EXISTS LOCALLY
261 if (m_internallyModified) {
262 m_internallyModified = false;
263 return;
264 }
265
266 this->setExternallyModified(true);
267
268 if (!this->m_autoReload) {
269 this->m_alerts->append(DocumentHandler::externallyModifiedAlert());
270 return;
271 }
272
273 Q_EMIT this->loadFile(this->fileUrl());
274 }
275 });
276}
277
278DocumentHandler::~DocumentHandler()
279{
280 this->m_worker.quit();
281 this->m_worker.wait();
282
283 --DocumentHandler::m_instanceCount;
284
285 if (!DocumentHandler::m_instanceCount) {
286 delete DocumentHandler::m_repository;
287 DocumentHandler::m_repository = nullptr;
288 }
289}
290
292{
293 if (text != this->m_text) {
294 this->m_text = text;
295 Q_EMIT textChanged();
296 }
297}
298
300{
301 return this->m_autoReload;
302}
303
304void DocumentHandler::setAutoReload(const bool &value)
305{
306 if (value == this->m_autoReload)
307 return;
308
309 this->m_autoReload = value;
310 Q_EMIT this->autoReloadChanged();
311}
312
313bool DocumentHandler::autoSave() const
314{
315 return m_autoSave;
316}
317
318void DocumentHandler::setAutoSave(const bool &value)
319{
320 if (m_autoSave == value)
321 return;
322
323 m_autoSave = value;
324 Q_EMIT autoSaveChanged();
325
326 if (m_autoSave) {
327 if (!m_autoSaveTimer.isActive())
328 m_autoSaveTimer.start(AUTOSAVE_TIMEOUT);
329 } else
330 m_autoSaveTimer.stop();
331}
332
334{
335 if (auto doc = this->textDocument())
336 return doc->isModified();
337
338 return false;
339}
340
342{
343 return this->m_externallyModified;
344}
345
347{
348 if (value == this->m_externallyModified)
349 return;
350
351 this->m_externallyModified = value;
352 Q_EMIT this->externallyModifiedChanged();
353}
354
355void DocumentHandler::setStyle()
356{
357 if (!DocumentHandler::m_repository)
358 DocumentHandler::m_repository = new KSyntaxHighlighting::Repository();
359
360 qDebug() << "Setting ths tyle" << m_formatName;
361 if (!m_enableSyntaxHighlighting || m_formatName == "None") {
362 this->m_highlighter->setDocument(nullptr);
363 // this->m_highlighter->setTheme(KSyntaxHighlighting::Theme());
364 // this->m_highlighter->setDefinition(m_repository->definitionForName( "None" ));
365 // this->m_highlighter->rehighlight();
366 return;
367 }
368
369 qDebug() << "Setting the style for syntax highligthing";
370
371 const auto def = m_repository->definitionForName(this->m_formatName);
372 if (!def.isValid()) {
373 qDebug() << "Highliging definition is not valid" << def.name() << def.filePath() << def.author() << m_formatName;
374 return;
375 }
376
377 if (!m_highlighter->document()) {
378 this->m_highlighter->setDocument(this->textDocument());
379 }
380
381 qDebug() << "Highliging definition info" << def.name() << def.filePath() << def.author() << m_formatName;
382
383 this->m_highlighter->setDefinition(def);
384
385 if (m_theme.isEmpty()) {
386 const bool isDark = DocumentHandler::isDark(this->m_backgroundColor);
387 const auto style = DocumentHandler::m_repository->defaultTheme(isDark ? KSyntaxHighlighting::Repository::DarkTheme : KSyntaxHighlighting::Repository::LightTheme);
388 this->m_highlighter->setTheme(style);
389
390 } else {
391 qDebug() << "Applying theme << " << m_theme << DocumentHandler::m_repository->theme(m_theme).isValid();
392 const auto style = DocumentHandler::m_repository->theme(m_theme);
393 this->m_highlighter->setTheme(style);
394 this->m_highlighter->rehighlight();
395 }
396
397 refreshAllBlocks();
398}
399
400void DocumentHandler::refreshAllBlocks()
401{
402 if (textDocument()) {
403 for (QTextBlock it = textDocument()->begin(); it != textDocument()->end(); it = it.next())
404 {
405 Q_EMIT this->textDocument()->documentLayout()->updateBlock(it);
406 }
407 }
408}
409
410QString DocumentHandler::formatName() const
411{
412 return this->m_formatName;
413}
414
416{
417 if (this->m_formatName != formatName) {
418 this->m_formatName = formatName;
419 Q_EMIT this->formatNameChanged();
420 }
421
422 this->setStyle();
423}
424
426{
427 return this->m_backgroundColor;
428}
429
431{
432 if (this->m_backgroundColor == color)
433 return;
434
435 this->m_backgroundColor = color;
436 Q_EMIT this->backgroundColorChanged();
437
438 if (!DocumentHandler::m_repository)
439 DocumentHandler::m_repository = new KSyntaxHighlighting::Repository();
440}
441
443{
444 return this->m_alerts;
445}
446
447QQuickTextDocument *DocumentHandler::document() const
448{
449 return m_document;
450}
451
453{
454 this->m_document = document;
455 Q_EMIT documentChanged();
456
457 if (this->textDocument()) {
458 this->textDocument()->setModified(false);
459 connect(this->textDocument(), &QTextDocument::modificationChanged, this, &DocumentHandler::modifiedChanged);
460
461 connect(this->textDocument(), &QTextDocument::blockCountChanged, this, &DocumentHandler::lineCountChanged);
462
463 // connect(this->textDocument(), &QTextDocument::cursorPositionChanged, [this](const QTextCursor &)
464 // {
465 // qDebug() << "Cursors position changed";
466 // Q_EMIT currentLineIndexChanged();
467 // });
468
469 this->load(m_fileUrl);
470
471 QTextOption textOptions = this->textDocument()->defaultTextOption();
472 textOptions.setTabStopDistance(m_tabSpace);
473 textDocument()->setDefaultTextOption(textOptions);
474 }
475}
476
477int DocumentHandler::cursorPosition() const
478{
479 return m_cursorPosition;
480}
481
483{
484 if(m_cursorPosition == position)
485 {
486 return;
487 }
488
489 m_cursorPosition = position;
490 Q_EMIT cursorPositionChanged();
491}
492
493int DocumentHandler::selectionStart() const
494{
495 return m_selectionStart;
496}
497
499{
500 if (position == m_selectionStart)
501 return;
502
503 m_selectionStart = position;
504 Q_EMIT selectionStartChanged();
505}
506
507int DocumentHandler::selectionEnd() const
508{
509 return m_selectionEnd;
510}
511
513{
514 if (position == m_selectionEnd)
515 return;
516
517 m_selectionEnd = position;
518 Q_EMIT selectionEndChanged();
519}
520
521QString DocumentHandler::fontFamily() const
522{
523 QTextCursor cursor = textCursor();
524 if (cursor.isNull())
525 return QString();
526 QTextCharFormat format = cursor.charFormat();
527 return format.font().family();
528}
529
531{
532 QTextCharFormat format;
533 format.setFontFamilies({family});
534 mergeFormatOnWordOrSelection(format);
535 Q_EMIT fontFamilyChanged();
536}
537
538QColor DocumentHandler::textColor() const
539{
540 QTextCursor cursor = textCursor();
541 if (cursor.isNull())
542 return QColor(Qt::black);
543 QTextCharFormat format = cursor.charFormat();
544 return format.foreground().color();
545}
546
548{
549 QTextCharFormat format;
550 format.setForeground(QBrush(color));
551 mergeFormatOnWordOrSelection(format);
552 Q_EMIT textColorChanged();
553}
554
555Qt::Alignment DocumentHandler::alignment() const
556{
557 QTextCursor cursor = textCursor();
558 if (cursor.isNull())
559 return Qt::AlignLeft;
560 return textCursor().blockFormat().alignment();
561}
562
564{
565 QTextBlockFormat format;
566 format.setAlignment(alignment);
567 QTextCursor cursor = textCursor();
568 cursor.mergeBlockFormat(format);
569 Q_EMIT alignmentChanged();
570}
571
572bool DocumentHandler::bold() const
573{
574 QTextCursor cursor = textCursor();
575 if (cursor.isNull())
576 return false;
577 return textCursor().charFormat().fontWeight() == QFont::Bold;
578}
579
581{
582 QTextCharFormat format;
583 format.setFontWeight(bold ? QFont::Bold : QFont::Normal);
584 mergeFormatOnWordOrSelection(format);
585 Q_EMIT boldChanged();
586}
587
588bool DocumentHandler::uppercase() const
589{
590 QTextCursor cursor = textCursor();
591 if (cursor.isNull())
592 return false;
593 return textCursor().charFormat().fontCapitalization() == QFont::AllUppercase;
594}
595
597{
598 QTextCharFormat format;
600 mergeFormatOnWordOrSelection(format);
601 Q_EMIT uppercaseChanged();
602}
603
604bool DocumentHandler::italic() const
605{
606 QTextCursor cursor = textCursor();
607 if (cursor.isNull())
608 return false;
609 return textCursor().charFormat().fontItalic();
610}
611
613{
614 QTextCharFormat format;
615 format.setFontItalic(italic);
616 mergeFormatOnWordOrSelection(format);
617 Q_EMIT italicChanged();
618}
619
620bool DocumentHandler::underline() const
621{
622 QTextCursor cursor = textCursor();
623 if (cursor.isNull())
624 return false;
625 return textCursor().charFormat().fontUnderline();
626}
627
629{
630 QTextCharFormat format;
631 format.setFontUnderline(underline);
632 mergeFormatOnWordOrSelection(format);
633 Q_EMIT underlineChanged();
634}
635
637{
638 return this->isRich;
639}
640
641int DocumentHandler::fontSize() const
642{
643 QTextCursor cursor = textCursor();
644 if (cursor.isNull())
645 return 0;
646 QTextCharFormat format = cursor.charFormat();
647 return format.font().pointSize();
648}
649
651{
652 if (size <= 0)
653 return;
654
655 QTextCursor cursor = textCursor();
656 if (cursor.isNull())
657 return;
658
659 if (!cursor.hasSelection())
661
662 if (cursor.charFormat().property(QTextFormat::FontPointSize).toInt() == size)
663 return;
664
665 QTextCharFormat format;
666 format.setFontPointSize(size);
667 mergeFormatOnWordOrSelection(format);
668 Q_EMIT fontSizeChanged();
669}
670
671void DocumentHandler::setTabSpace(qreal value)
672{
673 if (m_tabSpace == value)
674 return;
675
676 m_tabSpace = value;
677
678 if (textDocument()) {
679 QTextOption textOptions = this->textDocument()->defaultTextOption();
680 textOptions.setTabStopDistance(m_tabSpace);
681 textDocument()->setDefaultTextOption(textOptions);
682 }
683
684 Q_EMIT tabSpaceChanged();
685 refreshAllBlocks();
686}
687
688qreal DocumentHandler::tabSpace() const
689{
690 return m_tabSpace;
691}
692
693QString DocumentHandler::fileName() const
694{
695 const QString filePath = QQmlFile::urlToLocalFileOrQrc(m_fileUrl);
696 const QString fileName = QFileInfo(filePath).fileName();
697 if (fileName.isEmpty())
698 return QStringLiteral("untitled.txt");
699 return fileName;
700}
701
702QString DocumentHandler::fileType() const
703{
704 return QFileInfo(fileName()).suffix();
705}
706
707QUrl DocumentHandler::fileUrl() const
708{
709 return m_fileUrl;
710}
711
713{
714 if (url == m_fileUrl)
715 return;
716
717 m_fileUrl = url;
718
719 load(m_fileUrl);
720
721 Q_EMIT fileUrlChanged();
722 Q_EMIT fileInfoChanged();
723}
724
725QVariantMap DocumentHandler::fileInfo() const
726{
727
728 const QFileInfo file(m_fileUrl.toLocalFile());
729 if(file.exists())
730 {
731 return QVariantMap();
732 }
733
734 QVariantMap map = {
735 {FMH::MODEL_NAME[FMH::MODEL_KEY::LABEL], file.fileName()},
736 {FMH::MODEL_NAME[FMH::MODEL_KEY::NAME], file.fileName()}
737 };
738
739 return map;
740}
741
742void DocumentHandler::load(const QUrl &url)
743{
744 qDebug() << "TRYING TO LOAD FILE << " << url << url.isEmpty();
745 if (!textDocument())
746 return;
747
748 if (m_fileUrl.isLocalFile() && !FMH::fileExists(m_fileUrl))
749 return;
750
751 QQmlEngine *engine = qmlEngine(this);
752 if (!engine) {
753 qWarning() << "load() called before DocumentHandler has QQmlEngine";
754 return;
755 }
756
757 this->m_watcher->removePaths(this->m_watcher->files());
758 this->m_watcher->addPath(m_fileUrl.toLocalFile());
759
760 Q_EMIT this->loadFile(m_fileUrl);
761
762 if (m_enableSyntaxHighlighting) {
764 }
765}
766
768{
769 if (url.isEmpty() || !url.isValid())
770 return;
771
772 QTextDocument *doc = this->textDocument();
773 if (!doc)
774 return;
775
776 this->m_internallyModified = true;
777
778 // QTextDocumentWriter textWriter(url.toLocalFile());
779 // if(!textWriter.write(this->textDocument()))
780 // {
781 // Q_EMIT error(i18nd("mauikittexteditor","Cannot save file ")+ url.toString());
782 // qWarning() << "can not save file" << textWriter.supportedDocumentFormats() << textWriter.format();
783 // this->m_alerts->append(this->canNotSaveAlert(i18nd("mauikittexteditor","Cannot save file ")+ url.toString()));
784 // return;
785 // }
786
787 const QString filePath = url.toLocalFile();
788 const bool isHtml = QFileInfo(filePath).suffix().contains(QLatin1String("html"));
789 QFile file(filePath);
790 if (!file.open(QFile::WriteOnly | QFile::Truncate | (isHtml ? QFile::NotOpen : QFile::Text))) {
791 Q_EMIT error(i18nd("mauikittexteditor","Cannot save: ") + file.errorString());
792 this->m_alerts->append(this->canNotSaveAlert(i18nd("mauikittexteditor","Cannot save file ") + file.errorString() + url.toString()));
793
794 return;
795 }
796 file.write((isHtml ? doc->toHtml() : doc->toPlainText()).toUtf8());
797 file.close();
798 Q_EMIT fileSaved();
799
800 doc->setModified(false);
801
802 if (url == m_fileUrl)
803 return;
804
805 m_fileUrl = url;
806 Q_EMIT fileUrlChanged();
807}
808
810{
811 if (!DocumentHandler::m_repository)
812 DocumentHandler::m_repository = new KSyntaxHighlighting::Repository();
813 const auto res = DocumentHandler::m_repository->definitionForFileName(fileName.toString());
814
815 return res.isValid() ? res.name() : QString();
816}
817
819{
820 if (!DocumentHandler::m_repository)
821 m_repository = new KSyntaxHighlighting::Repository();
822
823 const auto definitions = DocumentHandler::m_repository->definitions();
824 return std::accumulate(definitions.constBegin(), definitions.constEnd(), QStringList(), [](QStringList &languages, const auto &definition) -> QStringList {
825 languages.append(definition.name());
826 return languages;
827 });
828}
829
830
831void DocumentHandler::reset()
832{
833 Q_EMIT fontFamilyChanged();
834 Q_EMIT alignmentChanged();
835 Q_EMIT boldChanged();
836 Q_EMIT italicChanged();
837 Q_EMIT underlineChanged();
838 Q_EMIT fontSizeChanged();
839 Q_EMIT textColorChanged();
840}
841
842QTextCursor DocumentHandler::textCursor() const
843{
844 QTextDocument *doc = textDocument();
845 if (!doc)
846 return QTextCursor();
847
848 QTextCursor cursor = QTextCursor(doc);
849 if (m_selectionStart != m_selectionEnd) {
850 cursor.setPosition(m_selectionStart);
851 cursor.setPosition(m_selectionEnd, QTextCursor::KeepAnchor);
852 } else {
853 cursor.setPosition(m_cursorPosition);
854 }
855 return cursor;
856}
857
858QTextDocument *DocumentHandler::textDocument() const
859{
860 if (!m_document)
861 return nullptr;
862
863 return m_document->textDocument();
864}
865
866void DocumentHandler::mergeFormatOnWordOrSelection(const QTextCharFormat &format)
867{
868 QTextCursor cursor = textCursor();
869 if (!cursor.hasSelection())
871 cursor.mergeCharFormat(format);
872}
873
874void DocumentHandler::find(const QString &query,const bool &forward)
875{
876 qDebug() << "Asked to find" << query;
877 QTextDocument *doc = textDocument();
878
879 if (!doc) {
880 return;
881 }
882
883 QTextDocument::FindFlags searchFlags;
884 QTextDocument::FindFlags newFlags = searchFlags;
885
886 if (!forward)
887 {
888 newFlags = searchFlags | QTextDocument::FindBackward;
889 }
890
891 if (m_findCaseSensitively)
892 {
893 newFlags = newFlags | QTextDocument::FindCaseSensitively;
894 }
895
896 if (m_findWholeWords)
897 {
898 newFlags = newFlags | QTextDocument::FindWholeWords;
899 }
900
901 QTextCursor start = this->textCursor();
902
903 if(query != m_searchQuery )
904 {
906 m_searchQuery = query;
907 }
908
909 if (!start.isNull() && !start.atEnd())
910 {
911 QTextCursor found = doc->find(m_searchQuery, start, newFlags);
912 if (found.isNull())
913 {
914 if (!forward)
916 else
918
919 this->setCursorPosition(start.position());
920
921 found = doc->find(m_searchQuery, start, newFlags);
922 }
923
924 if (!found.isNull())
925 {
926 // found.movePosition(QTextCursor::WordRight, QTextCursor::MoveAnchor);
930 Q_EMIT searchFound(selectionStart(), selectionEnd());
931 }
932 }
933}
934
935void DocumentHandler::replace(const QString &query, const QString &value)
936{
937 if(value.isEmpty())
938 {
939 return;
940 }
941
942 if (this->textDocument()) {
943
944 if(m_searchQuery.isEmpty() || query != m_searchQuery)
945 {
946 find(query);
947 }
948
949 auto cursor = this->textCursor();
950 cursor.beginEditBlock();
951 cursor.insertText(value);
952 cursor.endEditBlock();
953
954 find(query);
955 }
956}
957
958void DocumentHandler::replaceAll(const QString &query, const QString &value)
959{
960 QTextDocument *doc = textDocument();
961
962 if (!doc) {
963 return;
964 }
965
966 QTextCursor newCursor(doc);
967 newCursor.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor);
968
969 if(newCursor.isNull() || newCursor.atEnd())
970 {
971 return;
972 }
973
974 QTextDocument::FindFlags searchFlags;
975 QTextDocument::FindFlags newFlags = searchFlags;
976
977 if (m_findCaseSensitively)
978 {
979 newFlags = searchFlags | QTextDocument::FindCaseSensitively;
980 }
981
982 if (m_findWholeWords)
983 {
984 newFlags = searchFlags | QTextDocument::FindWholeWords;
985 }
986
987 while (!newCursor.isNull() && !newCursor.atEnd()) {
988 newCursor = doc->find(query, newCursor, newFlags);
989
990 if (!newCursor.isNull()) {
991
992 // newCursor.movePosition(QTextCursor::NoMove,
993 // QTextCursor::KeepAnchor);
994
995 newCursor.beginEditBlock();
996 newCursor.insertText(value);
997 newCursor.endEditBlock();
998
999 }
1000 }
1001}
1002
1003bool DocumentHandler::isFoldable(const int &line) const
1004{
1005 if(!m_highlighter)
1006 return false;
1007
1008 if(auto doc = this->textDocument())
1009 {
1010 return m_highlighter->startsFoldingRegion(doc->findBlockByLineNumber(line));
1011 }
1012
1013 return false;
1014}
1015
1016bool DocumentHandler::isFolded(const int &line) const
1017{
1018 if(!m_highlighter)
1019 return false;
1020
1021 if(auto doc = this->textDocument())
1022 {
1023 auto block = doc->findBlockByLineNumber(line);
1024
1025 if (!block.isValid())
1026 return false;
1027
1028 const auto nextBlock = block.next();
1029
1030 if (!nextBlock.isValid())
1031 return false;
1032
1033 return !nextBlock.isVisible();
1034 }
1035
1036 return false;
1037}
1038
1039void DocumentHandler::toggleFold(const int &line)
1040{
1041 if(!m_highlighter)
1042 return;
1043
1044 if(auto doc = this->textDocument())
1045 {
1046 auto startBlock = doc->findBlockByLineNumber(line);
1047
1048 // we also want to fold the last line of the region, therefore the ".next()"
1049 const auto endBlock =
1050 m_highlighter->findFoldingRegionEnd(startBlock).next();
1051
1052 qDebug() << "Fold line"<< line << startBlock.position() << endBlock.position() << doc->blockCount();
1053 // fold
1054 auto block = startBlock.next();
1055 while (block.isValid() && block != endBlock)
1056 {
1057 block.setVisible(false);
1058 block.setLineCount(0);
1059 block = block.next();
1060 }
1061
1062
1063 for (QTextBlock it = startBlock; it != endBlock; it = it.next())
1064 {
1065 Q_EMIT this->textDocument()->documentLayout()->updateBlock(it);
1066 }
1067
1068 // redraw document
1069 // doc->markContentsDirty(startBlock.position(), endBlock.position());
1070 qDebug() << "Fold line"<< line << startBlock.position() << endBlock.position() << doc->blockCount();
1071
1072 // // update scrollbars
1074 doc->documentLayout()->documentSize());
1075 }
1076}
1077
1079{
1080 QTextDocument *doc = textDocument();
1081
1082 if (!doc) {
1083 return 0;
1084 }
1085
1086 return int(doc->documentLayout()->blockBoundingRect(doc->findBlockByLineNumber(line)).height());
1087}
1088
1089int DocumentHandler::lineCount()
1090{
1091 if (!this->textDocument())
1092 return 0;
1093 return this->textDocument()->blockCount();
1094}
1095
1097{
1098 if (!this->textDocument())
1099 return -1;
1100
1101 return this->textDocument()->findBlock(m_cursorPosition).blockNumber();
1102}
1103
1104int DocumentHandler::goToLine(const int& line)
1105{
1106 if (!this->textDocument())
1107 return this->cursorPosition();
1108 const auto block = this->textDocument()->findBlockByLineNumber(line);
1109 return block.position() + block.length()-1;
1110}
1111
1113{
1114 if (m_enableSyntaxHighlighting == value) {
1115 return;
1116 }
1117
1118 m_enableSyntaxHighlighting = value;
1119
1120 if (!m_enableSyntaxHighlighting) {
1121 this->setFormatName("None");
1122 } else {
1124 }
1125
1126 Q_EMIT enableSyntaxHighlightingChanged();
1127}
1128
1129bool DocumentHandler::enableSyntaxHighlighting() const
1130{
1131 return m_enableSyntaxHighlighting;
1132}
1133
1135{
1136 if (m_theme == theme)
1137 return;
1138
1139 m_theme = theme;
1140 setStyle();
1141 qDebug() << "changinf the theme<< " << theme << m_theme;
1142 Q_EMIT themeChanged();
1143}
1144
1145QString DocumentHandler::theme() const
1146{
1147 return m_theme;
1148}
The Alerts class.
The DocumentAlert class.
void done(int index)
done
void setActions(QVector< AlertAction > actions)
setActions
int getId() const
getId
void setIndex(const int &index)
setIndex
bool getAutoReload() const
getAutoReload
void setAutoSave(const bool &value)
setAutoSave
void setDocument(QQuickTextDocument *document)
setDocument
void setBackgroundColor(const QColor &color)
setBackgroundColor
void setCursorPosition(int position)
setCursorPosition
void setFileUrl(const QUrl &url)
setFileUrl
int lineHeight(const int &line)
lineHeight
int getCurrentLineIndex()
getCurrentLineIndex
QColor getBackgroundColor() const
getBackgroundColor
void setItalic(bool italic)
setItalic
bool getIsRich() const
getIsRich
void setBold(bool bold)
setBold
void setUnderline(bool underline)
setUnderline
static const QStringList getLanguageNameList()
getLanguageNameList
void setText(const QString &text)
setText
void find(const QString &query, const bool &forward=true)
find
void setUppercase(bool uppercase)
setUppercase
void setEnableSyntaxHighlighting(const bool &value)
setEnableSyntaxHighlighting
void setSelectionEnd(int position)
setSelectionEnd
void setExternallyModified(const bool &value)
setExternallyModified
void setFontSize(int size)
setFontSize
bool getExternallyModified() const
getExternallyModified
Alerts * getAlerts() const
getAlerts
void setAutoReload(const bool &value)
setAutoReload
void setSelectionStart(int position)
setSelectionStart
void setFontFamily(const QString &family)
setFontFamily
void setAlignment(Qt::Alignment alignment)
setAlignment
bool getModified() const
getModified
static const QString getLanguageNameFromFileName(const QUrl &fileName)
getLanguageNameFromFileName
void setTextColor(const QColor &color)
setTextColor
static bool isDark(const QColor &color)
isDark
void setTheme(const QString &theme)
setTheme
void setFormatName(const QString &formatName)
setFormatName
void saveAs(const QUrl &url)
saveAs
The FileLoader class.
void loadFile(const QUrl &url)
loadFile
void fileReady(QString array, QUrl url)
fileReady
Q_INVOKABLE KSyntaxHighlighting::Theme defaultTheme(DefaultTheme t=LightTheme) const
Q_INVOKABLE KSyntaxHighlighting::Theme theme(const QString &themeName) const
Q_INVOKABLE KSyntaxHighlighting::Definition definitionForName(const QString &defName) const
Q_INVOKABLE QList< KSyntaxHighlighting::Definition > definitions() const
Q_INVOKABLE KSyntaxHighlighting::Definition definitionForFileName(const QString &fileName) const
QTextBlock findFoldingRegionEnd(const QTextBlock &startBlock) const
void setTheme(const Theme &theme) override
bool startsFoldingRegion(const QTextBlock &startBlock) const
void setDefinition(const Definition &def) override
Q_SCRIPTABLE Q_NOREPLY void start()
QString i18nd(const char *domain, const char *text, const TYPE &arg...)
bool fileExists(const QUrl &path)
static const QHash< MODEL_KEY, QString > MODEL_NAME
KGuiItem reset()
const QList< QKeySequence > & begin()
void beginInsertRows(const QModelIndex &parent, int first, int last)
void beginRemoveRows(const QModelIndex &parent, int first, int last)
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const const override
virtual QRectF blockBoundingRect(const QTextBlock &block) const const=0
virtual QSizeF documentSize() const const=0
void documentSizeChanged(const QSizeF &newSize)
void updateBlock(const QTextBlock &block)
const QColor & color() const const
bool open(FILE *fh, OpenMode mode, FileHandleFlags handleFlags)
virtual void close() override
bool exists(const QString &path)
QString fileName() const const
QString suffix() const const
bool addPath(const QString &path)
void fileChanged(const QString &path)
QStringList files() const const
QStringList removePaths(const QStringList &paths)
QString family() const const
int pointSize() const const
QString errorString() const const
QByteArray readAll()
qint64 write(const QByteArray &data)
void append(QList< T > &&value)
int row() const const
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void deleteLater()
void moveToThread(QThread *targetThread)
QObject * parent() const const
QTextDocument * textDocument() const const
qreal height() const const
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
QString fromStdString(const std::string &str)
bool isEmpty() const const
QTextDocument * document() const const
void setDocument(QTextDocument *doc)
bool mightBeRichText(const QString &text)
typedef Alignment
int blockNumber() const const
bool isVisible() const const
QTextBlock next() const const
int position() const const
Qt::Alignment alignment() const const
void setAlignment(Qt::Alignment alignment)
QFont font() const const
QFont::Capitalization fontCapitalization() const const
bool fontItalic() const const
bool fontUnderline() const const
int fontWeight() const const
void setFontCapitalization(QFont::Capitalization capitalization)
void setFontFamilies(const QStringList &families)
void setFontItalic(bool italic)
void setFontPointSize(qreal size)
void setFontUnderline(bool underline)
void setFontWeight(int weight)
void beginEditBlock()
QTextBlockFormat blockFormat() const const
QTextCharFormat charFormat() const const
void endEditBlock()
bool hasSelection() const const
void insertText(const QString &text)
bool isNull() const const
void mergeBlockFormat(const QTextBlockFormat &modifier)
void mergeCharFormat(const QTextCharFormat &modifier)
bool movePosition(MoveOperation operation, MoveMode mode, int n)
int position() const const
void select(SelectionType selection)
int selectionEnd() const const
int selectionStart() const const
void setPosition(int pos, MoveMode m)
typedef FindFlags
void blockCountChanged(int newBlockCount)
QAbstractTextDocumentLayout * documentLayout() const const
QTextBlock end() const const
QTextCursor find(const QRegularExpression &expr, const QTextCursor &cursor, FindFlags options) const const
QTextBlock findBlock(int pos) const const
QTextBlock findBlockByLineNumber(int lineNumber) const const
void modificationChanged(bool changed)
void setModified(bool m)
void setDefaultTextOption(const QTextOption &option)
QString toHtml() const const
QString toPlainText() const const
QBrush foreground() const const
QVariant property(int propertyId) const const
void setForeground(const QBrush &brush)
void setTabStopDistance(qreal tabStopDistance)
void finished()
void quit()
bool wait(QDeadlineTimer deadline)
bool isActive() const const
void start()
void stop()
void timeout()
QUrl fromLocalFile(const QString &localFile)
bool isEmpty() const const
bool isLocalFile() const const
bool isValid() const const
QString toLocalFile() const const
QString toString(FormattingOptions options) const const
QVariant fromValue(T &&value)
int toInt(bool *ok) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Jul 26 2024 11:52:34 by doxygen 1.11.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.