51#include "documenthandler.h"
53#include <KLocalizedString>
55#include <QAbstractTextDocumentLayout>
59#include <QFileSelector>
60#include <QFileSystemWatcher>
62#include <QQmlFileSelector>
63#include <QTextCharFormat>
64#include <QTextDocument>
67#include <MauiKit4/Core/fmh.h>
69#include <KSyntaxHighlighting/Definition>
70#include <KSyntaxHighlighting/Repository>
71#include <KSyntaxHighlighting/SyntaxHighlighter>
72#include <KSyntaxHighlighting/Theme>
74#define AUTOSAVE_TIMEOUT 5000
80int DocumentHandler::m_instanceCount = 0;
89 qDebug() <<
"REMOVING ALL DOCUMENTS ALERTS" << this->m_alerts.size();
90 for (
auto *alert : std::as_const(m_alerts)) {
98 if (role == ROLES::ALERT)
104int Alerts::rowCount(
const QModelIndex &parent)
const
109 return this->m_alerts.count();
114 return {{ROLES::ALERT,
"alert"}};
119 for (
const auto &alert_ : std::as_const(m_alerts)) {
120 if (alert_->getId() == alert->
getId())
129 if (this->contains(alert))
132 const auto index = this->rowCount();
138 auto item = this->m_alerts.takeAt(
index);
147 this->m_alerts << alert;
158 const auto array = file.
readAll();
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);
168 const auto reloadAction = [
this]() {
169 Q_EMIT this->loadFile(this->fileUrl());
172 const auto autoReloadAction = [
this]() {
174 Q_EMIT this->loadFile(this->fileUrl());
177 alert->
setActions({{
i18nd(
"mauikittexteditor",
"Reload"), reloadAction}, {
i18nd(
"mauikittexteditor",
"Auto Reload"), autoReloadAction}, {
i18nd(
"mauikittexteditor",
"Ignore"), []() {}}});
183 auto alert =
new DocumentAlert(
i18nd(
"mauikittexteditor",
"File can not be saved"), details, DocumentAlert::DANGER_LEVEL, Alerts::SAVE_ERROR);
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);
193 const auto saveAction = [
this]() {
194 this->
saveAs(this->fileUrl());
201DocumentHandler::DocumentHandler(
QObject *parent)
205, m_cursorPosition(-1)
209, m_alerts(new
Alerts(this))
215 FileLoader *m_loader =
new FileLoader;
220 this->setText(array);
222 if (this->textDocument()) {
223 this->textDocument()->setModified(
false);
226 Q_EMIT this->isRichChanged();
238 if (m_autoSave && getModified() && !m_fileUrl.isEmpty()) {
239 qDebug() <<
"Autosaving file" << m_fileUrl;
241 m_autoSaveTimer.start(AUTOSAVE_TIMEOUT);
246 m_autoSaveTimer.start(AUTOSAVE_TIMEOUT);
248 connect(
this, &DocumentHandler::cursorPositionChanged, [
this]() {
249 Q_EMIT this->currentLineIndexChanged();
256 this->m_alerts->append(DocumentHandler::missingAlert());
261 if (m_internallyModified) {
262 m_internallyModified =
false;
266 this->setExternallyModified(
true);
268 if (!this->m_autoReload) {
269 this->m_alerts->append(DocumentHandler::externallyModifiedAlert());
278DocumentHandler::~DocumentHandler()
280 this->m_worker.quit();
281 this->m_worker.wait();
283 --DocumentHandler::m_instanceCount;
285 if (!DocumentHandler::m_instanceCount) {
286 delete DocumentHandler::m_repository;
287 DocumentHandler::m_repository =
nullptr;
293 if (text != this->m_text) {
301 return this->m_autoReload;
306 if (value == this->m_autoReload)
309 this->m_autoReload = value;
310 Q_EMIT this->autoReloadChanged();
313bool DocumentHandler::autoSave()
const
320 if (m_autoSave == value)
327 if (!m_autoSaveTimer.isActive())
328 m_autoSaveTimer.start(AUTOSAVE_TIMEOUT);
330 m_autoSaveTimer.stop();
335 if (
auto doc = this->textDocument())
336 return doc->isModified();
343 return this->m_externallyModified;
348 if (value == this->m_externallyModified)
351 this->m_externallyModified = value;
352 Q_EMIT this->externallyModifiedChanged();
355void DocumentHandler::setStyle()
357 if (!DocumentHandler::m_repository)
360 qDebug() <<
"Setting ths tyle" << m_formatName;
361 if (!m_enableSyntaxHighlighting || m_formatName ==
"None") {
369 qDebug() <<
"Setting the style for syntax highligthing";
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;
377 if (!m_highlighter->document()) {
378 this->m_highlighter->setDocument(this->textDocument());
381 qDebug() <<
"Highliging definition info" << def.name() << def.filePath() << def.author() << m_formatName;
383 this->m_highlighter->setDefinition(def);
385 if (m_theme.isEmpty()) {
388 this->m_highlighter->setTheme(style);
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();
400void DocumentHandler::refreshAllBlocks()
402 if (textDocument()) {
403 for (QTextBlock it = textDocument()->
begin(); it != textDocument()->end(); it = it.next())
405 Q_EMIT this->textDocument()->documentLayout()->updateBlock(it);
412 return this->m_formatName;
417 if (this->m_formatName != formatName) {
418 this->m_formatName = formatName;
419 Q_EMIT this->formatNameChanged();
427 return this->m_backgroundColor;
432 if (this->m_backgroundColor == color)
435 this->m_backgroundColor = color;
436 Q_EMIT this->backgroundColorChanged();
438 if (!DocumentHandler::m_repository)
444 return this->m_alerts;
454 this->m_document = document;
457 if (this->textDocument()) {
458 this->textDocument()->setModified(
false);
469 this->load(m_fileUrl);
471 QTextOption textOptions = this->textDocument()->defaultTextOption();
473 textDocument()->setDefaultTextOption(textOptions);
477int DocumentHandler::cursorPosition()
const
479 return m_cursorPosition;
484 if(m_cursorPosition == position)
489 m_cursorPosition = position;
490 Q_EMIT cursorPositionChanged();
493int DocumentHandler::selectionStart()
const
495 return m_selectionStart;
500 if (position == m_selectionStart)
503 m_selectionStart = position;
504 Q_EMIT selectionStartChanged();
507int DocumentHandler::selectionEnd()
const
509 return m_selectionEnd;
514 if (position == m_selectionEnd)
517 m_selectionEnd = position;
518 Q_EMIT selectionEndChanged();
534 mergeFormatOnWordOrSelection(format);
535 Q_EMIT fontFamilyChanged();
551 mergeFormatOnWordOrSelection(format);
552 Q_EMIT textColorChanged();
560 return textCursor().blockFormat().alignment();
569 Q_EMIT alignmentChanged();
572bool DocumentHandler::bold()
const
577 return textCursor().charFormat().fontWeight() ==
QFont::Bold;
584 mergeFormatOnWordOrSelection(format);
588bool DocumentHandler::uppercase()
const
600 mergeFormatOnWordOrSelection(format);
601 Q_EMIT uppercaseChanged();
604bool DocumentHandler::italic()
const
609 return textCursor().charFormat().fontItalic();
616 mergeFormatOnWordOrSelection(format);
620bool DocumentHandler::underline()
const
625 return textCursor().charFormat().fontUnderline();
632 mergeFormatOnWordOrSelection(format);
633 Q_EMIT underlineChanged();
641int DocumentHandler::fontSize()
const
667 mergeFormatOnWordOrSelection(format);
671void DocumentHandler::setTabSpace(qreal value)
673 if (m_tabSpace == value)
678 if (textDocument()) {
688qreal DocumentHandler::tabSpace()
const
695 const QString filePath = QQmlFile::urlToLocalFileOrQrc(m_fileUrl);
697 if (fileName.isEmpty())
698 return QStringLiteral(
"untitled.txt");
707QUrl DocumentHandler::fileUrl()
const
714 if (url == m_fileUrl)
725QVariantMap DocumentHandler::fileInfo()
const
728 const QFileInfo file(m_fileUrl.toLocalFile());
731 return QVariantMap();
742void DocumentHandler::load(
const QUrl &url)
744 qDebug() <<
"TRYING TO LOAD FILE << " << url << url.
isEmpty();
753 qWarning() <<
"load() called before DocumentHandler has QQmlEngine";
757 this->m_watcher->removePaths(this->m_watcher->files());
758 this->m_watcher->addPath(m_fileUrl.toLocalFile());
760 Q_EMIT this->loadFile(m_fileUrl);
762 if (m_enableSyntaxHighlighting) {
776 this->m_internallyModified =
true;
789 QFile file(filePath);
792 this->m_alerts->append(this->canNotSaveAlert(
i18nd(
"mauikittexteditor",
"Cannot save file ") + file.
errorString() + url.
toString()));
802 if (url == m_fileUrl)
811 if (!DocumentHandler::m_repository)
813 const auto res = DocumentHandler::m_repository->definitionForFileName(fileName.toString());
815 return res.isValid() ? res.name() :
QString();
820 if (!DocumentHandler::m_repository)
823 const auto definitions = DocumentHandler::m_repository->definitions();
825 languages.
append(definition.name());
831void DocumentHandler::reset()
833 Q_EMIT fontFamilyChanged();
834 Q_EMIT alignmentChanged();
837 Q_EMIT underlineChanged();
839 Q_EMIT textColorChanged();
844 QTextDocument *doc = textDocument();
846 return QTextCursor();
848 QTextCursor cursor = QTextCursor(doc);
849 if (m_selectionStart != m_selectionEnd) {
863 return m_document->textDocument();
866void DocumentHandler::mergeFormatOnWordOrSelection(
const QTextCharFormat &format)
868 QTextCursor cursor = textCursor();
876 qDebug() <<
"Asked to find" << query;
891 if (m_findCaseSensitively)
896 if (m_findWholeWords)
903 if(query != m_searchQuery )
906 m_searchQuery = query;
921 found = doc->
find(m_searchQuery,
start, newFlags);
930 Q_EMIT searchFound(selectionStart(), selectionEnd());
935void DocumentHandler::replace(
const QString &query,
const QString &value)
942 if (this->textDocument()) {
944 if(m_searchQuery.isEmpty() || query != m_searchQuery)
949 auto cursor = this->textCursor();
958void DocumentHandler::replaceAll(
const QString &query,
const QString &value)
960 QTextDocument *doc = textDocument();
966 QTextCursor newCursor(doc);
969 if(newCursor.isNull() || newCursor.atEnd())
977 if (m_findCaseSensitively)
982 if (m_findWholeWords)
987 while (!newCursor.isNull() && !newCursor.atEnd()) {
988 newCursor = doc->
find(query, newCursor, newFlags);
990 if (!newCursor.isNull()) {
995 newCursor.beginEditBlock();
996 newCursor.insertText(value);
997 newCursor.endEditBlock();
1003bool DocumentHandler::isFoldable(
const int &line)
const
1008 if(
auto doc = this->textDocument())
1016bool DocumentHandler::isFolded(
const int &line)
const
1021 if(
auto doc = this->textDocument())
1025 if (!block.isValid())
1028 const auto nextBlock = block.
next();
1030 if (!nextBlock.isValid())
1039void DocumentHandler::toggleFold(
const int &line)
1044 if(
auto doc = this->textDocument())
1049 const auto endBlock =
1050 m_highlighter->findFoldingRegionEnd(startBlock).
next();
1052 qDebug() <<
"Fold line"<< line << startBlock.position() << endBlock.position() << doc->
blockCount();
1054 auto block = startBlock.next();
1055 while (block.isValid() && block != endBlock)
1057 block.setVisible(
false);
1058 block.setLineCount(0);
1059 block = block.next();
1063 for (QTextBlock it = startBlock; it != endBlock; it = it.next())
1065 Q_EMIT this->textDocument()->documentLayout()->updateBlock(it);
1070 qDebug() <<
"Fold line"<< line << startBlock.position() << endBlock.position() << doc->
blockCount();
1089int DocumentHandler::lineCount()
1091 if (!this->textDocument())
1098 if (!this->textDocument())
1101 return this->textDocument()->findBlock(m_cursorPosition).blockNumber();
1104int DocumentHandler::goToLine(
const int& line)
1106 if (!this->textDocument())
1107 return this->cursorPosition();
1109 return block.
position() + block.length()-1;
1114 if (m_enableSyntaxHighlighting == value) {
1118 m_enableSyntaxHighlighting = value;
1120 if (!m_enableSyntaxHighlighting) {
1126 Q_EMIT enableSyntaxHighlightingChanged();
1129bool DocumentHandler::enableSyntaxHighlighting()
const
1131 return m_enableSyntaxHighlighting;
1136 if (m_theme == theme)
1141 qDebug() <<
"changinf the theme<< " << theme << m_theme;
void setActions(QVector< AlertAction > actions)
setActions
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
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_SCRIPTABLE QString start(QString train="")
QString i18nd(const char *domain, const char *text, const TYPE &arg...)
bool fileExists(const QUrl &path)
static const QHash< MODEL_KEY, QString > MODEL_NAME
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=0
virtual QModelIndex parent(const QModelIndex &index) const const=0
virtual QRectF blockBoundingRect(const QTextBlock &block) const const=0
virtual QSizeF documentSize() const const=0
void documentSizeChanged(const QSizeF &newSize)
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
void fileChanged(const QString &path)
QString family() const const
int pointSize() const const
QString errorString() const const
qint64 write(const QByteArray &data)
void append(QList< T > &&value)
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void moveToThread(QThread *targetThread)
qreal height() const const
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
QString fromStdString(const std::string &str)
bool isEmpty() const const
void setDocument(QTextDocument *doc)
bool mightBeRichText(const QString &text)
bool isVisible() const const
QTextBlock next() const const
int position() const const
void setAlignment(Qt::Alignment alignment)
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)
QTextCharFormat charFormat() const const
bool hasSelection() const const
void insertText(const QString &text)
bool isNull() const const
void mergeBlockFormat(const QTextBlockFormat &modifier)
void mergeCharFormat(const QTextCharFormat &modifier)
int position() const const
void select(SelectionType selection)
int selectionEnd() const const
int selectionStart() const const
void setPosition(int pos, MoveMode m)
void blockCountChanged(int newBlockCount)
QAbstractTextDocumentLayout * documentLayout() const const
QTextCursor find(const QRegularExpression &expr, const QTextCursor &cursor, FindFlags options) const const
QTextBlock findBlockByLineNumber(int lineNumber) const const
void modificationChanged(bool changed)
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)
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