diff --git a/discover/ReadFile.cpp b/discover/ReadFile.cpp index 91c895e9..0bb07d49 100644 --- a/discover/ReadFile.cpp +++ b/discover/ReadFile.cpp @@ -1,112 +1,112 @@ /* * Copyright (C) 2018 Aleix Pol Gonzalez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library/Lesser General Public License * version 2, or (at your option) any later version, as published by the * Free Software Foundation * * 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/Lesser 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. */ #include "ReadFile.h" #include ReadFile::ReadFile() { connect(&m_watcher, &QFileSystemWatcher::fileChanged, this, &ReadFile::openNow); - connect(&m_file, &QFile::readyRead, this, &ReadFile::processAll); + connect(&m_file, &QFile::readyRead, this, &ReadFile::process); } void ReadFile::componentComplete() { completed = true; openNow(); } void ReadFile::setPath(QString path) { processPath(path); if (path == m_file.fileName()) return; if (path.isEmpty()) return; if (m_file.isOpen()) m_watcher.removePath(m_file.fileName()); m_file.setFileName(path); + m_sizeOnSet = m_file.size() + 1; openNow(); m_watcher.addPath(m_file.fileName()); } void ReadFile::openNow() { if (!completed) return; if (!m_contents.isEmpty()) { m_contents.clear(); Q_EMIT contentsChanged(m_contents); } m_file.close(); const auto open = m_file.open(QIODevice::ReadOnly | QIODevice::Text); Q_EMIT pathChanged(path()); if (!open) return; m_stream.reset(new QTextStream(&m_file)); - processAll(); + m_stream->seek(m_sizeOnSet); + process(); } void ReadFile::processPath(QString& path) { const QRegularExpression envRx(QStringLiteral("\\$([A-Z_]+)")); auto matchIt = envRx.globalMatch(path); while(matchIt.hasNext()) { auto match = matchIt.next(); path.replace(match.capturedStart(), match.capturedLength(), QString::fromUtf8(qgetenv(match.capturedRef(1).toUtf8().constData()))); } } -void ReadFile::process(uint max) +void ReadFile::process() { - QString read = m_stream->readAll(); - if (max>0) - read = read.right(max); + const QString read = m_stream->readAll(); - if (m_filter.isValid()) { + if (m_filter.isValid() && !m_filter.pattern().isEmpty()) { auto it = m_filter.globalMatch(read); while(it.hasNext()) { const auto match = it.next(); m_contents.append(match.capturedRef(match.lastCapturedIndex())); m_contents.append(QLatin1Char('\n')); } } else m_contents += read; Q_EMIT contentsChanged(m_contents); } void ReadFile::setFilter(const QString& filter) { m_filter = QRegularExpression(filter); if (!m_filter.isValid()) qDebug() << "error" << m_filter.errorString(); Q_ASSERT(filter.isEmpty() || m_filter.isValid()); } QString ReadFile::filter() const { return m_filter.pattern(); } diff --git a/discover/ReadFile.h b/discover/ReadFile.h index 1579b6c6..a53732ad 100644 --- a/discover/ReadFile.h +++ b/discover/ReadFile.h @@ -1,68 +1,68 @@ /* * Copyright (C) 2018 Aleix Pol Gonzalez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library/Lesser General Public License * version 2, or (at your option) any later version, as published by the * Free Software Foundation * * 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/Lesser 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 READFILE_H #define READFILE_H #include #include #include #include #include #include class ReadFile : public QObject, public QQmlParserStatus { Q_OBJECT Q_INTERFACES(QQmlParserStatus) Q_PROPERTY(QString contents READ contents NOTIFY contentsChanged) Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged) Q_PROPERTY(QString filter READ filter WRITE setFilter FINAL) public: ReadFile(); QString contents() const { return m_contents; } QString path() const { return m_file.fileName(); } void setPath(QString path); QString filter() const; void setFilter(const QString &filter); void classBegin() override {} void componentComplete() override; Q_SIGNALS: void pathChanged(const QString &path); void contentsChanged(const QString &contents); private: - void processAll() { return process(0); } - void process(uint max); + void process(); void openNow(); void processPath(QString& path); bool completed = false; QFile m_file; QString m_contents; QSharedPointer m_stream; QFileSystemWatcher m_watcher; QRegularExpression m_filter; + qint64 m_sizeOnSet = 0; }; #endif // READFILE_H diff --git a/discover/qml/UpdatesPage.qml b/discover/qml/UpdatesPage.qml index 6de55414..d487facc 100644 --- a/discover/qml/UpdatesPage.qml +++ b/discover/qml/UpdatesPage.qml @@ -1,301 +1,301 @@ import QtQuick.Controls 1.2 import QtQuick.Controls 2.3 as QQC2 import QtQuick.Layouts 1.1 import QtQuick 2.4 import org.kde.discover 2.0 import org.kde.discover.app 1.0 import "navigation.js" as Navigation import org.kde.kirigami 2.1 as Kirigami DiscoverPage { id: page title: i18n("Updates") property string footerLabel: "" ResourcesUpdatesModel { id: resourcesUpdatesModel onPassiveMessage: window.showPassiveNotification(message) onIsProgressingChanged: { if (!isProgressing) { resourcesUpdatesModel.prepare() } } Component.onCompleted: { if (!isProgressing) { resourcesUpdatesModel.prepare() } } } UpdateModel { id: updateModel backend: resourcesUpdatesModel } Kirigami.Action { id: updateAction text: page.unselected>0 ? i18n("Update Selected") : i18n("Update All") visible: updateModel.toUpdateCount iconName: "update-none" enabled: !resourcesUpdatesModel.isProgressing onTriggered: resourcesUpdatesModel.updateAll() } footer: QQC2.ScrollView { id: scv width: parent.width height: visible ? Kirigami.Units.gridUnit * 10 : 0 visible: log.contents.length > 0 QQC2.TextArea { readOnly: true text: log.contents - onTextChanged: cursorPosition = text.length + cursorPosition: text.length - 1 font.family: "monospace" ReadFile { id: log filter: ".*ALPM-SCRIPTLET\\] .*" path: "/var/log/pacman.log" } } } Kirigami.Action { id: cancelUpdateAction iconName: "dialog-cancel" text: i18n("Cancel") enabled: resourcesUpdatesModel.transaction && resourcesUpdatesModel.transaction.isCancellable onTriggered: resourcesUpdatesModel.transaction.cancel() } readonly property int unselected: (updateModel.totalUpdatesCount - updateModel.toUpdateCount) readonly property QtObject currentAction: resourcesUpdatesModel.isProgressing ? cancelUpdateAction : updateAction actions { left: refreshAction main: currentAction } header: QQC2.ToolBar { visible: (updateModel.totalUpdatesCount > 0 && resourcesUpdatesModel.isProgressing) || updateModel.hasUpdates RowLayout { anchors.fill: parent LabelBackground { Layout.leftMargin: Kirigami.Units.gridUnit text: updateModel.toUpdateCount + " (" + updateModel.updateSize+")" } QQC2.Label { text: i18n("updates selected") } LabelBackground { id: unselectedItem text: page.unselected visible: page.unselected>0 } QQC2.Label { text: i18n("updates not selected") visible: unselectedItem.visible } Item { Layout.fillWidth: true } enabled: page.currentAction.enabled } } supportsRefreshing: true onRefreshingChanged: { showPassiveNotification("Fetching updates...") ResourcesModel.updateAction.triggered() refreshing = false } ListView { id: updatesView currentIndex: -1 footer: ColumnLayout { anchors.right: parent.right anchors.left: parent.left Kirigami.Heading { Layout.fillWidth: true Layout.alignment: Qt.AlignHCenter horizontalAlignment: Text.AlignHCenter visible: page.footerLabel !== "" text: page.footerLabel } Kirigami.Icon { Layout.alignment: Qt.AlignHCenter visible: page.footerLabel !== "" source: "update-none" opacity: 0.3 width: 200 height: 200 } Item { visible: page.footerLabel === "" height: Kirigami.Units.gridUnit width: 1 } } model: updateModel section { property: "section" delegate: Kirigami.Heading { x: Kirigami.Units.gridUnit level: 2 text: section } } spacing: Kirigami.Units.smallSpacing delegate: Kirigami.AbstractListItem { backgroundColor: Kirigami.Theme.viewBackgroundColor x: Kirigami.Units.gridUnit width: ListView.view.width - Kirigami.Units.gridUnit * 2 highlighted: ListView.isCurrentItem onEnabledChanged: if (!enabled) { layout.extended = false; } Keys.onReturnPressed: { itemChecked.clicked() } Keys.onPressed: if (event.key===Qt.Key_Alt) layout.extended = true Keys.onReleased: if (event.key===Qt.Key_Alt) layout.extended = false ColumnLayout { id: layout property bool extended: false onExtendedChanged: if (extended) { updateModel.fetchChangelog(index) } RowLayout { Layout.fillWidth: true Layout.fillHeight: true CheckBox { id: itemChecked Layout.alignment: Qt.AlignVCenter checked: model.checked == Qt.Checked onClicked: model.checked = (model.checked==Qt.Checked ? Qt.Unchecked : Qt.Checked) } Kirigami.Icon { Layout.fillHeight: true Layout.preferredWidth: height source: decoration smooth: true } QQC2.Label { Layout.fillWidth: true text: i18n("%1 (%2)", display, version) elide: Text.ElideRight } LabelBackground { Layout.minimumWidth: Kirigami.Units.gridUnit * 6 text: size progress: resourceProgress/100 } } QQC2.Frame { Layout.fillWidth: true implicitHeight: view.contentHeight visible: layout.extended && changelog.length>0 QQC2.Label { id: view anchors { right: parent.right left: parent.left } text: changelog textFormat: Text.StyledText wrapMode: Text.WordWrap onLinkActivated: Qt.openUrlExternally(link) } //This saves a binding loop on implictHeight, as the Label //height is updated twice (first time with the wrong value) Behavior on implicitHeight { PropertyAnimation { duration: Kirigami.Units.shortDuration } } } Button { Layout.alignment: Qt.AlignRight text: i18n("More Information...") visible: layout.extended enabled: !resourcesUpdatesModel.isProgressing onClicked: Navigation.openApplication(resource) } } onClicked: { layout.extended = !layout.extended } } } readonly property alias secSinceUpdate: resourcesUpdatesModel.secsToLastUpdate state: ( updateModel.hasUpdates ? "has-updates" : ResourcesModel.isFetching ? "fetching" : resourcesUpdatesModel.isProgressing ? "progressing" : secSinceUpdate < 0 ? "unknown" : secSinceUpdate === 0 ? "now-uptodate" : secSinceUpdate < 1000 * 60 * 60 * 24 ? "uptodate" : secSinceUpdate < 1000 * 60 * 60 * 24 * 7 ? "medium" : "low" ) states: [ State { name: "fetching" PropertyChanges { target: page; title: i18nc("@info", "Fetching...") } PropertyChanges { target: page; footerLabel: i18nc("@info", "Looking for updates") } }, State { name: "progressing" PropertyChanges { target: page; title: i18nc("@info", "Updating...") } PropertyChanges { target: page; footerLabel: resourcesUpdatesModel.progress<=0 ? i18nc("@info", "Fetching updates") : "" } }, State { name: "has-updates" PropertyChanges { target: page; title: i18nc("@info", "Updates") } }, State { name: "now-uptodate" PropertyChanges { target: page; title: i18nc("@info", "The system is up to date") } PropertyChanges { target: page; footerLabel: i18nc("@info", "No updates") } }, State { name: "uptodate" PropertyChanges { target: page; title: i18nc("@info", "The system is up to date") } PropertyChanges { target: page; footerLabel: i18nc("@info", "No updates") } }, State { name: "medium" PropertyChanges { target: page; title: i18nc("@info", "No updates are available") } }, State { name: "low" PropertyChanges { target: page; title: i18nc("@info", "Should check for updates") } }, State { name: "unknown" PropertyChanges { target: page; title: i18nc("@info", "It is unknown when the last check for updates was") } } ] }