diff --git a/src/controls/Editor.qml b/src/controls/Editor.qml index 0f751bb..dbca87e 100644 --- a/src/controls/Editor.qml +++ b/src/controls/Editor.qml @@ -1,343 +1,343 @@ import QtQuick 2.10 import QtQuick.Controls 2.10 import QtQuick.Layouts 1.3 import org.kde.mauikit 1.0 as Maui import org.kde.kirigami 2.7 as Kirigami import "private" Maui.Page { id: control Kirigami.Theme.inherit: false Kirigami.Theme.colorSet: Kirigami.Theme.View property bool showLineCount : true property bool showSyntaxHighlighting: true property alias body : body property alias document : document property alias scrollView: _scrollView property alias text: body.text property alias uppercase: document.uppercase property alias underline: document.underline property alias italic: document.italic property alias bold: document.bold property alias canRedo: body.canRedo property alias fileUrl : document.fileUrl focus: true Maui.DocumentHandler { id: document document: body.textDocument cursorPosition: body.cursorPosition selectionStart: body.selectionStart selectionEnd: body.selectionEnd - textColor: control.Kirigami.Theme.textColor +// textColor: control.Kirigami.Theme.textColor backgroundColor: control.Kirigami.Theme.backgroundColor - +// onError: { body.text = message body.visible = true } } Row { z: _scrollView.z +1 visible: showLineCount anchors { right: parent.right bottom: parent.bottom margins: Maui.Style.space.big } width: implicitWidth height: implicitHeight Label { text: body.length + " / " + body.lineCount color: control.Kirigami.Theme.textColor opacity: 0.5 font.pointSize: Maui.Style.fontSizes.medium } } Menu { id: documentMenu z: 999 MenuItem { text: qsTr("Copy") onTriggered: body.copy() enabled: body.selectedText.length } MenuItem { text: qsTr("Cut") onTriggered: body.cut() enabled: !body.readOnly && body.selectedText.length } MenuItem { text: qsTr("Paste") onTriggered: body.paste() enabled: !body.readOnly } MenuItem { text: qsTr("Select all") onTriggered: body.selectAll() } MenuItem { text: qsTr("Web search") onTriggered: Qt.openUrlExternally("https://www.google.com/search?q="+body.selectedText) enabled: body.selectedText.length } } headBar.visible: !body.readOnly headBar.leftContent: [ ToolButton { icon.name: "edit-undo" enabled: body.canUndo onClicked: body.undo() opacity: enabled ? 1 : 0.5 }, ToolButton { icon.name: "edit-redo" enabled: body.canRedo onClicked: body.redo() opacity: enabled ? 1 : 0.5 }, Row { id: _editingActions visible: document.isRich && !body.readOnly ToolButton { icon.name: "format-text-bold" focusPolicy: Qt.TabFocus icon.color: checked ? control.Kirigami.Theme.highlightColor : control.Kirigami.Theme.textColor checkable: false checked: document.bold onClicked: document.bold = !document.bold } ToolButton { icon.name: "format-text-italic" icon.color: checked ? control.Kirigami.Theme.highlightColor : control.Kirigami.Theme.textColor focusPolicy: Qt.TabFocus checkable: false checked: document.italic onClicked: document.italic = !document.italic } ToolButton { icon.name: "format-text-underline" icon.color: checked ? control.Kirigami.Theme.highlightColor : control.Kirigami.Theme.textColor focusPolicy: Qt.TabFocus checkable: true checked: document.underline onClicked: document.underline = !document.underline } ToolButton { icon.name: "format-text-uppercase" icon.color: checked ? control.Kirigami.Theme.highlightColor : control.Kirigami.Theme.textColor focusPolicy: Qt.TabFocus checkable: true checked: document.uppercase onClicked: document.uppercase = !document.uppercase } } ] // footBar.visible: !body.readOnly footBar.rightContent: [ ToolButton { icon.name: "zoom-in" onClicked: zoomIn() }, ToolButton { icon.name: "zoom-out" onClicked: zoomOut() }, ComboBox { visible: control.showSyntaxHighlighting - id: languagesListComboBox model: document.getLanguageNameList() - onCurrentIndexChanged: document.formatName = languagesListComboBox.model[currentIndex] + currentIndex: -1 + onCurrentIndexChanged: document.formatName = model[currentIndex] } ] ColumnLayout { anchors.fill: parent spacing: 0 Repeater { model: document.alerts Maui.ToolBar { id: _alertBar property var alert : model.alert readonly property int index_ : index Layout.fillWidth: true Kirigami.Theme.backgroundColor: { switch(alert.level) { case 0: return Kirigami.Theme.positiveTextColor case 1: return Kirigami.Theme.neutralTextColor case 2: return Kirigami.Theme.negativeTextColor } } leftContent: Maui.ListItemTemplate { Layout.fillWidth: true Layout.fillHeight: true label1.text: alert.title label2.text: alert.body } rightContent: Repeater { model: alert.actionLabels() Button { id: _alertAction property int index_ : index text: modelData onClicked: alert.triggerAction(_alertAction.index_, _alertBar.index_) Kirigami.Theme.backgroundColor: Qt.rgba(Kirigami.Theme.backgroundColor.r, Kirigami.Theme.backgroundColor.g, Kirigami.Theme.backgroundColor.b, 0.2) Kirigami.Theme.textColor: Kirigami.Theme.textColor } } } } PinchArea { id: pinchArea Layout.fillWidth: true Layout.fillHeight: true // enabled: Maui.Handy.hasTouch property real minScale: 1.0 property real maxScale: 3.0 // anchors.fill: parent pinch.minimumScale: minScale pinch.maximumScale: maxScale pinch.dragAxis: Pinch.XandYAxis onPinchFinished: { console.log("pinch.scale", pinch.scale) if(pinch.scale > 1.5) control.zoomIn() else control.zoomOut() } MouseArea{ anchors.fill: parent} Kirigami.ScrollablePage { id: _scrollView focus: true anchors.fill: parent - contentWidth: width + contentWidth: control.width contentHeight: body.height leftPadding: 0 rightPadding: 0 topPadding: 0 bottomPadding: 0 TextArea { id: body - width: parent.width + implicitWidth: control.width text: document.text - font.family: "Source Code Pro" +// font.family: "Source Code Pro" placeholderText: qsTr("Body") selectByKeyboard: !Kirigami.Settings.isMobile selectByMouse : !Kirigami.Settings.isMobile textFormat: TextEdit.AutoText - font.pointSize: Maui.Style.fontSizes.large +// font.pointSize: Maui.Style.fontSizes.large wrapMode: TextEdit.WrapAnywhere activeFocusOnPress: true activeFocusOnTab: true persistentSelection: true background: Rectangle { color: document.backgroundColor implicitWidth: body.implicitWidth implicitHeight: control.height } onPressed: { if(!Kirigami.Settings.isMobile && event.button === Qt.RightButton) documentMenu.popup() } } } // ScrollBar.vertical.height: _scrollView.height - body.topPadding // ScrollBar.vertical.y: body.topPadding } } function zoomIn() { body.font.pointSize = body.font.pointSize *1.5 } function zoomOut() { body.font.pointSize = body.font.pointSize / 1.5 } } diff --git a/src/utils/editor/documenthandler.cpp b/src/utils/editor/documenthandler.cpp index e79f769..7db619a 100644 --- a/src/utils/editor/documenthandler.cpp +++ b/src/utils/editor/documenthandler.cpp @@ -1,754 +1,738 @@ /**************************************************************************** * * ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the examples of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:BSD$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** BSD License Usage ** Alternatively, you may use this file under the terms of the BSD license ** as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of The Qt Company Ltd nor the names of its ** contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "documenthandler.h" #include #include #include #include #include #include #include #include #include #include #include #include +#include #include "fmh.h" #include "utils.h" #ifdef STATIC_MAUIKIT #include #include #include #include #else #include #include #include #include #endif /** * Global Variables */ KSyntaxHighlighting::Repository *DocumentHandler::m_repository = nullptr; int DocumentHandler::m_instanceCount = 0; Alerts::Alerts(QObject* parent) : QAbstractListModel(parent) {} Alerts::~Alerts() { qDebug()<< "REMOVING ALL DOCUMENTS ALERTS" << this->m_alerts.size(); for(auto *alert : this->m_alerts) { delete alert; alert = nullptr; } } QVariant Alerts::data(const QModelIndex& index, int role) const { if(role == ROLES::ALERT) return QVariant::fromValue(this->m_alerts.at(index.row())); return QVariant(); } int Alerts::rowCount(const QModelIndex& parent) const { if (parent.isValid()) return 0; return this->m_alerts.count(); } QHash Alerts::roleNames() const { return {{ROLES::ALERT, "alert"}}; } bool Alerts::contains(DocumentAlert* const alert) { for(const auto &alert_ : this->m_alerts) { if(alert_->getId() == alert->getId()) return true; } return false; } void Alerts::append(DocumentAlert *alert) { if(this->contains(alert)) return; const auto index = this->rowCount(); beginInsertRows(QModelIndex(), index, index); // watch out for when the alert is done: such as when an action is triggered connect(alert, &DocumentAlert::done, [&](int index) { this->beginRemoveRows(QModelIndex(), index, index); auto item = this->m_alerts.takeAt(index); if(item) { item->deleteLater(); item = nullptr; } this->endRemoveRows(); }); alert->setIndex(index); this->m_alerts << alert; endInsertRows(); } void FileLoader::loadFile(const QUrl& url) { if (FMH::fileExists(url)) { QFile file(url.toLocalFile()); if (file.open(QFile::ReadOnly)) { - const auto array = file.readAll(); - - const bool isHtml = QFileInfo(url.toLocalFile()).suffix().contains(QLatin1String("htm")); - QTextCodec *codec = isHtml ? QTextCodec::codecForHtml(array) : QTextCodec::codecForUtfText(array, QTextCodec::codecForLocale()); - + const auto array = file.readAll(); + QTextCodec *codec = QTextDocumentWriter(url.toLocalFile()).codec(); emit this->fileReady(codec->toUnicode(array), url); } } } DocumentAlert * DocumentHandler::externallyModifiedAlert() { auto alert = new DocumentAlert(tr("File changed externally"), tr("You can reload the file or save your changes now"), DocumentAlert::WARNING_LEVEL, Alerts::MODIFIED); const auto reloadAction = [&]() { emit this->loadFile(this->fileUrl()); }; const auto autoReloadAction = [&]() { this->setAutoReload(true); emit this->loadFile(this->fileUrl()); }; alert->setActions({{tr("Reload"), reloadAction}, {tr("Auto Reload"), autoReloadAction}, {tr("Ignore"), [&](){}}}); return alert; } DocumentAlert * DocumentHandler::canNotSaveAlert(const QString &details) { auto alert = new DocumentAlert(tr("File can not be saved"), details, DocumentAlert::DANGER_LEVEL, Alerts::SAVE_ERROR); alert->setActions({{tr("Ignore"), [&](){}}}); return alert; } DocumentAlert * DocumentHandler::missingAlert() { auto alert = new DocumentAlert(tr("Your file was removed"), tr("This file does not longer exists in your local storage, however you can save it again"), DocumentAlert::DANGER_LEVEL, Alerts::MISSING); const auto saveAction = [&]() { this->saveAs(this->fileUrl()); }; alert->setActions({{tr("Save"), saveAction}}); return alert; } DocumentHandler::DocumentHandler(QObject *parent) : QObject(parent) , m_document(nullptr) , m_watcher(new QFileSystemWatcher(this)) , m_cursorPosition(-1) , m_selectionStart(0) , m_selectionEnd(0) , m_highlighter(new KSyntaxHighlighting::SyntaxHighlighter(this)) , m_alerts(new Alerts(this)) { ++m_instanceCount; //start file loader thread implementation { FileLoader *m_loader = new FileLoader; m_loader->moveToThread(&m_worker); connect(&m_worker, &QThread::finished, m_loader, &QObject::deleteLater); connect(this, &DocumentHandler::loadFile, m_loader, &FileLoader::loadFile); connect(m_loader, &FileLoader::fileReady, [&](QString array, QUrl url) - { - this->isRich = QFileInfo(url.toLocalFile()).suffix().contains(QLatin1String("rtf"));// - emit this->isRichChanged(); + { + this->setFormatName(DocumentHandler::getLanguageNameFromFileName(url)); + this->setText(array); - this->setText(array); - if (QTextDocument *doc = this->textDocument()) - doc->setModified(false); - - this->setFormatName(DocumentHandler::getLanguageNameFromFileName(url)); + if (this->textDocument()) + this->textDocument()->setModified(false); emit this->loaded(url); reset(); }); m_worker.start(); } //end file loader thread implementation connect(this->m_watcher, &QFileSystemWatcher::fileChanged, [&](QString url) { if(this->fileUrl() == QUrl::fromLocalFile(url)) { //THE FILE WAS REMOVED if(!FMH::fileExists(this->fileUrl())) { this->m_alerts->append(DocumentHandler::missingAlert()); return; } //THE FILE CHANGED BUT STILL EXISTS LOCALLY if(m_internallyModified) { m_internallyModified = false; return; } this->setExternallyModified(true); if(!this->m_autoReload) { this->m_alerts->append(DocumentHandler::externallyModifiedAlert()); return; } emit this->loadFile(this->fileUrl()); } }); } DocumentHandler::~DocumentHandler() { this->m_worker.quit(); this->m_worker.wait(); --DocumentHandler::m_instanceCount; if (!DocumentHandler::m_instanceCount) { delete DocumentHandler::m_repository; DocumentHandler::m_repository = nullptr; } } void DocumentHandler::setText(const QString &text) { if (text != this->m_text) { this->m_text = text; emit textChanged(); } } bool DocumentHandler::getAutoReload() const { return this->m_autoReload; } void DocumentHandler::setAutoReload(const bool& value) { if(value == this->m_autoReload) return; this->m_autoReload = value; emit this->autoReloadChanged(); } bool DocumentHandler::getModified() const { if (auto doc = this->textDocument()) return doc->isModified(); return false; } bool DocumentHandler::getExternallyModified() const { return this->m_externallyModified; } void DocumentHandler::setExternallyModified(const bool& value) { if(value == this->m_externallyModified) return; this->m_externallyModified = value; emit this->externallyModifiedChanged(); } void DocumentHandler::setStyle() { if (!DocumentHandler::m_repository) DocumentHandler::m_repository = new KSyntaxHighlighting::Repository(); - const auto isDark = UTIL::isDark(this->m_backgroundColor); - + const auto isDark = UTIL::isDark(this->m_backgroundColor); const auto style = DocumentHandler::m_repository->defaultTheme(isDark ?KSyntaxHighlighting::Repository::DarkTheme : KSyntaxHighlighting::Repository::LightTheme); - this->m_highlighter->setTheme(style); + this->m_highlighter->setTheme(style); - this->m_highlighter->setDefinition(DocumentHandler::m_repository->definitionForName(this->m_formatName)); + this->m_highlighter->setDefinition(m_repository->definitionForName( this->m_formatName)); } QString DocumentHandler::formatName() const { return this->m_formatName; } void DocumentHandler::setFormatName(const QString& formatName) { - if (this->m_formatName != formatName) - { - this->m_formatName = formatName; - emit this->formatNameChanged(); - - this->setStyle(); - } + if (this->m_formatName == formatName) + return; + + this->m_formatName = formatName; + emit this->formatNameChanged(); + this->setStyle(); } QColor DocumentHandler::getBackgroundColor() const { return this->m_backgroundColor; } void DocumentHandler::setBackgroundColor(const QColor& color) { if(this->m_backgroundColor == color) return; this->m_backgroundColor = color; emit this->backgroundColorChanged(); - this->setStyle(); + if (!DocumentHandler::m_repository) + DocumentHandler::m_repository = new KSyntaxHighlighting::Repository(); } Alerts *DocumentHandler::getAlerts() const { return this->m_alerts; } QQuickTextDocument *DocumentHandler::document() const { return m_document; } void DocumentHandler::setDocument(QQuickTextDocument *document) { - if (document == m_document) - return; - - m_document = document; + this->m_document = document; emit documentChanged(); - if(auto doc = this->textDocument()) + if(this->textDocument()) { - doc->setModified(false); - m_highlighter->setDocument(doc); - connect(doc, &QTextDocument::modificationChanged, this, &DocumentHandler::modifiedChanged); + this->textDocument()->setModified(false); + m_highlighter->setDocument(this->textDocument()); + connect(this->textDocument(), &QTextDocument::modificationChanged, this, &DocumentHandler::modifiedChanged); } } int DocumentHandler::cursorPosition() const { return m_cursorPosition; } void DocumentHandler::setCursorPosition(int position) { if (position == m_cursorPosition) return; m_cursorPosition = position; reset(); emit cursorPositionChanged(); } int DocumentHandler::selectionStart() const { return m_selectionStart; } void DocumentHandler::setSelectionStart(int position) { if (position == m_selectionStart) return; m_selectionStart = position; emit selectionStartChanged(); } int DocumentHandler::selectionEnd() const { return m_selectionEnd; } void DocumentHandler::setSelectionEnd(int position) { if (position == m_selectionEnd) return; m_selectionEnd = position; emit selectionEndChanged(); } QString DocumentHandler::fontFamily() const { QTextCursor cursor = textCursor(); if (cursor.isNull()) return QString(); QTextCharFormat format = cursor.charFormat(); return format.font().family(); } void DocumentHandler::setFontFamily(const QString &family) { QTextCharFormat format; format.setFontFamily(family); mergeFormatOnWordOrSelection(format); emit fontFamilyChanged(); } QColor DocumentHandler::textColor() const { QTextCursor cursor = textCursor(); if (cursor.isNull()) return QColor(Qt::black); QTextCharFormat format = cursor.charFormat(); return format.foreground().color(); } void DocumentHandler::setTextColor(const QColor &color) { QTextCharFormat format; format.setForeground(QBrush(color)); mergeFormatOnWordOrSelection(format); emit textColorChanged(); } Qt::Alignment DocumentHandler::alignment() const { QTextCursor cursor = textCursor(); if (cursor.isNull()) return Qt::AlignLeft; return textCursor().blockFormat().alignment(); } void DocumentHandler::setAlignment(Qt::Alignment alignment) { QTextBlockFormat format; format.setAlignment(alignment); QTextCursor cursor = textCursor(); cursor.mergeBlockFormat(format); emit alignmentChanged(); } bool DocumentHandler::bold() const { QTextCursor cursor = textCursor(); if (cursor.isNull()) return false; return textCursor().charFormat().fontWeight() == QFont::Bold; } void DocumentHandler::setBold(bool bold) { QTextCharFormat format; format.setFontWeight(bold ? QFont::Bold : QFont::Normal); mergeFormatOnWordOrSelection(format); emit boldChanged(); } bool DocumentHandler::uppercase() const { QTextCursor cursor = textCursor(); if (cursor.isNull()) return false; return textCursor().charFormat().fontCapitalization() == QFont::AllUppercase; } void DocumentHandler::setUppercase(bool uppercase) { QTextCharFormat format; format.setFontCapitalization(uppercase ? QFont::AllUppercase : QFont::AllLowercase); mergeFormatOnWordOrSelection(format); emit uppercaseChanged(); } bool DocumentHandler::italic() const { QTextCursor cursor = textCursor(); if (cursor.isNull()) return false; return textCursor().charFormat().fontItalic(); } void DocumentHandler::setItalic(bool italic) { QTextCharFormat format; format.setFontItalic(italic); mergeFormatOnWordOrSelection(format); emit italicChanged(); } bool DocumentHandler::underline() const { QTextCursor cursor = textCursor(); if (cursor.isNull()) return false; return textCursor().charFormat().fontUnderline(); } void DocumentHandler::setUnderline(bool underline) { QTextCharFormat format; format.setFontUnderline(underline); mergeFormatOnWordOrSelection(format); emit underlineChanged(); } bool DocumentHandler::getIsRich() const { return this->isRich; } int DocumentHandler::fontSize() const { QTextCursor cursor = textCursor(); if (cursor.isNull()) return 0; QTextCharFormat format = cursor.charFormat(); return format.font().pointSize(); } void DocumentHandler::setFontSize(int size) { if (size <= 0) return; QTextCursor cursor = textCursor(); if (cursor.isNull()) return; if (!cursor.hasSelection()) cursor.select(QTextCursor::WordUnderCursor); if (cursor.charFormat().property(QTextFormat::FontPointSize).toInt() == size) return; QTextCharFormat format; format.setFontPointSize(size); mergeFormatOnWordOrSelection(format); emit fontSizeChanged(); } QString DocumentHandler::fileName() const { const QString filePath = QQmlFile::urlToLocalFileOrQrc(m_fileUrl); const QString fileName = QFileInfo(filePath).fileName(); if (fileName.isEmpty()) return QStringLiteral("untitled.txt"); return fileName; } QString DocumentHandler::fileType() const { return QFileInfo(fileName()).suffix(); } QUrl DocumentHandler::fileUrl() const { return m_fileUrl; } void DocumentHandler::setFileUrl(const QUrl& url) { this->load(url); } void DocumentHandler::load(const QUrl &url) { qDebug()<< "TRYING TO LOAD FILE << " << url << url.isEmpty(); if (url == m_fileUrl) return; m_fileUrl = url; emit fileUrlChanged(); if(m_fileUrl.isLocalFile() && !FMH::fileExists(m_fileUrl)) return; QQmlEngine *engine = qmlEngine(this); if (!engine) { qWarning() << "load() called before DocumentHandler has QQmlEngine"; return; } this->m_watcher->removePaths(this->m_watcher->files()); this->m_watcher->addPath(m_fileUrl.toLocalFile()); emit this->loadFile(m_fileUrl); } void DocumentHandler::saveAs(const QUrl &url) { if(url.isEmpty() || !url.isValid()) return; QTextDocument *doc = this->textDocument(); if (!doc) + return; + + this->m_internallyModified = true; + + QTextDocumentWriter textWriter(url.toLocalFile()); + if(!textWriter.write(this->textDocument())) + { + emit error(tr("Cannot save file ")+ url.toString()); + this->m_alerts->append(this->canNotSaveAlert(tr("Cannot save file ")+ url.toString())); return; + } - const bool isHtml = QFileInfo(url.toLocalFile()).suffix().contains(QLatin1String("html")); + doc->setModified(false); - this->m_internallyModified = true; + if (url == m_fileUrl) + return; - QFile file(url.toLocalFile()); - if (!file.open(QFile::WriteOnly | QFile::Truncate | (isHtml ? QFile::NotOpen : QFile::Text))) - { - emit error(tr("Cannot save: ") + file.errorString()); - this->m_alerts->append(this->canNotSaveAlert(file.errorString())); - }else - { - QTextStream out(&file); - out.setCodec("UTF-8"); - out << (isHtml ? doc->toHtml() : doc->toPlainText()).toUtf8(); - - file.close(); - doc->setModified(false); - - if (url == m_fileUrl) - return; - - m_fileUrl = url; - emit fileUrlChanged(); - } + m_fileUrl = url; + emit fileUrlChanged(); } const QString DocumentHandler::getLanguageNameFromFileName(const QUrl& fileName) { if (!DocumentHandler::m_repository) DocumentHandler::m_repository = new KSyntaxHighlighting::Repository(); const auto res = DocumentHandler::m_repository->definitionForFileName(fileName.toString()); return res.isValid() ? res.name() : QString(); } const QStringList DocumentHandler::getLanguageNameList() { if (!DocumentHandler::m_repository) m_repository = new KSyntaxHighlighting::Repository(); const auto definitions = DocumentHandler::m_repository->definitions(); return std::accumulate(definitions.constBegin(), definitions.constEnd(), QStringList(), [](QStringList &languages, const auto &definition ) -> QStringList { languages.append(definition.name()); return languages; }); } void DocumentHandler::reset() { emit fontFamilyChanged(); emit alignmentChanged(); emit boldChanged(); emit italicChanged(); emit underlineChanged(); emit fontSizeChanged(); emit textColorChanged(); } QTextCursor DocumentHandler::textCursor() const { QTextDocument *doc = textDocument(); if (!doc) return QTextCursor(); QTextCursor cursor = QTextCursor(doc); if (m_selectionStart != m_selectionEnd) { cursor.setPosition(m_selectionStart); cursor.setPosition(m_selectionEnd, QTextCursor::KeepAnchor); } else { cursor.setPosition(m_cursorPosition); } return cursor; } QTextDocument *DocumentHandler::textDocument() const { if (!m_document) return nullptr; return m_document->textDocument(); } void DocumentHandler::mergeFormatOnWordOrSelection(const QTextCharFormat &format) { QTextCursor cursor = textCursor(); if (!cursor.hasSelection()) cursor.select(QTextCursor::WordUnderCursor); cursor.mergeCharFormat(format); } diff --git a/src/utils/fmh.h b/src/utils/fmh.h index 41e357d..6f3f3ab 100644 --- a/src/utils/fmh.h +++ b/src/utils/fmh.h @@ -1,1112 +1,1112 @@ /* * Copyright 2018 Camilo Higuita * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef FMH_H #define FMH_H #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(Q_OS_ANDROID) #include "mauiandroid.h" #elif defined(Q_OS_LINUX) #include #include #include #include #endif // #ifdef COMPONENT_TAGGING // #include "tagging.h" // #endif namespace FMH { static constexpr bool isAndroid() { #if defined(Q_OS_ANDROID) return true; #elif defined(Q_OS_LINUX) return false; #elif defined(Q_OS_WIN32) return false; #elif defined(Q_OS_WIN64) return false; #elif defined(Q_OS_MACOS) return false; #elif defined(Q_OS_IOS) return false; #elif defined(Q_OS_HAIKU) return false; #endif } static constexpr bool isWindows() { #if defined(Q_OS_ANDROID) return false; #elif defined(Q_OS_LINUX) return false; #elif defined(Q_OS_WIN32) return true; #elif defined(Q_OS_WIN64) return true; #elif defined(Q_OS_MACOS) return false; #elif defined(Q_OS_IOS) return false; #elif defined(Q_OS_HAIKU) return false; #endif } static constexpr bool isLinux() { #if defined(Q_OS_ANDROID) return false; #elif defined(Q_OS_LINUX) return true; #elif defined(Q_OS_WIN32) return false; #elif defined(Q_OS_WIN64) return false; #elif defined(Q_OS_MACOS) return false; #elif defined(Q_OS_IOS) return false; #elif defined(Q_OS_HAIKU) return false; #endif } static constexpr bool isMac() { #if defined(Q_OS_ANDROID) return false; #elif defined(Q_OS_LINUX) return false; #elif defined(Q_OS_WIN32) return false; #elif defined(Q_OS_WIN64) return false; #elif defined(Q_OS_MACOS) return true; #elif defined(Q_OS_IOS) return false; #elif defined(Q_OS_HAIKU) return false; #endif } enum FILTER_TYPE : int { AUDIO, VIDEO, TEXT, IMAGE, DOCUMENT, NONE }; static QStringList AUDIO_MIMETYPES = {"audio/mpeg","audio/mp4","audio/flac","audio/ogg","audio/wav"}; static QStringList VIDEO_MIMETYPES = {"video/mp4", "video/x-matroska","video/webm","video/avi","video/flv","video/mpg", "video/wmv","video/mov","video/ogg","video/mpeg", "video/jpeg"}; - static QStringList TEXT_MIMETYPES = {"text/x-chdr", "text/x-c++src", "text/x-c++hdr", "text/css", "text/html", "text/plain", "text/richtext", "text/scriptlet", "text/x-vcard", "text/x-go", "text/x-cmake", "text/x-qml", "application/xml", "application/javascript", "application/json", "application/pgp-keys", "application/x-shellscript", "application/x-kicad-project"}; + static QStringList TEXT_MIMETYPES = {"text/markdown","text/x-chdr", "text/x-c++src", "text/x-c++hdr", "text/css", "text/html", "text/plain", "text/richtext", "text/scriptlet", "text/x-vcard", "text/x-go", "text/x-cmake", "text/x-qml", "application/xml", "application/javascript", "application/json", "application/pgp-keys", "application/x-shellscript", "application/x-kicad-project"}; static QStringList IMAGE_MIMETYPES = {"image/webp" , "image/png" , "image/gif" , "image/jpeg" , "image/web" , "image/svg" , "image/svg+xml"}; static QStringList DOCUMENT_MIMETYPES = {"application/pdf","application/rtf","application/doc","application/odf"}; static QMap SUPPORTED_MIMETYPES { {FMH::FILTER_TYPE::AUDIO, AUDIO_MIMETYPES}, {FMH::FILTER_TYPE::VIDEO, VIDEO_MIMETYPES}, {FMH::FILTER_TYPE::TEXT, TEXT_MIMETYPES}, {FMH::FILTER_TYPE::IMAGE, IMAGE_MIMETYPES}, {FMH::FILTER_TYPE::DOCUMENT, DOCUMENT_MIMETYPES} }; static const QStringList getMimeTypeSuffixes(const FMH::FILTER_TYPE &type, QString(*cb)(QString) = nullptr) { QStringList res; QMimeDatabase mimedb; for(const auto &mime : FMH::SUPPORTED_MIMETYPES[type]) { if(cb) for(const QString &_suffix : mimedb.mimeTypeForName(mime).suffixes()) res << cb(_suffix); else res << mimedb.mimeTypeForName(mime).suffixes(); } return res; } static const QHash FILTER_LIST = { {FILTER_TYPE::AUDIO, FMH::getMimeTypeSuffixes(FMH::FILTER_TYPE::AUDIO, [](QString suffix) -> QString {return "*."+suffix;})}, {FILTER_TYPE::VIDEO, FMH::getMimeTypeSuffixes(FMH::FILTER_TYPE::VIDEO, [](QString suffix)-> QString{return "*."+suffix;})}, {FILTER_TYPE::TEXT, FMH::getMimeTypeSuffixes(FMH::FILTER_TYPE::TEXT, [](QString suffix)-> QString{return "*."+suffix;})}, {FILTER_TYPE::DOCUMENT, FMH::getMimeTypeSuffixes(FMH::FILTER_TYPE::DOCUMENT, [](QString suffix)-> QString{return "*."+suffix;})}, {FILTER_TYPE::IMAGE, FMH::getMimeTypeSuffixes(FMH::FILTER_TYPE::IMAGE, [](QString suffix)-> QString{return "*."+suffix;})}, {FILTER_TYPE::NONE, QStringList()} }; enum MODEL_KEY : int { ICON, LABEL, PATH, URL, TYPE, GROUP, OWNER, SUFFIX, NAME, DATE, SIZE, MODIFIED, MIME, TAG, PERMISSIONS, THUMBNAIL, THUMBNAIL_1, THUMBNAIL_2, THUMBNAIL_3, HIDDEN, ICONSIZE, DETAILVIEW, SHOWTHUMBNAIL, SHOWTERMINAL, COUNT, SORTBY, USER, PASSWORD, SERVER, FOLDERSFIRST, VIEWTYPE, ADDDATE, FAV, FAVORITE, COLOR, RATE, FORMAT, PLACE, LOCATION, ALBUM, ARTIST, TRACK, DURATION, ARTWORK, PLAYLIST, LYRICS, WIKI, MOOD, SOURCETYPE, GENRE, NOTE, COMMENT, CONTEXT, SOURCE, TITLE, ID, PARENT_ID, RELEASEDATE, LICENSE, DESCRIPTION, BOOKMARK, ACCOUNT, ACCOUNTTYPE, VERSION, DOMAIN_M, CATEGORY, CONTENT, PIN, IMG, PREVIEW, LINK, STAMP, BOOK, /** ccdav keys **/ N, PHOTO, GENDER, ADR, ADR_2, ADR_3, EMAIL, EMAIL_2, EMAIL_3, LANG, NICKNAME, ORG, PROFILE, TZ, TEL, TEL_2, TEL_3, IM, /** other keys **/ CITY, STATE, COUNTRY, /** keys from opendesktop store **/ PACKAGE_ARCH, PACKAGE_TYPE, GPG_FINGERPRINT, GPG_SIGNATURE, PACKAGE_NAME, PRICE, REPOSITORY, TAGS, WAY, PIC, SMALL_PIC, CHANGED, COMMENTS, CREATED, DETAIL_PAGE, DETAILS, TOTAL_DOWNLOADS, GHNS_EXCLUDED, LANGUAGE, PERSON_ID, SCORE, SUMMARY, TYPE_ID, TYPE_NAME, XDG_TYPE, //file props SYMLINK, IS_SYMLINK, IS_DIR, IS_FILE, IS_REMOTE, EXECUTABLE, READABLE, WRITABLE, LAST_READ, }; static const QHash MODEL_NAME = { {MODEL_KEY::ICON, "icon"}, {MODEL_KEY::LABEL, "label"}, {MODEL_KEY::PATH, "path"}, {MODEL_KEY::URL, "url"}, {MODEL_KEY::TYPE, "type"}, {MODEL_KEY::GROUP, "group"}, {MODEL_KEY::OWNER, "owner"}, {MODEL_KEY::SUFFIX, "suffix"}, {MODEL_KEY::NAME, "name"}, {MODEL_KEY::DATE, "date"}, {MODEL_KEY::MODIFIED, "modified"}, {MODEL_KEY::MIME, "mime"}, {MODEL_KEY::SIZE, "size"}, {MODEL_KEY::TAG, "tag"}, {MODEL_KEY::PERMISSIONS, "permissions"}, {MODEL_KEY::THUMBNAIL, "thumbnail"}, {MODEL_KEY::THUMBNAIL_1, "thumbnail_1"}, {MODEL_KEY::THUMBNAIL_2, "thumbnail_2"}, {MODEL_KEY::THUMBNAIL_3, "thumbnail_3"}, {MODEL_KEY::ICONSIZE, "iconsize"}, {MODEL_KEY::HIDDEN, "hidden"}, {MODEL_KEY::DETAILVIEW, "detailview"}, {MODEL_KEY::SHOWTERMINAL, "showterminal"}, {MODEL_KEY::SHOWTHUMBNAIL, "showthumbnail"}, {MODEL_KEY::COUNT, "count"}, {MODEL_KEY::SORTBY, "sortby"}, {MODEL_KEY::USER, "user"}, {MODEL_KEY::PASSWORD, "password"}, {MODEL_KEY::SERVER, "server"}, {MODEL_KEY::FOLDERSFIRST, "foldersfirst"}, {MODEL_KEY::VIEWTYPE, "viewtype"}, {MODEL_KEY::ADDDATE, "adddate"}, {MODEL_KEY::FAV, "fav"}, {MODEL_KEY::FAVORITE, "favorite"}, {MODEL_KEY::COLOR, "color"}, {MODEL_KEY::RATE, "rate"}, {MODEL_KEY::FORMAT, "format"}, {MODEL_KEY::PLACE, "place"}, {MODEL_KEY::LOCATION, "location"}, {MODEL_KEY::ALBUM, "album"}, {MODEL_KEY::DURATION, "duration"}, {MODEL_KEY::RELEASEDATE, "releasedate"}, {MODEL_KEY::ARTIST, "artist"}, {MODEL_KEY::LYRICS, "lyrics"}, {MODEL_KEY::TRACK, "track"}, {MODEL_KEY::GENRE, "genre"}, {MODEL_KEY::WIKI, "wiki"}, {MODEL_KEY::CONTEXT, "context"}, {MODEL_KEY::SOURCETYPE, "sourcetype"}, {MODEL_KEY::ARTWORK, "artwork"}, {MODEL_KEY::NOTE, "note"}, {MODEL_KEY::MOOD, "mood"}, {MODEL_KEY::COMMENT, "comment"}, {MODEL_KEY::PLAYLIST, "playlist"}, {MODEL_KEY::SOURCE, "source"}, {MODEL_KEY::TITLE, "title"}, {MODEL_KEY::ID, "id"}, {MODEL_KEY::PERSON_ID, "personid"}, {MODEL_KEY::PARENT_ID, "parentid"}, {MODEL_KEY::LICENSE, "license"}, {MODEL_KEY::DESCRIPTION, "description"}, {MODEL_KEY::BOOKMARK, "bookmark"}, {MODEL_KEY::ACCOUNT, "account"}, {MODEL_KEY::ACCOUNTTYPE, "accounttype"}, {MODEL_KEY::VERSION, "version"}, {MODEL_KEY::DOMAIN_M, "domain"}, {MODEL_KEY::CATEGORY, "category"}, {MODEL_KEY::CONTENT, "content"}, {MODEL_KEY::PIN, "pin"}, {MODEL_KEY::IMG, "img"}, {MODEL_KEY::PREVIEW, "preview"}, {MODEL_KEY::LINK, "link"}, {MODEL_KEY::STAMP, "stamp"}, {MODEL_KEY::BOOK, "book"}, /** ccdav keys **/ {MODEL_KEY::N, "n"}, {MODEL_KEY::IM, "im"}, {MODEL_KEY::PHOTO, "photo"}, {MODEL_KEY::GENDER, "gender"}, {MODEL_KEY::ADR, "adr"}, {MODEL_KEY::ADR_2, "adr2"}, {MODEL_KEY::ADR_3, "adr3"}, {MODEL_KEY::EMAIL, "email"}, {MODEL_KEY::EMAIL_2, "email2"}, {MODEL_KEY::EMAIL_3, "email3"}, {MODEL_KEY::LANG, "lang"}, {MODEL_KEY::NICKNAME, "nickname"}, {MODEL_KEY::ORG, "org"}, {MODEL_KEY::PROFILE, "profile"}, {MODEL_KEY::TZ, "tz"}, {MODEL_KEY::TEL, "tel"}, {MODEL_KEY::TEL_2, "tel2"}, {MODEL_KEY::TEL_3, "tel3"}, {MODEL_KEY::CITY, "city"}, {MODEL_KEY::STATE, "state"}, {MODEL_KEY::COUNTRY, "country"}, // opendesktop keys {MODEL_KEY::PACKAGE_ARCH, "packagearch"}, {MODEL_KEY::PACKAGE_TYPE, "packagetype"}, {MODEL_KEY::GPG_FINGERPRINT, "gpgfingerprint"}, {MODEL_KEY::GPG_SIGNATURE, "gpgsignature"}, {MODEL_KEY::PACKAGE_NAME, "packagename"}, {MODEL_KEY::PRICE, "price"}, {MODEL_KEY::REPOSITORY, "repository"}, {MODEL_KEY::TAGS, "tags"}, {MODEL_KEY::WAY, "way"}, {MODEL_KEY::PIC, "pic"}, {MODEL_KEY::SMALL_PIC, "smallpic"}, {MODEL_KEY::CHANGED, "changed"}, {MODEL_KEY::COMMENTS, "comments"}, {MODEL_KEY::CREATED, "created"}, {MODEL_KEY::DETAIL_PAGE, "detailpage"}, {MODEL_KEY::DETAILS, "details"}, {MODEL_KEY::TOTAL_DOWNLOADS, "totaldownloads"}, {MODEL_KEY::GHNS_EXCLUDED, "ghnsexcluded"}, {MODEL_KEY::LANGUAGE, "language"}, {MODEL_KEY::SCORE, "score"}, {MODEL_KEY::SUMMARY, "summary"}, {MODEL_KEY::TYPE_ID, "typeid"}, {MODEL_KEY::TYPE_NAME, "typename"}, {MODEL_KEY::XDG_TYPE, "xdgtype"}, //file props {MODEL_KEY::SYMLINK, "symlink"}, {MODEL_KEY::IS_SYMLINK, "issymlink"}, {MODEL_KEY::LAST_READ, "lastread"}, {MODEL_KEY::READABLE, "readable"}, {MODEL_KEY::WRITABLE, "writeable"}, {MODEL_KEY::IS_DIR, "isdir"}, {MODEL_KEY::IS_FILE, "isfile"}, {MODEL_KEY::IS_REMOTE, "isremote"}, {MODEL_KEY::EXECUTABLE, "executable"} }; static const QHash MODEL_NAME_KEY = { {MODEL_NAME[MODEL_KEY::ICON], MODEL_KEY::ICON}, {MODEL_NAME[MODEL_KEY::LABEL], MODEL_KEY::LABEL}, {MODEL_NAME[MODEL_KEY::PATH], MODEL_KEY::PATH}, {MODEL_NAME[MODEL_KEY::URL], MODEL_KEY::URL}, {MODEL_NAME[MODEL_KEY::TYPE], MODEL_KEY::TYPE}, {MODEL_NAME[MODEL_KEY::GROUP], MODEL_KEY::GROUP}, {MODEL_NAME[MODEL_KEY::OWNER], MODEL_KEY::OWNER}, {MODEL_NAME[MODEL_KEY::SUFFIX], MODEL_KEY::SUFFIX}, {MODEL_NAME[MODEL_KEY::NAME], MODEL_KEY::NAME}, {MODEL_NAME[MODEL_KEY::DATE], MODEL_KEY::DATE}, {MODEL_NAME[MODEL_KEY::MODIFIED], MODEL_KEY::MODIFIED}, {MODEL_NAME[MODEL_KEY::MIME], MODEL_KEY::MIME}, {MODEL_NAME[MODEL_KEY::SIZE], MODEL_KEY::SIZE,}, {MODEL_NAME[MODEL_KEY::TAG], MODEL_KEY::TAG}, {MODEL_NAME[MODEL_KEY::PERMISSIONS], MODEL_KEY::PERMISSIONS}, {MODEL_NAME[MODEL_KEY::THUMBNAIL], MODEL_KEY::THUMBNAIL}, {MODEL_NAME[MODEL_KEY::THUMBNAIL_1], MODEL_KEY::THUMBNAIL_1}, {MODEL_NAME[MODEL_KEY::THUMBNAIL_2], MODEL_KEY::THUMBNAIL_2}, {MODEL_NAME[MODEL_KEY::THUMBNAIL_3], MODEL_KEY::THUMBNAIL_3}, {MODEL_NAME[MODEL_KEY::ICONSIZE], MODEL_KEY::ICONSIZE}, {MODEL_NAME[MODEL_KEY::HIDDEN], MODEL_KEY::HIDDEN}, {MODEL_NAME[MODEL_KEY::DETAILVIEW], MODEL_KEY::DETAILVIEW}, {MODEL_NAME[MODEL_KEY::SHOWTERMINAL], MODEL_KEY::SHOWTERMINAL}, {MODEL_NAME[MODEL_KEY::SHOWTHUMBNAIL], MODEL_KEY::SHOWTHUMBNAIL}, {MODEL_NAME[MODEL_KEY::COUNT], MODEL_KEY::COUNT}, {MODEL_NAME[MODEL_KEY::SORTBY], MODEL_KEY::SORTBY}, {MODEL_NAME[MODEL_KEY::USER], MODEL_KEY::USER}, {MODEL_NAME[MODEL_KEY::PASSWORD], MODEL_KEY::PASSWORD}, {MODEL_NAME[MODEL_KEY::SERVER], MODEL_KEY::SERVER}, {MODEL_NAME[MODEL_KEY::VIEWTYPE], MODEL_KEY::VIEWTYPE}, {MODEL_NAME[MODEL_KEY::ADDDATE], MODEL_KEY::ADDDATE}, {MODEL_NAME[MODEL_KEY::FAV], MODEL_KEY::FAV}, {MODEL_NAME[MODEL_KEY::FAVORITE], MODEL_KEY::FAVORITE}, {MODEL_NAME[MODEL_KEY::COLOR], MODEL_KEY::COLOR}, {MODEL_NAME[MODEL_KEY::RATE], MODEL_KEY::RATE}, {MODEL_NAME[MODEL_KEY::FORMAT], MODEL_KEY::FORMAT}, {MODEL_NAME[MODEL_KEY::PLACE], MODEL_KEY::PLACE}, {MODEL_NAME[MODEL_KEY::LOCATION], MODEL_KEY::LOCATION}, {MODEL_NAME[MODEL_KEY::ALBUM], MODEL_KEY::ALBUM}, {MODEL_NAME[MODEL_KEY::ARTIST], MODEL_KEY::ARTIST}, {MODEL_NAME[MODEL_KEY::DURATION], MODEL_KEY::DURATION}, {MODEL_NAME[MODEL_KEY::TRACK], MODEL_KEY::TRACK}, {MODEL_NAME[MODEL_KEY::GENRE], MODEL_KEY::GENRE}, {MODEL_NAME[MODEL_KEY::LYRICS], MODEL_KEY::LYRICS}, {MODEL_NAME[MODEL_KEY::RELEASEDATE], MODEL_KEY::RELEASEDATE}, {MODEL_NAME[MODEL_KEY::FORMAT], MODEL_KEY::FORMAT}, {MODEL_NAME[MODEL_KEY::WIKI], MODEL_KEY::WIKI}, {MODEL_NAME[MODEL_KEY::SOURCETYPE], MODEL_KEY::SOURCETYPE}, {MODEL_NAME[MODEL_KEY::ARTWORK], MODEL_KEY::ARTWORK}, {MODEL_NAME[MODEL_KEY::NOTE], MODEL_KEY::NOTE}, {MODEL_NAME[MODEL_KEY::MOOD], MODEL_KEY::MOOD}, {MODEL_NAME[MODEL_KEY::COMMENT], MODEL_KEY::COMMENT}, {MODEL_NAME[MODEL_KEY::CONTEXT], MODEL_KEY::CONTEXT}, {MODEL_NAME[MODEL_KEY::SOURCE], MODEL_KEY::SOURCE}, {MODEL_NAME[MODEL_KEY::TITLE], MODEL_KEY::TITLE}, {MODEL_NAME[MODEL_KEY::ID], MODEL_KEY::ID}, {MODEL_NAME[MODEL_KEY::PARENT_ID], MODEL_KEY::PARENT_ID}, {MODEL_NAME[MODEL_KEY::LICENSE], MODEL_KEY::LICENSE}, {MODEL_NAME[MODEL_KEY::DESCRIPTION], MODEL_KEY::DESCRIPTION}, {MODEL_NAME[MODEL_KEY::BOOKMARK], MODEL_KEY::BOOKMARK}, {MODEL_NAME[MODEL_KEY::ACCOUNT], MODEL_KEY::ACCOUNT}, {MODEL_NAME[MODEL_KEY::ACCOUNTTYPE], MODEL_KEY::ACCOUNTTYPE}, {MODEL_NAME[MODEL_KEY::VERSION], MODEL_KEY::VERSION}, {MODEL_NAME[MODEL_KEY::DOMAIN_M], MODEL_KEY::DOMAIN_M}, {MODEL_NAME[MODEL_KEY::CATEGORY], MODEL_KEY::CATEGORY}, {MODEL_NAME[MODEL_KEY::CONTENT], MODEL_KEY::CONTENT}, {MODEL_NAME[MODEL_KEY::PIN], MODEL_KEY::PIN}, {MODEL_NAME[MODEL_KEY::IMG], MODEL_KEY::IMG}, {MODEL_NAME[MODEL_KEY::PREVIEW], MODEL_KEY::PREVIEW}, {MODEL_NAME[MODEL_KEY::LINK], MODEL_KEY::LINK}, {MODEL_NAME[MODEL_KEY::STAMP], MODEL_KEY::STAMP}, {MODEL_NAME[MODEL_KEY::BOOK], MODEL_KEY::BOOK}, /** ccdav keys **/ {MODEL_NAME[MODEL_KEY::N], MODEL_KEY::N}, {MODEL_NAME[MODEL_KEY::IM], MODEL_KEY::IM}, {MODEL_NAME[MODEL_KEY::PHOTO], MODEL_KEY::PHOTO}, {MODEL_NAME[MODEL_KEY::GENDER], MODEL_KEY::GENDER}, {MODEL_NAME[MODEL_KEY::ADR], MODEL_KEY::ADR}, {MODEL_NAME[MODEL_KEY::ADR_2], MODEL_KEY::ADR_2}, {MODEL_NAME[MODEL_KEY::ADR_3], MODEL_KEY::ADR_3}, {MODEL_NAME[MODEL_KEY::EMAIL], MODEL_KEY::EMAIL}, {MODEL_NAME[MODEL_KEY::EMAIL_2], MODEL_KEY::EMAIL_2}, {MODEL_NAME[MODEL_KEY::EMAIL_3], MODEL_KEY::EMAIL_3}, {MODEL_NAME[MODEL_KEY::LANG], MODEL_KEY::LANG}, {MODEL_NAME[MODEL_KEY::NICKNAME], MODEL_KEY::NICKNAME}, {MODEL_NAME[MODEL_KEY::ORG], MODEL_KEY::ORG}, {MODEL_NAME[MODEL_KEY::PROFILE], MODEL_KEY::PROFILE}, {MODEL_NAME[MODEL_KEY::TZ], MODEL_KEY::TZ}, {MODEL_NAME[MODEL_KEY::TEL], MODEL_KEY::TEL}, {MODEL_NAME[MODEL_KEY::TEL_2], MODEL_KEY::TEL_2}, {MODEL_NAME[MODEL_KEY::TEL_3], MODEL_KEY::TEL_3}, {MODEL_NAME[MODEL_KEY::CITY], MODEL_KEY::CITY}, {MODEL_NAME[MODEL_KEY::STATE], MODEL_KEY::STATE}, {MODEL_NAME[MODEL_KEY::COUNTRY], MODEL_KEY::COUNTRY}, //opendesktop store keys {MODEL_NAME[MODEL_KEY::PACKAGE_ARCH], MODEL_KEY::PACKAGE_ARCH}, {MODEL_NAME[MODEL_KEY::PACKAGE_TYPE], MODEL_KEY::PACKAGE_TYPE}, {MODEL_NAME[MODEL_KEY::GPG_FINGERPRINT], MODEL_KEY::GPG_FINGERPRINT}, {MODEL_NAME[MODEL_KEY::GPG_SIGNATURE], MODEL_KEY::GPG_SIGNATURE}, {MODEL_NAME[MODEL_KEY::PACKAGE_NAME], MODEL_KEY::PACKAGE_NAME}, {MODEL_NAME[MODEL_KEY::PRICE], MODEL_KEY::PRICE}, {MODEL_NAME[MODEL_KEY::REPOSITORY], MODEL_KEY::REPOSITORY}, {MODEL_NAME[MODEL_KEY::TAGS], MODEL_KEY::TAGS}, {MODEL_NAME[MODEL_KEY::WAY], MODEL_KEY::WAY}, {MODEL_NAME[MODEL_KEY::PIC], MODEL_KEY::PIC}, {MODEL_NAME[MODEL_KEY::SMALL_PIC], MODEL_KEY::SMALL_PIC}, {MODEL_NAME[MODEL_KEY::CHANGED], MODEL_KEY::CHANGED}, {MODEL_NAME[MODEL_KEY::COMMENTS], MODEL_KEY::COMMENTS}, {MODEL_NAME[MODEL_KEY::CREATED], MODEL_KEY::CREATED}, {MODEL_NAME[MODEL_KEY::DETAIL_PAGE], MODEL_KEY::DETAIL_PAGE}, {MODEL_NAME[MODEL_KEY::DETAILS], MODEL_KEY::DETAILS}, {MODEL_NAME[MODEL_KEY::TOTAL_DOWNLOADS], MODEL_KEY::TOTAL_DOWNLOADS}, {MODEL_NAME[MODEL_KEY::GHNS_EXCLUDED], MODEL_KEY::GHNS_EXCLUDED}, {MODEL_NAME[MODEL_KEY::LANGUAGE], MODEL_KEY::LANGUAGE}, {MODEL_NAME[MODEL_KEY::PERSON_ID], MODEL_KEY::PERSON_ID}, {MODEL_NAME[MODEL_KEY::SCORE], MODEL_KEY::SCORE}, {MODEL_NAME[MODEL_KEY::SUMMARY], MODEL_KEY::SUMMARY}, {MODEL_NAME[MODEL_KEY::TYPE_ID], MODEL_KEY::TYPE_ID}, {MODEL_NAME[MODEL_KEY::TYPE_NAME], MODEL_KEY::TYPE_NAME}, {MODEL_NAME[MODEL_KEY::XDG_TYPE], MODEL_KEY::XDG_TYPE}, //file props {MODEL_NAME[MODEL_KEY::SYMLINK], MODEL_KEY::SYMLINK}, {MODEL_NAME[MODEL_KEY::IS_SYMLINK], MODEL_KEY::IS_SYMLINK}, {MODEL_NAME[MODEL_KEY::LAST_READ], MODEL_KEY::LAST_READ}, {MODEL_NAME[MODEL_KEY::READABLE], MODEL_KEY::READABLE}, {MODEL_NAME[MODEL_KEY::WRITABLE], MODEL_KEY::WRITABLE}, {MODEL_NAME[MODEL_KEY::IS_DIR], MODEL_KEY::IS_DIR}, {MODEL_NAME[MODEL_KEY::IS_FILE], MODEL_KEY::IS_FILE}, {MODEL_NAME[MODEL_KEY::IS_REMOTE], MODEL_KEY::IS_REMOTE}, {MODEL_NAME[MODEL_KEY::EXECUTABLE], MODEL_KEY::EXECUTABLE} }; //for now here to later on use it to allow auto cast qvariant to qstring template class MHash : public QHash { public: using QHash::QHash; MHash(const QHash &other) : QHash(other) {} }; typedef QHash MODEL; typedef QVector MODEL_LIST; static const inline QVector modelRoles(const FMH::MODEL &model) { const auto keys = model.keys(); return std::accumulate(keys.begin(), keys.end(), QVector(), [](QVector &res, const FMH::MODEL_KEY &key) { res.append(key); return res; }); } static const inline QString mapValue(const QVariantMap &map, const FMH::MODEL_KEY &key) { return map[FMH::MODEL_NAME[key]].toString(); } static const inline QVariantMap toMap(const FMH::MODEL& model) { QVariantMap map; for(const auto &key : model.keys()) map.insert(FMH::MODEL_NAME[key], model[key]); return map; } static const inline FMH::MODEL toModel(const QVariantMap& map) { FMH::MODEL model; for(const auto &key : map.keys()) model.insert(FMH::MODEL_NAME_KEY[key], map[key].toString()); return model; } static const inline FMH::MODEL_LIST toModelList(const QVariantList& list) { FMH::MODEL_LIST res; for(const auto &data : list) res << FMH::toModel(data.toMap()); return res; } static const inline QVariantList toMapList(const FMH::MODEL_LIST& list) { QVariantList res; for(const auto &data : list) res << FMH::toMap(data); return res; } static const inline FMH::MODEL filterModel(const FMH::MODEL &model, const QVector &keys) { FMH::MODEL res; for(const auto &key : keys) { if(model.contains(key)) res[key] = model[key]; } return res; } static const inline QStringList modelToList(const FMH::MODEL &model, const FMH::MODEL_KEY &key) { QStringList res; for(const auto &item : model) { if(item.contains(key)) res << item[key]; } return res; } struct PATH_CONTENT { QUrl path; // the url holding all the content FMH::MODEL_LIST content; // the content from the url }; #if defined Q_OS_ANDROID || defined Q_OS_WIN32 enum PATHTYPE_KEY : int { PLACES_PATH, REMOTE_PATH, DRIVES_PATH, REMOVABLE_PATH, TAGS_PATH, UNKNOWN_TYPE, APPS_PATH, TRASH_PATH, SEARCH_PATH, CLOUD_PATH, FISH_PATH, MTP_PATH, QUICK_PATH, OTHER_PATH, }; #else enum PATHTYPE_KEY : int { PLACES_PATH = KFilePlacesModel::GroupType::PlacesType, REMOTE_PATH = KFilePlacesModel::GroupType::RemoteType, DRIVES_PATH = KFilePlacesModel::GroupType::DevicesType, REMOVABLE_PATH = KFilePlacesModel::GroupType::RemovableDevicesType, TAGS_PATH = KFilePlacesModel::GroupType::TagsType, UNKNOWN_TYPE = KFilePlacesModel::GroupType::UnknownType, APPS_PATH = 9, TRASH_PATH = 10, SEARCH_PATH = 11, CLOUD_PATH = 12, FISH_PATH = 13, MTP_PATH = 14, QUICK_PATH = 15, OTHER_PATH = 16, }; #endif static const QHash PATHTYPE_SCHEME = { {PATHTYPE_KEY::PLACES_PATH, "file"}, {PATHTYPE_KEY::DRIVES_PATH, "drives"}, {PATHTYPE_KEY::APPS_PATH, "applications"}, {PATHTYPE_KEY::REMOTE_PATH, "remote"}, {PATHTYPE_KEY::REMOVABLE_PATH, "removable"}, {PATHTYPE_KEY::UNKNOWN_TYPE, "Unkown"}, {PATHTYPE_KEY::TRASH_PATH, "trash"}, {PATHTYPE_KEY::TAGS_PATH, "tags"}, {PATHTYPE_KEY::SEARCH_PATH, "search"}, {PATHTYPE_KEY::CLOUD_PATH, "cloud"}, {PATHTYPE_KEY::FISH_PATH, "fish"}, {PATHTYPE_KEY::MTP_PATH, "mtp"} }; static const QHash PATHTYPE_URI = { {PATHTYPE_KEY::PLACES_PATH, PATHTYPE_SCHEME[PATHTYPE_KEY::PLACES_PATH] + "://"}, {PATHTYPE_KEY::DRIVES_PATH, PATHTYPE_SCHEME[PATHTYPE_KEY::DRIVES_PATH] + "://"}, {PATHTYPE_KEY::APPS_PATH, PATHTYPE_SCHEME[PATHTYPE_KEY::APPS_PATH] + ":///"}, {PATHTYPE_KEY::REMOTE_PATH, PATHTYPE_SCHEME[PATHTYPE_KEY::REMOTE_PATH] + "://"}, {PATHTYPE_KEY::REMOVABLE_PATH, PATHTYPE_SCHEME[PATHTYPE_KEY::REMOVABLE_PATH] + "://"}, {PATHTYPE_KEY::UNKNOWN_TYPE, PATHTYPE_SCHEME[PATHTYPE_KEY::UNKNOWN_TYPE] + "://"}, {PATHTYPE_KEY::TRASH_PATH, PATHTYPE_SCHEME[PATHTYPE_KEY::TRASH_PATH] + "://"}, {PATHTYPE_KEY::TAGS_PATH, PATHTYPE_SCHEME[PATHTYPE_KEY::TAGS_PATH] + ":///"}, {PATHTYPE_KEY::SEARCH_PATH, PATHTYPE_SCHEME[PATHTYPE_KEY::SEARCH_PATH] + "://"}, {PATHTYPE_KEY::CLOUD_PATH, PATHTYPE_SCHEME[PATHTYPE_KEY::CLOUD_PATH] + ":///"}, {PATHTYPE_KEY::FISH_PATH, PATHTYPE_SCHEME[PATHTYPE_KEY::FISH_PATH] + "://"}, {PATHTYPE_KEY::MTP_PATH, PATHTYPE_SCHEME[PATHTYPE_KEY::MTP_PATH] + "://"} }; static const QHash PATHTYPE_LABEL = { {PATHTYPE_KEY::PLACES_PATH, ("Places")}, {PATHTYPE_KEY::DRIVES_PATH, ("Drives")}, {PATHTYPE_KEY::APPS_PATH, ("Apps")}, {PATHTYPE_KEY::REMOTE_PATH, ("Remote")}, {PATHTYPE_KEY::REMOVABLE_PATH, ("Removable")}, {PATHTYPE_KEY::UNKNOWN_TYPE, ("Unknown")}, {PATHTYPE_KEY::TRASH_PATH, ("Trash")}, {PATHTYPE_KEY::TAGS_PATH, ("Tags")}, {PATHTYPE_KEY::SEARCH_PATH, ("Search")}, {PATHTYPE_KEY::CLOUD_PATH, ("Cloud")}, {PATHTYPE_KEY::FISH_PATH, ("Remote")}, {PATHTYPE_KEY::MTP_PATH, ("Drives")}, {PATHTYPE_KEY::OTHER_PATH, ("Others")}, {PATHTYPE_KEY::QUICK_PATH, ("Quick")} }; const QString DataPath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation); const QString CloudCachePath = FMH::DataPath+"/Cloud/"; const QString DesktopPath = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::DesktopLocation)).toString(); const QString AppsPath = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation)).toString(); const QString RootPath = "file:///"; #if defined(Q_OS_ANDROID) const QString PicturesPath = QUrl::fromLocalFile(PATHS::PicturesPath).toString(); const QString DownloadsPath = QUrl::fromLocalFile(PATHS::DownloadsPath).toString(); const QString DocumentsPath = QUrl::fromLocalFile(PATHS::DocumentsPath).toString(); const QString HomePath = QUrl::fromLocalFile(PATHS::HomePath).toString(); const QString MusicPath = QUrl::fromLocalFile(PATHS::MusicPath).toString(); const QString VideosPath = QUrl::fromLocalFile(PATHS::VideosPath).toString(); const QStringList defaultPaths = { FMH::HomePath, FMH::DocumentsPath, FMH::PicturesPath, FMH::MusicPath, FMH::VideosPath, FMH::DownloadsPath }; #else const QString PicturesPath = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation)).toString(); const QString DownloadsPath = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::DownloadLocation)).toString(); const QString DocumentsPath = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)).toString(); const QString HomePath = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)).toString(); const QString MusicPath = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::MusicLocation)).toString(); const QString VideosPath = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::MoviesLocation)).toString(); const QStringList defaultPaths = { FMH::HomePath, FMH::DesktopPath, FMH::DocumentsPath, FMH::PicturesPath, FMH::MusicPath, FMH::VideosPath, FMH::DownloadsPath, FMH::RootPath }; #endif const QMap folderIcon { {PicturesPath, "folder-pictures"}, {DownloadsPath, "folder-download"}, {DocumentsPath, "folder-documents"}, {HomePath, "user-home"}, {MusicPath, "folder-music"}, {VideosPath, "folder-videos"}, {DesktopPath, "user-desktop"}, {AppsPath, "system-run"}, {RootPath, "folder-root"} }; /** * Checks if a local file exists. * The URL must represent a local file path, by using the scheme file:// **/ static inline bool fileExists(const QUrl &path) { if(!path.isLocalFile()) { qWarning() << "URL recived is not a local file" << path; return false; } return QFileInfo::exists(path.toLocalFile()); } static inline const QString fileDir(const QUrl& path)// the directory path of the file { QString res = path.toString(); if(path.isLocalFile()) { const QFileInfo file(path.toLocalFile()); if(file.isDir()) res = path.toString(); else res = QUrl::fromLocalFile(file.dir().absolutePath()).toString(); }else qWarning()<< "The path is not a local one. FM::fileDir"; return res; } static inline const QUrl parentDir(const QUrl &path) { if(!path.isLocalFile()) { qWarning() << "URL recived is not a local file, FM::parentDir" << path; return path; } QDir dir(path.toLocalFile()); dir.cdUp(); return QUrl::fromLocalFile(dir.absolutePath()); } /** * Return the configuration of a single directory represented * by a QVariantMap. * The passed path must be a local file URL. **/ static inline QVariantMap dirConf(const QUrl &path) { if(!path.isLocalFile()) { qWarning() << "URL recived is not a local file" << path; return QVariantMap(); } if(!FMH::fileExists(path)) return QVariantMap(); QString icon, iconsize, hidden, detailview, showthumbnail, showterminal; uint count = 0, sortby = FMH::MODEL_KEY::MODIFIED, viewType = 0; bool foldersFirst = false; #if defined Q_OS_ANDROID || defined Q_OS_WIN32 QSettings file(path.toLocalFile(), QSettings::Format::NativeFormat); file.beginGroup(QString("Desktop Entry")); icon = file.value("Icon").toString(); file.endGroup(); file.beginGroup(QString("Settings")); hidden = file.value("HiddenFilesShown").toString(); file.endGroup(); file.beginGroup(QString("MAUIFM")); iconsize = file.value("IconSize").toString(); detailview = file.value("DetailView").toString(); showthumbnail = file.value("ShowThumbnail").toString(); showterminal = file.value("ShowTerminal").toString(); count = file.value("Count").toInt(); sortby = file.value("SortBy").toInt(); foldersFirst = file.value("FoldersFirst").toBool(); viewType = file.value("ViewType").toInt(); file.endGroup(); #else KConfig file(path.toLocalFile()); icon = file.entryMap(QString("Desktop Entry"))["Icon"]; hidden = file.entryMap(QString("Settings"))["HiddenFilesShown"]; iconsize = file.entryMap(QString("MAUIFM"))["IconSize"]; detailview = file.entryMap(QString("MAUIFM"))["DetailView"]; showthumbnail = file.entryMap(QString("MAUIFM"))["ShowThumbnail"]; showterminal = file.entryMap(QString("MAUIFM"))["ShowTerminal"]; count = file.entryMap(QString("MAUIFM"))["Count"].toInt(); sortby = file.entryMap(QString("MAUIFM"))["SortBy"].toInt(); foldersFirst = file.entryMap(QString("MAUIFM"))["FoldersFirst"] == "true" ? true : false; viewType = file.entryMap(QString("MAUIFM"))["ViewType"].toInt(); #endif return QVariantMap({ {FMH::MODEL_NAME[FMH::MODEL_KEY::ICON], icon.isEmpty() ? "folder" : icon}, {FMH::MODEL_NAME[FMH::MODEL_KEY::ICONSIZE], iconsize}, {FMH::MODEL_NAME[FMH::MODEL_KEY::COUNT], count}, {FMH::MODEL_NAME[FMH::MODEL_KEY::SHOWTERMINAL], showterminal.isEmpty() ? "false" : showterminal}, {FMH::MODEL_NAME[FMH::MODEL_KEY::SHOWTHUMBNAIL], showthumbnail.isEmpty() ? "false" : showthumbnail}, {FMH::MODEL_NAME[FMH::MODEL_KEY::DETAILVIEW], detailview.isEmpty() ? "false" : detailview}, {FMH::MODEL_NAME[FMH::MODEL_KEY::HIDDEN], hidden.isEmpty() ? false : (hidden == "true" ? true : false)}, {FMH::MODEL_NAME[FMH::MODEL_KEY::SORTBY], sortby}, {FMH::MODEL_NAME[FMH::MODEL_KEY::FOLDERSFIRST], foldersFirst}, {FMH::MODEL_NAME[FMH::MODEL_KEY::VIEWTYPE], viewType} }); } static inline void setDirConf(const QUrl &path, const QString &group, const QString &key, const QVariant &value) { if(!path.isLocalFile()) { qWarning() << "URL recived is not a local file" << path; return; } #if defined Q_OS_ANDROID || defined Q_OS_WIN32 QSettings file(path.toLocalFile(), QSettings::Format::IniFormat); file.beginGroup(group); file.setValue(key, value); file.endGroup(); file.sync(); #else KConfig file(path.toLocalFile(), KConfig::SimpleConfig); auto kgroup = file.group(group); kgroup.writeEntry(key, value); // file.reparseConfiguration(); file.sync(); #endif } /** * Returns the icon name for certain file. * The file path must be represented as a local file URL. * It also looks into the directory config file to get custom set icons **/ static inline QString getIconName(const QUrl &path) { if(!path.isLocalFile()) qWarning() << "URL recived is not a local file. FMH::getIconName" << path; if(path.isLocalFile() && QFileInfo(path.toLocalFile()).isDir()) { if(folderIcon.contains(path.toString())) return folderIcon[path.toString()]; else { const auto icon = FMH::dirConf(QString(path.toString()+"/%1").arg(".directory"))[FMH::MODEL_NAME[FMH::MODEL_KEY::ICON]].toString(); return icon.isEmpty() ? "folder" : icon; } }else { #if defined Q_OS_ANDROID || defined Q_OS_WIN32 QMimeDatabase mime; const auto type = mime.mimeTypeForFile(path.toString()); return type.iconName(); #else KFileItem mime(path); return mime.iconName(); #endif } } static inline QString getMime(const QUrl &path) { if(!path.isLocalFile()) { qWarning() << "URL recived is not a local file, FMH::getMime" << path; return QString(); } const QMimeDatabase mimedb; return mimedb.mimeTypeForFile(path.toLocalFile()).name(); } static inline bool mimeInherits(const QString baseType, const QString &type) { const QMimeDatabase _m; return _m.mimeTypeForName(baseType).inherits(type); } static inline FMH::MODEL getFileInfoModel(const QUrl &path) { FMH::MODEL res; #if defined Q_OS_ANDROID || defined Q_OS_WIN32 const QFileInfo file(path.toLocalFile()); if(!file.exists()) return FMH::MODEL(); const auto mime = FMH::getMime(path); res = FMH::MODEL { {FMH::MODEL_KEY::GROUP, file.group()}, {FMH::MODEL_KEY::OWNER, file.owner()}, {FMH::MODEL_KEY::SUFFIX, file.completeSuffix()}, {FMH::MODEL_KEY::LABEL, /*file.isDir() ? file.baseName() :*/ path == FMH::HomePath ? QStringLiteral("Home") : file.fileName()}, {FMH::MODEL_KEY::NAME, file.baseName()}, {FMH::MODEL_KEY::DATE, file.birthTime().toString(Qt::TextDate)}, {FMH::MODEL_KEY::MODIFIED, file.lastModified().toString(Qt::TextDate)}, {FMH::MODEL_KEY::LAST_READ, file.lastRead().toString(Qt::TextDate)}, {FMH::MODEL_KEY::MIME, mime }, {FMH::MODEL_KEY::SYMLINK, file.symLinkTarget() }, {FMH::MODEL_KEY::SYMLINK, file.symLinkTarget() }, {FMH::MODEL_KEY::IS_SYMLINK, QVariant(file.isSymLink()).toString()}, {FMH::MODEL_KEY::IS_FILE, QVariant(file.isFile()).toString()}, {FMH::MODEL_KEY::HIDDEN, QVariant(file.isHidden()).toString()}, {FMH::MODEL_KEY::IS_DIR, QVariant(file.isDir()).toString()}, {FMH::MODEL_KEY::WRITABLE, QVariant(file.isWritable()).toString()}, {FMH::MODEL_KEY::READABLE, QVariant(file.isReadable()).toString()}, {FMH::MODEL_KEY::EXECUTABLE, QVariant(file.suffix().endsWith(".desktop")).toString()}, {FMH::MODEL_KEY::ICON, FMH::getIconName(path)}, {FMH::MODEL_KEY::SIZE, QString::number(file.size()) /*locale.formattedDataSize(file.size())*/}, {FMH::MODEL_KEY::PATH, path.toString()}, {FMH::MODEL_KEY::THUMBNAIL, path.toString()}, {FMH::MODEL_KEY::COUNT, file.isDir() ? QString::number(QDir(path.toLocalFile()).count() - 2) : "0"} }; #else KFileItem kfile(path, KFileItem::MimeTypeDetermination::NormalMimeTypeDetermination); res = FMH::MODEL { {FMH::MODEL_KEY::LABEL, kfile.name()}, {FMH::MODEL_KEY::NAME, kfile.name().remove(kfile.name().lastIndexOf("."), kfile.name().size())}, {FMH::MODEL_KEY::DATE, kfile.time(KFileItem::FileTimes::CreationTime).toString(Qt::TextDate)}, {FMH::MODEL_KEY::MODIFIED, kfile.time(KFileItem::FileTimes::ModificationTime).toString(Qt::TextDate)}, {FMH::MODEL_KEY::LAST_READ, kfile.time(KFileItem::FileTimes::AccessTime).toString(Qt::TextDate)}, {FMH::MODEL_KEY::PATH, kfile.mostLocalUrl().toString()}, {FMH::MODEL_KEY::THUMBNAIL, kfile.localPath()}, {FMH::MODEL_KEY::SYMLINK, kfile.linkDest()}, {FMH::MODEL_KEY::IS_SYMLINK, QVariant(kfile.isLink()).toString()}, {FMH::MODEL_KEY::HIDDEN, QVariant(kfile.isHidden()).toString()}, {FMH::MODEL_KEY::IS_DIR, QVariant(kfile.isDir()).toString()}, {FMH::MODEL_KEY::IS_FILE, QVariant(kfile.isFile()).toString()}, {FMH::MODEL_KEY::WRITABLE, QVariant(kfile.isWritable()).toString()}, {FMH::MODEL_KEY::READABLE, QVariant(kfile.isReadable()).toString()}, {FMH::MODEL_KEY::EXECUTABLE, QVariant(kfile.isDesktopFile()).toString()}, {FMH::MODEL_KEY::MIME, kfile.mimetype()}, {FMH::MODEL_KEY::GROUP, kfile.group()}, {FMH::MODEL_KEY::ICON, kfile.iconName()}, {FMH::MODEL_KEY::SIZE, QString::number(kfile.size())}, {FMH::MODEL_KEY::THUMBNAIL, kfile.mostLocalUrl().toString()}, {FMH::MODEL_KEY::OWNER, kfile.user()}, {FMH::MODEL_KEY::COUNT, kfile.isLocalFile() && kfile.isDir() ? QString::number(QDir(kfile.localPath()).count() - 2) : "0"} }; #endif return res; } static inline QVariantMap getFileInfo(const QUrl &path) { return FMH::toMap(FMH::getFileInfoModel(path)); } static inline FMH::MODEL getDirInfoModel(const QUrl &path, const QString &type = QString()) { auto res = getFileInfoModel(path); res[FMH::MODEL_KEY::TYPE] = type; return res; } static inline QVariantMap getDirInfo(const QUrl &path, const QString &type = QString()) { return FMH::toMap(FMH::getDirInfoModel(path)); } } #endif // FMH_H