File indexing completed on 2024-05-12 17:22:44

0001 // SPDX-FileCopyrightText: 2017 The Qt Company Ltd.
0002 // SPDX-License-Identifier:  BSD-3-Clause
0003 
0004 #include <QAbstractTextDocumentLayout>
0005 #include <QDebug>
0006 #include <QFile>
0007 #include <QFileInfo>
0008 #include <QFileSelector>
0009 #include <QFileSystemWatcher>
0010 #include <QQmlFile>
0011 #include <QQmlFileSelector>
0012 #include <QQuickTextDocument>
0013 #include <QTextCharFormat>
0014 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0015 #include <QTextCodec>
0016 #else
0017 #include <QStringConverter>
0018 #include <QStringDecoder>
0019 #endif
0020 #include <QTextDocument>
0021 #include <QTextDocumentWriter>
0022 #include <QUrl>
0023 
0024 #include <KLocalizedString>
0025 #include <KSyntaxHighlighting/Definition>
0026 #include <KSyntaxHighlighting/Repository>
0027 #include <KSyntaxHighlighting/SyntaxHighlighter>
0028 #include <KSyntaxHighlighting/Theme>
0029 #include <qstandardpaths.h>
0030 
0031 #include "documenthandler.h"
0032 
0033 #define AUTOSAVE_TIMEOUT 5000
0034 
0035 /**
0036  * Global Variables
0037  */
0038 KSyntaxHighlighting::Repository *DocumentHandler::m_repository = nullptr;
0039 int DocumentHandler::m_instanceCount = 0;
0040 
0041 void FileLoader::loadFile(const QUrl &url)
0042 {
0043     if (QFile::exists(url.path())) {
0044         QFile file(url.toLocalFile());
0045         if (file.open(QFile::ReadOnly)) {
0046             const auto array = file.readAll();
0047 #if QT_VERSION > QT_VERSION_CHECK(6, 0, 0)
0048             auto encoding = QStringConverter::encodingForData(array);
0049             if (encoding.has_value()) {
0050                 Q_EMIT this->fileReady(QStringDecoder(*encoding).decode(array), url);
0051             }
0052 #else
0053             QTextCodec *codec = QTextDocumentWriter(url.toLocalFile()).codec();
0054             Q_EMIT this->fileReady(codec->toUnicode(array), url);
0055 #endif
0056         }
0057     }
0058 }
0059 
0060 DocumentHandler::DocumentHandler(QObject *parent)
0061 : QObject(parent)
0062 , m_document(nullptr)
0063 , m_watcher(new QFileSystemWatcher(this))
0064 , m_cursorPosition(-1)
0065 , m_selectionStart(0)
0066 , m_selectionEnd(0)
0067 , m_highlighter(new KSyntaxHighlighting::SyntaxHighlighter(this))
0068 {
0069     ++m_instanceCount;
0070 
0071     // start file loader thread implementation
0072 
0073     {
0074         FileLoader *m_loader = new FileLoader;
0075         m_loader->moveToThread(&m_worker);
0076         connect(&m_worker, &QThread::finished, m_loader, &QObject::deleteLater);
0077         connect(this, &DocumentHandler::loadFile, m_loader, &FileLoader::loadFile);
0078         connect(m_loader, &FileLoader::fileReady, [this](QString array, QUrl url) {
0079             this->setText(array);
0080 
0081             if (this->textDocument()) {
0082                 this->textDocument()->setModified(false);
0083 
0084                 this->isRich = Qt::mightBeRichText(this->text());
0085                 Q_EMIT this->isRichChanged();
0086             }
0087 
0088             Q_EMIT this->loaded(url);
0089 
0090             reset();
0091         });
0092         m_worker.start();
0093     }
0094 
0095     // end file loader thread implementation
0096 
0097     connect(&m_autoSaveTimer, &QTimer::timeout, [this]() {
0098         if (m_autoSave && getModified() && !m_fileUrl.isEmpty()) {
0099             qDebug() << "Autosaving file" << m_fileUrl;
0100             saveAs(m_fileUrl);
0101             m_autoSaveTimer.start(AUTOSAVE_TIMEOUT);
0102         }
0103     });
0104 
0105     if (m_autoSave)
0106         m_autoSaveTimer.start(AUTOSAVE_TIMEOUT);
0107 
0108     connect(this, &DocumentHandler::cursorPositionChanged, [this]() {
0109         Q_EMIT this->currentLineIndexChanged();
0110     });
0111 
0112     connect(this->m_watcher, &QFileSystemWatcher::fileChanged, [this](QString url) {
0113         if (this->fileUrl() == QUrl::fromLocalFile(url)) {
0114             // THE FILE WAS REMOVED
0115 
0116             // THE FILE CHANGED BUT STILL EXISTS LOCALLY
0117             if (m_internallyModified) {
0118                 m_internallyModified = false;
0119                 return;
0120             }
0121 
0122             this->setExternallyModified(true);
0123 
0124             Q_EMIT this->loadFile(this->fileUrl());
0125         }
0126     });
0127 }
0128 
0129 DocumentHandler::~DocumentHandler()
0130 {
0131     this->m_worker.quit();
0132     this->m_worker.wait();
0133 
0134     --DocumentHandler::m_instanceCount;
0135 
0136     if (!DocumentHandler::m_instanceCount) {
0137         delete DocumentHandler::m_repository;
0138         DocumentHandler::m_repository = nullptr;
0139     }
0140 }
0141 
0142 void DocumentHandler::setText(const QString &text)
0143 {
0144     if (text != this->m_text) {
0145         this->m_text = text;
0146         Q_EMIT textChanged();
0147     }
0148 }
0149 
0150 bool DocumentHandler::getAutoReload() const
0151 {
0152     return this->m_autoReload;
0153 }
0154 
0155 void DocumentHandler::setAutoReload(const bool &value)
0156 {
0157     if (value == this->m_autoReload)
0158         return;
0159 
0160     this->m_autoReload = value;
0161     Q_EMIT this->autoReloadChanged();
0162 }
0163 
0164 bool DocumentHandler::autoSave() const
0165 {
0166     return m_autoSave;
0167 }
0168 
0169 void DocumentHandler::setAutoSave(const bool &value)
0170 {
0171     if (m_autoSave == value)
0172         return;
0173 
0174     m_autoSave = value;
0175     Q_EMIT autoSaveChanged();
0176 
0177     if (m_autoSave) {
0178         if (!m_autoSaveTimer.isActive())
0179             m_autoSaveTimer.start(AUTOSAVE_TIMEOUT);
0180     } else
0181         m_autoSaveTimer.stop();
0182 }
0183 
0184 bool DocumentHandler::getModified() const
0185 {
0186     if (auto doc = this->textDocument())
0187         return doc->isModified();
0188 
0189     return false;
0190 }
0191 
0192 bool DocumentHandler::getExternallyModified() const
0193 {
0194     return this->m_externallyModified;
0195 }
0196 
0197 void DocumentHandler::setExternallyModified(const bool &value)
0198 {
0199     if (value == this->m_externallyModified)
0200         return;
0201 
0202     this->m_externallyModified = value;
0203     Q_EMIT this->externallyModifiedChanged();
0204 }
0205 
0206 void DocumentHandler::setStyle()
0207 {
0208     if (!DocumentHandler::m_repository)
0209         DocumentHandler::m_repository = new KSyntaxHighlighting::Repository();
0210 
0211     qDebug() << "Setting ths tyle" << m_formatName;
0212     if (!m_enableSyntaxHighlighting || m_formatName == QStringLiteral("None")) {
0213         this->m_highlighter->setDocument(nullptr);
0214         return;
0215     }
0216 
0217     qDebug() << "Setting the style for syntax highligthing";
0218 
0219     const auto def = m_repository->definitionForName(this->m_formatName);
0220     if (!def.isValid()) {
0221         qDebug() << "Highliging definition is not valid" << def.name() << def.filePath() << def.author() << m_formatName;
0222         return;
0223     }
0224 
0225     if (!m_highlighter->document()) {
0226         this->m_highlighter->setDocument(this->textDocument());
0227     }
0228 
0229     qDebug() << "Highliging definition info" << def.name() << def.filePath() << def.author() << m_formatName;
0230 
0231     this->m_highlighter->setDefinition(def);
0232 
0233     if (m_theme.isEmpty()) {
0234         const bool isDark = DocumentHandler::isDark(this->m_backgroundColor);
0235         const auto style = DocumentHandler::m_repository->defaultTheme(isDark ? KSyntaxHighlighting::Repository::DarkTheme : KSyntaxHighlighting::Repository::LightTheme);
0236         this->m_highlighter->setTheme(style);
0237     } else {
0238         qDebug() << "Applying theme << " << m_theme << DocumentHandler::m_repository->theme(m_theme).isValid();
0239         const auto style = DocumentHandler::m_repository->theme(m_theme);
0240         this->m_highlighter->setTheme(style);
0241         this->m_highlighter->rehighlight();
0242     }
0243 
0244     refreshAllBlocks();
0245 }
0246 
0247 void DocumentHandler::refreshAllBlocks()
0248 {
0249     if (textDocument()) {
0250         for (QTextBlock it = textDocument()->begin(); it != textDocument()->end(); it = it.next())
0251         {
0252             Q_EMIT this->textDocument()->documentLayout()->updateBlock(it);
0253         }
0254     }
0255 }
0256 
0257 QString DocumentHandler::formatName() const
0258 {
0259     return this->m_formatName;
0260 }
0261 
0262 void DocumentHandler::setFormatName(const QString &formatName)
0263 {
0264     if (this->m_formatName != formatName) {
0265         this->m_formatName = formatName;
0266         Q_EMIT this->formatNameChanged();
0267     }
0268 
0269     this->setStyle();
0270 }
0271 
0272 QColor DocumentHandler::getBackgroundColor() const
0273 {
0274     return this->m_backgroundColor;
0275 }
0276 
0277 void DocumentHandler::setBackgroundColor(const QColor &color)
0278 {
0279     if (this->m_backgroundColor == color)
0280         return;
0281 
0282     this->m_backgroundColor = color;
0283     Q_EMIT this->backgroundColorChanged();
0284 
0285     if (!DocumentHandler::m_repository)
0286         DocumentHandler::m_repository = new KSyntaxHighlighting::Repository();
0287 }
0288 
0289 QQuickTextDocument *DocumentHandler::document() const
0290 {
0291     return m_document;
0292 }
0293 
0294 void DocumentHandler::setDocument(QQuickTextDocument *document)
0295 {
0296     this->m_document = document;
0297     Q_EMIT documentChanged();
0298 
0299     if (this->textDocument()) {
0300         this->textDocument()->setModified(false);
0301         connect(this->textDocument(), &QTextDocument::modificationChanged, this, &DocumentHandler::modifiedChanged);
0302 
0303         connect(this->textDocument(), &QTextDocument::blockCountChanged, this, &DocumentHandler::lineCountChanged);
0304 
0305         this->load(m_fileUrl);
0306 
0307         QTextOption textOptions = this->textDocument()->defaultTextOption();
0308         textOptions.setTabStopDistance(m_tabSpace);
0309         textDocument()->setDefaultTextOption(textOptions);
0310     }
0311 }
0312 
0313 int DocumentHandler::cursorPosition() const
0314 {
0315     return m_cursorPosition;
0316 }
0317 
0318 void DocumentHandler::setCursorPosition(int position)
0319 {
0320     if(m_cursorPosition == position)
0321     {
0322         return;
0323     }
0324 
0325     m_cursorPosition = position;
0326     Q_EMIT cursorPositionChanged();
0327 }
0328 
0329 int DocumentHandler::selectionStart() const
0330 {
0331     return m_selectionStart;
0332 }
0333 
0334 void DocumentHandler::setSelectionStart(int position)
0335 {
0336     if (position == m_selectionStart)
0337         return;
0338 
0339     m_selectionStart = position;
0340     Q_EMIT selectionStartChanged();
0341 }
0342 
0343 int DocumentHandler::selectionEnd() const
0344 {
0345     return m_selectionEnd;
0346 }
0347 
0348 void DocumentHandler::setSelectionEnd(int position)
0349 {
0350     if (position == m_selectionEnd)
0351         return;
0352 
0353     m_selectionEnd = position;
0354     Q_EMIT selectionEndChanged();
0355 }
0356 
0357 QString DocumentHandler::fontFamily() const
0358 {
0359     QTextCursor cursor = textCursor();
0360     if (cursor.isNull())
0361         return QString();
0362     QTextCharFormat format = cursor.charFormat();
0363     return format.font().family();
0364 }
0365 
0366 void DocumentHandler::setFontFamily(const QString &family)
0367 {
0368     QTextCharFormat format;
0369     format.setFontFamily(family);
0370     mergeFormatOnWordOrSelection(format);
0371     Q_EMIT fontFamilyChanged();
0372 }
0373 
0374 QColor DocumentHandler::textColor() const
0375 {
0376     QTextCursor cursor = textCursor();
0377     if (cursor.isNull())
0378         return QColor(Qt::black);
0379     QTextCharFormat format = cursor.charFormat();
0380     return format.foreground().color();
0381 }
0382 
0383 void DocumentHandler::setTextColor(const QColor &color)
0384 {
0385     QTextCharFormat format;
0386     format.setForeground(QBrush(color));
0387     mergeFormatOnWordOrSelection(format);
0388     Q_EMIT textColorChanged();
0389 }
0390 
0391 Qt::Alignment DocumentHandler::alignment() const
0392 {
0393     QTextCursor cursor = textCursor();
0394     if (cursor.isNull())
0395         return Qt::AlignLeft;
0396     return textCursor().blockFormat().alignment();
0397 }
0398 
0399 void DocumentHandler::setAlignment(Qt::Alignment alignment)
0400 {
0401     QTextBlockFormat format;
0402     format.setAlignment(alignment);
0403     QTextCursor cursor = textCursor();
0404     cursor.mergeBlockFormat(format);
0405     Q_EMIT alignmentChanged();
0406 }
0407 
0408 bool DocumentHandler::bold() const
0409 {
0410     QTextCursor cursor = textCursor();
0411     if (cursor.isNull())
0412         return false;
0413     return textCursor().charFormat().fontWeight() == QFont::Bold;
0414 }
0415 
0416 void DocumentHandler::setBold(bool bold)
0417 {
0418     QTextCharFormat format;
0419     format.setFontWeight(bold ? QFont::Bold : QFont::Normal);
0420     mergeFormatOnWordOrSelection(format);
0421     Q_EMIT boldChanged();
0422 }
0423 
0424 bool DocumentHandler::uppercase() const
0425 {
0426     QTextCursor cursor = textCursor();
0427     if (cursor.isNull())
0428         return false;
0429     return textCursor().charFormat().fontCapitalization() == QFont::AllUppercase;
0430 }
0431 
0432 void DocumentHandler::setUppercase(bool uppercase)
0433 {
0434     QTextCharFormat format;
0435     format.setFontCapitalization(uppercase ? QFont::AllUppercase : QFont::AllLowercase);
0436     mergeFormatOnWordOrSelection(format);
0437     Q_EMIT uppercaseChanged();
0438 }
0439 
0440 bool DocumentHandler::italic() const
0441 {
0442     QTextCursor cursor = textCursor();
0443     if (cursor.isNull())
0444         return false;
0445     return textCursor().charFormat().fontItalic();
0446 }
0447 
0448 void DocumentHandler::setItalic(bool italic)
0449 {
0450     QTextCharFormat format;
0451     format.setFontItalic(italic);
0452     mergeFormatOnWordOrSelection(format);
0453     Q_EMIT italicChanged();
0454 }
0455 
0456 bool DocumentHandler::underline() const
0457 {
0458     QTextCursor cursor = textCursor();
0459     if (cursor.isNull())
0460         return false;
0461     return textCursor().charFormat().fontUnderline();
0462 }
0463 
0464 void DocumentHandler::setUnderline(bool underline)
0465 {
0466     QTextCharFormat format;
0467     format.setFontUnderline(underline);
0468     mergeFormatOnWordOrSelection(format);
0469     Q_EMIT underlineChanged();
0470 }
0471 
0472 bool DocumentHandler::getIsRich() const
0473 {
0474     return this->isRich;
0475 }
0476 
0477 int DocumentHandler::fontSize() const
0478 {
0479     QTextCursor cursor = textCursor();
0480     if (cursor.isNull())
0481         return 0;
0482     QTextCharFormat format = cursor.charFormat();
0483     return format.font().pointSize();
0484 }
0485 
0486 void DocumentHandler::setFontSize(int size)
0487 {
0488     if (size <= 0)
0489         return;
0490 
0491     QTextCursor cursor = textCursor();
0492     if (cursor.isNull())
0493         return;
0494 
0495     if (!cursor.hasSelection())
0496         cursor.select(QTextCursor::WordUnderCursor);
0497 
0498     if (cursor.charFormat().property(QTextFormat::FontPointSize).toInt() == size)
0499         return;
0500 
0501     QTextCharFormat format;
0502     format.setFontPointSize(size);
0503     mergeFormatOnWordOrSelection(format);
0504     Q_EMIT fontSizeChanged();
0505 }
0506 
0507 void DocumentHandler::setTabSpace(qreal value)
0508 {
0509     if (m_tabSpace == value)
0510         return;
0511 
0512     m_tabSpace = value;
0513 
0514     if (textDocument()) {
0515         QTextOption textOptions = this->textDocument()->defaultTextOption();
0516         textOptions.setTabStopDistance(m_tabSpace);
0517         textDocument()->setDefaultTextOption(textOptions);
0518     }
0519 
0520     Q_EMIT tabSpaceChanged();
0521     refreshAllBlocks();
0522 }
0523 
0524 qreal DocumentHandler::tabSpace() const
0525 {
0526     return m_tabSpace;
0527 }
0528 
0529 QString DocumentHandler::fileName() const
0530 {
0531     const QString filePath = QQmlFile::urlToLocalFileOrQrc(m_fileUrl);
0532     const QString fileName = QFileInfo(filePath).fileName();
0533     if (fileName.isEmpty())
0534         return QStringLiteral("untitled.txt");
0535     return fileName;
0536 }
0537 
0538 QString DocumentHandler::fileType() const
0539 {
0540     return QFileInfo(fileName()).suffix();
0541 }
0542 
0543 QUrl DocumentHandler::fileUrl() const
0544 {
0545     return m_fileUrl;
0546 }
0547 
0548 void DocumentHandler::setFileUrl(const QUrl &url)
0549 {
0550     if (url == m_fileUrl)
0551         return;
0552 
0553     m_fileUrl = url;
0554 
0555     load(m_fileUrl);
0556 
0557     Q_EMIT fileUrlChanged();
0558     Q_EMIT fileInfoChanged();
0559 }
0560 
0561 QVariantMap DocumentHandler::fileInfo() const
0562 {
0563 
0564     const QFileInfo file(m_fileUrl.toLocalFile());
0565     if(file.exists())
0566     {
0567         return QVariantMap();
0568     }
0569 
0570     QVariantMap map = {};
0571 
0572     return map;
0573 }
0574 
0575 void DocumentHandler::load(const QUrl &url)
0576 {
0577     qDebug() << "TRYING TO LOAD FILE << " << url << url.isEmpty();
0578     if (!textDocument())
0579         return;
0580 
0581     if (m_fileUrl.isLocalFile() && !QFile::exists(url.path()))
0582         return;
0583 
0584     QQmlEngine *engine = qmlEngine(this);
0585     if (!engine) {
0586         qWarning() << "load() called before DocumentHandler has QQmlEngine";
0587         return;
0588     }
0589 
0590     this->m_watcher->removePaths(this->m_watcher->files());
0591     this->m_watcher->addPath(m_fileUrl.toLocalFile());
0592 
0593     Q_EMIT this->loadFile(m_fileUrl);
0594 
0595     if (m_enableSyntaxHighlighting) {
0596         this->setFormatName(DocumentHandler::getLanguageNameFromFileName(m_fileUrl));
0597     }
0598 }
0599 
0600 void DocumentHandler::saveAs(const QUrl &url)
0601 {
0602     if (url.isEmpty() || !url.isValid())
0603         return;
0604 
0605     QTextDocument *doc = this->textDocument();
0606     if (!doc)
0607         return;
0608 
0609     this->m_internallyModified = true;
0610 
0611     const QString filePath = url.toLocalFile();
0612     const bool isHtml = QFileInfo(filePath).suffix().contains(QLatin1String("htm"));
0613     QFile file(filePath);
0614     if (!file.open(QFile::WriteOnly | QFile::Truncate | (isHtml ? QFile::NotOpen : QFile::Text))) {
0615         Q_EMIT error(i18nd("notae","Cannot save: ") + file.errorString());
0616 
0617         return;
0618     }
0619     file.write((isHtml ? doc->toHtml() : doc->toPlainText()).toUtf8());
0620     file.close();
0621     Q_EMIT fileSaved();
0622 
0623     doc->setModified(false);
0624 
0625     if (url == m_fileUrl)
0626         return;
0627 
0628     m_fileUrl = url;
0629     Q_EMIT fileUrlChanged();
0630 }
0631 
0632 const QString DocumentHandler::getLanguageNameFromFileName(const QUrl &fileName)
0633 {
0634     if (!DocumentHandler::m_repository)
0635         DocumentHandler::m_repository = new KSyntaxHighlighting::Repository();
0636 
0637     const auto res = DocumentHandler::m_repository->definitionForFileName(fileName.toString());
0638 
0639     return res.isValid() ? res.name() : QString();
0640 }
0641 
0642 const QStringList DocumentHandler::getLanguageNameList()
0643 {
0644     if (!DocumentHandler::m_repository)
0645         m_repository = new KSyntaxHighlighting::Repository();
0646 
0647     const auto definitions = DocumentHandler::m_repository->definitions();
0648 
0649     QStringList names;
0650 
0651     for (auto definition : definitions) {
0652         names.append(definition.name());
0653     }
0654 
0655     return names;
0656 }
0657 
0658 const QStringList DocumentHandler::getThemes()
0659 {
0660     if (!DocumentHandler::m_repository)
0661         DocumentHandler::m_repository = new KSyntaxHighlighting::Repository();
0662 
0663     const auto themes = DocumentHandler::m_repository->themes();
0664 
0665     QStringList names;
0666 
0667     for (auto theme : themes) {
0668         names << theme.name();
0669     }
0670 
0671     return names;
0672 }
0673 
0674 void DocumentHandler::reset()
0675 {
0676     Q_EMIT fontFamilyChanged();
0677     Q_EMIT alignmentChanged();
0678     Q_EMIT boldChanged();
0679     Q_EMIT italicChanged();
0680     Q_EMIT underlineChanged();
0681     Q_EMIT fontSizeChanged();
0682     Q_EMIT textColorChanged();
0683 }
0684 
0685 QTextCursor DocumentHandler::textCursor() const
0686 {
0687     QTextDocument *doc = textDocument();
0688     if (!doc)
0689         return QTextCursor();
0690 
0691     QTextCursor cursor = QTextCursor(doc);
0692     if (m_selectionStart != m_selectionEnd) {
0693         cursor.setPosition(m_selectionStart);
0694         cursor.setPosition(m_selectionEnd, QTextCursor::KeepAnchor);
0695     } else {
0696         cursor.setPosition(m_cursorPosition);
0697     }
0698     return cursor;
0699 }
0700 
0701 QTextDocument *DocumentHandler::textDocument() const
0702 {
0703     if (!m_document)
0704         return nullptr;
0705 
0706     return m_document->textDocument();
0707 }
0708 
0709 void DocumentHandler::mergeFormatOnWordOrSelection(const QTextCharFormat &format)
0710 {
0711     QTextCursor cursor = textCursor();
0712     if (!cursor.hasSelection())
0713         cursor.select(QTextCursor::WordUnderCursor);
0714     cursor.mergeCharFormat(format);
0715 }
0716 
0717 void DocumentHandler::find(const QString &query,const bool &forward)
0718 {
0719     qDebug() << "Asked to find" << query;
0720     QTextDocument *doc = textDocument();
0721 
0722     if (!doc) {
0723         return;
0724     }
0725 
0726     QTextDocument::FindFlags searchFlags;
0727     QTextDocument::FindFlags newFlags = searchFlags;
0728 
0729     if (!forward) {
0730         newFlags = searchFlags | QTextDocument::FindBackward;
0731     }
0732 
0733     if (m_findCaseSensitively) {
0734         newFlags = newFlags | QTextDocument::FindCaseSensitively;
0735     }
0736 
0737     if (m_findWholeWords) {
0738         newFlags = newFlags | QTextDocument::FindWholeWords;
0739     }
0740 
0741     QTextCursor start = this->textCursor();
0742 
0743     if(query != m_searchQuery ) {
0744         start.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor);
0745         m_searchQuery = query;
0746     }
0747 
0748     if (!start.isNull() && !start.atEnd()) {
0749         QTextCursor found = doc->find(m_searchQuery, start, newFlags);
0750         if (found.isNull()) {
0751             if (!forward)
0752                 start.movePosition (QTextCursor::End, QTextCursor::MoveAnchor);
0753             else
0754                 start.movePosition (QTextCursor::Start, QTextCursor::MoveAnchor);
0755 
0756             this->setCursorPosition(start.position());
0757 
0758             found = doc->find(m_searchQuery, start, newFlags);
0759         }
0760 
0761         if (!found.isNull()) {
0762             setSelectionStart(found.selectionStart());
0763             setSelectionEnd(found.selectionEnd());
0764             setCursorPosition(found.position());
0765             Q_EMIT searchFound(selectionStart(), selectionEnd());
0766         }
0767     }
0768 }
0769 
0770 void DocumentHandler::replace(const QString &query, const QString &value)
0771 {
0772     if(value.isEmpty()) {
0773         return;
0774     }
0775 
0776     if (this->textDocument()) {
0777 
0778         if(m_searchQuery.isEmpty() || query != m_searchQuery)
0779         {
0780             find(query);
0781         }
0782 
0783         auto cursor = this->textCursor();
0784         cursor.beginEditBlock();
0785         cursor.insertText(value);
0786         cursor.endEditBlock();
0787 
0788         find(query);
0789     }
0790 }
0791 
0792 void DocumentHandler::replaceAll(const QString &query, const QString &value)
0793 {
0794     QTextDocument *doc = textDocument();
0795 
0796     if (!doc) {
0797         return;
0798     }
0799 
0800     QTextCursor newCursor(doc);
0801     newCursor.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor);
0802 
0803     if(newCursor.isNull() || newCursor.atEnd()) {
0804         return;
0805     }
0806 
0807     QTextDocument::FindFlags searchFlags;
0808     QTextDocument::FindFlags newFlags = searchFlags;
0809 
0810     if (m_findCaseSensitively) {
0811         newFlags = searchFlags | QTextDocument::FindCaseSensitively;
0812     }
0813 
0814     if (m_findWholeWords) {
0815         newFlags = searchFlags | QTextDocument::FindWholeWords;
0816     }
0817 
0818     while (!newCursor.isNull() && !newCursor.atEnd()) {
0819         newCursor = doc->find(query, newCursor, newFlags);
0820 
0821         if (!newCursor.isNull()) {
0822             newCursor.beginEditBlock();
0823             newCursor.insertText(value);
0824             newCursor.endEditBlock();
0825 
0826         }
0827     }
0828 }
0829 
0830 bool DocumentHandler::isFoldable(const int &line) const
0831 {
0832     if(!m_highlighter)
0833         return false;
0834 
0835     if(auto doc = this->textDocument())
0836     {
0837         return m_highlighter->startsFoldingRegion(doc->findBlockByLineNumber(line));
0838     }
0839 
0840     return false;
0841 }
0842 
0843 bool DocumentHandler::isFolded(const int &line) const
0844 {
0845     if(!m_highlighter)
0846         return false;
0847 
0848     if(auto doc = this->textDocument())
0849     {
0850         auto block = doc->findBlockByLineNumber(line);
0851 
0852         if (!block.isValid())
0853             return false;
0854 
0855         const auto nextBlock = block.next();
0856 
0857         if (!nextBlock.isValid())
0858             return false;
0859 
0860         return !nextBlock.isVisible();
0861     }
0862 
0863     return false;
0864 }
0865 
0866 void DocumentHandler::toggleFold(const int &line)
0867 {
0868     if(!m_highlighter)
0869         return;
0870 
0871     if(auto doc = this->textDocument())
0872     {
0873         auto startBlock = doc->findBlockByLineNumber(line);
0874 
0875         // we also want to fold the last line of the region, therefore the ".next()"
0876         const auto endBlock =
0877         m_highlighter->findFoldingRegionEnd(startBlock).next();
0878 
0879         qDebug() << "Fold line"<< line << startBlock.position() << endBlock.position() << doc->blockCount();
0880         // fold
0881         auto block = startBlock.next();
0882         while (block.isValid() && block != endBlock)
0883         {
0884             block.setVisible(false);
0885             block.setLineCount(0);
0886             block = block.next();
0887         }
0888 
0889 
0890         for (QTextBlock it = startBlock; it != endBlock; it = it.next())
0891         {
0892             Q_EMIT this->textDocument()->documentLayout()->updateBlock(it);
0893         }
0894 
0895         // redraw document
0896         //         doc->markContentsDirty(startBlock.position(), endBlock.position());
0897         qDebug() << "Fold line"<< line << startBlock.position() << endBlock.position() << doc->blockCount();
0898 
0899         //         // update scrollbars
0900         Q_EMIT doc->documentLayout()->documentSizeChanged(
0901             doc->documentLayout()->documentSize());
0902     }
0903 }
0904 
0905 int DocumentHandler::lineHeight(const int &line)
0906 {
0907     QTextDocument *doc = textDocument();
0908 
0909     if (!doc) {
0910         return 0;
0911     }
0912 
0913     return int(doc->documentLayout()->blockBoundingRect(doc->findBlockByLineNumber(line)).height());
0914 }
0915 
0916 int DocumentHandler::lineCount()
0917 {
0918     if (!this->textDocument())
0919         return 0;
0920     return this->textDocument()->blockCount();
0921 }
0922 
0923 int DocumentHandler::getCurrentLineIndex()
0924 {
0925     if (!this->textDocument())
0926         return -1;
0927 
0928     return this->textDocument()->findBlock(m_cursorPosition).blockNumber();
0929 }
0930 
0931 int DocumentHandler::goToLine(const int& line)
0932 {
0933     if (!this->textDocument())
0934         return this->cursorPosition();
0935     const auto block = this->textDocument()->findBlockByLineNumber(line);
0936     return block.position() + block.length()-1;
0937 }
0938 
0939 void DocumentHandler::setEnableSyntaxHighlighting(const bool &value)
0940 {
0941     if (m_enableSyntaxHighlighting == value) {
0942         return;
0943     }
0944 
0945     m_enableSyntaxHighlighting = value;
0946 
0947     if (!m_enableSyntaxHighlighting) {
0948         this->setFormatName(QStringLiteral("None"));
0949     } else {
0950         this->setFormatName(DocumentHandler::getLanguageNameFromFileName(m_fileUrl));
0951     }
0952 
0953     Q_EMIT enableSyntaxHighlightingChanged();
0954 }
0955 
0956 bool DocumentHandler::enableSyntaxHighlighting() const
0957 {
0958     return m_enableSyntaxHighlighting;
0959 }
0960 
0961 void DocumentHandler::setTheme(const QString &theme)
0962 {
0963     if (m_theme == theme)
0964         return;
0965 
0966     m_theme = theme;
0967     setStyle();
0968     qDebug() << "changinf the theme<< " << theme << m_theme;
0969     Q_EMIT themeChanged();
0970 }
0971 
0972 QString DocumentHandler::theme() const
0973 {
0974     return m_theme;
0975 }