copying qml/ArticleViewer.qml -> /scratchbox/users/ymarcoz/home/ymarcoz/workspace/feedingit/psa_harmattan/feedingit/deb_dist/feedingit-0.1.0/debian/feedingit/usr/share/feedingit/qml
copying qml/MainPage.qml -> /scratchbox/users/ymarcoz/home/ymarcoz/workspace/feedingit/psa_harmattan/feedingit/deb_dist/feedingit-0.1.0/debian/feedingit/usr/share/feedingit/qml
copying qml/main.qml -> /scratchbox/users/ymarcoz/home/ymarcoz/workspace/feedingit/psa_harmattan/feedingit/deb_dist/feedingit-0.1.0/debian/feedingit/usr/share/feedingit/qml
+copying qml/Settings.qml -> /scratchbox/users/ymarcoz/home/ymarcoz/workspace/feedingit/psa_harmattan/feedingit/deb_dist/feedingit-0.1.0/debian/feedingit/usr/share/feedingit/qml
copying qml/ArticleDisplay.qml -> /scratchbox/users/ymarcoz/home/ymarcoz/workspace/feedingit/psa_harmattan/feedingit/deb_dist/feedingit-0.1.0/debian/feedingit/usr/share/feedingit/qml
copying qml/AddFeed.qml -> /scratchbox/users/ymarcoz/home/ymarcoz/workspace/feedingit/psa_harmattan/feedingit/deb_dist/feedingit-0.1.0/debian/feedingit/usr/share/feedingit/qml
copying qml/feedSearch.js -> /scratchbox/users/ymarcoz/home/ymarcoz/workspace/feedingit/psa_harmattan/feedingit/deb_dist/feedingit-0.1.0/debian/feedingit/usr/share/feedingit/qml
* source package automatically created by stdeb 0.6.0+git
- -- Yves <yves@marcoz.org> Sun, 06 Nov 2011 21:13:19 -0800
+ -- Yves <yves@marcoz.org> Mon, 07 Nov 2011 21:25:40 -0800
Depends: ${misc:Depends}, python-pyside.qtgui, python-pyside.qtopengl, python-pyside.qtdeclarative, python-dbus, python-gconf
Breaks: ${python:Breaks}
Description: FeedingIt - RSS Reader
- This file should contain a writeup describing what your application does,
- and how to use it. The content of this file goes into the long_description
- field of setup.py, which in turn becomes the long version of the Description
- field in the debian/control file of the project.
+ FeedingIt is an easy to use RSS feed reader with portrait mode support and
+ automatic feed updates. You can swipe left and right to move between articles.
+ Import and export of subscription lists is supported for OPML files.
+ Images can be cached during the feed update, so you can view images in the
+ downloaded articles while offline.
.
Version: 0.1.0-1
Architecture: all
Maintainer: Yves <yves@marcoz.org>
-Installed-Size: 756
+Installed-Size: 764
Depends: python-pyside.qtgui, python-pyside.qtopengl, python-pyside.qtdeclarative, python-dbus, python-gconf
Breaks: python (<< 2.6)
Section: user/development
Priority: optional
Description: FeedingIt - RSS Reader
- This file should contain a writeup describing what your application does,
- and how to use it. The content of this file goes into the long_description
- field of setup.py, which in turn becomes the long version of the Description
- field in the debian/control file of the project.
+ FeedingIt is an easy to use RSS feed reader with portrait mode support and
+ automatic feed updates. You can swipe left and right to move between articles.
+ Import and export of subscription lists is supported for OPML files.
+ Images can be cached during the feed update, so you can view images in the
+ downloaded articles while offline.
4420c31f88de68fe6e1b7637abb06196 usr/bin/feedingit
6a42e9aebedfd157062bd5a9616dc935 usr/share/applications/feedingit.desktop
eda8cc6ffe8d842d6dfe0244b01b3042 usr/share/dbus-1/services/feedingit_status.service
-8a3883bbf4914140ca72678bfdac9230 usr/share/doc/feedingit/changelog.Debian.gz
+f0e50a0975405a76169cc01da42cdcff usr/share/doc/feedingit/changelog.Debian.gz
bac2be6ae9673ee5096e20e8b714c9cd usr/share/feedingit/BeautifulSoup.py
-fa5490f2022424a091b36e75f21e6596 usr/share/feedingit/XmlHandler.py
+8f6e980f2e1154103a90763be6c00dc4 usr/share/feedingit/XmlHandler.py
d41d8cd98f00b204e9800998ecf8427e usr/share/feedingit/__init__.py
6b5296119ef6bc859c3e3a8706fa7f0d usr/share/feedingit/config.py
-4724920e64b0f5d2828c1c245502a115 usr/share/feedingit/debugging.py
+9cf859c8297e4b0e8466cb5861eb75e7 usr/share/feedingit/debugging.py
fae02e730b76761d43a626fe19828d5e usr/share/feedingit/download.py
-b9b632032e30e584115aecab8c809f88 usr/share/feedingit/feedingit.py
+42786634079070f54f06838e922a1307 usr/share/feedingit/feedingit.py
afa4f462892136f59beaf96b6bf1cf96 usr/share/feedingit/feedparser.py
c1a0c0a9ccefd64d1e27bddb817c72a3 usr/share/feedingit/httpprogresshandler.py
f1e9ba0f44786f513659a7fa3111fc8a usr/share/feedingit/jobmanager.py
0201faa30d34c58d71f36ab42a7a8233 usr/share/feedingit/mainthread.py
d9c0665dfdd5cf19f1529ce88af95134 usr/share/feedingit/opml.py
-37230c16aad0865afbec03d8105dd491 usr/share/feedingit/qml/AddFeed.qml
+31d26f7ae14940f99ec8d47c7035fd04 usr/share/feedingit/qml/AddFeed.qml
af27062fdba0bc7a3df92116e8340d19 usr/share/feedingit/qml/ArticleDisplay.qml
-594c82729b9e53bd9ca143006e088893 usr/share/feedingit/qml/ArticleViewer.qml
+80858d581189fcf00ab1c6192be87e27 usr/share/feedingit/qml/ArticleViewer.qml
15083e9a1fac05c8efaaa085dfabcbcb usr/share/feedingit/qml/Articles.qml
-555d070ba1db23b7b553628c9ae4ce72 usr/share/feedingit/qml/Categories.qml
+8f480702a2107a8017fdd94226b9b7d4 usr/share/feedingit/qml/Categories.qml
cd30f5eaec0885358261d7a96bfaf8cd usr/share/feedingit/qml/FeedingIt.qml
-29e9f5811d5e5bb9b4078fb2fb8d5b17 usr/share/feedingit/qml/Feeds.qml
+02784496485eb5547a1ad48c043bb2a9 usr/share/feedingit/qml/Feeds.qml
bec5fe4599a3ad5799ed96d7ed81fb5f usr/share/feedingit/qml/MainPage.qml
-aa3fc0a4edbd17d93a9dc5c39c433c3d usr/share/feedingit/qml/TestWebview.qml
+1c500d41d190f8f8c6d478fda117bb28 usr/share/feedingit/qml/Settings.qml
+53595350196421039e3adefac380d21f usr/share/feedingit/qml/TestWebview.qml
508fde19c7bb7bc4892bd6642fbcb7d3 usr/share/feedingit/qml/TextInputClear.qml
cef5ae4af926a759f4a233336c00f017 usr/share/feedingit/qml/common/AddCat.qml
c39cde168ef8d608670c81be7c808701 usr/share/feedingit/qml/common/AddFeed.qml
1c7751b124aa1bdf4b89ec76cdf815a2 usr/share/feedingit/qml/i18n/FeedingIt.ts
7790a99425dd7c1046e6ae3b1ee72a03 usr/share/feedingit/qml/i18n/qml_en.qm
1674fcce45bcf3319e61d19a9adf4fdd usr/share/feedingit/qml/i18n/qml_en.ts
-c94a3937f0104446e0ceed1eb4aacf78 usr/share/feedingit/qml/main.qml
-7def699001828ea16976de3d5835dd3c usr/share/feedingit/rss_sqlite.py
+0ede84085fc61707b6ee27f72ef2808c usr/share/feedingit/qml/main.qml
+da4cc789664ec1980300d1df080c6d90 usr/share/feedingit/rss_sqlite.py
721777a26cd2a5b8466ce2aa2b99fad7 usr/share/feedingit/update_feeds.py
6ccf12dc4379e91800ae8505b2e86082 usr/share/feedingit/updatedbus.py
833ff79caab7c1fa89d6ff4a2f3bb3fd usr/share/feedingit/wc.py
8d49c002ad8bb98837e2a642eec86fc5 usr/share/icons/hicolor/64x64/apps/feedingit.png
-035a8a90300ae10602a25bd24a8121c7 usr/share/pyshared/feedingit-0.1.0.egg-info
+7f4a829935d2f4cfede75830a4dd61c7 usr/share/pyshared/feedingit-0.1.0.egg-info
1ce7b7194658769bb4173134a725d1ce usr/share/python/runtime.d/feedingit.rtupdate
def generateCategoryXml(self):
xml = "<?xml version=\"1.0\" encoding=\"utf-8\"?><xml>"
for cat in self.listing.getListOfCategories():
+ print cat
xml += "<category>"
xml += "<catname>%s</catname>" %sanitize(self.listing.getCategoryTitle(cat))
xml += "<catid>%s</catid>" % cat
+ xml += "<unread>%s</unread>" % self.listing.getCategoryUnread(cat)
xml += "</category>"
xml += "</xml>"
return xml
original_excepthook(exctype, value, tb)
def init(dot_directory, debug=False, max_logfiles=1, program_name=None):
- debug = True
if not os.path.isabs(dot_directory):
dot_directory = os.path.join(os.path.expanduser("~"), dot_directory)
import os
from os import mkdir, remove, stat, environ
from os.path import isfile, isdir, exists
+import codecs
# Comment the line below if you don't want to use OpenGL for QML rendering or if it is not supported
from PySide import QtOpenGL, QtCore
def getArticle(self, key, article):
feed = listing.getFeed(key)
try:
- file = open(feed.getContentLink(article))
+ file = codecs.open(feed.getContentLink(article), "r", "utf-8")
html = file.read().replace("body", "body bgcolor='#ffffff'", 1)
file.close()
except:
- html = "<html><body>Error retrieving article</body></html>"
+ html = u"<html><body>Error retrieving article</body></html>"
return html
@QtCore.Slot(str, result=str)
@QtCore.Slot()
def updateAll(self):
- for feed in listing.getSortedListOfKeys("Manual"):
- listing.updateFeed(feed)
-
+ for catid in listing.getListOfCategories():
+ for feed in listing.getSortedListOfKeys("Manual", category=catid):
+ listing.updateFeed(feed)
+
+ @QtCore.Slot(str)
+ def updateCategory(self, catid):
+ for feed in listing.getSortedListOfKeys("Manual", category=catid):
+ listing.updateFeed(feed)
+
@QtCore.Slot(str,str,str)
def addFeed(self, title, url, catid):
listing.addFeed(title,url, category=catid)
listing.addCategory(name)
@QtCore.Slot(str)
+ def removeFeed(self, key):
+ listing.removeFeed(key)
+
+ @QtCore.Slot(str)
+ def removeCategory(self, catid):
+ listing.removeCategory(catid)
+
+ @QtCore.Slot(str)
def markAllAsRead(self, key):
feed = listing.getFeed(key)
feed.markAllAsRead()
id: listing;
Item {
- width: articleViewer.width; height: 86
+ width: articleViewer.width; height: backRect.height + 3
id: listItem
- Rectangle { id: backRect; color: "#dddddd"; opacity: index % 2 ? 0.2 : 0.4; height: 84; width: listItem.width; y: 1 }
+ Rectangle {
+ id: backRect; color: settings.backColour; opacity: index % 2 ? 0.2 : 0.4;
+ height: articleText.height + 22; width: listItem.width;
+ y: 1
+ }
Text {
- anchors.fill: backRect
+ id: articleText
+ anchors.verticalCenter: backRect.verticalCenter
+ x: 3
+ width: listItem.width - 6;
+
anchors.margins: 5
verticalAlignment: Text.AlignVCenter; text: title;
- color: (unread=="True") ? "white" : "#7b97fd";
+ color: (unread=="True") ? settings.mainTextColour : settings.secondaryTextColour;
- width: listItem.width; wrapMode: Text.WordWrap; font.bold: false;
+ wrapMode: Text.WordWrap; font.bold: false;
font.pointSize: 18
}
MouseArea { anchors.fill: listItem;
signal categoryClicked(string cat)
function reload() {
- categories.reload();
+ //categories.reload();
+ categories.xml = controller.getCategoryXml()
+ //console.log(controller.getCategoryXml())
}
ListView {
XmlRole { name: "title"; query: "catname/string()" }
XmlRole { name: "catid"; query: "catid/string()"; isKey: true }
+ XmlRole { name: "unread"; query: "unread/string()" }
}
Component {
Item {
id: moveMe
height: parent.height
- Rectangle { color: "#eeeeee"; opacity: index % 2 ? 0.2 : 0.4; height: 84; width: wrapper.width; y: 1 }
+ Rectangle { color: settings.backColour; opacity: index % 2 ? 0.2 : 0.4; height: 84; width: wrapper.width; y: 1 }
// Rectangle {
// x: 6; y: 4; width: 77; height: parent.height - 9; color: "white"; smooth: true
// }
Column {
x: 15;
- width: wrapper.ListView.view.width /*- 95; y: 15*/; spacing: 2
+ width: wrapper.ListView.view.width /*- 95;*/
+ y: 15; spacing: 2
anchors.margins: 5
height: parent.height
- Text { text: title; color: "white"; width: parent.width; font.bold: true;
- height: parent.height
- elide: Text.ElideRight; style: Text.Raised; styleColor: "black"; font.pointSize: 20
+ Text { text: title; color: settings.mainTextColour; width: parent.width; font.bold: true;
+ //height: parent.height
+ elide: Text.ElideRight; style: Text.Raised;
+ styleColor: settings.mainShadowColour;
+ font.pointSize: settings.mainTextSize
verticalAlignment: Text.AlignVCenter;
}
+ Text {
+ text: qsTr("%1 unread items").arg(unread);
+ color: (unread=="0") ? settings.mainTextColour : settings.secondaryTextColour;
+ width: parent.width; font.bold: false;
+ elide: Text.ElideRight;
+ //style: Text.Raised; styleColor: settings.mainShadowColour;
+ font.pointSize: settings.secondaryTextSize
+ }
//Text { text: feedname; width: parent.width; elide: Text.ElideLeft; color: "#cccccc"; style: Text.Raised; styleColor: "black" }
}
- Item {
- x: wrapper.ListView.view.width - 128; y: 12
- height: 58; width: 58;
- //anchors.horizontalCenter: parent.horizontalCenter;
- Image { source: "common/images/wmEditIcon.png" }
- MouseArea {
- anchors.fill: parent; onClicked: { container.categoryEdit(catname, catid); }
- }
- visible: inEditMode
+// Item {
+// x: wrapper.ListView.view.width - 128; y: 12
+// height: 58; width: 58;
+// //anchors.horizontalCenter: parent.horizontalCenter;
+// Image { source: "common/images/wmEditIcon.png" }
+// MouseArea {
+// anchors.fill: parent; onClicked: { container.categoryEdit(catname, catid); }
+// }
+// visible: inEditMode
+// }
+// Item {
+// x: wrapper.ListView.view.width - 64; y: 12
+// height: 58; width: 58;
+// //anchors.horizontalCenter: parent.horizontalCenter;
+// Image { source: "common/images/delete.png" }
+// MouseArea {
+// anchors.fill: parent; onClicked: { container.categoryDeleted(catid); }
+// }
+// visible: inEditMode
+// }
+ }
+ MouseArea {
+ enabled: !inEditMode; anchors.fill: wrapper;
+ onClicked: { categoryClicked(catid); }
+ onPressAndHold: {
+ window.longPressCategory(catid)
}
- Item {
- x: wrapper.ListView.view.width - 64; y: 12
- height: 58; width: 58;
- //anchors.horizontalCenter: parent.horizontalCenter;
- Image { source: "common/images/delete.png" }
- MouseArea {
- anchors.fill: parent; onClicked: { container.categoryDeleted(catid); }
- }
- visible: inEditMode
+ }
+ Component.onCompleted: {
+ if (categories.count == 1) {
+ categoryClicked(catid)
}
}
- MouseArea { enabled: !inEditMode; anchors.fill: wrapper; onClicked: { categoryClicked(catid); } }
}
}
}
Item {
id: moveMe
- Rectangle { color: "#dddddd"; opacity: index % 2 ? 0.2 : 0.4; height: 84; width: wrapper.width; y: 1 }
+ Rectangle { color: settings.backColour; opacity: index % 2 ? 0.2 : 0.4; height: 84; width: wrapper.width; y: 1 }
Rectangle {
- x: 3; y: 4; width: 77; height: 77; color: "#000000"; smooth: true
- Image { width:32; height: 32; anchors.verticalCenter: parent.verticalCenter; anchors.horizontalCenter: parent.horizontalCenter;
- source: (updating=="True")? "common/images/loading.png" : (icon == "False") ? "common/images/feedingit.png" : icon;
- NumberAnimation on rotation {
- from: 0; to: 360; running: (updating=="True"); loops: Animation.Infinite; duration: 900
- }
+ x: 3; y: 4; width: 77; height: 77;
+ color: "#000000"; smooth: true
+ Image { width:32; height: 32; anchors.verticalCenter: parent.verticalCenter;
+ anchors.horizontalCenter: parent.horizontalCenter;
+ source: (icon == "False") ? "common/images/feedingit.png" : icon
+ // (updating=="True")? "common/images/loading.png" : (icon == "False") ? "common/images/feedingit.png" : icon;
+// NumberAnimation on rotation {
+// from: 0; to: 360; running: (updating=="True"); loops: Animation.Infinite; duration: 900
+// }
}
}
Column {
x: 92; width: wrapper.ListView.view.width - 95; y: 5; spacing: 2
- Text { text: title; color: "white"; width: parent.width; font.bold: true; elide: Text.ElideRight; style: Text.Raised; styleColor: "black"; font.pointSize: 18 }
- Text { text: updatedDate + " / " + qsTr("%1 unread items").arg(unread); color: (unread=="0") ? "white" : "#7b97fd"; width: parent.width; font.bold: false; elide: Text.ElideRight; style: Text.Raised; styleColor: "black"; font.pointSize: 12 }
+ Text { text: title; color: settings.mainTextColour;
+ width: parent.width;
+ font.bold: true; elide: Text.ElideRight; style: Text.Raised; styleColor: settings.mainShadowColour;
+ font.pointSize: settings.mainTextSize
+ }
+ Text {
+ text: updatedDate + " / " + qsTr("%1 unread items").arg(unread);
+ color: (unread=="0") ? settings.mainTextColour : settings.secondaryTextColour;
+ width: parent.width; font.bold: false; elide: Text.ElideRight;
+ //style: Text.Raised; styleColor: settings.mainShadowColour;
+ font.pointSize: settings.secondaryTextSize
+ }
//Text { text: feedname; width: parent.width; elide: Text.ElideLeft; color: "#cccccc"; style: Text.Raised; styleColor: "black" }
}
//// Item {
feedClicked(feedid)
}
+ onPressAndHold: {
+ window.longPressFeed(feedid)
+ }
}
}
WebView {
id: webView
- url: "http://www.google.com";
- //url: "/home/user/.feedingit/640fb167aca8bf5318ed721c5162f5eb.d/56a86b6b1675716ab54db83b1a78ab4c.html"
+ //url: "http://www.google.com";
+ //url: "http://www.viedemerde.fr/inclassable/5873986?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+viedemerde+%28Vie+de+merde%29"
+ url: "/home/ymarcoz/.feedingit/361f6abe590be9c681b5489e5d48331c.d/1b1033c75378fb19a8a70151d764cd33.html"
preferredWidth: flickable.width
preferredHeight: flickable.height
settings.defaultFontSize: 32
id: window
initialPage: categoryPage
- signal articleClosed()
+ signal longPressCategory(string catid)
+ signal longPressFeed(string key)
+ signal categoryReloadRequest()
+ signal feedReloadRequest()
property string feedid
property string catid
Component.onCompleted: theme.inverted= true
+ onLongPressFeed: {
+ unsubscribeFeedMenu.key = key
+ unsubscribeFeedMenu.open()
+ }
+
+ onLongPressCategory: {
+ unsubscribeCategoryMenu.catid = catid
+ unsubscribeCategoryMenu.open()
+ }
+
ToolBarLayout {
id: commonTools
visible: false
visualParent: pageStack
MenuLayout {
MenuItem { text: qsTr("Settings"); onClicked: {} }
+ MenuItem { text: qsTr("Invert Theme"); onClicked: { theme.inverted = !theme.inverted } }
MenuItem { text: qsTr("Add Feeds"); onClicked: { pageStack.push(addFeedPage) } }
MenuItem { text: qsTr("Update All Categories"); onClicked: controller.updateAll(); }
MenuItem { text: qsTr("About FeedingIt"); onClicked: query.open(); }
id: myFeedsMenu
visualParent: pageStack
MenuLayout {
- MenuItem { text: qsTr("Update All Feeds"); onClicked: controller.updateAll(); }
- MenuItem { text: qsTr("About FeedingIt"); onClicked: query.open(); }
+ MenuItem { text: qsTr("Update All Feeds"); onClicked: controller.updateCategory(catid); }
+ //MenuItem { text: qsTr("About FeedingIt"); onClicked: query.open(); }
}
}
MenuLayout {
MenuItem { text: qsTr("Mark All As Read"); onClicked: controller.markAllAsRead(feedid); }
MenuItem { text: qsTr("Update Feed"); onClicked: controller.updateFeed(feedid); }
- MenuItem { text: qsTr("About FeedingIt"); onClicked: query.open(); }
+ //MenuItem { text: qsTr("About FeedingIt"); onClicked: query.open(); }
+ }
+ }
+
+ Menu {
+ id: unsubscribeFeedMenu
+ visualParent: pageStack
+ property string key
+ MenuLayout {
+ //MenuItem { text: qsTr("Update"); onClicked: controller.updateFeed(parent.feedid); }
+ MenuItem { text: qsTr("Delete");
+ onClicked: {
+ feedConfirm.open()
+ unsubscribeFeedMenu.close()
+ }
+ }
+ }
+ QueryDialog {
+ id: feedConfirm
+ //icon: "common/images/feedingit.png"
+ //titleText: "Delete Feed?"
+ message: "Delete this feed?"
+ acceptButtonText: "OK"
+ rejectButtonText: "Cancel"
+ onAccepted: {
+ controller.removeFeed(unsubscribeFeedMenu.key);
+ window.feedReloadRequest()
+ }
+ }
+ }
+
+ Menu {
+ id: unsubscribeCategoryMenu
+ visualParent: pageStack
+ property string catid
+ MenuLayout {
+ //MenuItem { text: qsTr("Update"); onClicked: controller.updateFeed(parent.feedid); }
+ MenuItem { text: qsTr("Delete");
+ onClicked: {
+ categoryConfirm.open()
+ unsubscribeFeedMenu.close()
+ }
+ }
+ }
+ QueryDialog {
+ id: categoryConfirm
+ //icon: "common/images/feedingit.png"
+ //titleText: "Delete Feed?"
+ message: "Delete this category?"
+ acceptButtonText: "OK"
+ rejectButtonText: "Cancel"
+ onAccepted: {
+ controller.removeCategory(unsubscribeCategoryMenu.catid);
+ window.categoryReloadRequest()
+ }
}
}
acceptButtonText: "OK"
}
-// Page{
-// id: mainPage
-// Component.onCompleted: {
-// var main = Qt.createComponent("FeedingIt.qml");
-// main.createObject(mainPage);
-// }
-// }
-
Component {
id: categoryPage
Page {
tools: commonTools
Categories {
+ id: categoriesItem
onCategoryClicked: {
window.catid = cat
pageStack.push(feedsPage)
}
+
+ }
+ Connections {
+ target: window
+ onCategoryReloadRequest: {
+ console.log("category reloaded")
+ categoriesItem.reload()
+ }
}
}
}
window.feedid = feedid
pageStack.push(articlesPage)
}
+
}
ToolBarLayout {
id: feedsTools
}
Connections {
target: window
- onArticleClosed: feedsItem.reload()
+ onFeedReloadRequest: feedsItem.reload()
}
}
}
flipper.articleShown = false;
flipper.reload()
} else {
- window.articleClosed();
+ window.feedReloadRequest();
pageStack.pop();
}
}
id: addFeedPage
AddFeed {}
}
+
+ Settings {
+ id: settings
+ }
}
def getCategoryTitle(self, id):
return self.lookup('categories', 'title', id)
+
+ def getCategoryUnread(self, id):
+ count = 0
+ for key in self.getListOfFeeds(category=id):
+ try:
+ count = count + self.getFeedNumberOfUnreadItems(key)
+ except:
+ pass
+ return count
def getSortedListOfKeys(self, order, onlyUnread=False, category=1):
if order == "Most unread":
id=1
self.db.execute("INSERT INTO categories (id, title, unread, rank) VALUES (?, ?, 0, ?)", (id, title, rank))
self.db.commit()
+ self.cache_invalidate('categories')
def removeFeed(self, key):
if wc().available ():
if isdir(self.configdir+key+".d/"):
rmtree(self.configdir+key+".d/")
+ self.cache_invalidate('feeds')
def removeCategory(self, key):
if self.db.execute("SELECT count(*) FROM categories;").fetchone()[0] > 1:
self.db.execute("UPDATE categories SET rank=rank-1 WHERE rank>?;", (rank,) )
self.db.execute("UPDATE feeds SET category=1 WHERE category=?;", (key,) )
self.db.commit()
+ self.cache_invalidate('categories')
#def saveConfig(self):
# self.listOfFeeds["feedingit-order"] = self.sortedKeys
Author: Yves
Author-email: yves@marcoz.org
License: UNKNOWN
-Description: This file should contain a writeup describing what your application does,
- and how to use it. The content of this file goes into the long_description
- field of setup.py, which in turn becomes the long version of the Description
- field in the debian/control file of the project.
+Description: FeedingIt is an easy to use RSS feed reader with portrait mode support and
+ automatic feed updates. You can swipe left and right to move between articles.
+ Import and export of subscription lists is supported for OPML files.
+ Images can be cached during the feed update, so you can view images in the
+ downloaded articles while offline.
Platform: UNKNOWN
#!/usr/bin/make -f
# This file was automatically generated by stdeb 0.6.0+git at
-# Sun, 06 Nov 2011 21:13:19 -0800
+# Mon, 07 Nov 2011 21:25:40 -0800
%:
dh $@ --with python2 --buildsystem=python_distutils
-This file should contain a writeup describing what your application does,
-and how to use it. The content of this file goes into the long_description
-field of setup.py, which in turn becomes the long version of the Description
-field in the debian/control file of the project.
+FeedingIt is an easy to use RSS feed reader with portrait mode support and
+automatic feed updates. You can swipe left and right to move between articles.
+Import and export of subscription lists is supported for OPML files.
+Images can be cached during the feed update, so you can view images in the
+downloaded articles while offline.
def generateCategoryXml(self):
xml = "<?xml version=\"1.0\" encoding=\"utf-8\"?><xml>"
for cat in self.listing.getListOfCategories():
+ print cat
xml += "<category>"
xml += "<catname>%s</catname>" %sanitize(self.listing.getCategoryTitle(cat))
xml += "<catid>%s</catid>" % cat
+ xml += "<unread>%s</unread>" % self.listing.getCategoryUnread(cat)
xml += "</category>"
xml += "</xml>"
return xml
original_excepthook(exctype, value, tb)
def init(dot_directory, debug=False, max_logfiles=1, program_name=None):
- debug = True
if not os.path.isabs(dot_directory):
dot_directory = os.path.join(os.path.expanduser("~"), dot_directory)
import os
from os import mkdir, remove, stat, environ
from os.path import isfile, isdir, exists
+import codecs
# Comment the line below if you don't want to use OpenGL for QML rendering or if it is not supported
from PySide import QtOpenGL, QtCore
def getArticle(self, key, article):
feed = listing.getFeed(key)
try:
- file = open(feed.getContentLink(article))
+ file = codecs.open(feed.getContentLink(article), "r", "utf-8")
html = file.read().replace("body", "body bgcolor='#ffffff'", 1)
file.close()
except:
- html = "<html><body>Error retrieving article</body></html>"
+ html = u"<html><body>Error retrieving article</body></html>"
return html
@QtCore.Slot(str, result=str)
@QtCore.Slot()
def updateAll(self):
- for feed in listing.getSortedListOfKeys("Manual"):
- listing.updateFeed(feed)
-
+ for catid in listing.getListOfCategories():
+ for feed in listing.getSortedListOfKeys("Manual", category=catid):
+ listing.updateFeed(feed)
+
+ @QtCore.Slot(str)
+ def updateCategory(self, catid):
+ for feed in listing.getSortedListOfKeys("Manual", category=catid):
+ listing.updateFeed(feed)
+
@QtCore.Slot(str,str,str)
def addFeed(self, title, url, catid):
listing.addFeed(title,url, category=catid)
listing.addCategory(name)
@QtCore.Slot(str)
+ def removeFeed(self, key):
+ listing.removeFeed(key)
+
+ @QtCore.Slot(str)
+ def removeCategory(self, catid):
+ listing.removeCategory(catid)
+
+ @QtCore.Slot(str)
def markAllAsRead(self, key):
feed = listing.getFeed(key)
feed.markAllAsRead()
def getCategoryTitle(self, id):
return self.lookup('categories', 'title', id)
+
+ def getCategoryUnread(self, id):
+ count = 0
+ for key in self.getListOfFeeds(category=id):
+ try:
+ count = count + self.getFeedNumberOfUnreadItems(key)
+ except:
+ pass
+ return count
def getSortedListOfKeys(self, order, onlyUnread=False, category=1):
if order == "Most unread":
id=1
self.db.execute("INSERT INTO categories (id, title, unread, rank) VALUES (?, ?, 0, ?)", (id, title, rank))
self.db.commit()
+ self.cache_invalidate('categories')
def removeFeed(self, key):
if wc().available ():
if isdir(self.configdir+key+".d/"):
rmtree(self.configdir+key+".d/")
+ self.cache_invalidate('feeds')
def removeCategory(self, key):
if self.db.execute("SELECT count(*) FROM categories;").fetchone()[0] > 1:
self.db.execute("UPDATE categories SET rank=rank-1 WHERE rank>?;", (rank,) )
self.db.execute("UPDATE feeds SET category=1 WHERE category=?;", (key,) )
self.db.commit()
+ self.cache_invalidate('categories')
#def saveConfig(self):
# self.listOfFeeds["feedingit-order"] = self.sortedKeys
id: listing;
Item {
- width: articleViewer.width; height: 86
+ width: articleViewer.width; height: backRect.height + 3
id: listItem
- Rectangle { id: backRect; color: "#dddddd"; opacity: index % 2 ? 0.2 : 0.4; height: 84; width: listItem.width; y: 1 }
+ Rectangle {
+ id: backRect; color: settings.backColour; opacity: index % 2 ? 0.2 : 0.4;
+ height: articleText.height + 22; width: listItem.width;
+ y: 1
+ }
Text {
- anchors.fill: backRect
+ id: articleText
+ anchors.verticalCenter: backRect.verticalCenter
+ x: 3
+ width: listItem.width - 6;
+
anchors.margins: 5
verticalAlignment: Text.AlignVCenter; text: title;
- color: (unread=="True") ? "white" : "#7b97fd";
+ color: (unread=="True") ? settings.mainTextColour : settings.secondaryTextColour;
- width: listItem.width; wrapMode: Text.WordWrap; font.bold: false;
+ wrapMode: Text.WordWrap; font.bold: false;
font.pointSize: 18
}
MouseArea { anchors.fill: listItem;
signal categoryClicked(string cat)
function reload() {
- categories.reload();
+ //categories.reload();
+ categories.xml = controller.getCategoryXml()
+ //console.log(controller.getCategoryXml())
}
ListView {
XmlRole { name: "title"; query: "catname/string()" }
XmlRole { name: "catid"; query: "catid/string()"; isKey: true }
+ XmlRole { name: "unread"; query: "unread/string()" }
}
Component {
Item {
id: moveMe
height: parent.height
- Rectangle { color: "#eeeeee"; opacity: index % 2 ? 0.2 : 0.4; height: 84; width: wrapper.width; y: 1 }
+ Rectangle { color: settings.backColour; opacity: index % 2 ? 0.2 : 0.4; height: 84; width: wrapper.width; y: 1 }
// Rectangle {
// x: 6; y: 4; width: 77; height: parent.height - 9; color: "white"; smooth: true
// }
Column {
x: 15;
- width: wrapper.ListView.view.width /*- 95; y: 15*/; spacing: 2
+ width: wrapper.ListView.view.width /*- 95;*/
+ y: 15; spacing: 2
anchors.margins: 5
height: parent.height
- Text { text: title; color: "white"; width: parent.width; font.bold: true;
- height: parent.height
- elide: Text.ElideRight; style: Text.Raised; styleColor: "black"; font.pointSize: 20
+ Text { text: title; color: settings.mainTextColour; width: parent.width; font.bold: true;
+ //height: parent.height
+ elide: Text.ElideRight; style: Text.Raised;
+ styleColor: settings.mainShadowColour;
+ font.pointSize: settings.mainTextSize
verticalAlignment: Text.AlignVCenter;
}
+ Text {
+ text: qsTr("%1 unread items").arg(unread);
+ color: (unread=="0") ? settings.mainTextColour : settings.secondaryTextColour;
+ width: parent.width; font.bold: false;
+ elide: Text.ElideRight;
+ //style: Text.Raised; styleColor: settings.mainShadowColour;
+ font.pointSize: settings.secondaryTextSize
+ }
//Text { text: feedname; width: parent.width; elide: Text.ElideLeft; color: "#cccccc"; style: Text.Raised; styleColor: "black" }
}
- Item {
- x: wrapper.ListView.view.width - 128; y: 12
- height: 58; width: 58;
- //anchors.horizontalCenter: parent.horizontalCenter;
- Image { source: "common/images/wmEditIcon.png" }
- MouseArea {
- anchors.fill: parent; onClicked: { container.categoryEdit(catname, catid); }
- }
- visible: inEditMode
+// Item {
+// x: wrapper.ListView.view.width - 128; y: 12
+// height: 58; width: 58;
+// //anchors.horizontalCenter: parent.horizontalCenter;
+// Image { source: "common/images/wmEditIcon.png" }
+// MouseArea {
+// anchors.fill: parent; onClicked: { container.categoryEdit(catname, catid); }
+// }
+// visible: inEditMode
+// }
+// Item {
+// x: wrapper.ListView.view.width - 64; y: 12
+// height: 58; width: 58;
+// //anchors.horizontalCenter: parent.horizontalCenter;
+// Image { source: "common/images/delete.png" }
+// MouseArea {
+// anchors.fill: parent; onClicked: { container.categoryDeleted(catid); }
+// }
+// visible: inEditMode
+// }
+ }
+ MouseArea {
+ enabled: !inEditMode; anchors.fill: wrapper;
+ onClicked: { categoryClicked(catid); }
+ onPressAndHold: {
+ window.longPressCategory(catid)
}
- Item {
- x: wrapper.ListView.view.width - 64; y: 12
- height: 58; width: 58;
- //anchors.horizontalCenter: parent.horizontalCenter;
- Image { source: "common/images/delete.png" }
- MouseArea {
- anchors.fill: parent; onClicked: { container.categoryDeleted(catid); }
- }
- visible: inEditMode
+ }
+ Component.onCompleted: {
+ if (categories.count == 1) {
+ categoryClicked(catid)
}
}
- MouseArea { enabled: !inEditMode; anchors.fill: wrapper; onClicked: { categoryClicked(catid); } }
}
}
}
Item {
id: moveMe
- Rectangle { color: "#dddddd"; opacity: index % 2 ? 0.2 : 0.4; height: 84; width: wrapper.width; y: 1 }
+ Rectangle { color: settings.backColour; opacity: index % 2 ? 0.2 : 0.4; height: 84; width: wrapper.width; y: 1 }
Rectangle {
- x: 3; y: 4; width: 77; height: 77; color: "#000000"; smooth: true
- Image { width:32; height: 32; anchors.verticalCenter: parent.verticalCenter; anchors.horizontalCenter: parent.horizontalCenter;
- source: (updating=="True")? "common/images/loading.png" : (icon == "False") ? "common/images/feedingit.png" : icon;
- NumberAnimation on rotation {
- from: 0; to: 360; running: (updating=="True"); loops: Animation.Infinite; duration: 900
- }
+ x: 3; y: 4; width: 77; height: 77;
+ color: "#000000"; smooth: true
+ Image { width:32; height: 32; anchors.verticalCenter: parent.verticalCenter;
+ anchors.horizontalCenter: parent.horizontalCenter;
+ source: (icon == "False") ? "common/images/feedingit.png" : icon
+ // (updating=="True")? "common/images/loading.png" : (icon == "False") ? "common/images/feedingit.png" : icon;
+// NumberAnimation on rotation {
+// from: 0; to: 360; running: (updating=="True"); loops: Animation.Infinite; duration: 900
+// }
}
}
Column {
x: 92; width: wrapper.ListView.view.width - 95; y: 5; spacing: 2
- Text { text: title; color: "white"; width: parent.width; font.bold: true; elide: Text.ElideRight; style: Text.Raised; styleColor: "black"; font.pointSize: 18 }
- Text { text: updatedDate + " / " + qsTr("%1 unread items").arg(unread); color: (unread=="0") ? "white" : "#7b97fd"; width: parent.width; font.bold: false; elide: Text.ElideRight; style: Text.Raised; styleColor: "black"; font.pointSize: 12 }
+ Text { text: title; color: settings.mainTextColour;
+ width: parent.width;
+ font.bold: true; elide: Text.ElideRight; style: Text.Raised; styleColor: settings.mainShadowColour;
+ font.pointSize: settings.mainTextSize
+ }
+ Text {
+ text: updatedDate + " / " + qsTr("%1 unread items").arg(unread);
+ color: (unread=="0") ? settings.mainTextColour : settings.secondaryTextColour;
+ width: parent.width; font.bold: false; elide: Text.ElideRight;
+ //style: Text.Raised; styleColor: settings.mainShadowColour;
+ font.pointSize: settings.secondaryTextSize
+ }
//Text { text: feedname; width: parent.width; elide: Text.ElideLeft; color: "#cccccc"; style: Text.Raised; styleColor: "black" }
}
//// Item {
feedClicked(feedid)
}
+ onPressAndHold: {
+ window.longPressFeed(feedid)
+ }
}
}
WebView {
id: webView
- url: "http://www.google.com";
- //url: "/home/user/.feedingit/640fb167aca8bf5318ed721c5162f5eb.d/56a86b6b1675716ab54db83b1a78ab4c.html"
+ //url: "http://www.google.com";
+ //url: "http://www.viedemerde.fr/inclassable/5873986?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+viedemerde+%28Vie+de+merde%29"
+ url: "/home/ymarcoz/.feedingit/361f6abe590be9c681b5489e5d48331c.d/1b1033c75378fb19a8a70151d764cd33.html"
preferredWidth: flickable.width
preferredHeight: flickable.height
settings.defaultFontSize: 32
id: window
initialPage: categoryPage
- signal articleClosed()
+ signal longPressCategory(string catid)
+ signal longPressFeed(string key)
+ signal categoryReloadRequest()
+ signal feedReloadRequest()
property string feedid
property string catid
Component.onCompleted: theme.inverted= true
+ onLongPressFeed: {
+ unsubscribeFeedMenu.key = key
+ unsubscribeFeedMenu.open()
+ }
+
+ onLongPressCategory: {
+ unsubscribeCategoryMenu.catid = catid
+ unsubscribeCategoryMenu.open()
+ }
+
ToolBarLayout {
id: commonTools
visible: false
visualParent: pageStack
MenuLayout {
MenuItem { text: qsTr("Settings"); onClicked: {} }
+ MenuItem { text: qsTr("Invert Theme"); onClicked: { theme.inverted = !theme.inverted } }
MenuItem { text: qsTr("Add Feeds"); onClicked: { pageStack.push(addFeedPage) } }
MenuItem { text: qsTr("Update All Categories"); onClicked: controller.updateAll(); }
MenuItem { text: qsTr("About FeedingIt"); onClicked: query.open(); }
id: myFeedsMenu
visualParent: pageStack
MenuLayout {
- MenuItem { text: qsTr("Update All Feeds"); onClicked: controller.updateAll(); }
- MenuItem { text: qsTr("About FeedingIt"); onClicked: query.open(); }
+ MenuItem { text: qsTr("Update All Feeds"); onClicked: controller.updateCategory(catid); }
+ //MenuItem { text: qsTr("About FeedingIt"); onClicked: query.open(); }
}
}
MenuLayout {
MenuItem { text: qsTr("Mark All As Read"); onClicked: controller.markAllAsRead(feedid); }
MenuItem { text: qsTr("Update Feed"); onClicked: controller.updateFeed(feedid); }
- MenuItem { text: qsTr("About FeedingIt"); onClicked: query.open(); }
+ //MenuItem { text: qsTr("About FeedingIt"); onClicked: query.open(); }
+ }
+ }
+
+ Menu {
+ id: unsubscribeFeedMenu
+ visualParent: pageStack
+ property string key
+ MenuLayout {
+ //MenuItem { text: qsTr("Update"); onClicked: controller.updateFeed(parent.feedid); }
+ MenuItem { text: qsTr("Delete");
+ onClicked: {
+ feedConfirm.open()
+ unsubscribeFeedMenu.close()
+ }
+ }
+ }
+ QueryDialog {
+ id: feedConfirm
+ //icon: "common/images/feedingit.png"
+ //titleText: "Delete Feed?"
+ message: "Delete this feed?"
+ acceptButtonText: "OK"
+ rejectButtonText: "Cancel"
+ onAccepted: {
+ controller.removeFeed(unsubscribeFeedMenu.key);
+ window.feedReloadRequest()
+ }
+ }
+ }
+
+ Menu {
+ id: unsubscribeCategoryMenu
+ visualParent: pageStack
+ property string catid
+ MenuLayout {
+ //MenuItem { text: qsTr("Update"); onClicked: controller.updateFeed(parent.feedid); }
+ MenuItem { text: qsTr("Delete");
+ onClicked: {
+ categoryConfirm.open()
+ unsubscribeFeedMenu.close()
+ }
+ }
+ }
+ QueryDialog {
+ id: categoryConfirm
+ //icon: "common/images/feedingit.png"
+ //titleText: "Delete Feed?"
+ message: "Delete this category?"
+ acceptButtonText: "OK"
+ rejectButtonText: "Cancel"
+ onAccepted: {
+ controller.removeCategory(unsubscribeCategoryMenu.catid);
+ window.categoryReloadRequest()
+ }
}
}
acceptButtonText: "OK"
}
-// Page{
-// id: mainPage
-// Component.onCompleted: {
-// var main = Qt.createComponent("FeedingIt.qml");
-// main.createObject(mainPage);
-// }
-// }
-
Component {
id: categoryPage
Page {
tools: commonTools
Categories {
+ id: categoriesItem
onCategoryClicked: {
window.catid = cat
pageStack.push(feedsPage)
}
+
+ }
+ Connections {
+ target: window
+ onCategoryReloadRequest: {
+ console.log("category reloaded")
+ categoriesItem.reload()
+ }
}
}
}
window.feedid = feedid
pageStack.push(articlesPage)
}
+
}
ToolBarLayout {
id: feedsTools
}
Connections {
target: window
- onArticleClosed: feedsItem.reload()
+ onFeedReloadRequest: feedsItem.reload()
}
}
}
flipper.articleShown = false;
flipper.reload()
} else {
- window.articleClosed();
+ window.feedReloadRequest();
pageStack.pop();
}
}
id: addFeedPage
AddFeed {}
}
+
+ Settings {
+ id: settings
+ }
}
Standards-Version: 3.9.1
Build-Depends: python-all (>= 2.6.6-3), debhelper (>= 7.4.3)
Checksums-Sha1:
- 12a7cdb7b5cdb98d79bda6e6a4dc2562685423c3 176240 feedingit_0.1.0.orig.tar.gz
- 954096a5d2fa51bb359517d7578c684e4625f237 859 feedingit_0.1.0-1.debian.tar.gz
+ 0f46819e2084145776f49c7f7f2ee922a980687c 177736 feedingit_0.1.0.orig.tar.gz
+ bc99501e4cf014353f2f9d8807498a9d311337c7 931 feedingit_0.1.0-1.debian.tar.gz
Checksums-Sha256:
- 8421abef41b572247ce1292445c075eec032136b121e9d3bc550897881ea975a 176240 feedingit_0.1.0.orig.tar.gz
- 229779d81f6541d46086a972ee2d93db66abd4a7adc8be746f5c97d585aa4044 859 feedingit_0.1.0-1.debian.tar.gz
+ 223c41eba6bad5241f54e54e776a6e3bb272d07d91b1fa380848b7d8ce0128f4 177736 feedingit_0.1.0.orig.tar.gz
+ 58f20d85f1cae2b33fb346fc6bee304cd304b6674026540aee31b73748d49a7c 931 feedingit_0.1.0-1.debian.tar.gz
Files:
- c58fcc9048d74f69926cb53696c75c5e 176240 feedingit_0.1.0.orig.tar.gz
- e143c5daf921dac855fb227f77646a0e 859 feedingit_0.1.0-1.debian.tar.gz
+ 1b7ddb59ac327613cb42c0bbd7ffa06b 177736 feedingit_0.1.0.orig.tar.gz
+ 9c4aba31f4ca66c7cb5fe5dbcd83d100 931 feedingit_0.1.0-1.debian.tar.gz
Format: 1.8
-Date: Sun, 06 Nov 2011 21:13:19 -0800
+Date: Mon, 07 Nov 2011 21:25:40 -0800
Source: feedingit
Binary: feedingit
Architecture: source all
.
* source package automatically created by stdeb 0.6.0+git
Checksums-Sha1:
- ec3bc8cdf6562addb5a09da997d8ebdcd8f94b51 753 feedingit_0.1.0-1.dsc
- 12a7cdb7b5cdb98d79bda6e6a4dc2562685423c3 176240 feedingit_0.1.0.orig.tar.gz
- 954096a5d2fa51bb359517d7578c684e4625f237 859 feedingit_0.1.0-1.debian.tar.gz
- 6edf2daadc2cc46ce0a0733ac666207db8ca12b7 165780 feedingit_0.1.0-1_all.deb
+ 4872f7d851392a4c31cf7c4e6fba426ef7bfecf9 753 feedingit_0.1.0-1.dsc
+ 0f46819e2084145776f49c7f7f2ee922a980687c 177736 feedingit_0.1.0.orig.tar.gz
+ bc99501e4cf014353f2f9d8807498a9d311337c7 931 feedingit_0.1.0-1.debian.tar.gz
+ d5f6a17d75b19f6ae48a94c3fc685eb55d2cedab 167012 feedingit_0.1.0-1_all.deb
Checksums-Sha256:
- 5b4b10db2bd978e8b26dbda2533912d30a484ce10427ead1667517896f0f4c6c 753 feedingit_0.1.0-1.dsc
- 8421abef41b572247ce1292445c075eec032136b121e9d3bc550897881ea975a 176240 feedingit_0.1.0.orig.tar.gz
- 229779d81f6541d46086a972ee2d93db66abd4a7adc8be746f5c97d585aa4044 859 feedingit_0.1.0-1.debian.tar.gz
- 9e5238dfd7dded6daa107482e4a205442c57042a39e273c740ae8328e65a08b7 165780 feedingit_0.1.0-1_all.deb
+ a39151af4220e3ee0b11e210f1d41b78968f4dfc874b949dd914688e99e97d7f 753 feedingit_0.1.0-1.dsc
+ 223c41eba6bad5241f54e54e776a6e3bb272d07d91b1fa380848b7d8ce0128f4 177736 feedingit_0.1.0.orig.tar.gz
+ 58f20d85f1cae2b33fb346fc6bee304cd304b6674026540aee31b73748d49a7c 931 feedingit_0.1.0-1.debian.tar.gz
+ ef85ba37ee11a4bcdae73fc629c3a0acfa2e023cc0502409f0c37b9045a10eb8 167012 feedingit_0.1.0-1_all.deb
Files:
- 87eb4279d8f3744a01cf5de3ee7f87fe 753 user/development optional feedingit_0.1.0-1.dsc
- c58fcc9048d74f69926cb53696c75c5e 176240 user/development optional feedingit_0.1.0.orig.tar.gz
- e143c5daf921dac855fb227f77646a0e 859 user/development optional feedingit_0.1.0-1.debian.tar.gz
- 308928231246478c11cd5078aba9b193 165780 user/development optional feedingit_0.1.0-1_all.deb
+ b885f4386ce99ea355ff17988744f887 753 user/development optional feedingit_0.1.0-1.dsc
+ 1b7ddb59ac327613cb42c0bbd7ffa06b 177736 user/development optional feedingit_0.1.0.orig.tar.gz
+ 9c4aba31f4ca66c7cb5fe5dbcd83d100 931 user/development optional feedingit_0.1.0-1.debian.tar.gz
+ 762bca546128e97766835cf81a2d7a5b 167012 user/development optional feedingit_0.1.0-1_all.deb
-This file should contain a writeup describing what your application does,
-and how to use it. The content of this file goes into the long_description
-field of setup.py, which in turn becomes the long version of the Description
-field in the debian/control file of the project.
+FeedingIt is an easy to use RSS feed reader with portrait mode support and
+automatic feed updates. You can swipe left and right to move between articles.
+Import and export of subscription lists is supported for OPML files.
+Images can be cached during the feed update, so you can view images in the
+downloaded articles while offline.
def generateCategoryXml(self):
xml = "<?xml version=\"1.0\" encoding=\"utf-8\"?><xml>"
for cat in self.listing.getListOfCategories():
+ print cat
xml += "<category>"
xml += "<catname>%s</catname>" %sanitize(self.listing.getCategoryTitle(cat))
xml += "<catid>%s</catid>" % cat
+ xml += "<unread>%s</unread>" % self.listing.getCategoryUnread(cat)
xml += "</category>"
xml += "</xml>"
return xml
import os
from os import mkdir, remove, stat, environ
from os.path import isfile, isdir, exists
+import codecs
# Comment the line below if you don't want to use OpenGL for QML rendering or if it is not supported
from PySide import QtOpenGL, QtCore
def getArticle(self, key, article):
feed = listing.getFeed(key)
try:
- file = open(feed.getContentLink(article))
+ file = codecs.open(feed.getContentLink(article), "r", "utf-8")
html = file.read().replace("body", "body bgcolor='#ffffff'", 1)
file.close()
except:
- html = "<html><body>Error retrieving article</body></html>"
+ html = u"<html><body>Error retrieving article</body></html>"
return html
@QtCore.Slot(str, result=str)
@QtCore.Slot()
def updateAll(self):
- for feed in listing.getSortedListOfKeys("Manual"):
- listing.updateFeed(feed)
-
+ for catid in listing.getListOfCategories():
+ for feed in listing.getSortedListOfKeys("Manual", category=catid):
+ listing.updateFeed(feed)
+
+ @QtCore.Slot(str)
+ def updateCategory(self, catid):
+ for feed in listing.getSortedListOfKeys("Manual", category=catid):
+ listing.updateFeed(feed)
+
@QtCore.Slot(str,str,str)
def addFeed(self, title, url, catid):
listing.addFeed(title,url, category=catid)
listing.addCategory(name)
@QtCore.Slot(str)
+ def removeFeed(self, key):
+ listing.removeFeed(key)
+
+ @QtCore.Slot(str)
+ def removeCategory(self, catid):
+ listing.removeCategory(catid)
+
+ @QtCore.Slot(str)
def markAllAsRead(self, key):
feed = listing.getFeed(key)
feed.markAllAsRead()
#text = '''<div style="color: black; background-color: white;">'''
text = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">'
text += "<html><head><title>" + title + "</title>"
- text += '<meta http-equiv="Content-Type" content="text/html; charset="UTF-8"/>\n'
+ text += '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>\n'
#text += '<style> body {-webkit-user-select: none;} </style>'
text += '</head><body bgcolor=\"#ffffff\"><div><a href=\"' + link + '\">' + title + "</a>"
if author != None:
def getCategoryTitle(self, id):
return self.lookup('categories', 'title', id)
+
+ def getCategoryUnread(self, id):
+ count = 0
+ for key in self.getListOfFeeds(category=id):
+ try:
+ count = count + self.getFeedNumberOfUnreadItems(key)
+ except:
+ pass
+ return count
def getSortedListOfKeys(self, order, onlyUnread=False, category=1):
if order == "Most unread":
id=1
self.db.execute("INSERT INTO categories (id, title, unread, rank) VALUES (?, ?, 0, ?)", (id, title, rank))
self.db.commit()
+ self.cache_invalidate('categories')
def removeFeed(self, key):
if wc().available ():
if isdir(self.configdir+key+".d/"):
rmtree(self.configdir+key+".d/")
+ self.cache_invalidate('feeds')
def removeCategory(self, key):
if self.db.execute("SELECT count(*) FROM categories;").fetchone()[0] > 1:
self.db.execute("UPDATE categories SET rank=rank-1 WHERE rank>?;", (rank,) )
self.db.execute("UPDATE feeds SET category=1 WHERE category=?;", (key,) )
self.db.commit()
+ self.cache_invalidate('categories')
#def saveConfig(self):
# self.listOfFeeds["feedingit-order"] = self.sortedKeys
TextInputClear {
id: urlInput
placeHolder: "Feed URL"
-
+ width: parent.width
}
Button {
//controller.addFeed("",urlInput.text,1);
categoryDialog.confirmCategory("", urlInput.text);
}
+ width: 150
}
}
- } //searchTab
+ } //urlTab
+
+ Item {
+ id: categoryTab
+
+ Column {
+ Text {
+ id: categoryText
+ font.pixelSize: 22
+ color: "white"
+ text: "Enter the category name"
+ }
+
+ TextInputClear {
+ id: categoryInput
+ placeHolder: "name"
+ width: parent.width
+ }
+
+ Button {
+ id: categoryButton
+ text: "Add"
+ onClicked: {
+ if (categoryInput.text != "") {
+ controller.addCategory(categoryInput.text)
+ categoryDialog.reload()
+ window.categoryReloadRequest()
+ }
+ }
+ width: 150
+ }
+ }
+
+ } //categoryTab
ToolBarLayout {
id: tabTools
ToolIcon {
iconId: "toolbar-back"
- onClicked: tabGroup.currentTab.depth > 1 ? tabGroup.currentTab.pop() : pageStack.pop()
+ onClicked: {
+ if (tabGroup.currentTab.depth > 1) {
+ tabGroup.currentTab.pop()
+ } else {
+ pageStack.pop()
+ }
+ window.feedReloadRequest()
+ window.categoryReloadRequest()
+ }
}
ButtonRow {
TabButton {
text: "URL"
tab: urlTab
}
+ TabButton {
+ text: "Categories"
+ tab: categoryTab
+ }
}
+
}
}
open();
}
+ function reload() {
+ //categories.reload()
+ categories.xml = controller.getCategoryXml()
+ }
+
//model: []
//model: categories
id: listing;
Item {
- width: articleViewer.width; height: 86
+ width: articleViewer.width; height: backRect.height + 3
id: listItem
- Rectangle { id: backRect; color: "#dddddd"; opacity: index % 2 ? 0.2 : 0.4; height: 84; width: listItem.width; y: 1 }
+ Rectangle {
+ id: backRect; color: settings.backColour; opacity: index % 2 ? 0.2 : 0.4;
+ height: articleText.height + 22; width: listItem.width;
+ y: 1
+ }
Text {
- anchors.fill: backRect
+ id: articleText
+ anchors.verticalCenter: backRect.verticalCenter
+ x: 3
+ width: listItem.width - 6;
+
anchors.margins: 5
verticalAlignment: Text.AlignVCenter; text: title;
- color: (unread=="True") ? "white" : "#7b97fd";
+ color: (unread=="True") ? settings.mainTextColour : settings.secondaryTextColour;
- width: listItem.width; wrapMode: Text.WordWrap; font.bold: false;
+ wrapMode: Text.WordWrap; font.bold: false;
font.pointSize: 18
}
MouseArea { anchors.fill: listItem;
signal categoryClicked(string cat)
function reload() {
- categories.reload();
+ //categories.reload();
+ categories.xml = controller.getCategoryXml()
+ //console.log(controller.getCategoryXml())
}
ListView {
XmlRole { name: "title"; query: "catname/string()" }
XmlRole { name: "catid"; query: "catid/string()"; isKey: true }
+ XmlRole { name: "unread"; query: "unread/string()" }
}
Component {
Item {
id: moveMe
height: parent.height
- Rectangle { color: "#eeeeee"; opacity: index % 2 ? 0.2 : 0.4; height: 84; width: wrapper.width; y: 1 }
+ Rectangle { color: settings.backColour; opacity: index % 2 ? 0.2 : 0.4; height: 84; width: wrapper.width; y: 1 }
// Rectangle {
// x: 6; y: 4; width: 77; height: parent.height - 9; color: "white"; smooth: true
// }
Column {
x: 15;
- width: wrapper.ListView.view.width /*- 95; y: 15*/; spacing: 2
+ width: wrapper.ListView.view.width /*- 95;*/
+ y: 15; spacing: 2
anchors.margins: 5
height: parent.height
- Text { text: title; color: "white"; width: parent.width; font.bold: true;
- height: parent.height
- elide: Text.ElideRight; style: Text.Raised; styleColor: "black"; font.pointSize: 20
+ Text { text: title; color: settings.mainTextColour; width: parent.width; font.bold: true;
+ //height: parent.height
+ elide: Text.ElideRight; style: Text.Raised;
+ styleColor: settings.mainShadowColour;
+ font.pointSize: settings.mainTextSize
verticalAlignment: Text.AlignVCenter;
}
+ Text {
+ text: qsTr("%1 unread items").arg(unread);
+ color: (unread=="0") ? settings.mainTextColour : settings.secondaryTextColour;
+ width: parent.width; font.bold: false;
+ elide: Text.ElideRight;
+ //style: Text.Raised; styleColor: settings.mainShadowColour;
+ font.pointSize: settings.secondaryTextSize
+ }
//Text { text: feedname; width: parent.width; elide: Text.ElideLeft; color: "#cccccc"; style: Text.Raised; styleColor: "black" }
}
- Item {
- x: wrapper.ListView.view.width - 128; y: 12
- height: 58; width: 58;
- //anchors.horizontalCenter: parent.horizontalCenter;
- Image { source: "common/images/wmEditIcon.png" }
- MouseArea {
- anchors.fill: parent; onClicked: { container.categoryEdit(catname, catid); }
- }
- visible: inEditMode
+// Item {
+// x: wrapper.ListView.view.width - 128; y: 12
+// height: 58; width: 58;
+// //anchors.horizontalCenter: parent.horizontalCenter;
+// Image { source: "common/images/wmEditIcon.png" }
+// MouseArea {
+// anchors.fill: parent; onClicked: { container.categoryEdit(catname, catid); }
+// }
+// visible: inEditMode
+// }
+// Item {
+// x: wrapper.ListView.view.width - 64; y: 12
+// height: 58; width: 58;
+// //anchors.horizontalCenter: parent.horizontalCenter;
+// Image { source: "common/images/delete.png" }
+// MouseArea {
+// anchors.fill: parent; onClicked: { container.categoryDeleted(catid); }
+// }
+// visible: inEditMode
+// }
+ }
+ MouseArea {
+ enabled: !inEditMode; anchors.fill: wrapper;
+ onClicked: { categoryClicked(catid); }
+ onPressAndHold: {
+ window.longPressCategory(catid)
}
- Item {
- x: wrapper.ListView.view.width - 64; y: 12
- height: 58; width: 58;
- //anchors.horizontalCenter: parent.horizontalCenter;
- Image { source: "common/images/delete.png" }
- MouseArea {
- anchors.fill: parent; onClicked: { container.categoryDeleted(catid); }
- }
- visible: inEditMode
+ }
+ Component.onCompleted: {
+ if (categories.count == 1) {
+ categoryClicked(catid)
}
}
- MouseArea { enabled: !inEditMode; anchors.fill: wrapper; onClicked: { categoryClicked(catid); } }
}
}
}
Item {
id: moveMe
- Rectangle { color: "#dddddd"; opacity: index % 2 ? 0.2 : 0.4; height: 84; width: wrapper.width; y: 1 }
+ Rectangle { color: settings.backColour; opacity: index % 2 ? 0.2 : 0.4; height: 84; width: wrapper.width; y: 1 }
Rectangle {
- x: 3; y: 4; width: 77; height: 77; color: "#000000"; smooth: true
- Image { width:32; height: 32; anchors.verticalCenter: parent.verticalCenter; anchors.horizontalCenter: parent.horizontalCenter;
- source: (updating=="True")? "common/images/loading.png" : (icon == "False") ? "common/images/feedingit.png" : icon;
- NumberAnimation on rotation {
- from: 0; to: 360; running: (updating=="True"); loops: Animation.Infinite; duration: 900
- }
+ x: 3; y: 4; width: 77; height: 77;
+ color: "#000000"; smooth: true
+ Image { width:32; height: 32; anchors.verticalCenter: parent.verticalCenter;
+ anchors.horizontalCenter: parent.horizontalCenter;
+ source: (icon == "False") ? "common/images/feedingit.png" : icon
+ // (updating=="True")? "common/images/loading.png" : (icon == "False") ? "common/images/feedingit.png" : icon;
+// NumberAnimation on rotation {
+// from: 0; to: 360; running: (updating=="True"); loops: Animation.Infinite; duration: 900
+// }
}
}
Column {
x: 92; width: wrapper.ListView.view.width - 95; y: 5; spacing: 2
- Text { text: title; color: "white"; width: parent.width; font.bold: true; elide: Text.ElideRight; style: Text.Raised; styleColor: "black"; font.pointSize: 18 }
- Text { text: updatedDate + " / " + qsTr("%1 unread items").arg(unread); color: (unread=="0") ? "white" : "#7b97fd"; width: parent.width; font.bold: false; elide: Text.ElideRight; style: Text.Raised; styleColor: "black"; font.pointSize: 12 }
+ Text { text: title; color: settings.mainTextColour;
+ width: parent.width;
+ font.bold: true; elide: Text.ElideRight; style: Text.Raised; styleColor: settings.mainShadowColour;
+ font.pointSize: settings.mainTextSize
+ }
+ Text {
+ text: updatedDate + " / " + qsTr("%1 unread items").arg(unread);
+ color: (unread=="0") ? settings.mainTextColour : settings.secondaryTextColour;
+ width: parent.width; font.bold: false; elide: Text.ElideRight;
+ //style: Text.Raised; styleColor: settings.mainShadowColour;
+ font.pointSize: settings.secondaryTextSize
+ }
//Text { text: feedname; width: parent.width; elide: Text.ElideLeft; color: "#cccccc"; style: Text.Raised; styleColor: "black" }
}
//// Item {
feedClicked(feedid)
}
+ onPressAndHold: {
+ window.longPressFeed(feedid)
+ }
}
}
--- /dev/null
+import Qt 4.7
+Item {
+ property string mainTextColour: theme.inverted ? "white" : "black"
+ property string mainShadowColour: theme.inverted ? "black" : "white"
+ property int mainTextSize: 18
+ property string secondaryTextColour: theme.inverted? "#7b97fd" : "#5c71bd"
+ property int secondaryTextSize: 14
+
+ property string backColour: theme.inverted? "#dddddd" : "#222222"
+}
id: window
initialPage: categoryPage
- signal articleClosed()
+ signal longPressCategory(string catid)
+ signal longPressFeed(string key)
+ signal categoryReloadRequest()
+ signal feedReloadRequest()
property string feedid
property string catid
Component.onCompleted: theme.inverted= true
+ onLongPressFeed: {
+ unsubscribeFeedMenu.key = key
+ unsubscribeFeedMenu.open()
+ }
+
+ onLongPressCategory: {
+ unsubscribeCategoryMenu.catid = catid
+ unsubscribeCategoryMenu.open()
+ }
+
ToolBarLayout {
id: commonTools
visible: false
visualParent: pageStack
MenuLayout {
MenuItem { text: qsTr("Settings"); onClicked: {} }
+ MenuItem { text: qsTr("Invert Theme"); onClicked: { theme.inverted = !theme.inverted } }
MenuItem { text: qsTr("Add Feeds"); onClicked: { pageStack.push(addFeedPage) } }
MenuItem { text: qsTr("Update All Categories"); onClicked: controller.updateAll(); }
MenuItem { text: qsTr("About FeedingIt"); onClicked: query.open(); }
id: myFeedsMenu
visualParent: pageStack
MenuLayout {
- MenuItem { text: qsTr("Update All Feeds"); onClicked: controller.updateAll(); }
- MenuItem { text: qsTr("About FeedingIt"); onClicked: query.open(); }
+ MenuItem { text: qsTr("Update All Feeds"); onClicked: controller.updateCategory(catid); }
+ //MenuItem { text: qsTr("About FeedingIt"); onClicked: query.open(); }
}
}
MenuLayout {
MenuItem { text: qsTr("Mark All As Read"); onClicked: controller.markAllAsRead(feedid); }
MenuItem { text: qsTr("Update Feed"); onClicked: controller.updateFeed(feedid); }
- MenuItem { text: qsTr("About FeedingIt"); onClicked: query.open(); }
+ //MenuItem { text: qsTr("About FeedingIt"); onClicked: query.open(); }
+ }
+ }
+
+ Menu {
+ id: unsubscribeFeedMenu
+ visualParent: pageStack
+ property string key
+ MenuLayout {
+ //MenuItem { text: qsTr("Update"); onClicked: controller.updateFeed(parent.feedid); }
+ MenuItem { text: qsTr("Delete");
+ onClicked: {
+ feedConfirm.open()
+ unsubscribeFeedMenu.close()
+ }
+ }
+ }
+ QueryDialog {
+ id: feedConfirm
+ //icon: "common/images/feedingit.png"
+ //titleText: "Delete Feed?"
+ message: "Delete this feed?"
+ acceptButtonText: "OK"
+ rejectButtonText: "Cancel"
+ onAccepted: {
+ controller.removeFeed(unsubscribeFeedMenu.key);
+ window.feedReloadRequest()
+ }
+ }
+ }
+
+ Menu {
+ id: unsubscribeCategoryMenu
+ visualParent: pageStack
+ property string catid
+ MenuLayout {
+ //MenuItem { text: qsTr("Update"); onClicked: controller.updateFeed(parent.feedid); }
+ MenuItem { text: qsTr("Delete");
+ onClicked: {
+ categoryConfirm.open()
+ unsubscribeFeedMenu.close()
+ }
+ }
+ }
+ QueryDialog {
+ id: categoryConfirm
+ //icon: "common/images/feedingit.png"
+ //titleText: "Delete Feed?"
+ message: "Delete this category?"
+ acceptButtonText: "OK"
+ rejectButtonText: "Cancel"
+ onAccepted: {
+ controller.removeCategory(unsubscribeCategoryMenu.catid);
+ window.categoryReloadRequest()
+ }
}
}
acceptButtonText: "OK"
}
-// Page{
-// id: mainPage
-// Component.onCompleted: {
-// var main = Qt.createComponent("FeedingIt.qml");
-// main.createObject(mainPage);
-// }
-// }
-
Component {
id: categoryPage
Page {
tools: commonTools
Categories {
+ id: categoriesItem
onCategoryClicked: {
window.catid = cat
pageStack.push(feedsPage)
}
+
+ }
+ Connections {
+ target: window
+ onCategoryReloadRequest: {
+ console.log("category reloaded")
+ categoriesItem.reload()
+ }
}
}
}
window.feedid = feedid
pageStack.push(articlesPage)
}
+
}
ToolBarLayout {
id: feedsTools
ToolIcon { iconId: "toolbar-back";
onClicked: {
myArticlesMenu.close();
+ window.categoryReloadRequest()
pageStack.pop();
}
}
}
Connections {
target: window
- onArticleClosed: feedsItem.reload()
+ onFeedReloadRequest: feedsItem.reload()
}
}
}
flipper.articleShown = false;
flipper.reload()
} else {
- window.articleClosed();
+ window.feedReloadRequest();
pageStack.pop();
}
}
id: addFeedPage
AddFeed {}
}
+
+ Settings {
+ id: settings
+ }
}
#text = '''<div style="color: black; background-color: white;">'''
text = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">'
text += "<html><head><title>" + title + "</title>"
- text += '<meta http-equiv="Content-Type" content="text/html; charset="UTF-8"/>\n'
+ text += '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>\n'
#text += '<style> body {-webkit-user-select: none;} </style>'
text += '</head><body bgcolor=\"#ffffff\"><div><a href=\"' + link + '\">' + title + "</a>"
if author != None: