diff options
5 files changed, 314 insertions, 58 deletions
diff --git a/src-qt5/core/lumina-desktop/desktop-plugins/rssfeeder/RSSFeedPlugin.cpp b/src-qt5/core/lumina-desktop/desktop-plugins/rssfeeder/RSSFeedPlugin.cpp index d4a9aa44..0bfc090e 100644 --- a/src-qt5/core/lumina-desktop/desktop-plugins/rssfeeder/RSSFeedPlugin.cpp +++ b/src-qt5/core/lumina-desktop/desktop-plugins/rssfeeder/RSSFeedPlugin.cpp @@ -19,6 +19,7 @@ RSSFeedPlugin::RSSFeedPlugin(QWidget* parent, QString ID) : LDPlugin(parent, ID) ui->setupUi(this); //Load the global settings setprefix = "rssreader/"; //this structure/prefix should be used for *all* plugins of this type + RSS = new RSSReader(this, setprefix); //Create the options menu optionsMenu = new QMenu(this); @@ -28,10 +29,23 @@ RSSFeedPlugin::RSSFeedPlugin(QWidget* parent, QString ID) : LDPlugin(parent, ID) connect(ui->push_back2, SIGNAL(clicked()), this, SLOT(backToFeeds()) ); connect(ui->push_back3, SIGNAL(clicked()), this, SLOT(backToFeeds()) ); connect(ui->push_save_settings, SIGNAL(clicked()), this, SLOT(saveSettings()) ); - + connect(RSS, SIGNAL(rssChanged(QString)), this, SLOT(RSSItemChanged(QString)) ); + connect(RSS, SIGNAL(newChannelsAvailable()), this, SLOT(UpdateFeedList())); + connect(ui->tool_gotosite, SIGNAL(clicked()), this, SLOT(openFeedPage()) ); + connect(ui->push_rm_feed, SIGNAL(clicked()), this, SLOT(removeFeed()) ); + connect(ui->push_add_url, SIGNAL(clicked()), this, SLOT(addNewFeed()) ); updateOptionsMenu(); QTimer::singleShot(0,this, SLOT(loadIcons()) ); //qDebug() << " - Done with init"; + QStringList feeds; + if( !LSession::handle()->DesktopPluginSettings()->contains(setprefix+"currentfeeds") ){ + //First-time run of the plugin - automatically load the default feeds + feeds << "http://lumina-desktop.org/?feed=rss2"; //Lumina Desktop blog feed + LSession::handle()->DesktopPluginSettings()->setValue(setprefix+"currentfeeds", feeds); + }else{ + feeds = LSession::handle()->DesktopPluginSettings()->value(setprefix+"currentfeeds",QStringList()).toStringList(); + } + RSS->addUrls(feeds); } RSSFeedPlugin::~RSSFeedPlugin(){ @@ -47,16 +61,36 @@ void RSSFeedPlugin::updateOptionsMenu(){ optionsMenu->addAction(LXDG::findIcon("help-about",""), tr("View Feed Details"), this, SLOT(openFeedInfo()) ); optionsMenu->addAction(LXDG::findIcon("configure",""), tr("Settings"), this, SLOT(openSettings()) ); optionsMenu->addSeparator(); - optionsMenu->addAction(LXDG::findIcon("download",""), tr("Update Feeds Now"), this, SLOT(resync()) ); + optionsMenu->addAction(LXDG::findIcon("download",""), tr("Update Feeds Now"), this, SLOT(resyncFeeds()) ); } //Simplification functions for loading feed info onto widgets void RSSFeedPlugin::updateFeed(QString ID){ - + //Save the datetime this feed was read + LSession::handle()->DesktopPluginSettings()->setValue(setprefix+"feedReads/"+ID, QDateTime::currentDateTime() ); + + //Now clear/update the feed viewer (HTML) + ui->text_feed->clear(); + QString html; + RSSchannel data = RSS->dataForID(ID); + ui->label_lastupdate->setText( data.lastsync.toString(Qt::DefaultLocaleShortDate) ); + // - generate the html + // html.append("<ul style=\"margin-left: 3px;\">\n"); + for(int i=0; i<data.items.length(); i++){ + //html.append("<li>"); + html.append("<h3><a href=\""+data.items[i].link+"\">"+data.items[i].title+"</a></h3>"); + if(!data.items[i].pubdate.isNull()){html.append("<i>("+data.items[i].pubdate.toString(Qt::DefaultLocaleShortDate)+")</i><br>"); } + html.append(data.items[i].description); + //html.append("</li>\n"); + if(i+1 < data.items.length()){ html.append("<br>"); } + } + //html.append("</ul>\n"); + // - load that html into the viewer + ui->text_feed->setHtml(html); } void RSSFeedPlugin::updateFeedInfo(QString ID){ - + ui->page_feed_info->setWhatsThis(ID); } //================ @@ -80,6 +114,9 @@ void RSSFeedPlugin::backToFeeds(){ } void RSSFeedPlugin::openFeedInfo(){ + QString ID = ui->combo_feed->currentData().toString(); + if(ID.isEmpty()){ return; } + updateFeedInfo(ID); ui->stackedWidget->setCurrentWidget(ui->page_feed_info); } @@ -113,12 +150,16 @@ void RSSFeedPlugin::addNewFeed(){ ui->line_new_url->setFocus(); return; } + //Add the URL to the settings file for next login + QStringList feeds = LSession::handle()->DesktopPluginSettings()->value(setprefix+"currentfeeds",QStringList()).toStringList(); + feeds << url.toString(); + LSession::handle()->DesktopPluginSettings()->setValue(setprefix+"currentfeeds", feeds); //Set this URL as the current selection ui->combo_feed->setWhatsThis(url.toString()); //hidden field - will trigger an update in a moment //Add the URL to the backend - - UpdateFeedList(); //now re-load the feeds which are available + RSS->addUrls(QStringList() << url.toString()); + //UpdateFeedList(); //now re-load the feeds which are available //Now go back to the main page backToFeeds(); @@ -128,27 +169,38 @@ void RSSFeedPlugin::removeFeed(){ QString ID = ui->page_feed_info->whatsThis(); if(ID.isEmpty()){ return; } //Remove from the RSS feed object - - //Update the feed list - UpdateFeedList(); //now re-load the feeds which are available + RSS->removeUrl(ID); + //Remove the URL from the settings file for next login + QStringList feeds = LSession::handle()->DesktopPluginSettings()->value(setprefix+"currentfeeds",QStringList()).toStringList(); + feeds.removeAll(ID); + LSession::handle()->DesktopPluginSettings()->setValue(setprefix+"currentfeeds", feeds); //Now go back to the main page backToFeeds(); } +void RSSFeedPlugin::resyncFeeds(){ + RSS->addUrls( LSession::handle()->DesktopPluginSettings()->value(setprefix+"currentfeeds",QStringList()).toStringList() ); + RSS->syncNow(); +} + // - Feed Interactions void RSSFeedPlugin::currentFeedChanged(){ QString ID = ui->combo_feed->currentData().toString(); if(ID.isEmpty()){ return; } //no feed selected + //Remove the "unread" color from the feed + ui->combo_feed->setItemData( ui->combo_feed->currentIndex(), QBrush(Qt::transparent) , Qt::BackgroundRole); + updateFeed(ID); } void RSSFeedPlugin::openFeedPage(){ //Open main website for feed QString ID = ui->combo_feed->currentData().toString(); //Find the data associated with this feed - QString url; - + RSSchannel data = RSS->dataForID(ID); + QString url = data.link; + qDebug() << "Open Feed Page:" << url; //Now launch the browser if(!url.isEmpty()){ - QProcess::startDetached("lumina-open \""+url+"\""); + LSession::LaunchApplication("lumina-open \""+url+"\""); } } @@ -159,7 +211,6 @@ void RSSFeedPlugin::saveSettings(){ if(ui->combo_sync_units->currentIndex()==1){ DI = DI*60; } //convert from hours to minutes set->setValue(setprefix+"default_interval_minutes", DI); set->sync(); - //Now prod the feed object that something changed //Now go back to the feeds backToFeeds(); @@ -167,12 +218,66 @@ void RSSFeedPlugin::saveSettings(){ //Feed Object interactions void RSSFeedPlugin::UpdateFeedList(){ - + + QString activate = ui->combo_feed->whatsThis(); + if(!activate.isEmpty()){ ui->combo_feed->setWhatsThis(""); } + if(activate.isEmpty()){ activate = ui->combo_feed->currentData().toString(); } // keep current item selected + //Now get/list all the available feeds + QStringList IDS = RSS->channels(); //this is pre-sorted by title of the feed + //qDebug() << "Update RSS Feed List:" << IDS << activate; + for(int i=0; i<IDS.length(); i++){ + bool newitem = false; + if(ui->combo_feed->count()<=i){ newitem = true; } + else{ + QString cid = ui->combo_feed->itemData(i).toString(); + if(IDS[i]!=cid){ + if(IDS.contains(cid)){ newitem = true; } //this item is just out of order + else{ ui->combo_feed->removeItem(i); } //item no longer is valid + } + } + if(newitem){ + //Need to add a new item at this point in the menu + RSSchannel info = RSS->dataForID(IDS[i]); + if(info.title.isEmpty()){ + //invalid/empty channel + ui->combo_feed->insertItem(i, IDS[i], IDS[i]); //just show the URL + }else{ + ui->combo_feed->insertItem(i, info.icon, info.title, IDS[i]); + } + } + } + //Remove any extra items on the end of the list + for(int i=IDS.length(); i<ui->combo_feed->count(); i++){ + ui->combo_feed->removeItem(i); i--; + } + //Now activate the proper item as needed + if(IDS.contains(activate)){ + ui->combo_feed->setCurrentIndex( IDS.indexOf(activate) ); + } } void RSSFeedPlugin::RSSItemChanged(QString ID){ - + for(int i=0; i<ui->combo_feed->count(); i++){ + if(ui->combo_feed->itemData(i).toString()!=ID){ continue; } + RSSchannel info = RSS->dataForID(ID); + if(info.title.isEmpty()){ + ui->combo_feed->setItemText(i, ID); + ui->combo_feed->setItemIcon(i, LXDG::findIcon("dialog-cancel","") ); + }else{ + ui->combo_feed->setItemText(i, info.title); + ui->combo_feed->setItemIcon(i, info.icon ); + QColor color(Qt::transparent); + if( info.lastBuildDate > LSession::handle()->DesktopPluginSettings()->value(setprefix+"feedReads/"+ID,QDateTime()).toDateTime() ){ + color = QColor(255,10,10,100); //semi-transparent red + } + ui->combo_feed->setItemData(i, QBrush(color) , Qt::BackgroundRole); + } + } + if(ID == ui->combo_feed->currentData().toString()){ + currentFeedChanged(); //re-load the current feed + } } + //================== // PUBLIC SLOTS //================== diff --git a/src-qt5/core/lumina-desktop/desktop-plugins/rssfeeder/RSSFeedPlugin.h b/src-qt5/core/lumina-desktop/desktop-plugins/rssfeeder/RSSFeedPlugin.h index a29e5ec1..ea6ee5e7 100644 --- a/src-qt5/core/lumina-desktop/desktop-plugins/rssfeeder/RSSFeedPlugin.h +++ b/src-qt5/core/lumina-desktop/desktop-plugins/rssfeeder/RSSFeedPlugin.h @@ -12,6 +12,8 @@ #include <QTimer> #include "../LDPlugin.h" +#include "RSSObjects.h" + namespace Ui{ class RSSFeedPlugin; }; @@ -30,6 +32,7 @@ private: Ui::RSSFeedPlugin *ui; QMenu *optionsMenu; QString setprefix; //settings prefix + RSSReader *RSS; void updateOptionsMenu(); @@ -49,6 +52,7 @@ private slots: // - Feed Management void addNewFeed(); // the "add" button (current url in widget on page) void removeFeed(); // the "remove" button (current feed for page) + void resyncFeeds(); // - Feed Interactions void currentFeedChanged(); void openFeedPage(); //Open the website in a browser diff --git a/src-qt5/core/lumina-desktop/desktop-plugins/rssfeeder/RSSFeedPlugin.ui b/src-qt5/core/lumina-desktop/desktop-plugins/rssfeeder/RSSFeedPlugin.ui index d9972473..7689e88b 100644 --- a/src-qt5/core/lumina-desktop/desktop-plugins/rssfeeder/RSSFeedPlugin.ui +++ b/src-qt5/core/lumina-desktop/desktop-plugins/rssfeeder/RSSFeedPlugin.ui @@ -102,7 +102,7 @@ </layout> </item> <item> - <widget class="QTextEdit" name="text_feed"> + <widget class="QTextBrowser" name="text_feed"> <property name="undoRedoEnabled"> <bool>false</bool> </property> @@ -119,6 +119,12 @@ p, li { white-space: pre-wrap; } <property name="textInteractionFlags"> <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set> </property> + <property name="openExternalLinks"> + <bool>true</bool> + </property> + <property name="openLinks"> + <bool>true</bool> + </property> </widget> </item> </layout> @@ -191,8 +197,8 @@ p, li { white-space: pre-wrap; } <rect> <x>0</x> <y>0</y> - <width>228</width> - <height>178</height> + <width>100</width> + <height>30</height> </rect> </property> </widget> diff --git a/src-qt5/core/lumina-desktop/desktop-plugins/rssfeeder/RSSObjects.cpp b/src-qt5/core/lumina-desktop/desktop-plugins/rssfeeder/RSSObjects.cpp index 45a6bfeb..f525d19c 100644 --- a/src-qt5/core/lumina-desktop/desktop-plugins/rssfeeder/RSSObjects.cpp +++ b/src-qt5/core/lumina-desktop/desktop-plugins/rssfeeder/RSSObjects.cpp @@ -8,11 +8,20 @@ #include <QNetworkRequest> #include <QXmlStreamReader> +#include "LSession.h" + //============ // PUBLIC //============ -RSSReader::RSSReader(QObject *parent) : QObject(parent){ +RSSReader::RSSReader(QObject *parent, QString settingsPrefix) : QObject(parent){ NMAN = new QNetworkAccessManager(this); + connect(NMAN, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*)) ); + + setprefix = settingsPrefix; + syncTimer = new QTimer(this); + syncTimer->setInterval(300000); //5 minutes + connect(syncTimer, SIGNAL(timeout()), this, SLOT(checkTimes())); + syncTimer->start(); } RSSReader::~RSSReader(){ @@ -21,81 +30,201 @@ RSSReader::~RSSReader(){ //Information retrieval QStringList RSSReader::channels(){ - return QStringList( data.keys() ); + QStringList urls = hash.keys(); + QStringList ids; + //sort all the channels by title before output + for(int i=0; i<urls.length(); i++){ + QString title = hash[urls[i]].title; + if(title.isEmpty()){ title = "ZZZ"; } //put currently-invalid ones at the end of the list + ids << title+" : "+urls[i]; + } + ids.sort(); + //Now strip off all the titles again to just get the IDs + for(int i=0; i<ids.length(); i++){ + ids[i] = ids[i].section(" : ",-1); + } + return ids; } -Rsschannel RSSReader::dataForID(QString ID){ - +RSSchannel RSSReader::dataForID(QString ID){ + if(hash.contains(ID)){ return hash[ID]; } + else{ return RSSchannel(); } } //Initial setup -void RSSReader::addUrls(){ - +void RSSReader::addUrls(QStringList urls){ + //qDebug() << "Add URLS:" << urls; + for(int i=0; i<urls.length(); i++){ + //Note: Make sure we get the complete URL form for accurate comparison later + QString url = QUrl(urls[i]).toString(); + if(hash.contains(url)){ continue; } //already handled + hash.insert(url, RSSchannel()); //put an empty struct into the hash for now + requestRSS(url); //startup the initial request for this url + } + emit newChannelsAvailable(); } void RSSReader::removeUrl(QString ID){ - + if(hash.contains(ID)){ hash.remove(ID); } + emit newChannelsAvailable(); } //================= // PUBLIC SLOTS //================= void RSSReader::syncNow(){ - + QStringList urls = hash.keys(); + for(int i=0; i<urls.length(); i++){ + requestRSS(urls[i]); + } } //================= // PRIVATE //================= -void RSSchannel::requestRSS(QString url){ - NMAN->get( QNetworkRequest( QUrl(url) ) ); +void RSSReader::requestRSS(QString url){ + if(!outstandingURLS.contains(url)){ + //qDebug() << "Request URL:" << url; + NMAN->get( QNetworkRequest( QUrl(url) ) ); + outstandingURLS << url; + } } //RSS parsing functions -RSSchannel RSSReader::readRSS(QIODevice *device){ - QXmlStreamReader xml(device); +RSSchannel RSSReader::readRSS(QByteArray bytes){ + //Note: We could expand this later to support multiple "channel"s per Feed + // but it seems like there is normally only one channel anyway + //qDebug() << "Read RSS:" << bytes; + QXmlStreamReader xml(bytes); RSSchannel rssinfo; + //qDebug() << "Can Read XML Stream:" << !xml.hasError(); + if(xml.hasError()){ qDebug() << " - Error String:" << xml.errorString(); } if(xml.readNextStartElement()){ - if(xml.name() = "rss" && xml.attributes().value("version" =="2.0")){ + //qDebug() << " - RSS Element:" << xml.name(); + if(xml.name() == "rss" && xml.attributes().value("version") =="2.0"){ while(xml.readNextStartElement()){ if(xml.name()=="channel"){ rssinfo = readRSSChannel(&xml); } + else{ xml.skipCurrentElement(); } } } } + if(xml.hasError()){ qDebug() << " - Error String:" << xml.errorString(); } return rssinfo; } RSSchannel RSSReader::readRSSChannel(QXmlStreamReader *rss){ RSSchannel info; - while(rss.readNextStartElement()){ - if(rss.name()=="item"){ info.items << readRSSItem(rss); } - else if(rss.name()=="title"){ info.title = rss.readElementText(); } - else if(rss.name()=="link"){ info.link = rss.readElementText(); } - else if(rss.name()=="description"){ info.description = rss.readElementText(); } - else if(rss.name()=="lastBuildDate"){ info.lastBuildDate = RSSDateTime(rss.readElementText()); } - else if(rss.name()=="pubDate"){ info.lastPubDate = RSSDateTime(rss.readElementText()); } - else if(rss.name()=="image"){ info.link = rss.readElementText(); } - else if(rss.name()=="guid"){ info.guid = rss.readElementText(); } - //else if(rss.name()=="skipHours"){ info.link = rss.readElementText(); } - //else if(rss.name()=="skipDays"){ info.link = rss.readElementText(); } - else if(rss.name()=="ttl"){ info.timetolive = rss.readElementText().toInt(); } + info.timetolive = -1; + while(rss->readNextStartElement()){ + //qDebug() << " - RSS Element (channel):" <<rss->name(); + if(rss->name()=="item"){ info.items << readRSSItem(rss); } + else if(rss->name()=="title"){ info.title = rss->readElementText(); } + else if(rss->name()=="link"){ info.link = rss->readElementText(); } + else if(rss->name()=="description"){ info.description = rss->readElementText(); } + else if(rss->name()=="lastBuildDate"){ info.lastBuildDate = RSSDateTime(rss->readElementText()); } + else if(rss->name()=="pubDate"){ info.lastPubDate = RSSDateTime(rss->readElementText()); } + else if(rss->name()=="image"){ readRSSImage(&info, rss); } + //else if(rss->name()=="skipHours"){ info.link = rss->readElementText(); } + //else if(rss->name()=="skipDays"){ info.link = rss->readElementText(); } + else if(rss->name()=="ttl"){ info.timetolive = rss->readElementText().toInt(); } + else{ rss->skipCurrentElement(); } } return info; } + RSSitem RSSReader::readRSSItem(QXmlStreamReader *rss){ - + RSSitem item; + while(rss->readNextStartElement()){ + //qDebug() << " - RSS Element (Item):" << rss->name(); + if(rss->name()=="title"){ item.title = rss->readElementText(); } + else if(rss->name()=="link"){ item.link = rss->readElementText(); } + else if(rss->name()=="description"){ item.description = rss->readElementText(); } + else if(rss->name()=="comments"){ item.comments_url = rss->readElementText(); } + else if(rss->name()=="author"){ + //Special handling - this field can contain both email and name + QString raw = rss->readElementText(); + if(raw.contains("@")){ + item.author_email = raw.split(" ").filter("@").first(); + item.author = raw.remove(item.author_email).remove("(").remove(")").simplified(); //the name is often put within parentheses after the email + }else{ item.author = raw; } + } + else if(rss->name()=="guid"){ item.guid = rss->readElementText(); } + else if(rss->name()=="pubDate"){ item.pubdate = RSSDateTime(rss->readElementText()); } + else{ rss->skipCurrentElement(); } + } + return item; } -QDateTime RSSReader::RSSDateTime(QString datetime){ +void RSSReader::readRSSImage(RSSchannel *item, QXmlStreamReader *rss){ + while(rss->readNextStartElement()){ + //qDebug() << " - RSS Element (Image):" << rss->name(); + if(rss->name()=="url"){ item->icon_url = rss->readElementText(); } + else if(rss->name()=="title"){ item->icon_title = rss->readElementText(); } + else if(rss->name()=="link"){ item->icon_link = rss->readElementText(); } + else if(rss->name()=="width"){ item->icon_size.setWidth(rss->readElementText().toInt()); } + else if(rss->name()=="height"){ item->icon_size.setHeight(rss->readElementText().toInt()); } + else if(rss->name()=="description"){ item->icon_description = rss->readElementText(); } + } + //Go ahead and kick off the request for the icon + if(!item->icon_url.isEmpty()){ requestRSS(item->icon_url); } +} +QDateTime RSSReader::RSSDateTime(QString datetime){ + return QDateTime::fromString(datetime, Qt::RFC2822Date); } //================= // PRIVATE SLOTS //================= void RSSReader::replyFinished(QNetworkReply *reply){ - RSSchannel info = ReadRSS(reply); //QNetworkReply can be used as QIODevice - reply->deleteLater(); //clean up - //Validate the info and announce any changes - + QString url = reply->request().url().toString(); + //qDebug() << "Got Reply:" << url; + QByteArray data = reply->readAll(); + outstandingURLS.removeAll(url); + if(!hash.contains(url)){ + //qDebug() << " - hash does not contain URL!!"; + //URL removed from list while a request was outstanding? + //Could also be an icon fetch response + QStringList keys = hash.keys(); + for(int i=0; i<keys.length(); i++){ + if(hash[keys[i]].icon_url == url){ + //Icon fetch response + RSSchannel info = hash[keys[i]]; + QByteArray bytes = reply->readAll(); + QImage img = QImage::fromData(bytes); + info.icon = QIcon( QPixmap::fromImage(img) ); + qDebug() << "Got Icon response:" << url << bytes; + qDebug() << info.icon; + hash.insert(keys[i], info); //insert back into the hash + emit rssChanged(keys[i]); + break; + } + } + reply->deleteLater(); + }else{ + //RSS reply + RSSchannel info = readRSS(data); //QNetworkReply can be used as QIODevice + reply->deleteLater(); //clean up + //Validate the info and announce any changes + if(info.title.isEmpty() || info.link.isEmpty() || info.description.isEmpty()){ return; } //bad info/read + //Update the bookkeeping elements of the info + if(info.timetolive<=0){ info.timetolive = LSession::handle()->DesktopPluginSettings()->value(setprefix+"default_interval_minutes", 60).toInt(); } + if(info.timetolive <=0){ info.timetolive = 60; } //error in integer conversion from settings? + info.lastsync = QDateTime::currentDateTime(); info.nextsync = info.lastsync.addSecs(info.timetolive * 60); + //Now see if anything changed and save the info into the hash + bool changed = (hash[url].lastBuildDate.isNull() || (hash[url].lastBuildDate < info.lastBuildDate) ); + bool newinfo = false; + if(changed){ newinfo = hash[url].title.isEmpty(); } //no previous info from this URL + hash.insert(url, info); + if(newinfo){ emit newChannelsAvailable(); } //new channel + else if(changed){ emit rssChanged(url); } //update to existing channel + } +} +void RSSReader::checkTimes(){ + if(LSession::handle()->DesktopPluginSettings()->value(setprefix+"manual_sync_only", false).toBool()){ return; } + QStringList urls = hash.keys(); + QDateTime cdt = QDateTime::currentDateTime(); + for(int i=0; i<urls.length(); i++){ + if(hash[urls[i]].nextsync < cdt){ requestRSS(urls[i]); } + } } diff --git a/src-qt5/core/lumina-desktop/desktop-plugins/rssfeeder/RSSObjects.h b/src-qt5/core/lumina-desktop/desktop-plugins/rssfeeder/RSSObjects.h index a1cffc3f..82ad9f86 100644 --- a/src-qt5/core/lumina-desktop/desktop-plugins/rssfeeder/RSSObjects.h +++ b/src-qt5/core/lumina-desktop/desktop-plugins/rssfeeder/RSSObjects.h @@ -13,6 +13,8 @@ #include <QDateTime> #include <QList> #include <QIcon> +#include <QTimer> +#include <QXmlStreamReader> //Contained in the Qt "core" module - don't need the full "xml" module for this struct RSSitem{ //Required Fields @@ -36,47 +38,57 @@ struct RSSchannel{ //QStringList skipdays; // - icon info QIcon icon; - QString iconurl, icontitle, iconlink; - QSize iconsize; + QString icon_url, icon_title, icon_link, icon_description; + QSize icon_size; //All items within this channel QList<RSSitem> items; //Optional RSS2 elements ignored: // "cloud", "textInput", "rating", "language", "copyright", "managingEditor", "webMaster", // "category", "generator", "docs" + + //Internal data for bookkeeping + QDateTime lastsync, nextsync; }; class RSSReader : public QObject{ Q_OBJECT public: - RSSReader(QObject *parent); + RSSReader(QObject *parent, QString settingsPrefix); ~RSSReader(); //Information retrieval QStringList channels(); //returns all ID's - Rsschannel dataForID(QString ID); + RSSchannel dataForID(QString ID); //Initial setup - void addUrls(); + void addUrls(QStringList urls); void removeUrl(QString ID); public slots: void syncNow(); //not generally needed private: - QHash<QString, RSSchannel> data; // ID/data - //Network request functions + //Internal data objects + QHash<QString, RSSchannel> hash; // ID/data + QString setprefix; + QTimer *syncTimer; QNetworkAccessManager *NMAN; + QStringList outstandingURLS; + + //Network request function void requestRSS(QString url); //RSS parsing functions - RSSchannel readRSS(QByteArray rss); - RSSchannel readRSSChannel(QXmlStreamReader *rss) + RSSchannel readRSS(QByteArray bytes); + RSSchannel readRSSChannel(QXmlStreamReader *rss); RSSitem readRSSItem(QXmlStreamReader *rss); + void readRSSImage(RSSchannel *item, QXmlStreamReader *rss); QDateTime RSSDateTime(QString datetime); private slots: void replyFinished(QNetworkReply *reply); + void checkTimes(); signals: void rssChanged(QString); //ID |