2022-05-13 13:12:05 +00:00
|
|
|
// Copyright (C) 2017 The Qt Company Ltd.
|
|
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
2016-07-06 11:04:07 +00:00
|
|
|
|
|
|
|
#include "documenthandler.h"
|
|
|
|
|
2016-08-11 19:55:35 +00:00
|
|
|
#include <QFile>
|
2016-07-06 11:04:07 +00:00
|
|
|
#include <QFileInfo>
|
2016-07-26 12:15:31 +00:00
|
|
|
#include <QFileSelector>
|
2020-08-10 10:35:25 +00:00
|
|
|
#include <QMimeDatabase>
|
2016-07-06 11:04:07 +00:00
|
|
|
#include <QQmlFile>
|
2016-07-26 12:15:31 +00:00
|
|
|
#include <QQmlFileSelector>
|
2016-07-06 11:04:07 +00:00
|
|
|
#include <QQuickTextDocument>
|
|
|
|
#include <QTextCharFormat>
|
2020-06-17 11:29:17 +00:00
|
|
|
#include <QStringDecoder>
|
2016-07-06 11:04:07 +00:00
|
|
|
#include <QTextDocument>
|
2017-02-23 14:20:32 +00:00
|
|
|
#include <QDebug>
|
2016-07-06 11:04:07 +00:00
|
|
|
|
|
|
|
DocumentHandler::DocumentHandler(QObject *parent)
|
|
|
|
: QObject(parent)
|
2016-08-11 19:21:47 +00:00
|
|
|
, m_document(nullptr)
|
2016-07-06 11:04:07 +00:00
|
|
|
, m_cursorPosition(-1)
|
|
|
|
, m_selectionStart(0)
|
|
|
|
, m_selectionEnd(0)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
QQuickTextDocument *DocumentHandler::document() const
|
|
|
|
{
|
2016-08-11 19:21:47 +00:00
|
|
|
return m_document;
|
2016-07-06 11:04:07 +00:00
|
|
|
}
|
|
|
|
|
2016-08-11 19:21:47 +00:00
|
|
|
void DocumentHandler::setDocument(QQuickTextDocument *document)
|
2016-07-06 11:04:07 +00:00
|
|
|
{
|
2016-08-11 19:21:47 +00:00
|
|
|
if (document == m_document)
|
2016-07-06 11:04:07 +00:00
|
|
|
return;
|
|
|
|
|
2019-08-19 14:01:05 +00:00
|
|
|
if (m_document)
|
2021-09-23 15:45:44 +00:00
|
|
|
disconnect(m_document->textDocument(), &QTextDocument::modificationChanged, this, &DocumentHandler::modifiedChanged);
|
2016-08-11 19:21:47 +00:00
|
|
|
m_document = document;
|
2019-08-19 14:01:05 +00:00
|
|
|
if (m_document)
|
|
|
|
connect(m_document->textDocument(), &QTextDocument::modificationChanged, this, &DocumentHandler::modifiedChanged);
|
2016-07-06 11:04:07 +00:00
|
|
|
emit documentChanged();
|
|
|
|
}
|
|
|
|
|
2016-08-11 19:03:35 +00:00
|
|
|
int DocumentHandler::cursorPosition() const
|
2016-07-06 11:04:07 +00:00
|
|
|
{
|
2016-08-11 19:03:35 +00:00
|
|
|
return m_cursorPosition;
|
2016-07-06 11:04:07 +00:00
|
|
|
}
|
|
|
|
|
2016-08-11 19:03:35 +00:00
|
|
|
void DocumentHandler::setCursorPosition(int position)
|
2016-07-06 11:04:07 +00:00
|
|
|
{
|
2016-08-11 19:03:35 +00:00
|
|
|
if (position == m_cursorPosition)
|
2016-07-06 11:04:07 +00:00
|
|
|
return;
|
|
|
|
|
2016-08-11 19:03:35 +00:00
|
|
|
m_cursorPosition = position;
|
|
|
|
reset();
|
2016-08-11 19:31:29 +00:00
|
|
|
emit cursorPositionChanged();
|
2016-07-06 11:04:07 +00:00
|
|
|
}
|
|
|
|
|
2016-08-11 19:03:35 +00:00
|
|
|
int DocumentHandler::selectionStart() const
|
2016-07-06 11:04:07 +00:00
|
|
|
{
|
2016-08-11 19:03:35 +00:00
|
|
|
return m_selectionStart;
|
2016-07-06 11:04:07 +00:00
|
|
|
}
|
|
|
|
|
2016-08-11 19:03:35 +00:00
|
|
|
void DocumentHandler::setSelectionStart(int position)
|
2016-07-06 11:04:07 +00:00
|
|
|
{
|
2016-08-11 19:03:35 +00:00
|
|
|
if (position == m_selectionStart)
|
2016-07-06 11:04:07 +00:00
|
|
|
return;
|
|
|
|
|
2016-08-11 19:03:35 +00:00
|
|
|
m_selectionStart = position;
|
|
|
|
emit selectionStartChanged();
|
2016-07-06 11:04:07 +00:00
|
|
|
}
|
|
|
|
|
2016-08-11 19:03:35 +00:00
|
|
|
int DocumentHandler::selectionEnd() const
|
2016-07-06 11:04:07 +00:00
|
|
|
{
|
2016-08-11 19:03:35 +00:00
|
|
|
return m_selectionEnd;
|
2016-07-06 11:04:07 +00:00
|
|
|
}
|
|
|
|
|
2016-08-11 19:03:35 +00:00
|
|
|
void DocumentHandler::setSelectionEnd(int position)
|
2016-07-06 11:04:07 +00:00
|
|
|
{
|
2016-08-11 19:03:35 +00:00
|
|
|
if (position == m_selectionEnd)
|
2016-07-06 11:04:07 +00:00
|
|
|
return;
|
|
|
|
|
2016-08-11 19:03:35 +00:00
|
|
|
m_selectionEnd = position;
|
|
|
|
emit selectionEndChanged();
|
2016-07-06 11:04:07 +00:00
|
|
|
}
|
|
|
|
|
2016-08-11 19:03:35 +00:00
|
|
|
QColor DocumentHandler::textColor() const
|
2016-07-06 11:04:07 +00:00
|
|
|
{
|
|
|
|
QTextCursor cursor = textCursor();
|
2016-08-11 19:03:35 +00:00
|
|
|
if (cursor.isNull())
|
|
|
|
return QColor(Qt::black);
|
|
|
|
QTextCharFormat format = cursor.charFormat();
|
|
|
|
return format.foreground().color();
|
2016-07-06 11:04:07 +00:00
|
|
|
}
|
|
|
|
|
2016-08-11 19:03:35 +00:00
|
|
|
void DocumentHandler::setTextColor(const QColor &color)
|
2016-07-06 11:04:07 +00:00
|
|
|
{
|
2016-08-11 19:03:35 +00:00
|
|
|
QTextCharFormat format;
|
|
|
|
format.setForeground(QBrush(color));
|
|
|
|
mergeFormatOnWordOrSelection(format);
|
|
|
|
emit textColorChanged();
|
2016-07-06 11:04:07 +00:00
|
|
|
}
|
|
|
|
|
2016-08-11 19:03:35 +00:00
|
|
|
Qt::Alignment DocumentHandler::alignment() const
|
2016-07-06 11:04:07 +00:00
|
|
|
{
|
2016-08-11 19:03:35 +00:00
|
|
|
QTextCursor cursor = textCursor();
|
|
|
|
if (cursor.isNull())
|
|
|
|
return Qt::AlignLeft;
|
|
|
|
return textCursor().blockFormat().alignment();
|
2016-07-06 11:04:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void DocumentHandler::setAlignment(Qt::Alignment alignment)
|
|
|
|
{
|
|
|
|
QTextBlockFormat format;
|
|
|
|
format.setAlignment(alignment);
|
|
|
|
QTextCursor cursor = textCursor();
|
|
|
|
cursor.mergeBlockFormat(format);
|
|
|
|
emit alignmentChanged();
|
|
|
|
}
|
|
|
|
|
2016-08-11 20:29:26 +00:00
|
|
|
QString DocumentHandler::fileName() const
|
2016-07-06 11:04:07 +00:00
|
|
|
{
|
2016-08-11 20:29:26 +00:00
|
|
|
const QString filePath = QQmlFile::urlToLocalFileOrQrc(m_fileUrl);
|
|
|
|
const QString fileName = QFileInfo(filePath).fileName();
|
|
|
|
if (fileName.isEmpty())
|
|
|
|
return QStringLiteral("untitled.txt");
|
|
|
|
return fileName;
|
2016-07-06 11:04:07 +00:00
|
|
|
}
|
|
|
|
|
2016-08-11 20:29:26 +00:00
|
|
|
QString DocumentHandler::fileType() const
|
2016-07-06 11:04:07 +00:00
|
|
|
{
|
2016-08-11 20:29:26 +00:00
|
|
|
return QFileInfo(fileName()).suffix();
|
2016-08-11 19:03:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QUrl DocumentHandler::fileUrl() const
|
|
|
|
{
|
|
|
|
return m_fileUrl;
|
|
|
|
}
|
|
|
|
|
2016-08-11 20:41:11 +00:00
|
|
|
void DocumentHandler::load(const QUrl &fileUrl)
|
2016-08-11 19:03:35 +00:00
|
|
|
{
|
|
|
|
if (fileUrl == m_fileUrl)
|
|
|
|
return;
|
|
|
|
|
2016-07-26 12:15:31 +00:00
|
|
|
QQmlEngine *engine = qmlEngine(this);
|
|
|
|
if (!engine) {
|
|
|
|
qWarning() << "load() called before DocumentHandler has QQmlEngine";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const QUrl path = QQmlFileSelector::get(engine)->selector()->select(fileUrl);
|
|
|
|
const QString fileName = QQmlFile::urlToLocalFileOrQrc(path);
|
2016-08-11 19:03:35 +00:00
|
|
|
if (QFile::exists(fileName)) {
|
2020-08-10 10:35:25 +00:00
|
|
|
QMimeType mime = QMimeDatabase().mimeTypeForFile(fileName);
|
2016-08-11 19:03:35 +00:00
|
|
|
QFile file(fileName);
|
|
|
|
if (file.open(QFile::ReadOnly)) {
|
2020-06-17 11:29:17 +00:00
|
|
|
QByteArray data = file.readAll();
|
2020-08-10 10:35:25 +00:00
|
|
|
if (QTextDocument *doc = textDocument()) {
|
|
|
|
doc->setBaseUrl(path.adjusted(QUrl::RemoveFilename));
|
|
|
|
doc->setModified(false);
|
|
|
|
if (mime.inherits("text/markdown")) {
|
|
|
|
emit loaded(QString::fromUtf8(data), Qt::MarkdownText);
|
|
|
|
} else {
|
2020-10-06 14:17:16 +00:00
|
|
|
auto encoding = QStringConverter::encodingForHtml(data);
|
2020-08-10 10:35:25 +00:00
|
|
|
if (encoding) {
|
|
|
|
QStringDecoder decoder(*encoding);
|
|
|
|
emit loaded(decoder(data), Qt::AutoText);
|
|
|
|
} else {
|
|
|
|
// fall back to utf8
|
|
|
|
emit loaded(QString::fromUtf8(data), Qt::AutoText);
|
|
|
|
}
|
|
|
|
}
|
2020-06-17 11:29:17 +00:00
|
|
|
}
|
|
|
|
|
2016-08-11 19:03:35 +00:00
|
|
|
reset();
|
|
|
|
}
|
|
|
|
}
|
2016-08-11 20:29:26 +00:00
|
|
|
|
|
|
|
m_fileUrl = fileUrl;
|
2016-08-11 19:03:35 +00:00
|
|
|
emit fileUrlChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
void DocumentHandler::saveAs(const QUrl &fileUrl)
|
|
|
|
{
|
2016-08-11 19:21:47 +00:00
|
|
|
QTextDocument *doc = textDocument();
|
|
|
|
if (!doc)
|
2016-08-11 19:03:35 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
const QString filePath = fileUrl.toLocalFile();
|
|
|
|
const bool isHtml = QFileInfo(filePath).suffix().contains(QLatin1String("htm"));
|
|
|
|
QFile file(filePath);
|
|
|
|
if (!file.open(QFile::WriteOnly | QFile::Truncate | (isHtml ? QFile::NotOpen : QFile::Text))) {
|
|
|
|
emit error(tr("Cannot save: ") + file.errorString());
|
|
|
|
return;
|
|
|
|
}
|
2016-08-11 19:21:47 +00:00
|
|
|
file.write((isHtml ? doc->toHtml() : doc->toPlainText()).toUtf8());
|
2016-08-11 19:03:35 +00:00
|
|
|
file.close();
|
2016-08-11 20:29:26 +00:00
|
|
|
|
|
|
|
if (fileUrl == m_fileUrl)
|
|
|
|
return;
|
|
|
|
|
|
|
|
m_fileUrl = fileUrl;
|
|
|
|
emit fileUrlChanged();
|
2016-08-11 19:03:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void DocumentHandler::reset()
|
|
|
|
{
|
|
|
|
emit alignmentChanged();
|
|
|
|
emit textColorChanged();
|
2021-09-23 15:45:44 +00:00
|
|
|
emit fontChanged();
|
2016-08-11 19:03:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QTextCursor DocumentHandler::textCursor() const
|
|
|
|
{
|
2016-08-11 19:21:47 +00:00
|
|
|
QTextDocument *doc = textDocument();
|
|
|
|
if (!doc)
|
2016-08-11 19:03:35 +00:00
|
|
|
return QTextCursor();
|
|
|
|
|
2016-08-11 19:21:47 +00:00
|
|
|
QTextCursor cursor = QTextCursor(doc);
|
2016-08-11 19:03:35 +00:00
|
|
|
if (m_selectionStart != m_selectionEnd) {
|
|
|
|
cursor.setPosition(m_selectionStart);
|
|
|
|
cursor.setPosition(m_selectionEnd, QTextCursor::KeepAnchor);
|
|
|
|
} else {
|
|
|
|
cursor.setPosition(m_cursorPosition);
|
|
|
|
}
|
|
|
|
return cursor;
|
|
|
|
}
|
|
|
|
|
2016-08-11 19:21:47 +00:00
|
|
|
QTextDocument *DocumentHandler::textDocument() const
|
|
|
|
{
|
|
|
|
if (!m_document)
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
return m_document->textDocument();
|
|
|
|
}
|
|
|
|
|
2016-08-11 19:03:35 +00:00
|
|
|
void DocumentHandler::mergeFormatOnWordOrSelection(const QTextCharFormat &format)
|
|
|
|
{
|
|
|
|
QTextCursor cursor = textCursor();
|
|
|
|
if (!cursor.hasSelection())
|
|
|
|
cursor.select(QTextCursor::WordUnderCursor);
|
|
|
|
cursor.mergeCharFormat(format);
|
2016-07-06 11:04:07 +00:00
|
|
|
}
|
2019-08-19 14:01:05 +00:00
|
|
|
|
|
|
|
bool DocumentHandler::modified() const
|
|
|
|
{
|
|
|
|
return m_document && m_document->textDocument()->isModified();
|
|
|
|
}
|
|
|
|
|
|
|
|
void DocumentHandler::setModified(bool m)
|
|
|
|
{
|
|
|
|
if (m_document)
|
|
|
|
m_document->textDocument()->setModified(m);
|
|
|
|
}
|
2020-03-10 09:40:47 +00:00
|
|
|
|
2021-09-23 15:45:44 +00:00
|
|
|
QFont DocumentHandler::font() const
|
|
|
|
{
|
|
|
|
QTextCursor cursor = textCursor();
|
|
|
|
if (cursor.isNull())
|
|
|
|
return m_document->textDocument()->defaultFont();
|
|
|
|
QTextCharFormat format = cursor.charFormat();
|
|
|
|
return format.font();
|
|
|
|
}
|
|
|
|
|
|
|
|
void DocumentHandler::setFont(const QFont & font){
|
|
|
|
|
|
|
|
QTextCursor cursor = textCursor();
|
|
|
|
if (!cursor.isNull() && cursor.charFormat().font() == font)
|
|
|
|
return;
|
|
|
|
|
|
|
|
QTextCharFormat format;
|
|
|
|
format.setFont(font);
|
|
|
|
mergeFormatOnWordOrSelection(format);
|
|
|
|
|
|
|
|
emit fontChanged();
|
|
|
|
}
|
|
|
|
|
2020-03-10 09:40:47 +00:00
|
|
|
#include "moc_documenthandler.cpp"
|