New export/import dialog. Rewritten export functionality.
This commit is contained in:
parent
f033cd27fc
commit
94100ff90a
@ -349,6 +349,7 @@ set(APP_SOURCES
|
||||
src/gui/feedstoolbar.cpp
|
||||
src/gui/toolbareditor.cpp
|
||||
src/gui/messagessearchlineedit.cpp
|
||||
src/gui/formimportexport.cpp
|
||||
|
||||
# DYNAMIC-SHORTCUTS sources.
|
||||
src/dynamic-shortcuts/shortcutcatcher.cpp
|
||||
@ -378,6 +379,7 @@ set(APP_SOURCES
|
||||
src/core/feedsmodelfeed.cpp
|
||||
src/core/parsingfactory.cpp
|
||||
src/core/feeddownloader.cpp
|
||||
src/core/feedsimportexportmodel.cpp
|
||||
|
||||
# NETWORK-WEB sources.
|
||||
src/network-web/basenetworkaccessmanager.cpp
|
||||
@ -431,6 +433,7 @@ set(APP_HEADERS
|
||||
src/gui/feedstoolbar.h
|
||||
src/gui/toolbareditor.h
|
||||
src/gui/messagessearchlineedit.h
|
||||
src/gui/formimportexport.h
|
||||
|
||||
# DYNAMIC-SHORTCUTS headers.
|
||||
src/dynamic-shortcuts/dynamicshortcutswidget.h
|
||||
@ -452,6 +455,7 @@ set(APP_HEADERS
|
||||
src/core/feedsmodel.h
|
||||
src/core/feedsproxymodel.h
|
||||
src/core/feeddownloader.h
|
||||
src/core/feedsimportexportmodel.h
|
||||
|
||||
# NETWORK-WEB headers.
|
||||
src/network-web/webpage.h
|
||||
@ -473,6 +477,7 @@ set(APP_FORMS
|
||||
src/gui/formcategorydetails.ui
|
||||
src/gui/formfeeddetails.ui
|
||||
src/gui/toolbareditor.ui
|
||||
src/gui/formimportexport.ui
|
||||
)
|
||||
|
||||
# APP translations.
|
||||
|
315
src/core/feedsimportexportmodel.cpp
Normal file
315
src/core/feedsimportexportmodel.cpp
Normal file
@ -0,0 +1,315 @@
|
||||
// This file is part of RSS Guard.
|
||||
//
|
||||
// Copyright (C) 2011-2014 by Martin Rotter <rotter.martinos@gmail.com>
|
||||
//
|
||||
// RSS Guard 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 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// RSS Guard 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 RSS Guard. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "core/feedsimportexportmodel.h"
|
||||
|
||||
#include "core/feedsmodelfeed.h"
|
||||
#include "core/feedsmodelcategory.h"
|
||||
#include "definitions/definitions.h"
|
||||
|
||||
#include <QDomDocument>
|
||||
#include <QDomElement>
|
||||
#include <QDomAttr>
|
||||
#include <QStack>
|
||||
#include <QLocale>
|
||||
|
||||
|
||||
FeedsImportExportModel::FeedsImportExportModel(QObject *parent)
|
||||
: QAbstractItemModel(parent), m_checkStates(QHash<FeedsModelRootItem*, Qt::CheckState>()), m_recursiveChange(false) {
|
||||
}
|
||||
|
||||
FeedsImportExportModel::~FeedsImportExportModel() {
|
||||
}
|
||||
|
||||
FeedsModelRootItem *FeedsImportExportModel::itemForIndex(const QModelIndex &index) const {
|
||||
if (index.isValid() && index.model() == this) {
|
||||
return static_cast<FeedsModelRootItem*>(index.internalPointer());
|
||||
}
|
||||
else {
|
||||
return m_rootItem;
|
||||
}
|
||||
}
|
||||
|
||||
FeedsModelRootItem *FeedsImportExportModel::rootItem() const {
|
||||
return m_rootItem;
|
||||
}
|
||||
|
||||
void FeedsImportExportModel::setRootItem(FeedsModelRootItem *rootItem) {
|
||||
m_rootItem = rootItem;
|
||||
}
|
||||
|
||||
bool FeedsImportExportModel::exportToOMPL20(QByteArray &result) {
|
||||
QDomDocument opml_document;
|
||||
QDomProcessingInstruction xml_declaration = opml_document.createProcessingInstruction("xml",
|
||||
"version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"");
|
||||
opml_document.appendChild(xml_declaration);
|
||||
|
||||
// Adde OPML 2.0 metadata.
|
||||
opml_document.appendChild(opml_document.createElement("opml"));
|
||||
opml_document.documentElement().setAttribute("version", "2.0");
|
||||
|
||||
QDomElement elem_opml_head = opml_document.createElement("head");
|
||||
|
||||
QDomElement elem_opml_title = opml_document.createElement("title");
|
||||
QDomText text_opml_title = opml_document.createTextNode(QString(APP_NAME));
|
||||
elem_opml_title.appendChild(text_opml_title);
|
||||
elem_opml_head.appendChild(elem_opml_title);
|
||||
|
||||
QDomElement elem_opml_created = opml_document.createElement("dateCreated");
|
||||
QDomText text_opml_created = opml_document.createTextNode(QLocale::c().toString(QDateTime::currentDateTimeUtc(),
|
||||
"ddd, dd MMM yyyy hh:mm:ss") + " GMT");
|
||||
elem_opml_created.appendChild(text_opml_created);
|
||||
elem_opml_head.appendChild(elem_opml_created);
|
||||
opml_document.documentElement().appendChild(elem_opml_head);
|
||||
|
||||
QDomElement elem_opml_body = opml_document.createElement("body");
|
||||
|
||||
QStack<FeedsModelRootItem*> items_to_process; items_to_process.push(m_rootItem);
|
||||
QStack<QDomElement> elements_to_use; elements_to_use.push(elem_opml_body);
|
||||
|
||||
// Process all unprocessed nodes.
|
||||
while (!items_to_process.isEmpty()) {
|
||||
QDomElement active_element = elements_to_use.pop();
|
||||
FeedsModelRootItem *active_item = items_to_process.pop();
|
||||
|
||||
foreach (FeedsModelRootItem *child_item, active_item->childItems()) {
|
||||
if (!m_checkStates.contains(child_item) || m_checkStates[child_item] != Qt::Checked) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (child_item->kind()) {
|
||||
case FeedsModelRootItem::Category: {
|
||||
QDomElement outline_category = opml_document.createElement("outline");
|
||||
outline_category.setAttribute("text", child_item->title());
|
||||
active_element.appendChild(outline_category);
|
||||
items_to_process.push(child_item);
|
||||
elements_to_use.push(outline_category);
|
||||
break;
|
||||
}
|
||||
|
||||
case FeedsModelRootItem::Feed: {
|
||||
FeedsModelFeed *child_feed = static_cast<FeedsModelFeed*>(child_item);
|
||||
QDomElement outline_feed = opml_document.createElement("outline");
|
||||
outline_feed.setAttribute("text", child_feed->title());
|
||||
outline_feed.setAttribute("xmlUrl", child_feed->url());
|
||||
outline_feed.setAttribute("description", child_feed->description());
|
||||
outline_feed.setAttribute("encoding", child_feed->encoding());
|
||||
|
||||
switch (child_feed->type()) {
|
||||
case FeedsModelFeed::Rss0X:
|
||||
case FeedsModelFeed::Rss2X:
|
||||
outline_feed.setAttribute("version", "RSS");
|
||||
break;
|
||||
|
||||
case FeedsModelFeed::Rdf:
|
||||
outline_feed.setAttribute("version", "RSS");
|
||||
break;
|
||||
|
||||
case FeedsModelFeed::Atom10:
|
||||
outline_feed.setAttribute("version", "ATOM");
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (child_feed->passwordProtected()) {
|
||||
outline_feed.setAttribute("username", child_feed->username());
|
||||
outline_feed.setAttribute("password", child_feed->password());
|
||||
}
|
||||
|
||||
active_element.appendChild(outline_feed);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
opml_document.documentElement().appendChild(elem_opml_body);
|
||||
|
||||
result = opml_document.toByteArray(2);
|
||||
return true;
|
||||
}
|
||||
|
||||
QModelIndex FeedsImportExportModel::index(int row, int column, const QModelIndex &parent) const {
|
||||
if (!hasIndex(row, column, parent)) {
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
FeedsModelRootItem *parent_item = itemForIndex(parent);
|
||||
FeedsModelRootItem *child_item = parent_item->child(row);
|
||||
|
||||
if (child_item) {
|
||||
return createIndex(row, column, child_item);
|
||||
}
|
||||
else {
|
||||
return QModelIndex();
|
||||
}
|
||||
}
|
||||
|
||||
QModelIndex FeedsImportExportModel::indexForItem(FeedsModelRootItem *item) const {
|
||||
if (item == NULL || item->kind() == FeedsModelRootItem::RootItem) {
|
||||
// Root item lies on invalid index.
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
QList<QModelIndex> parents;
|
||||
|
||||
// Start with root item (which obviously has invalid index).
|
||||
parents << indexForItem(m_rootItem);
|
||||
|
||||
while (!parents.isEmpty()) {
|
||||
QModelIndex active_index = parents.takeFirst();
|
||||
int row_count = rowCount(active_index);
|
||||
|
||||
if (row_count > 0) {
|
||||
// This index has children.
|
||||
// Lets take a look if our target item is among them.
|
||||
FeedsModelRootItem *active_item = itemForIndex(active_index);
|
||||
int candidate_index = active_item->childItems().indexOf(item);
|
||||
|
||||
if (candidate_index >= 0) {
|
||||
// We found our item.
|
||||
return index(candidate_index, 0, active_index);
|
||||
}
|
||||
else {
|
||||
// Item is not found, add all "categories" from active_item.
|
||||
for (int i = 0; i < row_count; i++) {
|
||||
FeedsModelRootItem *possible_category = active_item->child(i);
|
||||
|
||||
if (possible_category->kind() == FeedsModelRootItem::Category) {
|
||||
parents << index(i, 0, active_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
QModelIndex FeedsImportExportModel::parent(const QModelIndex &child) const {
|
||||
if (!child.isValid()) {
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
FeedsModelRootItem *child_item = itemForIndex(child);
|
||||
FeedsModelRootItem *parent_item = child_item->parent();
|
||||
|
||||
if (parent_item == m_rootItem) {
|
||||
return QModelIndex();
|
||||
}
|
||||
else {
|
||||
return createIndex(parent_item->row(), 0, parent_item);
|
||||
}
|
||||
}
|
||||
|
||||
int FeedsImportExportModel::rowCount(const QModelIndex &parent) const {
|
||||
if (parent.column() > 0) {
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
return itemForIndex(parent)->childCount();
|
||||
}
|
||||
}
|
||||
|
||||
int FeedsImportExportModel::columnCount(const QModelIndex &parent) const {
|
||||
Q_UNUSED(parent)
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
QVariant FeedsImportExportModel::data(const QModelIndex &index, int role) const {
|
||||
if (index.column() != 0) {
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
FeedsModelRootItem *item = itemForIndex(index);
|
||||
|
||||
if (role == Qt::CheckStateRole) {
|
||||
if (m_checkStates.contains(item)) {
|
||||
return m_checkStates.value(item);
|
||||
}
|
||||
else {
|
||||
return static_cast<int>(Qt::Unchecked);
|
||||
}
|
||||
}
|
||||
else {
|
||||
return item->data(index.column(), role);
|
||||
}
|
||||
}
|
||||
|
||||
bool FeedsImportExportModel::setData(const QModelIndex &index, const QVariant &value, int role) {
|
||||
if (index.isValid() && index.column() == 0 && role == Qt::CheckStateRole) {
|
||||
FeedsModelRootItem *item = itemForIndex(index);
|
||||
|
||||
if (item != m_rootItem) {
|
||||
m_checkStates[item] = static_cast<Qt::CheckState>(value.toInt());
|
||||
emit dataChanged(index, index);
|
||||
|
||||
if (m_recursiveChange) {
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach(FeedsModelRootItem *child, item->childItems()) {
|
||||
setData(indexForItem(child), value, Qt::CheckStateRole);
|
||||
}
|
||||
|
||||
QModelIndex parent_index = index;
|
||||
m_recursiveChange = true;
|
||||
|
||||
while ((parent_index = parent_index.parent()).isValid()) {
|
||||
// We now have parent index.
|
||||
item = item->parent();
|
||||
|
||||
// Check children of this new parent item.
|
||||
Qt::CheckState parent_state = Qt::Unchecked;
|
||||
foreach (FeedsModelRootItem *child_of_parent, item->childItems()) {
|
||||
if (m_checkStates.contains(child_of_parent) && m_checkStates[child_of_parent] == Qt::Checked) {
|
||||
parent_state = Qt::Checked;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
setData(parent_index, parent_state, Qt::CheckStateRole);
|
||||
}
|
||||
|
||||
m_recursiveChange = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Qt::ItemFlags FeedsImportExportModel::flags(const QModelIndex &index) const {
|
||||
if (!index.isValid()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
|
||||
|
||||
if ( index.column() == 0 ) {
|
||||
flags |= Qt::ItemIsUserCheckable;
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
61
src/core/feedsimportexportmodel.h
Normal file
61
src/core/feedsimportexportmodel.h
Normal file
@ -0,0 +1,61 @@
|
||||
// This file is part of RSS Guard.
|
||||
//
|
||||
// Copyright (C) 2011-2014 by Martin Rotter <rotter.martinos@gmail.com>
|
||||
//
|
||||
// RSS Guard 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 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// RSS Guard 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 RSS Guard. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef FEEDIMPORTEXPORTMODEL_H
|
||||
#define FEEDIMPORTEXPORTMODEL_H
|
||||
|
||||
#include <QAbstractItemModel>
|
||||
|
||||
#include "core/feedsmodelrootitem.h"
|
||||
|
||||
|
||||
class FeedsImportExportModel : public QAbstractItemModel {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit FeedsImportExportModel(QObject *parent = 0);
|
||||
virtual ~FeedsImportExportModel();
|
||||
|
||||
QModelIndex index(int row, int column, const QModelIndex &parent) const;
|
||||
QModelIndex parent(const QModelIndex &child) const;
|
||||
int rowCount(const QModelIndex &parent) const;
|
||||
int columnCount(const QModelIndex &parent) const;
|
||||
QVariant data(const QModelIndex &index, int role) const;
|
||||
bool setData(const QModelIndex &index, const QVariant &value, int role);
|
||||
Qt::ItemFlags flags(const QModelIndex &index) const;
|
||||
|
||||
// Returns feed/category which lies at the specified index or
|
||||
// root item if index is invalid.
|
||||
FeedsModelRootItem *itemForIndex(const QModelIndex &index) const;
|
||||
|
||||
// Returns source QModelIndex on which lies given item.
|
||||
QModelIndex indexForItem(FeedsModelRootItem *item) const;
|
||||
|
||||
FeedsModelRootItem *rootItem() const;
|
||||
void setRootItem(FeedsModelRootItem *rootItem);
|
||||
|
||||
// Exports to OPML 2.0
|
||||
// NOTE: http://dev.opml.org/spec2.html
|
||||
bool exportToOMPL20(QByteArray &result);
|
||||
|
||||
private:
|
||||
QHash<FeedsModelRootItem*, Qt::CheckState> m_checkStates;
|
||||
FeedsModelRootItem *m_rootItem;
|
||||
bool m_recursiveChange;
|
||||
};
|
||||
|
||||
#endif // FEEDIMPORTEXPORTMODEL_H
|
@ -28,11 +28,6 @@
|
||||
#include <QSqlQuery>
|
||||
#include <QSqlRecord>
|
||||
#include <QPair>
|
||||
#include <QDomDocument>
|
||||
#include <QDomElement>
|
||||
#include <QDomAttr>
|
||||
#include <QStack>
|
||||
#include <QLocale>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
@ -144,109 +139,6 @@ int FeedsModel::rowCount(const QModelIndex &parent) const {
|
||||
}
|
||||
}
|
||||
|
||||
bool FeedsModel::exportToFile(FeedsModel::ExternalFeedsFileType type, QByteArray &result) {
|
||||
switch (type) {
|
||||
case OPML20:
|
||||
return exportToOMPL20(result);
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool FeedsModel::exportToOMPL20(QByteArray &result) {
|
||||
QDomDocument opml_document;
|
||||
QDomProcessingInstruction xml_declaration = opml_document.createProcessingInstruction("xml",
|
||||
"version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"");
|
||||
opml_document.appendChild(xml_declaration);
|
||||
|
||||
// Adde OPML 2.0 metadata.
|
||||
opml_document.appendChild(opml_document.createElement("opml"));
|
||||
opml_document.documentElement().setAttribute("version", "2.0");
|
||||
|
||||
QDomElement elem_opml_head = opml_document.createElement("head");
|
||||
|
||||
QDomElement elem_opml_title = opml_document.createElement("title");
|
||||
QDomText text_opml_title = opml_document.createTextNode(QString(APP_NAME));
|
||||
elem_opml_title.appendChild(text_opml_title);
|
||||
elem_opml_head.appendChild(elem_opml_title);
|
||||
|
||||
QDomElement elem_opml_created = opml_document.createElement("dateCreated");
|
||||
QDomText text_opml_created = opml_document.createTextNode(QLocale::c().toString(QDateTime::currentDateTimeUtc(),
|
||||
"ddd, dd MMM yyyy hh:mm:ss") + " GMT");
|
||||
elem_opml_created.appendChild(text_opml_created);
|
||||
elem_opml_head.appendChild(elem_opml_created);
|
||||
opml_document.documentElement().appendChild(elem_opml_head);
|
||||
|
||||
QDomElement elem_opml_body = opml_document.createElement("body");
|
||||
|
||||
QStack<FeedsModelRootItem*> items_to_process; items_to_process.push(m_rootItem);
|
||||
QStack<QDomElement> elements_to_use; elements_to_use.push(elem_opml_body);
|
||||
|
||||
// Process all unprocessed nodes.
|
||||
while (!items_to_process.isEmpty()) {
|
||||
QDomElement active_element = elements_to_use.pop();
|
||||
FeedsModelRootItem *active_item = items_to_process.pop();
|
||||
|
||||
foreach (FeedsModelRootItem *child_item, active_item->childItems()) {
|
||||
switch (child_item->kind()) {
|
||||
case FeedsModelRootItem::Category: {
|
||||
QDomElement outline_category = opml_document.createElement("outline");
|
||||
outline_category.setAttribute("text", child_item->title());
|
||||
active_element.appendChild(outline_category);
|
||||
items_to_process.push(child_item);
|
||||
elements_to_use.push(outline_category);
|
||||
break;
|
||||
}
|
||||
|
||||
case FeedsModelRootItem::Feed: {
|
||||
FeedsModelFeed *child_feed = static_cast<FeedsModelFeed*>(child_item);
|
||||
QDomElement outline_feed = opml_document.createElement("outline");
|
||||
outline_feed.setAttribute("text", child_feed->title());
|
||||
outline_feed.setAttribute("xmlUrl", child_feed->url());
|
||||
outline_feed.setAttribute("description", child_feed->description());
|
||||
outline_feed.setAttribute("encoding", child_feed->encoding());
|
||||
|
||||
switch (child_feed->type()) {
|
||||
case FeedsModelFeed::Rss0X:
|
||||
case FeedsModelFeed::Rss2X:
|
||||
outline_feed.setAttribute("version", "RSS");
|
||||
break;
|
||||
|
||||
case FeedsModelFeed::Rdf:
|
||||
outline_feed.setAttribute("version", "RSS");
|
||||
break;
|
||||
|
||||
case FeedsModelFeed::Atom10:
|
||||
outline_feed.setAttribute("version", "ATOM");
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (child_feed->passwordProtected()) {
|
||||
outline_feed.setAttribute("username", child_feed->username());
|
||||
outline_feed.setAttribute("password", child_feed->password());
|
||||
}
|
||||
|
||||
active_element.appendChild(outline_feed);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
opml_document.documentElement().appendChild(elem_opml_body);
|
||||
|
||||
|
||||
result = opml_document.toByteArray(2);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FeedsModel::removeItem(const QModelIndex &index) {
|
||||
if (index.isValid()) {
|
||||
QModelIndex parent_index = index.parent();
|
||||
@ -597,7 +489,6 @@ FeedsModelCategory *FeedsModel::categoryForIndex(const QModelIndex &index) const
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
QModelIndex FeedsModel::indexForItem(FeedsModelRootItem *item) const {
|
||||
if (item == NULL || item->kind() == FeedsModelRootItem::RootItem) {
|
||||
// Root item lies on invalid index.
|
||||
|
@ -39,10 +39,6 @@ class FeedsModel : public QAbstractItemModel {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum ExternalFeedsFileType {
|
||||
OPML20 = 0
|
||||
};
|
||||
|
||||
// Constructors and destructors.
|
||||
explicit FeedsModel(QObject *parent = 0);
|
||||
virtual ~FeedsModel();
|
||||
@ -72,13 +68,6 @@ class FeedsModel : public QAbstractItemModel {
|
||||
return m_rootItem->countOfUnreadMessages();
|
||||
}
|
||||
|
||||
// Import/export.
|
||||
bool exportToFile(ExternalFeedsFileType type, QByteArray &result);
|
||||
|
||||
// Exports to OPML 2.0
|
||||
// NOTE: http://dev.opml.org/spec2.html
|
||||
bool exportToOMPL20(QByteArray &result);
|
||||
|
||||
// Removes item with given index.
|
||||
bool removeItem(const QModelIndex &index);
|
||||
|
||||
|
183
src/gui/formimportexport.cpp
Normal file
183
src/gui/formimportexport.cpp
Normal file
@ -0,0 +1,183 @@
|
||||
// This file is part of RSS Guard.
|
||||
//
|
||||
// Copyright (C) 2011-2014 by Martin Rotter <rotter.martinos@gmail.com>
|
||||
//
|
||||
// RSS Guard 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 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// RSS Guard 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 RSS Guard. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "gui/formimportexport.h"
|
||||
|
||||
#include "core/feedsimportexportmodel.h"
|
||||
#include "core/feedsmodel.h"
|
||||
#include "miscellaneous/application.h"
|
||||
#include "gui/feedmessageviewer.h"
|
||||
#include "gui/formmain.h"
|
||||
#include "gui/feedsview.h"
|
||||
|
||||
#include <QFileDialog>
|
||||
#include <QTextStream>
|
||||
|
||||
|
||||
FormImportExport::FormImportExport(QWidget *parent) : QDialog(parent), m_ui(new Ui::FormImportExport), m_mode(Import) {
|
||||
m_ui->setupUi(this);
|
||||
m_model = new FeedsImportExportModel(m_ui->m_treeFeeds);
|
||||
m_ui->m_treeFeeds->setModel(m_model);
|
||||
|
||||
m_ui->m_lblSelectFile->setStatus(WidgetWithStatus::Error, tr("No file is selected."), tr("No file is selected."));
|
||||
m_ui->m_buttonBox->button(QDialogButtonBox::Ok)->disconnect();
|
||||
m_ui->m_lblResult->setStatus(WidgetWithStatus::Warning, tr("No operation executed yet."), tr("No operation executed yet."));
|
||||
|
||||
connect(m_ui->m_buttonBox->button(QDialogButtonBox::Ok), SIGNAL(clicked()), this, SLOT(performAction()));
|
||||
connect(m_ui->m_btnSelectFile, SIGNAL(clicked()), this, SLOT(selectFile()));
|
||||
}
|
||||
|
||||
FormImportExport::~FormImportExport() {
|
||||
delete m_ui;
|
||||
}
|
||||
|
||||
FormImportExport::Mode FormImportExport::mode() const {
|
||||
return m_mode;
|
||||
}
|
||||
|
||||
void FormImportExport::setMode(const Mode &mode) {
|
||||
m_mode = mode;
|
||||
|
||||
switch (m_mode) {
|
||||
case Export: {
|
||||
m_model->setRootItem(qApp->mainForm()->tabWidget()->feedMessageViewer()->feedsView()->sourceModel()->rootItem());
|
||||
m_ui->m_treeFeeds->expandAll();
|
||||
setWindowTitle(tr("Export feeds"));
|
||||
setWindowIcon(qApp->icons()->fromTheme("document-export"));
|
||||
m_ui->m_groupFile->setTitle(tr("Destination file"));
|
||||
m_ui->m_groupFeeds->setTitle(tr("Source feeds && categories"));
|
||||
break;
|
||||
}
|
||||
|
||||
case Import: {
|
||||
m_ui->m_groupFile->setTitle(tr("Source file"));
|
||||
m_ui->m_groupFeeds->setTitle(tr("Target feeds && categories"));
|
||||
m_ui->m_treeFeeds->setDisabled(true);
|
||||
setWindowTitle(tr("Import feeds"));
|
||||
setWindowIcon(qApp->icons()->fromTheme("document-import"));
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void FormImportExport::selectFile() {
|
||||
switch (m_mode) {
|
||||
case Import:
|
||||
selectImportFile();
|
||||
break;
|
||||
|
||||
case Export: {
|
||||
selectExportFile();
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void FormImportExport::selectExportFile() {
|
||||
QString filter_opml20 = tr("OPML 2.0 files (*.opml)");
|
||||
|
||||
QString filter;
|
||||
QString selected_filter;
|
||||
|
||||
// Add more filters here.
|
||||
filter += filter_opml20;
|
||||
|
||||
QString selected_file = QFileDialog::getSaveFileName(this, tr("Select file for feeds export"),
|
||||
QDir::homePath(), filter, &selected_filter);
|
||||
|
||||
|
||||
|
||||
if (!selected_file.isEmpty()) {
|
||||
if (selected_filter == filter_opml20) {
|
||||
m_conversionType = OPML20;
|
||||
}
|
||||
// NOTE: Add other types here.
|
||||
|
||||
m_ui->m_lblSelectFile->setStatus(WidgetWithStatus::Ok, QDir::toNativeSeparators(selected_file), tr("File is selected."));
|
||||
}
|
||||
else {
|
||||
m_ui->m_lblSelectFile->setStatus(WidgetWithStatus::Error, tr("No file is selected."), tr("No file is selected."));
|
||||
}
|
||||
|
||||
m_ui->m_buttonBox->button(QDialogButtonBox::Ok)->setDisabled(selected_file.isEmpty());
|
||||
}
|
||||
|
||||
void FormImportExport::selectImportFile() {
|
||||
// TODO: vyber soubor a kdyz je vybranej, tak rozparsovat a vytvorit dle nej strukturu
|
||||
// itemů a narvat do modelu.
|
||||
}
|
||||
|
||||
void FormImportExport::performAction() {
|
||||
switch (m_mode) {
|
||||
case Import:
|
||||
importFeeds();
|
||||
break;
|
||||
|
||||
case Export:
|
||||
exportFeeds();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void FormImportExport::exportFeeds() {
|
||||
switch (m_conversionType) {
|
||||
case OPML20: {
|
||||
QByteArray result_data;
|
||||
bool result_export = m_model->exportToOMPL20(result_data);
|
||||
|
||||
if (result_export) {
|
||||
// Save exported data.
|
||||
QFile output_file(m_ui->m_lblSelectFile->label()->text());
|
||||
|
||||
if (output_file.open(QIODevice::Unbuffered | QIODevice::Truncate | QIODevice::WriteOnly)) {
|
||||
QTextStream stream(&output_file);
|
||||
|
||||
stream.setCodec("UTF-8");
|
||||
stream << QString::fromUtf8(result_data);
|
||||
output_file.flush();
|
||||
output_file.close();
|
||||
|
||||
m_ui->m_lblResult->setStatus(WidgetWithStatus::Ok, tr("Feeds were exported successfully."),
|
||||
tr("Feeds were exported successfully."));
|
||||
}
|
||||
else {
|
||||
m_ui->m_lblResult->setStatus(WidgetWithStatus::Error, tr("Cannot write into destination file."),
|
||||
tr("Cannot write into destination file."));
|
||||
}
|
||||
}
|
||||
else {
|
||||
m_ui->m_lblResult->setStatus(WidgetWithStatus::Error, tr("Critical error occurred."), tr("Critical error occurred."));
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void FormImportExport::importFeeds() {
|
||||
|
||||
}
|
68
src/gui/formimportexport.h
Normal file
68
src/gui/formimportexport.h
Normal file
@ -0,0 +1,68 @@
|
||||
// This file is part of RSS Guard.
|
||||
//
|
||||
// Copyright (C) 2011-2014 by Martin Rotter <rotter.martinos@gmail.com>
|
||||
//
|
||||
// RSS Guard 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 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// RSS Guard 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 RSS Guard. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef FORMEXPORT_H
|
||||
#define FORMEXPORT_H
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
#include "ui_formimportexport.h"
|
||||
|
||||
|
||||
namespace Ui {
|
||||
class FormExport;
|
||||
}
|
||||
|
||||
class FeedsImportExportModel;
|
||||
|
||||
class FormImportExport : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum ConversionType {
|
||||
OPML20 = 0
|
||||
};
|
||||
|
||||
enum Mode {
|
||||
Import,
|
||||
Export
|
||||
};
|
||||
|
||||
explicit FormImportExport(QWidget *parent = 0);
|
||||
virtual ~FormImportExport();
|
||||
|
||||
Mode mode() const;
|
||||
void setMode(const Mode &mode);
|
||||
|
||||
private slots:
|
||||
void performAction();
|
||||
void selectFile();
|
||||
|
||||
private:
|
||||
void selectExportFile();
|
||||
void selectImportFile();
|
||||
|
||||
void exportFeeds();
|
||||
void importFeeds();
|
||||
|
||||
Ui::FormImportExport *m_ui;
|
||||
Mode m_mode;
|
||||
ConversionType m_conversionType;
|
||||
FeedsImportExportModel *m_model;
|
||||
};
|
||||
|
||||
#endif // FORMEXPORT_H
|
148
src/gui/formimportexport.ui
Normal file
148
src/gui/formimportexport.ui
Normal file
@ -0,0 +1,148 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>FormImportExport</class>
|
||||
<widget class="QDialog" name="FormImportExport">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>478</width>
|
||||
<height>434</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Dialog</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="m_groupFile">
|
||||
<property name="title">
|
||||
<string/>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QPushButton" name="m_btnSelectFile">
|
||||
<property name="text">
|
||||
<string>&Select file</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="LabelWithStatus" name="m_lblSelectFile" native="true">
|
||||
<property name="layoutDirection">
|
||||
<enum>Qt::RightToLeft</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="m_groupFeeds">
|
||||
<property name="title">
|
||||
<string/>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QTreeView" name="m_treeFeeds">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>1</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="indentation">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="rootIsDecorated">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="uniformRowHeights">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="animated">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="allColumnsShowFocus">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<attribute name="headerVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>Operation results</string>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout_2">
|
||||
<item row="0" column="0">
|
||||
<widget class="LabelWithStatus" name="m_lblResult" native="true">
|
||||
<property name="layoutDirection">
|
||||
<enum>Qt::RightToLeft</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="m_buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>LabelWithStatus</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>labelwithstatus.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>m_buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>FormImportExport</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>m_buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>FormImportExport</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
@ -34,6 +34,7 @@
|
||||
#include "gui/statusbar.h"
|
||||
#include "gui/feedmessageviewer.h"
|
||||
#include "gui/formupdate.h"
|
||||
#include "gui/formimportexport.h"
|
||||
|
||||
#include <QCloseEvent>
|
||||
#include <QSessionManager>
|
||||
@ -436,7 +437,13 @@ void FormMain::loadWebBrowserMenu(int index) {
|
||||
m_ui->m_actionCloseCurrentTab->setEnabled(m_ui->m_tabWidget->tabBar()->tabType(index) == TabBar::Closable);
|
||||
}
|
||||
|
||||
void FormMain::exportFeeds() {
|
||||
void FormMain::exportFeeds() {
|
||||
QPointer<FormImportExport> form = new FormImportExport(this);
|
||||
form.data()->setMode(FormImportExport::Export);
|
||||
form.data()->exec();
|
||||
delete form.data();
|
||||
|
||||
/*
|
||||
QString filter_opml20 = tr("OPML 2.0 files (*.opml)");
|
||||
|
||||
QString filter;
|
||||
@ -476,11 +483,14 @@ void FormMain::exportFeeds() {
|
||||
output_file.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
void FormMain::importFeeds() {
|
||||
|
||||
QPointer<FormImportExport> form = new FormImportExport(this);
|
||||
form.data()->setMode(FormImportExport::Import);
|
||||
form.data()->exec();
|
||||
delete form.data();
|
||||
}
|
||||
|
||||
void FormMain::changeEvent(QEvent *event) {
|
||||
|
@ -17,7 +17,7 @@
|
||||
<item row="0" column="1">
|
||||
<widget class="QStackedWidget" name="m_stackedSettings">
|
||||
<property name="currentIndex">
|
||||
<number>3</number>
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="m_pageGeneral">
|
||||
<layout class="QFormLayout" name="formLayout_5">
|
||||
@ -241,8 +241,8 @@ Authors of this application are NOT responsible for lost data.</string>
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>100</width>
|
||||
<height>30</height>
|
||||
<width>588</width>
|
||||
<height>390</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
|
Loading…
x
Reference in New Issue
Block a user