diff --git a/src/contents/ui/OptionsOverview.qml b/src/contents/ui/OptionsOverview.qml index 635c045..cebea73 100644 --- a/src/contents/ui/OptionsOverview.qml +++ b/src/contents/ui/OptionsOverview.qml @@ -1,156 +1,156 @@ /*************************************************************************** * * * Copyright 2014-2015 Sebastian Kügler * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, 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 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 . * * * ***************************************************************************/ import QtQuick 2.3 import QtQuick.Layouts 1.0 import org.kde.kirigami 2.0 as Kirigami ColumnLayout { id: optionsOverview property int buttonSize: Kirigami.Units.gridUnit * 2 RowLayout { id: layout anchors.fill: parent height: buttonSize spacing: 0 // anchors.leftMargin: Kirigami.Units.gridUnit / 2 // anchors.rightMargin: Kirigami.Units.gridUnit / 2 //visible: navigationShown //spacing: Kirigami.Units.smallSpacing OptionButton { id: backButton enabled: currentWebView.canGoBack iconSource: "go-previous" onClicked: { options.state = "hidden"; currentWebView.goBack() } } OptionButton { id: forwardButton enabled: currentWebView.canGoForward iconSource: "go-next" onClicked: { options.state = "hidden"; currentWebView.goForward() } } OptionButton { id: reloadButton iconSource: currentWebView.loading ? "process-stop" : "view-refresh" onClicked: { options.state = "hidden"; currentWebView.loading ? currentWebView.stop() : currentWebView.reload() } } OptionButton { id: bookmarkButton iconSource: "bookmarks" onClicked: { print("Adding bookmark"); var request = new Object;// FIXME request.url = currentWebView.url; request.title = currentWebView.title; - request.iconSource = currentWebView.iconSource; + request.icon = currentWebView.icon; request.bookmarked = true; browserManager.addBookmark(request); options.state = "hidden" } } } Item { Layout.preferredHeight: Kirigami.Units.smallSpacing Layout.fillWidth: true } OptionButton { iconSource: "tab-duplicate" Layout.fillWidth: true Layout.preferredHeight: buttonSize onClicked: { contentView.state = "tabs" options.state = "hidden" } //checked: contentView.state == "tabs" text: i18n("Tabs") } OptionButton { iconSource: "bookmarks" Layout.fillWidth: true Layout.preferredHeight: buttonSize onClicked: { contentView.state = "bookmarks" options.state = "hidden" } //checked: contentView.state == "bookmarks" text: i18n("Bookmarks") } OptionButton { iconSource: "view-history" Layout.fillWidth: true Layout.preferredHeight: buttonSize onClicked: { contentView.state = "history" options.state = "hidden" } //checked: contentView.state == "bookmarks" text: i18n("History") } OptionButton { iconSource: "configure" Layout.fillWidth: true Layout.preferredHeight: buttonSize text: i18n("Settings") //checked: contentView.state == "settings" onClicked: { contentView.state = "settings" options.state = "hidden" } } } diff --git a/src/contents/ui/UrlDelegate.qml b/src/contents/ui/UrlDelegate.qml index 180a37c..4cee49f 100644 --- a/src/contents/ui/UrlDelegate.qml +++ b/src/contents/ui/UrlDelegate.qml @@ -1,115 +1,116 @@ /*************************************************************************** * * * Copyright 2014-2015 Sebastian Kügler * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, 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 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 . * * * ***************************************************************************/ import QtQuick 2.3 import QtQuick.Controls 2.0 as Controls import QtQuick.Layouts 1.0 import org.kde.kirigami 2.0 as Kirigami Controls.ItemDelegate { id: urlDelegate height: Kirigami.Units.gridUnit * 3 width: parent.width //Rectangle { anchors.fill: parent; color: "white"; opacity: 0.5; } onClicked: { load(url) // tabs.newTab(url) // contentView.state = "hidden" } signal removed - Kirigami.Icon { + Image { id: urlIcon width: height + fillMode: Image.PreserveAspectFit anchors { left: parent.left top: parent.top topMargin: Kirigami.Units.gridUnit / 2 bottomMargin: Kirigami.Units.gridUnit / 2 bottom: parent.bottom margins: Kirigami.Units.smallSpacing } - source: icon + source: model.icon ? model.icon : "" } Image { anchors.fill: urlIcon source: preview == undefined ? "" : preview } Controls.Label { id: urlTitle text: title anchors { left: urlIcon.right leftMargin: Kirigami.Units.largeSpacing / 2 right: parent.right bottom: parent.verticalCenter top: urlIcon.top //margins: Kirigami.Units.smallSpacing } } Controls.Label { id: urlUrl text: url opacity: 0.6 font.pointSize: theme.smallestFont.pointSize anchors { left: urlIcon.right leftMargin: Kirigami.Units.largeSpacing / 2 right: removeIcon.left top: urlIcon.verticalCenter bottom: parent.bottom //margins: Kirigami.Units.smallSpacing } } Kirigami.Icon { id: removeIcon width: height source: "list-remove" //visible: bookmarked anchors { right: parent.right top: parent.top topMargin: Kirigami.Units.gridUnit bottomMargin: Kirigami.Units.gridUnit bottom: parent.bottom margins: Kirigami.Units.smallSpacing } MouseArea { anchors.fill: parent onClicked: urlDelegate.removed(); } } } diff --git a/src/contents/ui/WebView.qml b/src/contents/ui/WebView.qml index b831632..d5196e4 100644 --- a/src/contents/ui/WebView.qml +++ b/src/contents/ui/WebView.qml @@ -1,92 +1,98 @@ /*************************************************************************** * * * Copyright 2014-2015 Sebastian Kügler * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, 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 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 . * * * ***************************************************************************/ import QtQuick 2.3 import QtQuick.Controls 2.0 import QtWebEngine 1.4 WebEngineView { id: webEngineView property string errorCode: "" property string errorString: "" property string userAgent: "Mozilla/5.0 (Linux; Plasma Mobile, like Android 7.0 ) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/60.0.3112.107 Mobile Safari/537.36" width: pageWidth height: pageHeight profile.httpUserAgent: userAgent //Rectangle { color: "yellow"; opacity: 0.3; anchors.fill: parent } focus: true onLoadingChanged: { // Doesn't work!?! //print("Loading: " + loading); print(" url: " + loadRequest.url) //print(" icon: " + webEngineView.icon) //print(" title: " + webEngineView.title) /* Handle * - WebEngineView::LoadStartedStatus, * - WebEngineView::LoadStoppedStatus, * - WebEngineView::LoadSucceededStatus and * - WebEngineView::LoadFailedStatus */ var ec = ""; var es = ""; //print("Load: " + loadRequest.errorCode + " " + loadRequest.errorString); if (loadRequest.status == WebEngineView.LoadStartedStatus) { if (contentView.state != "settings") { // Kludge! contentView.state = "hidden"; } } if (loadRequest.status == WebEngineView.LoadSucceededStatus) { // record history, set current page info //contentView.state = "hidden" //pageInfo.url = webEngineView.url; //pageInfo.title = webEngineView.title; //pageInfo.icon = webEngineView.icon; addHistoryEntry(); } if (loadRequest.status == WebEngineView.LoadFailedStatus) { print("Load failed: " + loadRequest.errorCode + " " + loadRequest.errorString); ec = loadRequest.errorCode; es = loadRequest.errorString; contentView.state = "hidden" } errorCode = ec; errorString = es; } // onLoadProgressChanged: { // if (loadProgress > 50) { // contentView.state = "hidden"; // } // } + Component.onCompleted: { print("WebView completed."); var settings = webEngineView.settings; print("Settings: " + settings); } + + onIconChanged: { + if (icon) + browserManager.history.updateIcon(url, icon) + } } diff --git a/src/urlmodel.cpp b/src/urlmodel.cpp index a1df6b2..b8b9871 100644 --- a/src/urlmodel.cpp +++ b/src/urlmodel.cpp @@ -1,219 +1,238 @@ /*************************************************************************** * * * Copyright 2014-2015 Sebastian Kügler * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, 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 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 "urlmodel.h" #include #include #include #include #include #include //#include #include #include using namespace AngelFish; UrlModel::UrlModel(const QString &fileName, QObject *parent) : QAbstractListModel(parent), m_fileName(fileName) { m_roleNames.insert(url, "url"); m_roleNames.insert(title, "title"); m_roleNames.insert(icon, "icon"); m_roleNames.insert(preview, "preview"); m_roleNames.insert(lastVisited, "lastVisited"); m_roleNames.insert(bookmarked, "bookmarked"); //m_fakeData = fakeData(); //setSourceData(&m_fakeData); //save(); } void UrlModel::setSourceData(QJsonArray &data) { if (m_data != data) { m_data = data; //modelReset(); ?? } } QJsonArray UrlModel::sourceData() const { return m_data; } QHash UrlModel::roleNames() const { return m_roleNames; } int UrlModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent) if (m_data.size() <= 0) { return 0; } else { return m_data.size(); } } QVariant UrlModel::data(const QModelIndex &index, int role) const { if (index.isValid()) { QJsonObject currentData = m_data.at(index.row()).toObject(); switch (role) { case lastVisited: return QDateTime::fromString(currentData.value(key(role)).toString(), Qt::ISODate); case bookmarked: return currentData.value(key(role)).toBool(); } if (currentData.value(key(role)).isUndefined()) { return QVariant(); } return currentData.value(key(role)).toString(); } return QVariant(); } void UrlModel::update() { // FIXME: Can we be more fine-grained, please? beginResetModel(); endResetModel(); //emit QAbstractItemModel::modelReset(); // auto topleft = index(0); // auto bottomright = index(rowCount(topleft)); -// emit dataChanged(topleft, bottomright); + // emit dataChanged(topleft, bottomright); +} + +bool UrlModel::updateIcon(const QString &url, const QString &iconSource) +{ + qDebug() << "updateIcon: " << url << " " << iconSource; + bool found = false; + for (int i = 0; i < m_data.count(); i++) { + const QString u = m_data.at(i).toObject()[key(UrlModel::url)].toString(); + if (u == url) { + auto obj = m_data[i].toObject(); + obj[key(UrlModel::icon)] = iconSource; + m_data[i] = obj; + emit dataChanged(index(i), index(i), {UrlModel::Roles::icon}); + found = true; + } + } + return found; } QString UrlModel::filePath() const { QFileInfo fi(m_fileName); if (fi.isAbsolute()) { return m_fileName; } return QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) \ + QStringLiteral("/angelfish/") \ + m_fileName; } bool UrlModel::load() { QFile jsonFile(filePath()); if (!jsonFile.exists()) { return false; } if (!jsonFile.open(QIODevice::ReadOnly)) { qDebug() << "Could not open" << m_fileName; return false; } //QJsonDocument jdoc = QJsonDocument::fromBinaryData(jsonFile.readAll()); QJsonDocument jdoc = QJsonDocument::fromJson(jsonFile.readAll()); jsonFile.close(); qDebug() << "Loaded from file:" << jdoc.array().count() << filePath(); QJsonArray plugins = jdoc.array(); setSourceData(plugins); return true; } bool UrlModel::save() { QVariantMap vm; QVariantMap urlsVm; vm[QStringLiteral("Version")] = QStringLiteral("1.0"); vm[QStringLiteral("Timestamp")] = QDateTime::currentMSecsSinceEpoch(); QJsonArray urls; - Q_FOREACH (auto url, m_data) { + Q_FOREACH (const auto &url, m_data) { urls << url; } QJsonDocument jdoc; jdoc.setArray(urls); QString destfile = m_fileName; QString destdir = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + QStringLiteral("/angelfish/"); QDir dir(destdir); const QFileInfo fi(m_fileName); if (!fi.isAbsolute()) { destfile = destdir + m_fileName; } if (!dir.mkpath(".")) { qDebug() << "Destdir doesn't exist and I can't create it: " << destdir; return false; } QFile file(destfile); if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { qWarning() << "Failed to open " << destfile; return false; } file.write(jdoc.toJson()); // file.write(jdoc.toBinaryData()); qWarning() << "Wrote " << destfile << " (" << urls.count() << " urls) ";// << jdoc.toJson(); return true; } QString UrlModel::key(int role) const { return QString::fromLocal8Bit(m_roleNames[role]); } void UrlModel::add(const QJsonObject &data) { - foreach (auto urldata, m_data) { + foreach (const auto &urldata, m_data) { if (urldata == data) { return; } } + beginInsertRows(QModelIndex(), m_data.size(), m_data.size()); m_data.append(data); - update(); + endInsertRows(); } void UrlModel::remove(const QString& url) { for (int i = 0; i < m_data.count(); i++) { const QString u = m_data.at(i).toObject()[key(UrlModel::url)].toString(); if (u == url) { + beginRemoveRows(QModelIndex(), i, i); m_data.removeAt(i); + endRemoveRows(); //int n = m_data.count(); //qDebug() << "!!! Removed: " << url << " now" << m_data.count() << " was " << n; - update(); return; } } } diff --git a/src/urlmodel.h b/src/urlmodel.h index 8f1b3ae..8c0a6a2 100644 --- a/src/urlmodel.h +++ b/src/urlmodel.h @@ -1,77 +1,78 @@ /*************************************************************************** * * * Copyright 2014-2015 Sebastian Kügler * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, 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 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 URLMODEL_H #define URLMODEL_H #include #include #include namespace AngelFish { class UrlModel : public QAbstractListModel { Q_OBJECT public: enum Roles { url = Qt::UserRole + 1, title, icon, preview, lastVisited, bookmarked }; explicit UrlModel(const QString &filename, QObject *parent = 0); void setSourceData(QJsonArray &data); QJsonArray sourceData() const; QString key(int role) const; bool load(); bool save(); void add(const QJsonObject &data); void remove(const QString &url); virtual QHash roleNames() const; virtual int rowCount(const QModelIndex &parent) const; virtual QVariant data(const QModelIndex &index, int role) const; void update(); + Q_INVOKABLE bool updateIcon(const QString &url, const QString &iconSource); QJsonArray fakeData(); QString filePath() const; private: QJsonArray m_data; QHash m_roleNames; QString m_fileName; }; } // namespace #endif // URLMODEL_H