Massive cleanups.
This commit is contained in:
		
							parent
							
								
									f1f84acbe6
								
							
						
					
					
						commit
						cefbefda2a
					
				
					 24 changed files with 745 additions and 894 deletions
				
			
		|  | @ -320,8 +320,6 @@ 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 | ||||
|  |  | |||
|  | @ -20,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> | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
|  | @ -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.
 | ||||
|  |  | |||
|  | @ -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; | ||||
| } | ||||
|  |  | |||
|  | @ -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; | ||||
| }; | ||||
|  |  | |||
|  | @ -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/networkfactory.h" | ||||
| #include "core/textfactory.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."); | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -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) | ||||
|  |  | |||
|  | @ -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; | ||||
| } | ||||
|  | @ -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
 | ||||
|  | @ -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."); | ||||
|   } | ||||
| } | ||||
|  | @ -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
 | ||||
|  | @ -19,11 +19,12 @@ | |||
| 
 | ||||
| #include "core/defs.h" | ||||
| #include "core/silentnetworkaccessmanager.h" | ||||
| #include "core/feedsmodelstandardfeed.h" | ||||
| #include "core/settings.h" | ||||
| 
 | ||||
| #include <QEventLoop> | ||||
| #include <QTimer> | ||||
| #include <QIcon> | ||||
| #include <QPixmap> | ||||
| #include <QTextDocument> | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -22,8 +22,6 @@ | |||
| #include <QApplication> | ||||
| 
 | ||||
| 
 | ||||
| class FeedsModelStandardFeed; | ||||
| 
 | ||||
| class NetworkFactory { | ||||
|     Q_DECLARE_TR_FUNCTIONS(NetworkFactory) | ||||
| 
 | ||||
|  |  | |||
|  | @ -17,8 +17,6 @@ | |||
| 
 | ||||
| #include "core/silentnetworkaccessmanager.h" | ||||
| 
 | ||||
| #include "core/feedsmodelstandardfeed.h" | ||||
| 
 | ||||
| #include <QNetworkReply> | ||||
| #include <QAuthenticator> | ||||
| #include <QApplication> | ||||
|  |  | |||
|  | @ -21,7 +21,7 @@ | |||
| #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 "gui/formmain.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, | ||||
|  |  | |||
|  | @ -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; | ||||
|       } | ||||
| 
 | ||||
|  | @ -553,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() { | ||||
|  |  | |||
|  | @ -103,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); | ||||
|  |  | |||
|  | @ -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(); | ||||
|  |  | |||
|  | @ -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 &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 &feed</string> | ||||
|     <string>New &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 &internal browser</string> | ||||
|    </property> | ||||
|   </action> | ||||
|   <action name="m_actionAddStandardCategory"> | ||||
|   <action name="m_actionAddCategory"> | ||||
|    <property name="text"> | ||||
|     <string>New standard &category</string> | ||||
|     <string>New &category</string> | ||||
|    </property> | ||||
|    <property name="toolTip"> | ||||
|     <string>Add new standard category.</string> | ||||
|     <string>Add new category.</string> | ||||
|    </property> | ||||
|   </action> | ||||
|   <action name="m_actionNoActions"> | ||||
|  |  | |||
|  | @ -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 { | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
|  | @ -23,7 +23,6 @@ | |||
| #include "core/feedsmodelrootitem.h" | ||||
| #include "core/feedsmodelcategory.h" | ||||
| #include "core/feedsmodelfeed.h" | ||||
| #include "core/feedsmodelstandardfeed.h" | ||||
| #include "core/networkfactory.h" | ||||
| #include "gui/iconthemefactory.h" | ||||
| #include "gui/baselineedit.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); | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue