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 }