Merge branch 'master' of bitbucket.org:skunkos/rssguard

This commit is contained in:
Martin Rotter 2014-03-26 19:50:55 +01:00
commit a49fd801d9
44 changed files with 826 additions and 960 deletions

View File

@ -279,8 +279,6 @@ set(APP_SOURCES
src/gui/shortcutcatcher.cpp
src/gui/shortcutbutton.cpp
src/gui/dynamicshortcutswidget.cpp
src/gui/webbrowser.cpp
src/gui/webview.cpp
src/gui/baselineedit.cpp
src/gui/locationlineedit.cpp
src/gui/tabwidget.cpp
@ -307,10 +305,6 @@ set(APP_SOURCES
src/core/systemfactory.cpp
src/core/localization.cpp
src/core/dynamicshortcuts.cpp
src/core/basenetworkaccessmanager.cpp
src/core/webpage.cpp
src/core/webbrowsernetworkaccessmanager.cpp
src/core/silentnetworkaccessmanager.cpp
src/core/textfactory.cpp
src/core/databasefactory.cpp
src/core/messagesmodel.cpp
@ -320,14 +314,20 @@ set(APP_SOURCES
src/core/feedsmodelcategory.cpp
src/core/feedsmodelrootitem.cpp
src/core/feedsmodelfeed.cpp
src/core/feedsmodelstandardcategory.cpp
src/core/feedsmodelstandardfeed.cpp
src/core/parsingfactory.cpp
src/core/feeddownloader.cpp
src/core/networkfactory.cpp
src/core/webfactory.cpp
# Basic application sources.
# NETWORK-WEB sources.
src/network-web/basenetworkaccessmanager.cpp
src/network-web/webpage.cpp
src/network-web/webbrowsernetworkaccessmanager.cpp
src/network-web/silentnetworkaccessmanager.cpp
src/network-web/networkfactory.cpp
src/network-web/webfactory.cpp
src/network-web/webbrowser.cpp
src/network-web/webview.cpp
# MAIN sources.
src/main.cpp
)
@ -349,8 +349,6 @@ set(APP_HEADERS
src/gui/shortcutcatcher.h
src/gui/shortcutbutton.h
src/gui/dynamicshortcutswidget.h
src/gui/webbrowser.h
src/gui/webview.h
src/gui/baselineedit.h
src/gui/locationlineedit.h
src/gui/tabwidget.h
@ -373,10 +371,7 @@ set(APP_HEADERS
# CORE headers.
src/core/settings.h
src/core/localization.h
src/core/basenetworkaccessmanager.h
src/core/webbrowsernetworkaccessmanager.h
src/core/silentnetworkaccessmanager.h
src/core/webpage.h
src/network-web/webpage.h
src/core/systemfactory.h
src/core/databasefactory.h
src/core/messagesmodel.h
@ -384,7 +379,14 @@ set(APP_HEADERS
src/core/feedsmodel.h
src/core/feedsproxymodel.h
src/core/feeddownloader.h
src/core/webfactory.h
# NETWORK-WEB headers.
src/network-web/basenetworkaccessmanager.h
src/network-web/webbrowsernetworkaccessmanager.h
src/network-web/silentnetworkaccessmanager.h
src/network-web/webfactory.h
src/network-web/webbrowser.h
src/network-web/webview.h
)
# Add form files.
@ -454,6 +456,7 @@ endif(${USE_QT_5})
include_directories (
${CMAKE_CURRENT_SOURCE_DIR}/src
${CMAKE_CURRENT_SOURCE_DIR}/src/gui
${CMAKE_CURRENT_SOURCE_DIR}/src/network-web
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_BINARY_DIR}/src
)

View File

@ -5,6 +5,7 @@ Fixed:
<ul>
<li>Informative buttons now do not accept focus.</li>
<li>Button for resetting keyboard shortcut now works.</li>
<li>Fixed web browser tab title/icon handling (bug #39).</li>
</ul>
Added:
@ -19,6 +20,7 @@ Added:
Changed:
<ul>
<li>Massive code cleanups.</li>
<li>Removed "progress bar" from address text edit of web browser.</li>
<li>Removed "blau" skin due to its slowness.</li>
<li>Tab order and default widget changed for category/feed add/edit dialogs.</li>

View File

@ -19,8 +19,8 @@
#include "core/defs.h"
#include "core/databasefactory.h"
#include "core/feedsmodelstandardcategory.h"
#include "core/feedsmodelstandardfeed.h"
#include "core/feedsmodelcategory.h"
#include "core/feedsmodelfeed.h"
#include "core/textfactory.h"
#include "gui/iconthemefactory.h"
#include "gui/iconfactory.h"
@ -177,7 +177,7 @@ bool FeedsModel::removeItem(const QModelIndex &index) {
return false;
}
bool FeedsModel::addStandardCategory(FeedsModelStandardCategory *category,
bool FeedsModel::addCategory(FeedsModelCategory *category,
FeedsModelRootItem *parent) {
// Get index of parent item (parent standard category).
QModelIndex parent_index = indexForItem(parent);
@ -225,8 +225,8 @@ bool FeedsModel::addStandardCategory(FeedsModelStandardCategory *category,
return true;
}
bool FeedsModel::editStandardCategory(FeedsModelStandardCategory *original_category,
FeedsModelStandardCategory *new_category) {
bool FeedsModel::editCategory(FeedsModelCategory *original_category,
FeedsModelCategory *new_category) {
QSqlDatabase database = DatabaseFactory::instance()->connection(objectName(),
DatabaseFactory::FromSettings);
QSqlQuery query_update_category(database);
@ -281,7 +281,7 @@ bool FeedsModel::editStandardCategory(FeedsModelStandardCategory *original_categ
return true;
}
bool FeedsModel::addStandardFeed(FeedsModelStandardFeed *feed,
bool FeedsModel::addFeed(FeedsModelFeed *feed,
FeedsModelRootItem *parent) {
// Get index of parent item (parent standard category).
QModelIndex parent_index = indexForItem(parent);
@ -336,8 +336,8 @@ bool FeedsModel::addStandardFeed(FeedsModelStandardFeed *feed,
return true;
}
bool FeedsModel::editStandardFeed(FeedsModelStandardFeed *original_feed,
FeedsModelStandardFeed *new_feed) {
bool FeedsModel::editFeed(FeedsModelFeed *original_feed,
FeedsModelFeed *new_feed) {
QSqlDatabase database = DatabaseFactory::instance()->connection(objectName(),
DatabaseFactory::FromSettings);
QSqlQuery query_update_feed(database);
@ -413,34 +413,32 @@ QList<FeedsModelFeed*> FeedsModel::feedsForScheduledUpdate(bool auto_update_now)
QList<FeedsModelFeed*> feeds_for_update;
foreach (FeedsModelFeed *feed, allFeeds()) {
FeedsModelStandardFeed *std_feed = static_cast<FeedsModelStandardFeed*>(feed);
switch (std_feed->autoUpdateType()) {
case FeedsModelStandardFeed::DontAutoUpdate:
switch (feed->autoUpdateType()) {
case FeedsModelFeed::DontAutoUpdate:
// Do not auto-update this feed ever.
continue;
case FeedsModelStandardFeed::DefaultAutoUpdate:
case FeedsModelFeed::DefaultAutoUpdate:
if (auto_update_now) {
feeds_for_update.append(feed);
}
break;
case FeedsModelStandardFeed::SpecificAutoUpdate:
case FeedsModelFeed::SpecificAutoUpdate:
default:
int remaining_interval = std_feed->autoUpdateRemainingInterval();
int remaining_interval = feed->autoUpdateRemainingInterval();
if (--remaining_interval <= 0) {
// Interval of this feed passed, include this feed in the output list
// and reset the interval.
feeds_for_update.append(feed);
std_feed->setAutoUpdateRemainingInterval(std_feed->autoUpdateInitialInterval());
feed->setAutoUpdateRemainingInterval(feed->autoUpdateInitialInterval());
}
else {
// Interval did not pass, set new decremented interval and do NOT
// include this feed in the output list.
std_feed->setAutoUpdateRemainingInterval(remaining_interval);
feed->setAutoUpdateRemainingInterval(remaining_interval);
}
break;
@ -613,13 +611,12 @@ void FeedsModel::loadFromDatabase() {
case FeedsModelCategory::Standard: {
CategoryAssignmentItem pair;
pair.first = query_categories.value(CAT_DB_PARENT_ID_INDEX).toInt();
pair.second = FeedsModelStandardCategory::loadFromRecord(query_categories.record());
pair.second = FeedsModelCategory::loadFromRecord(query_categories.record());
categories << pair;
break;
}
case FeedsModelCategory::Feedly:
default:
break;
}
@ -639,13 +636,13 @@ void FeedsModel::loadFromDatabase() {
FeedsModelFeed::Type type = static_cast<FeedsModelFeed::Type>(query_feeds.value(FDS_DB_TYPE_INDEX).toInt());
switch (type) {
case FeedsModelFeed::StandardAtom10:
case FeedsModelFeed::StandardRdf:
case FeedsModelFeed::StandardRss0X:
case FeedsModelFeed::StandardRss2X: {
case FeedsModelFeed::Atom10:
case FeedsModelFeed::Rdf:
case FeedsModelFeed::Rss0X:
case FeedsModelFeed::Rss2X: {
FeedAssignmentItem pair;
pair.first = query_feeds.value(FDS_DB_CATEGORY_INDEX).toInt();
pair.second = FeedsModelStandardFeed::loadFromRecord(query_feeds.record());
pair.second = FeedsModelFeed::loadFromRecord(query_feeds.record());
pair.second->setType(type);
feeds << pair;

View File

@ -28,8 +28,6 @@
class FeedsModelCategory;
class FeedsModelFeed;
class FeedsModelStandardCategory;
class FeedsModelStandardFeed;
typedef QList<QPair<int, FeedsModelCategory*> > CategoryAssignment;
typedef QPair<int, FeedsModelCategory*> CategoryAssignmentItem;
@ -72,20 +70,20 @@ class FeedsModel : public QAbstractItemModel {
bool removeItem(const QModelIndex &index);
// Standard category manipulators.
bool addStandardCategory(FeedsModelStandardCategory *category,
FeedsModelRootItem *parent);
bool editStandardCategory(FeedsModelStandardCategory *original_category,
FeedsModelStandardCategory *new_category);
bool addCategory(FeedsModelCategory *category,
FeedsModelRootItem *parent);
bool editCategory(FeedsModelCategory *original_category,
FeedsModelCategory *new_category);
// Standard feed manipulators.
bool addStandardFeed(FeedsModelStandardFeed *feed,
FeedsModelRootItem *parent);
bool addFeed(FeedsModelFeed *feed,
FeedsModelRootItem *parent);
// New feed is just temporary feed, it is not added to the model.
// It is used to fetch its data to the original feed
// and the original feed is moved if needed.
bool editStandardFeed(FeedsModelStandardFeed *original_feed,
FeedsModelStandardFeed *new_feed);
bool editFeed(FeedsModelFeed *original_feed,
FeedsModelFeed *new_feed);
// Returns the list of updates which should be updated
// according to auto-update schedule.

View File

@ -17,12 +17,20 @@
#include "core/feedsmodelcategory.h"
#include "core/feedsmodelstandardcategory.h"
#include "core/feedsmodelstandardfeed.h"
#include "core/defs.h"
#include "core/databasefactory.h"
#include "core/textfactory.h"
#include "core/settings.h"
#include "gui/iconthemefactory.h"
#include "gui/iconfactory.h"
#include <QVariant>
#include <QSqlQuery>
FeedsModelCategory::FeedsModelCategory(FeedsModelRootItem *parent_item)
: FeedsModelRootItem(parent_item) {
m_type = Standard;
m_kind = FeedsModelRootItem::Category;
}
@ -40,4 +48,113 @@ FeedsModelCategory::FeedsModelCategory(const FeedsModelCategory &other)
}
FeedsModelCategory::~FeedsModelCategory() {
qDebug("Destroying FeedsModelCategory instance.");
}
QVariant FeedsModelCategory::data(int column, int role) const {
switch (role) {
case Qt::ToolTipRole:
if (column == FDS_MODEL_TITLE_INDEX) {
//: Tooltip for standard feed.
return tr("%1 (category)\n"
"%2%3").arg(m_title,
m_description,
m_childItems.size() == 0 ?
tr("\n\nThis category does not contain any nested items.") :
"");
}
else if (column == FDS_MODEL_COUNTS_INDEX) {
//: Tooltip for "unread" column of feed list.
return tr("%n unread message(s).", "", countOfUnreadMessages());
}
else {
return QVariant();
}
case Qt::EditRole:
if (column == FDS_MODEL_TITLE_INDEX) {
return m_title;
}
else if (column == FDS_MODEL_COUNTS_INDEX) {
return countOfUnreadMessages();
}
else {
return QVariant();
}
case Qt::FontRole:
return countOfUnreadMessages() > 0 ? m_boldFont : m_normalFont;
case Qt::DisplayRole:
if (column == FDS_MODEL_TITLE_INDEX) {
return m_title;
}
else if (column == FDS_MODEL_COUNTS_INDEX) {
return Settings::instance()->value(APP_CFG_FEEDS,
"count_format",
"(%unread)").toString()
.replace("%unread", QString::number(countOfUnreadMessages()))
.replace("%all", QString::number(countOfAllMessages()));
}
else {
return QVariant();
}
case Qt::DecorationRole:
if (column == FDS_MODEL_TITLE_INDEX) {
return m_icon;
}
else {
return QVariant();
}
case Qt::TextAlignmentRole:
if (column == FDS_MODEL_COUNTS_INDEX) {
return Qt::AlignCenter;
}
else {
return QVariant();
}
default:
return QVariant();
}
}
bool FeedsModelCategory::removeItself() {
bool result = true;
// Remove all child items (feeds, categories.)
foreach (FeedsModelRootItem *child, m_childItems) {
result &= child->removeItself();
}
if (!result) {
return result;
}
// Children are removed, remove this standard category too.
QSqlDatabase database = DatabaseFactory::instance()->connection("FeedsModelCategory",
DatabaseFactory::FromSettings);
QSqlQuery query_remove(database);
query_remove.setForwardOnly(true);
// Remove all messages from this standard feed.
query_remove.prepare("DELETE FROM Categories WHERE id = :category;");
query_remove.bindValue(":category", id());
return query_remove.exec();
}
FeedsModelCategory *FeedsModelCategory::loadFromRecord(const QSqlRecord &record) {
FeedsModelCategory *category = new FeedsModelCategory(NULL);
category->setId(record.value(CAT_DB_ID_INDEX).toInt());
category->setTitle(record.value(CAT_DB_TITLE_INDEX).toString());
category->setDescription(record.value(CAT_DB_DESCRIPTION_INDEX).toString());
category->setCreationDate(TextFactory::parseDateTime(record.value(CAT_DB_DCREATED_INDEX).value<qint64>()).toLocalTime());
category->setIcon(IconFactory::fromByteArray(record.value(CAT_DB_ICON_INDEX).toByteArray()));
return category;
}

View File

@ -20,6 +20,9 @@
#include "core/feedsmodelrootitem.h"
#include <QSqlRecord>
#include <QApplication>
class FeedsModelFeed;
@ -27,12 +30,13 @@ class FeedsModelFeed;
// NOTE: This class should be derived to create PARTICULAR category types.
// NOTE: This class should not be instantiated directly.
class FeedsModelCategory : public FeedsModelRootItem {
Q_DECLARE_TR_FUNCTIONS(FeedsModelCategory)
public:
// Describes possible types of categories.
// NOTE: This is equivavelnt to Categories(type).
enum Type {
Standard = 0,
Feedly = 1
Standard = 0
};
// Constructors and destructors
@ -40,6 +44,13 @@ class FeedsModelCategory : public FeedsModelRootItem {
explicit FeedsModelCategory(const FeedsModelCategory &other);
virtual ~FeedsModelCategory();
// Returns the actual data representation of standard category.
QVariant data(int column, int role) const;
// Removes category and all its children from persistent
// database.
bool removeItself();
// All types of categories offer these getters/setters.
inline Type type() const {
return m_type;
@ -49,6 +60,9 @@ class FeedsModelCategory : public FeedsModelRootItem {
m_type = type;
}
// Loads particular "standard category" from given sql record.
static FeedsModelCategory *loadFromRecord(const QSqlRecord &record);
protected:
Type m_type;
};

View File

@ -17,23 +17,38 @@
#include "core/feedsmodelfeed.h"
#include "core/defs.h"
#include "core/settings.h"
#include "core/parsingfactory.h"
#include "core/databasefactory.h"
#include "core/textfactory.h"
#include "network-web/networkfactory.h"
#include "gui/iconfactory.h"
#include "gui/iconthemefactory.h"
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QSqlError>
#include <QVariant>
#include <QTextCodec>
#include <QDomDocument>
#include <QDomNode>
#include <QDomElement>
#include <QXmlStreamReader>
FeedsModelFeed::FeedsModelFeed(FeedsModelRootItem *parent_item)
: FeedsModelRootItem(parent_item),
m_type(StandardRss0X),
m_type(Rss0X),
m_totalCount(0),
m_unreadCount(0) {
m_unreadCount(0),
m_autoUpdateType(DontAutoUpdate),
m_autoUpdateInitialInterval(DEFAULT_AUTO_UPDATE_INTERVAL) {
m_kind = FeedsModelRootItem::Feed;
}
FeedsModelFeed::~FeedsModelFeed() {
qDebug("Destroying FeedsModelFeed instance.");
}
int FeedsModelFeed::childCount() const {
@ -51,16 +66,16 @@ int FeedsModelFeed::countOfUnreadMessages() const {
QString FeedsModelFeed::typeToString(FeedsModelFeed::Type type) {
switch (type) {
case StandardAtom10:
case Atom10:
return "ATOM 1.0";
case StandardRdf:
return "RDF";
case Rdf:
return "RDF (RSS 1.0)";
case StandardRss0X:
case Rss0X:
return "RSS 0.91/0.92/0.93";
case StandardRss2X:
case Rss2X:
default:
return "RSS 2.0/2.0.1";
}
@ -88,5 +103,384 @@ void FeedsModelFeed::updateCounts(bool including_total_count) {
}
}
FeedsModelFeed *FeedsModelFeed::loadFromRecord(const QSqlRecord &record) {
FeedsModelFeed *feed = new FeedsModelFeed();
feed->setTitle(record.value(FDS_DB_TITLE_INDEX).toString());
feed->setId(record.value(FDS_DB_ID_INDEX).toInt());
feed->setDescription(record.value(FDS_DB_DESCRIPTION_INDEX).toString());
feed->setCreationDate(TextFactory::parseDateTime(record.value(FDS_DB_DCREATED_INDEX).value<qint64>()).toLocalTime());
feed->setIcon(IconFactory::fromByteArray(record.value(FDS_DB_ICON_INDEX).toByteArray()));
feed->setEncoding(record.value(FDS_DB_ENCODING_INDEX).toString());
feed->setUrl(record.value(FDS_DB_URL_INDEX).toString());
feed->setPasswordProtected(record.value(FDS_DB_PROTECTED_INDEX).toBool());
feed->setUsername(record.value(FDS_DB_USERNAME_INDEX).toString());
feed->setPassword(record.value(FDS_DB_PASSWORD_INDEX).toString());
feed->setAutoUpdateType(static_cast<FeedsModelFeed::AutoUpdateType>(record.value(FDS_DB_UPDATE_TYPE_INDEX).toInt()));
feed->setAutoUpdateInitialInterval(record.value(FDS_DB_UPDATE_INTERVAL_INDEX).toInt());
feed->updateCounts();
return feed;
}
QPair<FeedsModelFeed*, QNetworkReply::NetworkError> FeedsModelFeed::guessFeed(const QString &url,
const QString &username,
const QString &password) {
QPair<FeedsModelFeed*, QNetworkReply::NetworkError> result; result.first = NULL;
// Try to obtain icon.
QIcon icon_data;
if ((result.second = NetworkFactory::downloadIcon(url, 5000, icon_data)) ==
QNetworkReply::NoError) {
// Icon for feed was downloaded and is stored now in _icon_data.
result.first = new FeedsModelFeed();
result.first->setIcon(icon_data);
}
QByteArray feed_contents;
if ((result.second = NetworkFactory::downloadFeedFile(url,
Settings::instance()->value(APP_CFG_FEEDS, "feed_update_timeout", DOWNLOAD_TIMEOUT).toInt(),
feed_contents,
!username.isEmpty(),
username,
password)) == QNetworkReply::NoError) {
// Feed XML was obtained, now we need to try to guess
// its encoding before we can read further data.
QString xml_schema_encoding;
QString xml_contents_encoded;
QRegExp encoding_rexp("encoding=\"[^\"]\\S+\"");
if (encoding_rexp.indexIn(feed_contents) != -1 &&
!(xml_schema_encoding = encoding_rexp.cap(0)).isEmpty()) {
// Some "encoding" attribute was found get the encoding
// out of it.
encoding_rexp.setPattern("[^\"]\\S+[^\"]");
encoding_rexp.indexIn(xml_schema_encoding, 9);
xml_schema_encoding = encoding_rexp.cap(0);
}
if (result.first == NULL) {
result.first = new FeedsModelFeed();
}
QTextCodec *custom_codec = QTextCodec::codecForName(xml_schema_encoding.toLocal8Bit());
if (custom_codec != NULL) {
// Feed encoding was probably guessed.
xml_contents_encoded = custom_codec->toUnicode(feed_contents);
result.first->setEncoding(xml_schema_encoding);
}
else {
// Feed encoding probably not guessed, set it as
// default.
xml_contents_encoded = feed_contents;
result.first->setEncoding(DEFAULT_FEED_ENCODING);
}
// Feed XML was obtained, guess it now.
QDomDocument xml_document;
QString error_msg;
int error_line, error_column;
if (!xml_document.setContent(xml_contents_encoded,
&error_msg,
&error_line,
&error_column)) {
qDebug("XML of feed '%s' is not valid and cannot be loaded. Error: '%s' "
"(line %d, column %d).",
qPrintable(url),
qPrintable(error_msg),
error_line, error_column);
result.second = QNetworkReply::UnknownContentError;
// XML is invalid, exit.
return result;
}
QDomElement root_element = xml_document.documentElement();
QString root_tag_name = root_element.tagName();
if (root_tag_name == "rdf:RDF") {
// We found RDF feed.
QDomElement channel_element = root_element.namedItem("channel").toElement();
result.first->setType(Rdf);
result.first->setTitle(channel_element.namedItem("title").toElement().text());
result.first->setDescription(channel_element.namedItem("description").toElement().text());
}
else if (root_tag_name == "rss") {
// We found RSS 0.91/0.92/0.93/2.0/2.0.1 feed.
QString rss_type = root_element.attribute("version", "2.0");
if (rss_type == "0.91" || rss_type == "0.92" || rss_type == "0.93") {
result.first->setType(Rss0X);
}
else {
result.first->setType(Rss2X);
}
QDomElement channel_element = root_element.namedItem("channel").toElement();
result.first->setTitle(channel_element.namedItem("title").toElement().text());
result.first->setDescription(channel_element.namedItem("description").toElement().text());
}
else if (root_tag_name == "feed") {
// We found ATOM feed.
result.first->setType(Atom10);
result.first->setTitle(root_element.namedItem("title").toElement().text());
result.first->setDescription(root_element.namedItem("subtitle").toElement().text());
}
else {
// File was downloaded and it really was XML file
// but feed format was NOT recognized.
result.second = QNetworkReply::UnknownContentError;
}
}
return result;
}
QVariant FeedsModelFeed::data(int column, int role) const {
switch (role) {
case Qt::DisplayRole:
if (column == FDS_MODEL_TITLE_INDEX) {
return m_title;
}
else if (column == FDS_MODEL_COUNTS_INDEX) {
return Settings::instance()->value(APP_CFG_FEEDS,
"count_format",
"(%unread)").toString()
.replace("%unread", QString::number(countOfUnreadMessages()))
.replace("%all", QString::number(countOfAllMessages()));
}
else {
return QVariant();
}
case Qt::EditRole:
if (column == FDS_MODEL_TITLE_INDEX) {
return m_title;
}
else if (column == FDS_MODEL_COUNTS_INDEX) {
return countOfUnreadMessages();
}
else {
return QVariant();
}
case Qt::DecorationRole:
if (column == FDS_MODEL_TITLE_INDEX) {
return m_icon;
}
else {
return QVariant();
}
case Qt::ToolTipRole:
if (column == FDS_MODEL_TITLE_INDEX) {
QString auto_update_string;
switch (m_autoUpdateType) {
case DontAutoUpdate:
//: Describes feed auto-update status.
auto_update_string = tr("does not use auto-update");
break;
case DefaultAutoUpdate:
//: Describes feed auto-update status.
auto_update_string = tr("uses global settings");
break;
case SpecificAutoUpdate:
default:
//: Describes feed auto-update status.
auto_update_string = tr("uses specific settings "
"(%n minute(s) to next auto-update)",
0,
m_autoUpdateRemainingInterval);
break;
}
//: Tooltip for feed.
return tr("%1 (%2)\n"
"%3\n\n"
"Encoding: %4\n"
"Auto-update status: %5").arg(m_title,
FeedsModelFeed::typeToString(m_type),
m_description,
m_encoding,
auto_update_string);
}
else if (column == FDS_MODEL_COUNTS_INDEX) {
//: Tooltip for "unread" column of feed list.
return tr("%n unread message(s).", 0, countOfUnreadMessages());
}
else {
return QVariant();
}
case Qt::TextAlignmentRole:
if (column == FDS_MODEL_COUNTS_INDEX) {
return Qt::AlignCenter;
}
else {
return QVariant();
}
case Qt::FontRole:
return countOfUnreadMessages() > 0 ? m_boldFont : m_normalFont;
default:
return QVariant();
}
}
void FeedsModelFeed::update() {
QByteArray feed_contents;
int download_timeout = Settings::instance()->value(APP_CFG_FEEDS, "feed_update_timeout", DOWNLOAD_TIMEOUT).toInt();
QNetworkReply::NetworkError download_result = NetworkFactory::downloadFeedFile(url(),
download_timeout,
feed_contents,
passwordProtected(),
username(),
password());
if (download_result != QNetworkReply::NoError) {
qWarning("Error during fetching of new messages for feed '%s' (id %d).",
qPrintable(url()),
id());
return;
}
// Encode downloaded data for further parsing.
QTextCodec *codec = QTextCodec::codecForName(encoding().toLocal8Bit());
QString formatted_feed_contents;
if (codec == NULL) {
// No suitable codec for this encoding was found.
// Use non-converted data.
formatted_feed_contents = feed_contents;
}
else {
formatted_feed_contents = codec->toUnicode(feed_contents);
}
// Feed data are downloaded and encoded.
// Parse data and obtain messages.
QList<Message> messages;
switch (type()) {
case FeedsModelFeed::Rss0X:
case FeedsModelFeed::Rss2X:
messages = ParsingFactory::parseAsRSS20(formatted_feed_contents);
break;
case FeedsModelFeed::Rdf:
messages = ParsingFactory::parseAsRDF(formatted_feed_contents);
break;
case FeedsModelFeed::Atom10:
messages = ParsingFactory::parseAsATOM10(formatted_feed_contents);
default:
break;
}
updateMessages(messages);
}
bool FeedsModelFeed::removeItself() {
QSqlDatabase database = DatabaseFactory::instance()->connection("FeedsModelFeed",
DatabaseFactory::FromSettings);
QSqlQuery query_remove(database);
query_remove.setForwardOnly(true);
// Remove all messages from this standard feed.
query_remove.prepare("DELETE FROM Messages WHERE feed = :feed;");
query_remove.bindValue(":feed", id());
if (!query_remove.exec()) {
return false;
}
// Remove feed itself.
query_remove.prepare("DELETE FROM Feeds WHERE id = :feed;");
query_remove.bindValue(":feed", id());
return query_remove.exec();
}
void FeedsModelFeed::updateMessages(const QList<Message> &messages) {
int feed_id = id();
QSqlDatabase database = DatabaseFactory::instance()->connection("FeedsModelFeed",
DatabaseFactory::FromSettings);
// Prepare queries.
QSqlQuery query_select(database);
QSqlQuery query_insert(database);
// Used to check if give feed contains with message with given
// title, url and date_created.
// WARNING: One feed CANNOT contain two (or more) messages with same
// AUTHOR AND TITLE AND URL AND DATE_CREATED.
query_select.setForwardOnly(true);
query_select.prepare("SELECT id, feed, date_created FROM Messages "
"WHERE feed = :feed AND title = :title AND url = :url AND author = :author;");
// Used to insert new messages.
query_insert.setForwardOnly(true);
query_insert.prepare("INSERT INTO Messages "
"(feed, title, url, author, date_created, contents) "
"VALUES (:feed, :title, :url, :author, :date_created, :contents);");
if (!database.transaction()) {
database.rollback();
qDebug("Transaction start for message downloader failed.");
return;
}
foreach (const Message &message, messages) {
query_select.bindValue(":feed", feed_id);
query_select.bindValue(":title", message.m_title);
query_select.bindValue(":url", message.m_url);
query_select.bindValue(":author", message.m_author);
query_select.exec();
QList<qint64> datetime_stamps;
while (query_select.next()) {
datetime_stamps << query_select.value(2).value<qint64>();
}
query_select.finish();
if (datetime_stamps.size() == 0 ||
(message.m_createdFromFeed &&
!datetime_stamps.contains(message.m_created.toMSecsSinceEpoch()))) {
// Message is not fetched in this feed yet
// or it is. If it is, then go
// through datetime stamps of stored messages
// and check if new (not auto-generated timestamp
// is among them and add this message if it is not.
query_insert.bindValue(":feed", feed_id);
query_insert.bindValue(":title", message.m_title);
query_insert.bindValue(":url", message.m_url);
query_insert.bindValue(":author", message.m_author);
query_insert.bindValue(":date_created", message.m_created.toMSecsSinceEpoch());
query_insert.bindValue(":contents", message.m_contents);
query_insert.exec();
query_insert.finish();
}
}
if (!database.commit()) {
database.rollback();
qDebug("Transaction commit for message downloader failed.");
}
}

View File

@ -21,19 +21,36 @@
#include "core/feedsmodelrootitem.h"
#include <QMetaType>
#include <QDateTime>
#include <QSqlRecord>
#include <QPair>
#include <QNetworkReply>
#include <QApplication>
class Message;
// Represents BASE class for feeds contained in FeedsModel.
// NOTE: This class should be derived to create PARTICULAR feed types.
class FeedsModelFeed : public FeedsModelRootItem {
Q_DECLARE_TR_FUNCTIONS(FeedsModelFeed)
public:
// Describes possible types of feeds.
// NOTE: This is equivalent to attribute Feeds(type).
enum Type {
StandardRss0X = 0,
StandardRss2X = 1,
StandardRdf = 2,
StandardAtom10 = 3
Rss0X = 0,
Rss2X = 1,
Rdf = 2,
Atom10 = 3
};
// Specifies the auto-update strategy for the feed.
enum AutoUpdateType {
DontAutoUpdate = 0,
DefaultAutoUpdate = 1,
SpecificAutoUpdate = 2
};
// Constructors and destructors.
@ -49,12 +66,15 @@ class FeedsModelFeed : public FeedsModelRootItem {
int countOfAllMessages() const;
int countOfUnreadMessages() const;
// Each feed can be "updated".
// NOTE: This method is used in the "update worker".
// For example, it can fetch new messages from a remote destination
// and store them in a local database and so on.
virtual void update() {
}
// Obtains data related to this feed.
QVariant data(int column, int role) const;
// Perform fetching of new messages.
void update();
// Removes this standard feed from persistent
// storage.
bool removeItself();
// Other getters/setters.
inline Type type() const {
@ -89,6 +109,61 @@ class FeedsModelFeed : public FeedsModelRootItem {
m_password = password;
}
inline QString encoding() const {
return m_encoding;
}
inline void setEncoding(const QString &encoding) {
m_encoding = encoding;
}
inline QString url() const {
return m_url;
}
inline void setUrl(const QString &url) {
m_url = url;
}
inline int autoUpdateInitialInterval() const {
return m_autoUpdateInitialInterval;
}
inline void setAutoUpdateInitialInterval(int auto_update_interval) {
// If new initial auto-update interval is set, then
// we should reset time that remains to the next auto-update.
m_autoUpdateInitialInterval = auto_update_interval;
m_autoUpdateRemainingInterval = auto_update_interval;
}
inline AutoUpdateType autoUpdateType() const {
return m_autoUpdateType;
}
inline void setAutoUpdateType(const AutoUpdateType &autoUpdateType) {
m_autoUpdateType = autoUpdateType;
}
inline int autoUpdateRemainingInterval() const {
return m_autoUpdateRemainingInterval;
}
inline void setAutoUpdateRemainingInterval(int autoUpdateRemainingInterval) {
m_autoUpdateRemainingInterval = autoUpdateRemainingInterval;
}
// Loads standard feed object from given SQL record.
static FeedsModelFeed *loadFromRecord(const QSqlRecord &record);
// Tries to guess feed hidden under given URL
// and uses given credentials.
// Returns pointer to guessed feed (if at least partially
// guessed) and retrieved error/status code from network layer
// or NULL feed.
static QPair<FeedsModelFeed*, QNetworkReply::NetworkError> guessFeed(const QString &url,
const QString &username,
const QString &password);
// Converts particular feed type to string.
static QString typeToString(Type type);
@ -97,6 +172,12 @@ class FeedsModelFeed : public FeedsModelRootItem {
void updateCounts(bool including_total_count = true);
protected:
// Persistently stores given messages into the database
// and updates existing messages if newer version is
// available.
void updateMessages(const QList<Message> &messages);
private:
bool m_passwordProtected;
QString m_username;
QString m_password;
@ -104,6 +185,13 @@ class FeedsModelFeed : public FeedsModelRootItem {
Type m_type;
int m_totalCount;
int m_unreadCount;
AutoUpdateType m_autoUpdateType;
int m_autoUpdateInitialInterval;
int m_autoUpdateRemainingInterval;
QString m_encoding;
QString m_url;
};
Q_DECLARE_METATYPE(FeedsModelFeed::Type)

View File

@ -1,146 +0,0 @@
// 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/feedsmodelstandardcategory.h"
#include "core/defs.h"
#include "core/databasefactory.h"
#include "core/textfactory.h"
#include "core/settings.h"
#include "gui/iconthemefactory.h"
#include "gui/iconfactory.h"
#include <QVariant>
#include <QSqlQuery>
FeedsModelStandardCategory::FeedsModelStandardCategory(FeedsModelRootItem *parent_item)
: FeedsModelCategory(parent_item) {
m_type = Standard;
}
FeedsModelStandardCategory::~FeedsModelStandardCategory() {
qDebug("Destroying FeedsModelStandardCategory instance.");
}
QVariant FeedsModelStandardCategory::data(int column, int role) const {
switch (role) {
case Qt::ToolTipRole:
if (column == FDS_MODEL_TITLE_INDEX) {
//: Tooltip for standard feed.
return tr("%1 (standard category)\n"
"%2%3").arg(m_title,
m_description,
m_childItems.size() == 0 ?
tr("\n\nThis category does not contain any nested items.") :
"");
}
else if (column == FDS_MODEL_COUNTS_INDEX) {
//: Tooltip for "unread" column of feed list.
return tr("%n unread message(s).", "", countOfUnreadMessages());
}
else {
return QVariant();
}
case Qt::EditRole:
if (column == FDS_MODEL_TITLE_INDEX) {
return m_title;
}
else if (column == FDS_MODEL_COUNTS_INDEX) {
return countOfUnreadMessages();
}
else {
return QVariant();
}
case Qt::FontRole:
return countOfUnreadMessages() > 0 ? m_boldFont : m_normalFont;
case Qt::DisplayRole:
if (column == FDS_MODEL_TITLE_INDEX) {
return m_title;
}
else if (column == FDS_MODEL_COUNTS_INDEX) {
return Settings::instance()->value(APP_CFG_FEEDS,
"count_format",
"(%unread)").toString()
.replace("%unread", QString::number(countOfUnreadMessages()))
.replace("%all", QString::number(countOfAllMessages()));
}
else {
return QVariant();
}
case Qt::DecorationRole:
if (column == FDS_MODEL_TITLE_INDEX) {
return m_icon;
}
else {
return QVariant();
}
case Qt::TextAlignmentRole:
if (column == FDS_MODEL_COUNTS_INDEX) {
return Qt::AlignCenter;
}
else {
return QVariant();
}
default:
return QVariant();
}
}
bool FeedsModelStandardCategory::removeItself() {
bool result = true;
// Remove all child items (feeds, categories.)
foreach (FeedsModelRootItem *child, m_childItems) {
result &= child->removeItself();
}
if (!result) {
return result;
}
// Children are removed, remove this standard category too.
QSqlDatabase database = DatabaseFactory::instance()->connection("FeedsModelStandardCategory",
DatabaseFactory::FromSettings);
QSqlQuery query_remove(database);
query_remove.setForwardOnly(true);
// Remove all messages from this standard feed.
query_remove.prepare("DELETE FROM Categories WHERE id = :category;");
query_remove.bindValue(":category", id());
return query_remove.exec();
}
FeedsModelStandardCategory *FeedsModelStandardCategory::loadFromRecord(const QSqlRecord &record) {
FeedsModelStandardCategory *category = new FeedsModelStandardCategory(NULL);
category->setId(record.value(CAT_DB_ID_INDEX).toInt());
category->setTitle(record.value(CAT_DB_TITLE_INDEX).toString());
category->setDescription(record.value(CAT_DB_DESCRIPTION_INDEX).toString());
category->setCreationDate(TextFactory::parseDateTime(record.value(CAT_DB_DCREATED_INDEX).value<qint64>()).toLocalTime());
category->setIcon(IconFactory::fromByteArray(record.value(CAT_DB_ICON_INDEX).toByteArray()));
return category;
}

View File

@ -1,52 +0,0 @@
// 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 FEEDSMODELSTANDARDCATEGORY_H
#define FEEDSMODELSTANDARDCATEGORY_H
#include "core/feedsmodelcategory.h"
#include <QSqlRecord>
#include <QDateTime>
#include <QApplication>
// Represents STANDARD category container.
// Standard category container can contain:
// a) other standard category containers,
// b) standard feeds,
// c) other containers and feeds (synchronized ones).
class FeedsModelStandardCategory : public FeedsModelCategory {
Q_DECLARE_TR_FUNCTIONS(FeedsModelStandardCategory)
public:
// Constructors and destructors.
explicit FeedsModelStandardCategory(FeedsModelRootItem *parent_item = NULL);
virtual ~FeedsModelStandardCategory();
// Returns the actual data representation of standard category.
QVariant data(int column, int role) const;
// Removes category and all its children from persistent
// database.
bool removeItself();
// Loads particular "standard category" from given sql record.
static FeedsModelStandardCategory *loadFromRecord(const QSqlRecord &record);
};
#endif // FEEDSMODELSTANDARDCATEGORY_H

View File

@ -1,431 +0,0 @@
// 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/feedsmodelstandardfeed.h"
#include "core/defs.h"
#include "core/settings.h"
#include "core/parsingfactory.h"
#include "core/databasefactory.h"
#include "core/networkfactory.h"
#include "core/textfactory.h"
#include "gui/iconfactory.h"
#include "gui/iconthemefactory.h"
#include <QVariant>
#include <QTextCodec>
#include <QSqlQuery>
#include <QDomDocument>
#include <QDomNode>
#include <QDomElement>
#include <QXmlStreamReader>
FeedsModelStandardFeed::FeedsModelStandardFeed(FeedsModelRootItem *parent_item)
: FeedsModelFeed(parent_item),
m_autoUpdateType(DontAutoUpdate),
m_autoUpdateInitialInterval(DEFAULT_AUTO_UPDATE_INTERVAL) {
}
FeedsModelStandardFeed::~FeedsModelStandardFeed() {
qDebug("Destroying FeedsModelStandardFeed instance.");
}
FeedsModelStandardFeed *FeedsModelStandardFeed::loadFromRecord(const QSqlRecord &record) {
FeedsModelStandardFeed *feed = new FeedsModelStandardFeed(NULL);
feed->setTitle(record.value(FDS_DB_TITLE_INDEX).toString());
feed->setId(record.value(FDS_DB_ID_INDEX).toInt());
feed->setDescription(record.value(FDS_DB_DESCRIPTION_INDEX).toString());
feed->setCreationDate(TextFactory::parseDateTime(record.value(FDS_DB_DCREATED_INDEX).value<qint64>()).toLocalTime());
feed->setIcon(IconFactory::fromByteArray(record.value(FDS_DB_ICON_INDEX).toByteArray()));
feed->setEncoding(record.value(FDS_DB_ENCODING_INDEX).toString());
feed->setUrl(record.value(FDS_DB_URL_INDEX).toString());
feed->setPasswordProtected(record.value(FDS_DB_PROTECTED_INDEX).toBool());
feed->setUsername(record.value(FDS_DB_USERNAME_INDEX).toString());
feed->setPassword(record.value(FDS_DB_PASSWORD_INDEX).toString());
feed->setAutoUpdateType(static_cast<FeedsModelStandardFeed::AutoUpdateType>(record.value(FDS_DB_UPDATE_TYPE_INDEX).toInt()));
feed->setAutoUpdateInitialInterval(record.value(FDS_DB_UPDATE_INTERVAL_INDEX).toInt());
feed->updateCounts();
return feed;
}
QPair<FeedsModelStandardFeed*, QNetworkReply::NetworkError> FeedsModelStandardFeed::guessFeed(const QString &url,
const QString &username,
const QString &password) {
QPair<FeedsModelStandardFeed*, QNetworkReply::NetworkError> result; result.first = NULL;
// Try to obtain icon.
QIcon icon_data;
if ((result.second = NetworkFactory::downloadIcon(url,
5000,
icon_data)) ==
QNetworkReply::NoError) {
// Icon for feed was downloaded and is stored now in _icon_data.
result.first = new FeedsModelStandardFeed();
result.first->setIcon(icon_data);
}
QByteArray feed_contents;
if ((result.second = NetworkFactory::downloadFeedFile(url,
Settings::instance()->value(APP_CFG_FEEDS, "feed_update_timeout", DOWNLOAD_TIMEOUT).toInt(),
feed_contents,
!username.isEmpty(),
username,
password)) == QNetworkReply::NoError) {
// Feed XML was obtained, now we need to try to guess
// its encoding before we can read further data.
QString xml_schema_encoding;
QString xml_contents_encoded;
QRegExp encoding_rexp("encoding=\"[^\"]\\S+\"");
if (encoding_rexp.indexIn(feed_contents) != -1 &&
!(xml_schema_encoding = encoding_rexp.cap(0)).isEmpty()) {
// Some "encoding" attribute was found get the encoding
// out of it.
encoding_rexp.setPattern("[^\"]\\S+[^\"]");
encoding_rexp.indexIn(xml_schema_encoding, 9);
xml_schema_encoding = encoding_rexp.cap(0);
}
if (result.first == NULL) {
result.first = new FeedsModelStandardFeed();
}
QTextCodec *custom_codec = QTextCodec::codecForName(xml_schema_encoding.toLocal8Bit());
if (custom_codec != NULL) {
// Feed encoding was probably guessed.
xml_contents_encoded = custom_codec->toUnicode(feed_contents);
result.first->setEncoding(xml_schema_encoding);
}
else {
// Feed encoding probably not guessed, set it as
// default.
xml_contents_encoded = feed_contents;
result.first->setEncoding(DEFAULT_FEED_ENCODING);
}
// Feed XML was obtained, guess it now.
QDomDocument xml_document;
QString error_msg;
int error_line, error_column;
if (!xml_document.setContent(xml_contents_encoded,
&error_msg,
&error_line,
&error_column)) {
qDebug("XML of feed '%s' is not valid and cannot be loaded. Error: '%s' "
"(line %d, column %d).",
qPrintable(url),
qPrintable(error_msg),
error_line, error_column);
result.second = QNetworkReply::UnknownContentError;
// XML is invalid, exit.
return result;
}
QDomElement root_element = xml_document.documentElement();
QString root_tag_name = root_element.tagName();
if (root_tag_name == "rdf:RDF") {
// We found RDF feed.
QDomElement channel_element = root_element.namedItem("channel").toElement();
result.first->setType(StandardRdf);
result.first->setTitle(channel_element.namedItem("title").toElement().text());
result.first->setDescription(channel_element.namedItem("description").toElement().text());
}
else if (root_tag_name == "rss") {
// We found RSS 0.91/0.92/0.93/2.0/2.0.1 feed.
QString rss_type = root_element.attribute("version", "2.0");
if (rss_type == "0.91" || rss_type == "0.92" || rss_type == "0.93") {
result.first->setType(StandardRss0X);
}
else {
result.first->setType(StandardRss2X);
}
QDomElement channel_element = root_element.namedItem("channel").toElement();
result.first->setTitle(channel_element.namedItem("title").toElement().text());
result.first->setDescription(channel_element.namedItem("description").toElement().text());
}
else if (root_tag_name == "feed") {
// We found ATOM feed.
result.first->setType(StandardAtom10);
result.first->setTitle(root_element.namedItem("title").toElement().text());
result.first->setDescription(root_element.namedItem("subtitle").toElement().text());
}
else {
// File was downloaded and it really was XML file
// but feed format was NOT recognized.
result.second = QNetworkReply::UnknownContentError;
}
}
return result;
}
QVariant FeedsModelStandardFeed::data(int column, int role) const {
switch (role) {
case Qt::DisplayRole:
if (column == FDS_MODEL_TITLE_INDEX) {
return m_title;
}
else if (column == FDS_MODEL_COUNTS_INDEX) {
return Settings::instance()->value(APP_CFG_FEEDS,
"count_format",
"(%unread)").toString()
.replace("%unread", QString::number(countOfUnreadMessages()))
.replace("%all", QString::number(countOfAllMessages()));
}
else {
return QVariant();
}
case Qt::EditRole:
if (column == FDS_MODEL_TITLE_INDEX) {
return m_title;
}
else if (column == FDS_MODEL_COUNTS_INDEX) {
return countOfUnreadMessages();
}
else {
return QVariant();
}
case Qt::DecorationRole:
if (column == FDS_MODEL_TITLE_INDEX) {
return m_icon;
}
else {
return QVariant();
}
case Qt::ToolTipRole:
if (column == FDS_MODEL_TITLE_INDEX) {
QString auto_update_string;
switch (m_autoUpdateType) {
case DontAutoUpdate:
//: Describes feed auto-update status.
auto_update_string = tr("does not use auto-update");
break;
case DefaultAutoUpdate:
//: Describes feed auto-update status.
auto_update_string = tr("uses global settings");
break;
case SpecificAutoUpdate:
default:
//: Describes feed auto-update status.
auto_update_string = tr("uses specific settings "
"(%n minute(s) to next auto-update)",
0,
m_autoUpdateRemainingInterval);
break;
}
//: Tooltip for feed.
return tr("%1 (%2)\n"
"%3\n\n"
"Encoding: %4\n"
"Auto-update status: %5").arg(m_title,
FeedsModelFeed::typeToString(m_type),
m_description,
m_encoding,
auto_update_string);
}
else if (column == FDS_MODEL_COUNTS_INDEX) {
//: Tooltip for "unread" column of feed list.
return tr("%n unread message(s).", 0, countOfUnreadMessages());
}
else {
return QVariant();
}
case Qt::TextAlignmentRole:
if (column == FDS_MODEL_COUNTS_INDEX) {
return Qt::AlignCenter;
}
else {
return QVariant();
}
case Qt::FontRole:
return countOfUnreadMessages() > 0 ? m_boldFont : m_normalFont;
default:
return QVariant();
}
}
void FeedsModelStandardFeed::update() {
QByteArray feed_contents;
int download_timeout = Settings::instance()->value(APP_CFG_FEEDS, "feed_update_timeout", DOWNLOAD_TIMEOUT).toInt();
QNetworkReply::NetworkError download_result = NetworkFactory::downloadFeedFile(url(),
download_timeout,
feed_contents,
passwordProtected(),
username(),
password());
if (download_result != QNetworkReply::NoError) {
qWarning("Error during fetching of new messages for feed '%s' (id %d).",
qPrintable(url()),
id());
return;
}
// Encode downloaded data for further parsing.
QTextCodec *codec = QTextCodec::codecForName(encoding().toLocal8Bit());
QString formatted_feed_contents;
if (codec == NULL) {
// No suitable codec for this encoding was found.
// Use non-converted data.
formatted_feed_contents = feed_contents;
}
else {
formatted_feed_contents = codec->toUnicode(feed_contents);
}
// Feed data are downloaded and encoded.
// Parse data and obtain messages.
QList<Message> messages;
switch (type()) {
case FeedsModelFeed::StandardRss0X:
case FeedsModelFeed::StandardRss2X:
messages = ParsingFactory::parseAsRSS20(formatted_feed_contents);
break;
case FeedsModelFeed::StandardRdf:
messages = ParsingFactory::parseAsRDF(formatted_feed_contents);
break;
case FeedsModelFeed::StandardAtom10:
messages = ParsingFactory::parseAsATOM10(formatted_feed_contents);
default:
break;
}
updateMessages(messages);
}
bool FeedsModelStandardFeed::removeItself() {
QSqlDatabase database = DatabaseFactory::instance()->connection("FeedsModelStandardFeed",
DatabaseFactory::FromSettings);
QSqlQuery query_remove(database);
query_remove.setForwardOnly(true);
// Remove all messages from this standard feed.
query_remove.prepare("DELETE FROM Messages WHERE feed = :feed;");
query_remove.bindValue(":feed", id());
if (!query_remove.exec()) {
return false;
}
// Remove feed itself.
query_remove.prepare("DELETE FROM Feeds WHERE id = :feed;");
query_remove.bindValue(":feed", id());
return query_remove.exec();
}
void FeedsModelStandardFeed::updateMessages(const QList<Message> &messages) {
int feed_id = id();
QSqlDatabase database = DatabaseFactory::instance()->connection("FeedsModelStandardFeed",
DatabaseFactory::FromSettings);
// Prepare queries.
QSqlQuery query_select(database);
QSqlQuery query_insert(database);
// Used to check if give feed contains with message with given
// title, url and date_created.
// WARNING: One feed CANNOT contain two (or more) messages with same
// AUTHOR AND TITLE AND URL AND DATE_CREATED.
query_select.setForwardOnly(true);
query_select.prepare("SELECT id, feed, date_created FROM Messages "
"WHERE feed = :feed AND title = :title AND url = :url AND author = :author;");
// Used to insert new messages.
query_insert.setForwardOnly(true);
query_insert.prepare("INSERT INTO Messages "
"(feed, title, url, author, date_created, contents) "
"VALUES (:feed, :title, :url, :author, :date_created, :contents);");
if (!database.transaction()) {
database.rollback();
qDebug("Transaction start for message downloader failed.");
return;
}
foreach (const Message &message, messages) {
query_select.bindValue(":feed", feed_id);
query_select.bindValue(":title", message.m_title);
query_select.bindValue(":url", message.m_url);
query_select.bindValue(":author", message.m_author);
query_select.exec();
QList<qint64> datetime_stamps;
while (query_select.next()) {
datetime_stamps << query_select.value(2).value<qint64>();
}
query_select.finish();
if (datetime_stamps.size() == 0 ||
(message.m_createdFromFeed &&
!datetime_stamps.contains(message.m_created.toMSecsSinceEpoch()))) {
// Message is not fetched in this feed yet
// or it is. If it is, then go
// through datetime stamps of stored messages
// and check if new (not auto-generated timestamp
// is among them and add this message if it is not.
query_insert.bindValue(":feed", feed_id);
query_insert.bindValue(":title", message.m_title);
query_insert.bindValue(":url", message.m_url);
query_insert.bindValue(":author", message.m_author);
query_insert.bindValue(":date_created", message.m_created.toMSecsSinceEpoch());
query_insert.bindValue(":contents", message.m_contents);
query_insert.exec();
query_insert.finish();
}
}
if (!database.commit()) {
database.rollback();
qDebug("Transaction commit for message downloader failed.");
}
}

View File

@ -1,130 +0,0 @@
// 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 FEEDSMODELSTANDARDFEED_H
#define FEEDSMODELSTANDARDFEED_H
#include "core/feedsmodelfeed.h"
#include <QDateTime>
#include <QSqlRecord>
#include <QPair>
#include <QNetworkReply>
#include <QApplication>
class Message;
// Represents STANDARD RSS/RDF/ATOM feed with no
// online synchronization services (NO TT-RSS, NO FEEDLY).
// So, parent item is either root item or category.
class FeedsModelStandardFeed : public FeedsModelFeed {
Q_DECLARE_TR_FUNCTIONS(FeedsModelStandardFeed)
public:
enum AutoUpdateType {
DontAutoUpdate = 0,
DefaultAutoUpdate = 1,
SpecificAutoUpdate = 2
};
// Constructors and destructors.
explicit FeedsModelStandardFeed(FeedsModelRootItem *parent_item = NULL);
virtual ~FeedsModelStandardFeed();
// Obtains data related to this feed.
QVariant data(int column, int role) const;
// Perform fetching of new messages.
void update();
// Removes this standard feed from persistent
// storage.
bool removeItself();
// Various getters/setters.
inline QString encoding() const {
return m_encoding;
}
inline void setEncoding(const QString &encoding) {
m_encoding = encoding;
}
inline QString url() const {
return m_url;
}
inline void setUrl(const QString &url) {
m_url = url;
}
inline int autoUpdateInitialInterval() const {
return m_autoUpdateInitialInterval;
}
inline void setAutoUpdateInitialInterval(int auto_update_interval) {
// If new initial auto-update interval is set, then
// we should reset time that remains to the next auto-update.
m_autoUpdateInitialInterval = auto_update_interval;
m_autoUpdateRemainingInterval = auto_update_interval;
}
inline AutoUpdateType autoUpdateType() const {
return m_autoUpdateType;
}
inline void setAutoUpdateType(const AutoUpdateType &autoUpdateType) {
m_autoUpdateType = autoUpdateType;
}
inline int autoUpdateRemainingInterval() const {
return m_autoUpdateRemainingInterval;
}
inline void setAutoUpdateRemainingInterval(int autoUpdateRemainingInterval) {
m_autoUpdateRemainingInterval = autoUpdateRemainingInterval;
}
// Loads standard feed object from given SQL record.
static FeedsModelStandardFeed *loadFromRecord(const QSqlRecord &record);
// Tries to guess feed hidden under given URL
// and uses given credentials.
// Returns pointer to guessed feed (if at least partially
// guessed) and retrieved error/status code from network layer
// or NULL feed.
static QPair<FeedsModelStandardFeed*, QNetworkReply::NetworkError> guessFeed(const QString &url,
const QString &username,
const QString &password);
protected:
// Persistently stores given messages into the database
// and updates existing messages if newer version is
// available.
void updateMessages(const QList<Message> &messages);
private:
AutoUpdateType m_autoUpdateType;
int m_autoUpdateInitialInterval;
int m_autoUpdateRemainingInterval;
QString m_encoding;
QString m_url;
};
#endif // FEEDSMODELSTANDARDFEED_H

View File

@ -17,8 +17,8 @@
#include "core/parsingfactory.h"
#include "core/webfactory.h"
#include "core/textfactory.h"
#include "network-web/webfactory.h"
#include <QDomDocument>
#include <QDomElement>

View File

@ -18,7 +18,7 @@
#include "core/systemfactory.h"
#include "core/defs.h"
#include "core/networkfactory.h"
#include "network-web/networkfactory.h"
#if defined(Q_OS_WIN)
#include "qtsingleapplication/qtsingleapplication.h"

View File

@ -21,9 +21,9 @@
#include "core/databasefactory.h"
#include "core/messagesproxymodel.h"
#include "core/feeddownloader.h"
#include "core/feedsmodelstandardfeed.h"
#include "core/feedsmodelfeed.h"
#include "core/systemfactory.h"
#include "gui/webbrowser.h"
#include "network-web/webbrowser.h"
#include "gui/formmain.h"
#include "gui/iconthemefactory.h"
#include "gui/messagesview.h"
@ -249,10 +249,10 @@ void FeedMessageViewer::createConnections() {
SIGNAL(triggered()), m_feedsView, SLOT(updateSelectedFeeds()));
connect(form_main->m_ui->m_actionUpdateAllFeeds,
SIGNAL(triggered()), m_feedsView, SLOT(updateAllFeeds()));
connect(form_main->m_ui->m_actionAddStandardCategory,
SIGNAL(triggered()), m_feedsView, SLOT(addNewStandardCategory()));
connect(form_main->m_ui->m_actionAddStandardFeed,
SIGNAL(triggered()), m_feedsView, SLOT(addNewStandardFeed()));
connect(form_main->m_ui->m_actionAddCategory,
SIGNAL(triggered()), m_feedsView, SLOT(addNewCategory()));
connect(form_main->m_ui->m_actionAddFeed,
SIGNAL(triggered()), m_feedsView, SLOT(addNewFeed()));
connect(form_main->m_ui->m_actionEditSelectedFeedCategory,
SIGNAL(triggered()), m_feedsView, SLOT(editSelectedItem()));
connect(form_main->m_ui->m_actionViewSelectedItemsNewspaperMode,

View File

@ -24,8 +24,7 @@
#include "core/feedsproxymodel.h"
#include "core/feedsmodelrootitem.h"
#include "core/feedsmodelcategory.h"
#include "core/feedsmodelstandardfeed.h"
#include "core/feedsmodelstandardcategory.h"
#include "core/feedsmodelfeed.h"
#include "gui/formmain.h"
#include "gui/formstandardcategorydetails.h"
#include "gui/formstandardfeeddetails.h"
@ -250,7 +249,7 @@ void FeedsView::clearAllFeeds() {
setAllFeedsClearStatus(1);
}
void FeedsView::addNewStandardCategory() {
void FeedsView::addNewCategory() {
if (!SystemFactory::instance()->applicationCloseLock()->tryLock()) {
// Lock was not obtained because
// it is used probably by feed updater or application
@ -281,7 +280,7 @@ void FeedsView::addNewStandardCategory() {
SystemFactory::instance()->applicationCloseLock()->unlock();
}
void FeedsView::editStandardCategory(FeedsModelStandardCategory *category) {
void FeedsView::editCategory(FeedsModelCategory *category) {
QPointer<FormStandardCategoryDetails> form_pointer = new FormStandardCategoryDetails(m_sourceModel, this);
form_pointer.data()->exec(category);
@ -289,7 +288,7 @@ void FeedsView::editStandardCategory(FeedsModelStandardCategory *category) {
delete form_pointer.data();
}
void FeedsView::addNewStandardFeed() {
void FeedsView::addNewFeed() {
if (!SystemFactory::instance()->applicationCloseLock()->tryLock()) {
// Lock was not obtained because
// it is used probably by feed updater or application
@ -320,7 +319,7 @@ void FeedsView::addNewStandardFeed() {
SystemFactory::instance()->applicationCloseLock()->unlock();
}
void FeedsView::editStandardFeed(FeedsModelStandardFeed *feed) {
void FeedsView::editFeed(FeedsModelFeed *feed) {
QPointer<FormStandardFeedDetails> form_pointer = new FormStandardFeedDetails(m_sourceModel, this);
form_pointer.data()->exec(feed);
@ -359,7 +358,7 @@ void FeedsView::editSelectedItem() {
switch (category->type()) {
case FeedsModelCategory::Standard: {
// User wants to edit standard category.
editStandardCategory(static_cast<FeedsModelStandardCategory*>(category));
editCategory(static_cast<FeedsModelCategory*>(category));
break;
}
@ -370,12 +369,12 @@ void FeedsView::editSelectedItem() {
else if ((feed = isCurrentIndexFeed()) != NULL) {
// Feed is selected.
switch (feed->type()) {
case FeedsModelFeed::StandardAtom10:
case FeedsModelFeed::StandardRdf:
case FeedsModelFeed::StandardRss0X:
case FeedsModelFeed::StandardRss2X: {
case FeedsModelFeed::Atom10:
case FeedsModelFeed::Rdf:
case FeedsModelFeed::Rss0X:
case FeedsModelFeed::Rss2X: {
// User wants to edit standard feed.
editStandardFeed(static_cast<FeedsModelStandardFeed*>(feed));
editFeed(static_cast<FeedsModelFeed*>(feed));
break;
}
@ -431,6 +430,8 @@ void FeedsView::deleteSelectedItem() {
if (m_sourceModel->removeItem(m_proxyModel->mapToSource(current_index))) {
// Item WAS removed, update counts.
// TODO:_I do not need to update counts of all items here.
// Updating counts of parent item (feed) should be enough.
updateCountsOfAllFeeds(true);
}
else {
@ -551,7 +552,8 @@ void FeedsView::initializeContextMenuEmptySpace() {
m_contextMenuEmptySpace = new QMenu(tr("Context menu for feeds"), this);
m_contextMenuEmptySpace->addActions(QList<QAction*>() <<
FormMain::instance()->m_ui->m_actionUpdateAllFeeds <<
FormMain::instance()->m_ui->m_actionAddStandardFeed);
FormMain::instance()->m_ui->m_actionAddCategory <<
FormMain::instance()->m_ui->m_actionAddFeed);
}
void FeedsView::setupAppearance() {

View File

@ -38,6 +38,7 @@ class FeedsView : public QTreeView {
explicit FeedsView(QWidget *parent = 0);
virtual ~FeedsView();
// Fundamental accessors.
inline FeedsProxyModel *model() {
return m_proxyModel;
}
@ -66,6 +67,8 @@ class FeedsView : public QTreeView {
FeedsModelCategory *isCurrentIndexCategory() const;
FeedsModelFeed *isCurrentIndexFeed() const;
// Saves/loads expand states of all nodes (feeds/categories) of the list
// to/from settings.
void saveExpandedStates();
void loadExpandedStates();
@ -82,7 +85,6 @@ class FeedsView : public QTreeView {
void markSelectedFeedsReadStatus(int read);
void markSelectedFeedsRead();
void markSelectedFeedsUnread();
void markAllFeedsReadStatus(int read);
void markAllFeedsRead();
@ -101,12 +103,12 @@ class FeedsView : public QTreeView {
void deleteSelectedItem();
// Standard category manipulators.
void addNewStandardCategory();
void editStandardCategory(FeedsModelStandardCategory *category);
void addNewCategory();
void editCategory(FeedsModelCategory *category);
// Standard feed manipulators.
void addNewStandardFeed();
void editStandardFeed(FeedsModelStandardFeed *feed);
void addNewFeed();
void editFeed(FeedsModelFeed *feed);
// Reloads counts for selected feeds.
void updateCountsOfSelectedFeeds(bool update_total_too = true);
@ -124,6 +126,7 @@ class FeedsView : public QTreeView {
m_sourceModel->countOfAllMessages());
}
// Selects next/previous item (feed/category) in the list.
void selectNextItem();
void selectPreviousItem();

View File

@ -21,12 +21,12 @@
#include "core/settings.h"
#include "core/systemfactory.h"
#include "core/databasefactory.h"
#include "core/webfactory.h"
#include "network-web/webfactory.h"
#include "network-web/webbrowser.h"
#include "gui/formabout.h"
#include "gui/formsettings.h"
#include "gui/feedsview.h"
#include "gui/messagebox.h"
#include "gui/webbrowser.h"
#include "gui/iconthemefactory.h"
#include "gui/systemtrayicon.h"
#include "gui/tabbar.h"
@ -115,8 +115,8 @@ QList<QAction*> FormMain::allActions() {
m_ui->m_actionEditSelectedFeedCategory <<
m_ui->m_actionDeleteSelectedFeedCategory <<
m_ui->m_actionViewSelectedItemsNewspaperMode <<
m_ui->m_actionAddStandardCategory <<
m_ui->m_actionAddStandardFeed <<
m_ui->m_actionAddCategory <<
m_ui->m_actionAddFeed <<
m_ui->m_actionSelectNextFeedCategory <<
m_ui->m_actionSelectPreviousFeedCategory <<
m_ui->m_actionSelectNextMessage <<
@ -291,8 +291,8 @@ void FormMain::setupIcons() {
m_ui->m_actionClearAllFeeds->setIcon(icon_theme_factory->fromTheme("mail-remove"));
m_ui->m_actionDeleteSelectedFeedCategory->setIcon(icon_theme_factory->fromTheme("item-remove"));
m_ui->m_actionDeleteSelectedMessages->setIcon(icon_theme_factory->fromTheme("mail-remove"));
m_ui->m_actionAddStandardCategory->setIcon(icon_theme_factory->fromTheme("folder-category"));
m_ui->m_actionAddStandardFeed->setIcon(icon_theme_factory->fromTheme("folder-feed"));
m_ui->m_actionAddCategory->setIcon(icon_theme_factory->fromTheme("folder-category"));
m_ui->m_actionAddFeed->setIcon(icon_theme_factory->fromTheme("folder-feed"));
m_ui->m_actionEditSelectedFeedCategory->setIcon(icon_theme_factory->fromTheme("item-edit"));
m_ui->m_actionMarkAllFeedsRead->setIcon(icon_theme_factory->fromTheme("mail-mark-read"));
m_ui->m_actionMarkSelectedFeedsAsRead->setIcon(icon_theme_factory->fromTheme("mail-mark-read"));
@ -309,7 +309,6 @@ void FormMain::setupIcons() {
m_ui->m_actionSelectNextMessage->setIcon(icon_theme_factory->fromTheme("go-down"));
m_ui->m_actionSelectPreviousMessage->setIcon(icon_theme_factory->fromTheme("go-up"));
// Setup icons for underlying components: opened web browsers...
foreach (WebBrowser *browser, WebBrowser::runningWebBrowsers()) {
browser->setupIcons();

View File

@ -15,7 +15,16 @@
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="margin">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
@ -39,7 +48,7 @@
<x>0</x>
<y>0</y>
<width>979</width>
<height>20</height>
<height>21</height>
</rect>
</property>
<widget class="QMenu" name="m_menuFile">
@ -111,8 +120,8 @@
<property name="title">
<string>Add &amp;new feed/category</string>
</property>
<addaction name="m_actionAddStandardCategory"/>
<addaction name="m_actionAddStandardFeed"/>
<addaction name="m_actionAddCategory"/>
<addaction name="m_actionAddFeed"/>
</widget>
<addaction name="m_actionUpdateAllFeeds"/>
<addaction name="m_actionUpdateSelectedFeedsCategories"/>
@ -300,12 +309,12 @@
<string>Deletes all messages from selected feeds.</string>
</property>
</action>
<action name="m_actionAddStandardFeed">
<action name="m_actionAddFeed">
<property name="text">
<string>New standard &amp;feed</string>
<string>New &amp;feed</string>
</property>
<property name="toolTip">
<string>Add new standard feed.</string>
<string>Add new feed.</string>
</property>
</action>
<action name="m_actionOpenSelectedSourceArticlesExternally">
@ -323,12 +332,12 @@
<string>Open selected source articles in &amp;internal browser</string>
</property>
</action>
<action name="m_actionAddStandardCategory">
<action name="m_actionAddCategory">
<property name="text">
<string>New standard &amp;category</string>
<string>New &amp;category</string>
</property>
<property name="toolTip">
<string>Add new standard category.</string>
<string>Add new category.</string>
</property>
</action>
<action name="m_actionNoActions">

View File

@ -22,11 +22,12 @@
#include "core/databasefactory.h"
#include "core/localization.h"
#include "core/systemfactory.h"
#include "core/webfactory.h"
#include "core/feeddownloader.h"
#include "core/dynamicshortcuts.h"
#include "core/webbrowsernetworkaccessmanager.h"
#include "core/silentnetworkaccessmanager.h"
#include "network-web/webfactory.h"
#include "network-web/webbrowsernetworkaccessmanager.h"
#include "network-web/silentnetworkaccessmanager.h"
#include "network-web/webbrowser.h"
#include "core/feedsmodel.h"
#include "gui/iconthemefactory.h"
#include "gui/skinfactory.h"
@ -34,7 +35,6 @@
#include "gui/feedmessageviewer.h"
#include "gui/feedsview.h"
#include "gui/formmain.h"
#include "gui/webbrowser.h"
#include "gui/messagebox.h"
#include "qtsingleapplication/qtsingleapplication.h"

View File

@ -20,7 +20,6 @@
#include "core/defs.h"
#include "core/feedsmodelrootitem.h"
#include "core/feedsmodelcategory.h"
#include "core/feedsmodelstandardcategory.h"
#include "core/feedsmodel.h"
#include "gui/iconthemefactory.h"
#include "gui/feedsview.h"
@ -70,7 +69,7 @@ void FormStandardCategoryDetails::createConnections() {
connect(m_actionUseDefaultIcon, SIGNAL(triggered()), this, SLOT(onUseDefaultIcon()));
}
void FormStandardCategoryDetails::setEditableCategory(FeedsModelStandardCategory *editable_category) {
void FormStandardCategoryDetails::setEditableCategory(FeedsModelCategory *editable_category) {
m_editableCategory = editable_category;
m_ui->m_cmbParentCategory->setCurrentIndex(m_ui->m_cmbParentCategory->findData(QVariant::fromValue((void*) editable_category->parent())));
@ -79,7 +78,7 @@ void FormStandardCategoryDetails::setEditableCategory(FeedsModelStandardCategory
m_ui->m_btnIcon->setIcon(editable_category->icon());
}
int FormStandardCategoryDetails::exec(FeedsModelStandardCategory *input_category) {
int FormStandardCategoryDetails::exec(FeedsModelCategory *input_category) {
// Load categories.
loadCategories(m_feedsModel->allCategories().values(),
m_feedsModel->rootItem(),
@ -105,7 +104,7 @@ int FormStandardCategoryDetails::exec(FeedsModelStandardCategory *input_category
void FormStandardCategoryDetails::apply() {
FeedsModelRootItem *parent = static_cast<FeedsModelRootItem*>(m_ui->m_cmbParentCategory->itemData(m_ui->m_cmbParentCategory->currentIndex()).value<void*>());
FeedsModelStandardCategory *new_category = new FeedsModelStandardCategory();
FeedsModelCategory *new_category = new FeedsModelCategory();
new_category->setTitle(m_ui->m_txtTitle->lineEdit()->text());
new_category->setCreationDate(QDateTime::currentDateTime());
@ -115,7 +114,7 @@ void FormStandardCategoryDetails::apply() {
if (m_editableCategory == NULL) {
// Add the category.
if (m_feedsModel->addStandardCategory(new_category, parent)) {
if (m_feedsModel->addCategory(new_category, parent)) {
accept();
}
else {
@ -133,7 +132,7 @@ void FormStandardCategoryDetails::apply() {
}
}
else {
if (m_feedsModel->editStandardCategory(m_editableCategory, new_category)) {
if (m_feedsModel->editCategory(m_editableCategory, new_category)) {
accept();
}
else {

View File

@ -28,7 +28,7 @@ namespace Ui {
}
class FeedsModelCategory;
class FeedsModelStandardCategory;
class FeedsModelCategory;
class FeedsModel;
class FeedsModelRootItem;
class QMenu;
@ -44,7 +44,7 @@ class FormStandardCategoryDetails : public QDialog {
public slots:
// Executes add/edit standard category dialog.
int exec(FeedsModelStandardCategory *input_category);
int exec(FeedsModelCategory *input_category);
protected slots:
// Applies changes.
@ -64,7 +64,7 @@ class FormStandardCategoryDetails : public QDialog {
void createConnections();
// Sets the category which will be edited.
void setEditableCategory(FeedsModelStandardCategory *editable_category);
void setEditableCategory(FeedsModelCategory *editable_category);
// Initializes the dialog.
void initialize();
@ -78,7 +78,7 @@ class FormStandardCategoryDetails : public QDialog {
private:
Ui::FormStandardCategoryDetails *m_ui;
FeedsModelStandardCategory *m_editableCategory;
FeedsModelCategory *m_editableCategory;
FeedsModel *m_feedsModel;
QMenu *m_iconMenu;

View File

@ -23,8 +23,7 @@
#include "core/feedsmodelrootitem.h"
#include "core/feedsmodelcategory.h"
#include "core/feedsmodelfeed.h"
#include "core/feedsmodelstandardfeed.h"
#include "core/networkfactory.h"
#include "network-web/networkfactory.h"
#include "gui/iconthemefactory.h"
#include "gui/baselineedit.h"
#include "gui/messagebox.h"
@ -57,7 +56,7 @@ FormStandardFeedDetails::~FormStandardFeedDetails() {
delete m_ui;
}
int FormStandardFeedDetails::exec(FeedsModelStandardFeed *input_feed) {
int FormStandardFeedDetails::exec(FeedsModelFeed *input_feed) {
// Load categories.
loadCategories(m_feedsModel->allCategories().values(),
m_feedsModel->rootItem());
@ -151,15 +150,15 @@ void FormStandardFeedDetails::onAuthenticationSwitched() {
}
void FormStandardFeedDetails::onAutoUpdateTypeChanged(int new_index) {
FeedsModelStandardFeed::AutoUpdateType auto_update_type = static_cast<FeedsModelStandardFeed::AutoUpdateType>(m_ui->m_cmbAutoUpdateType->itemData(new_index).toInt());
FeedsModelFeed::AutoUpdateType auto_update_type = static_cast<FeedsModelFeed::AutoUpdateType>(m_ui->m_cmbAutoUpdateType->itemData(new_index).toInt());
switch (auto_update_type) {
case FeedsModelStandardFeed::DontAutoUpdate:
case FeedsModelStandardFeed::DefaultAutoUpdate:
case FeedsModelFeed::DontAutoUpdate:
case FeedsModelFeed::DefaultAutoUpdate:
m_ui->m_spinAutoUpdateInterval->setEnabled(false);
break;
case FeedsModelStandardFeed::SpecificAutoUpdate:
case FeedsModelFeed::SpecificAutoUpdate:
default:
m_ui->m_spinAutoUpdateInterval->setEnabled(true);
}
@ -203,8 +202,8 @@ void FormStandardFeedDetails::onUseDefaultIcon() {
void FormStandardFeedDetails::apply() {
FeedsModelRootItem *parent = static_cast<FeedsModelRootItem*>(m_ui->m_cmbParentCategory->itemData(m_ui->m_cmbParentCategory->currentIndex()).value<void*>());
FeedsModelStandardFeed::Type type = static_cast<FeedsModelStandardFeed::Type>(m_ui->m_cmbType->itemData(m_ui->m_cmbType->currentIndex()).value<int>());
FeedsModelStandardFeed *new_feed = new FeedsModelStandardFeed();
FeedsModelFeed::Type type = static_cast<FeedsModelFeed::Type>(m_ui->m_cmbType->itemData(m_ui->m_cmbType->currentIndex()).value<int>());
FeedsModelFeed *new_feed = new FeedsModelFeed();
// Setup data for new_feed.
new_feed->setTitle(m_ui->m_txtTitle->lineEdit()->text());
@ -217,13 +216,13 @@ void FormStandardFeedDetails::apply() {
new_feed->setPasswordProtected(m_ui->m_gbAuthentication->isChecked());
new_feed->setUsername(m_ui->m_txtUsername->lineEdit()->text());
new_feed->setPassword(m_ui->m_txtPassword->lineEdit()->text());
new_feed->setAutoUpdateType(static_cast<FeedsModelStandardFeed::AutoUpdateType>(m_ui->m_cmbAutoUpdateType->itemData(m_ui->m_cmbAutoUpdateType->currentIndex()).toInt()));
new_feed->setAutoUpdateType(static_cast<FeedsModelFeed::AutoUpdateType>(m_ui->m_cmbAutoUpdateType->itemData(m_ui->m_cmbAutoUpdateType->currentIndex()).toInt()));
new_feed->setAutoUpdateInitialInterval(m_ui->m_spinAutoUpdateInterval->value());
new_feed->setParent(parent);
if (m_editableFeed == NULL) {
// Add the feed.
if (m_feedsModel->addStandardFeed(new_feed, parent)) {
if (m_feedsModel->addFeed(new_feed, parent)) {
accept();
}
else {
@ -242,7 +241,7 @@ void FormStandardFeedDetails::apply() {
}
else {
// Edit the feed.
if (m_feedsModel->editStandardFeed(m_editableFeed, new_feed)) {
if (m_feedsModel->editFeed(m_editableFeed, new_feed)) {
accept();
}
else {
@ -262,9 +261,9 @@ void FormStandardFeedDetails::apply() {
}
void FormStandardFeedDetails::guessFeed() {
QPair<FeedsModelStandardFeed*, QNetworkReply::NetworkError> result = FeedsModelStandardFeed::guessFeed(m_ui->m_txtUrl->lineEdit()->text(),
m_ui->m_txtUsername->lineEdit()->text(),
m_ui->m_txtPassword->lineEdit()->text());
QPair<FeedsModelFeed*, QNetworkReply::NetworkError> result = FeedsModelFeed::guessFeed(m_ui->m_txtUrl->lineEdit()->text(),
m_ui->m_txtUsername->lineEdit()->text(),
m_ui->m_txtPassword->lineEdit()->text());
if (result.first != NULL) {
// Icon or whole feed was guessed.
@ -332,7 +331,7 @@ void FormStandardFeedDetails::createConnections() {
connect(m_actionUseDefaultIcon, SIGNAL(triggered()), this, SLOT(onUseDefaultIcon()));
}
void FormStandardFeedDetails::setEditableFeed(FeedsModelStandardFeed *editable_feed) {
void FormStandardFeedDetails::setEditableFeed(FeedsModelFeed *editable_feed) {
m_editableFeed = editable_feed;
m_ui->m_cmbParentCategory->setCurrentIndex(m_ui->m_cmbParentCategory->findData(QVariant::fromValue((void*) editable_feed->parent())));
@ -381,10 +380,10 @@ void FormStandardFeedDetails::initialize() {
#endif
// Add standard feed types.
m_ui->m_cmbType->addItem(FeedsModelFeed::typeToString(FeedsModelFeed::StandardAtom10), QVariant::fromValue((int) FeedsModelFeed::StandardAtom10));
m_ui->m_cmbType->addItem(FeedsModelFeed::typeToString(FeedsModelFeed::StandardRdf), QVariant::fromValue((int) FeedsModelFeed::StandardRdf));
m_ui->m_cmbType->addItem(FeedsModelFeed::typeToString(FeedsModelFeed::StandardRss0X), QVariant::fromValue((int) FeedsModelFeed::StandardRss0X));
m_ui->m_cmbType->addItem(FeedsModelFeed::typeToString(FeedsModelFeed::StandardRss2X), QVariant::fromValue((int) FeedsModelFeed::StandardRss2X));
m_ui->m_cmbType->addItem(FeedsModelFeed::typeToString(FeedsModelFeed::Atom10), QVariant::fromValue((int) FeedsModelFeed::Atom10));
m_ui->m_cmbType->addItem(FeedsModelFeed::typeToString(FeedsModelFeed::Rdf), QVariant::fromValue((int) FeedsModelFeed::Rdf));
m_ui->m_cmbType->addItem(FeedsModelFeed::typeToString(FeedsModelFeed::Rss0X), QVariant::fromValue((int) FeedsModelFeed::Rss0X));
m_ui->m_cmbType->addItem(FeedsModelFeed::typeToString(FeedsModelFeed::Rss2X), QVariant::fromValue((int) FeedsModelFeed::Rss2X));
// Load available encodings.
QList<QByteArray> encodings = QTextCodec::availableCodecs();
@ -421,9 +420,9 @@ void FormStandardFeedDetails::initialize() {
// Setup auto-update options.
m_ui->m_spinAutoUpdateInterval->setValue(DEFAULT_AUTO_UPDATE_INTERVAL);
m_ui->m_cmbAutoUpdateType->addItem(tr("Auto-update using global interval"), QVariant::fromValue((int) FeedsModelStandardFeed::DefaultAutoUpdate));
m_ui->m_cmbAutoUpdateType->addItem(tr("Auto-update every"), QVariant::fromValue((int) FeedsModelStandardFeed::SpecificAutoUpdate));
m_ui->m_cmbAutoUpdateType->addItem(tr("Do not auto-update at all"), QVariant::fromValue((int) FeedsModelStandardFeed::DontAutoUpdate));
m_ui->m_cmbAutoUpdateType->addItem(tr("Auto-update using global interval"), QVariant::fromValue((int) FeedsModelFeed::DefaultAutoUpdate));
m_ui->m_cmbAutoUpdateType->addItem(tr("Auto-update every"), QVariant::fromValue((int) FeedsModelFeed::SpecificAutoUpdate));
m_ui->m_cmbAutoUpdateType->addItem(tr("Do not auto-update at all"), QVariant::fromValue((int) FeedsModelFeed::DontAutoUpdate));
// Set tab order.
setTabOrder(m_ui->m_cmbParentCategory, m_ui->m_cmbType);

View File

@ -28,7 +28,7 @@ namespace Ui {
}
class FeedsModel;
class FeedsModelStandardFeed;
class FeedsModelFeed;
class FeedsModelCategory;
class FeedsModelRootItem;
@ -42,7 +42,7 @@ class FormStandardFeedDetails : public QDialog {
public slots:
// Executes add/edit standard feed dialog.
int exec(FeedsModelStandardFeed *input_feed);
int exec(FeedsModelFeed *input_feed);
protected slots:
// Applies changes.
@ -71,7 +71,7 @@ class FormStandardFeedDetails : public QDialog {
void createConnections();
// Sets the feed which will be edited.
void setEditableFeed(FeedsModelStandardFeed *editable_feed);
void setEditableFeed(FeedsModelFeed *editable_feed);
// Initializes the dialog.
void initialize();
@ -82,7 +82,7 @@ class FormStandardFeedDetails : public QDialog {
private:
Ui::FormStandardFeedDetails *m_ui;
FeedsModelStandardFeed *m_editableFeed;
FeedsModelFeed *m_editableFeed;
FeedsModel *m_feedsModel;
QMenu *m_iconMenu;

View File

@ -19,8 +19,8 @@
#include "core/defs.h"
#include "core/systemfactory.h"
#include "core/networkfactory.h"
#include "core/webfactory.h"
#include "network-web/networkfactory.h"
#include "network-web/webfactory.h"
#include "gui/iconthemefactory.h"
#include "gui/messagebox.h"
#include "gui/systemtrayicon.h"

View File

@ -20,8 +20,8 @@
#include "core/messagesproxymodel.h"
#include "core/messagesmodel.h"
#include "core/settings.h"
#include "core/networkfactory.h"
#include "core/webfactory.h"
#include "network-web/networkfactory.h"
#include "network-web/webfactory.h"
#include "gui/formmain.h"
#include "gui/messagebox.h"

View File

@ -20,9 +20,9 @@
#include "core/defs.h"
#include "core/settings.h"
#include "core/textfactory.h"
#include "network-web/webbrowser.h"
#include "gui/tabbar.h"
#include "gui/iconthemefactory.h"
#include "gui/webbrowser.h"
#include "gui/formmain.h"
#include "gui/feedmessageviewer.h"
#include "gui/plaintoolbutton.h"
@ -109,20 +109,33 @@ void TabWidget::checkTabBarVisibility() {
}
void TabWidget::tabInserted(int index) {
QTabWidget::tabInserted(index);
QTabWidget::tabInserted(index);
checkTabBarVisibility();
int count_of_tabs = count();
if (index < count_of_tabs - 1 && count_of_tabs > 1) {
// New tab was inserted and the tab is not the last one.
fixContentsAfterMove(index, count_of_tabs - 1);
}
}
void TabWidget::tabRemoved(int index) {
QTabWidget::tabRemoved(index);
checkTabBarVisibility();
int count_of_tabs = count();
if (index < count_of_tabs && count_of_tabs > 1) {
// Some tab was removed and the tab was not the last one.
fixContentsAfterMove(index, count_of_tabs - 1);
}
}
void TabWidget::createConnections() {
connect(tabBar(), SIGNAL(tabCloseRequested(int)), this, SLOT(closeTab(int)));
connect(tabBar(), SIGNAL(emptySpaceDoubleClicked()), this, SLOT(addEmptyBrowser()));
connect(tabBar(), SIGNAL(tabMoved(int,int)), this, SLOT(fixContentsAfterMove(int,int)));
connect(tabBar(), SIGNAL(currentChanged(int)), this, SLOT(fixContentAfterIndexChange(int)));
}
void TabWidget::initializeTabs() {
@ -324,17 +337,12 @@ void TabWidget::changeTitle(int index, const QString &new_title) {
setTabToolTip(index, new_title);
}
void TabWidget::fixContentAfterIndexChange(int from) {
fixContentsIndexes(from, count() - 1);
}
void TabWidget::fixContentsAfterMove(int from, int to) {
fixContentsIndexes(qMin(from, to), qMax(from, to));
}
from = qMin(from, to);
to = qMax(from, to);
void TabWidget::fixContentsIndexes(int starting_index, int ending_index) {
for ( ; starting_index <= ending_index; starting_index++) {
TabContent *content = static_cast<TabContent*>(widget(starting_index));
content->setIndex(starting_index);
for ( ; from <= to; from++) {
TabContent *content = static_cast<TabContent*>(widget(from));
content->setIndex(from);
}
}

View File

@ -85,12 +85,8 @@ class TabWidget : public QTabWidget {
public slots:
// Fixes tabs indexes.
void fixContentAfterIndexChange(int from);
void fixContentsAfterMove(int from, int to);
// Fixes indexes of tab contents.
void fixContentsIndexes(int starting_index, int ending_index);
// Called when number of tab pages changes.
void checkTabBarVisibility();

View File

@ -15,7 +15,7 @@
// 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/basenetworkaccessmanager.h"
#include "network-web/basenetworkaccessmanager.h"
#include "core/defs.h"
#include "core/settings.h"

View File

@ -15,15 +15,16 @@
// 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/networkfactory.h"
#include "network-web/networkfactory.h"
#include "core/defs.h"
#include "core/silentnetworkaccessmanager.h"
#include "core/feedsmodelstandardfeed.h"
#include "core/settings.h"
#include "network-web/silentnetworkaccessmanager.h"
#include <QEventLoop>
#include <QTimer>
#include <QIcon>
#include <QPixmap>
#include <QTextDocument>

View File

@ -22,8 +22,6 @@
#include <QApplication>
class FeedsModelStandardFeed;
class NetworkFactory {
Q_DECLARE_TR_FUNCTIONS(NetworkFactory)

View File

@ -15,9 +15,7 @@
// 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/silentnetworkaccessmanager.h"
#include "core/feedsmodelstandardfeed.h"
#include "network-web/silentnetworkaccessmanager.h"
#include <QNetworkReply>
#include <QAuthenticator>

View File

@ -18,7 +18,7 @@
#ifndef SILENTNETWORKACCESSMANAGER_H
#define SILENTNETWORKACCESSMANAGER_H
#include "core/basenetworkaccessmanager.h"
#include "network-web/basenetworkaccessmanager.h"
#include <QPointer>

View File

@ -15,13 +15,13 @@
// 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/webbrowser.h"
#include "network-web/webbrowser.h"
#include "core/defs.h"
#include "core/webbrowsernetworkaccessmanager.h"
#include "core/webpage.h"
#include "network-web/webbrowsernetworkaccessmanager.h"
#include "network-web/webpage.h"
#include "network-web/webview.h"
#include "gui/skinfactory.h"
#include "gui/webview.h"
#include "gui/formmain.h"
#include "gui/iconthemefactory.h"
#include "gui/tabwidget.h"

View File

@ -19,8 +19,8 @@
#define WEBBROWSER_H
#include "core/messagesmodel.h"
#include "network-web/webview.h"
#include "gui/tabcontent.h"
#include "gui/webview.h"
#include "gui/locationlineedit.h"
#include <QWidget>

View File

@ -15,7 +15,7 @@
// 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/webbrowsernetworkaccessmanager.h"
#include "network-web/webbrowsernetworkaccessmanager.h"
#include <QNetworkReply>
#include <QApplication>

View File

@ -18,7 +18,7 @@
#ifndef WEBBROWSERNETWORKACCESSMANAGER_H
#define WEBBROWSERNETWORKACCESSMANAGER_H
#include "core/basenetworkaccessmanager.h"
#include "network-web/basenetworkaccessmanager.h"
#include <QPointer>

View File

@ -1,4 +1,4 @@
#include "core/webfactory.h"
#include "network-web/webfactory.h"
#include "core/defs.h"
#include "core/settings.h"

View File

@ -15,10 +15,10 @@
// 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/webpage.h"
#include "network-web/webpage.h"
#include "core/webbrowsernetworkaccessmanager.h"
#include "gui/webbrowser.h"
#include "network-web/webbrowsernetworkaccessmanager.h"
#include "network-web/webbrowser.h"
#include <QNetworkReply>
#include <QWebFrame>

View File

@ -15,11 +15,11 @@
// 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/webview.h"
#include "network-web/webview.h"
#include "core/defs.h"
#include "core/settings.h"
#include "core/webpage.h"
#include "network-web/webpage.h"
#include "gui/skinfactory.h"
#include "gui/iconthemefactory.h"