From 2263ab5869648d0eb7d82d8794aae03fd0ce954d Mon Sep 17 00:00:00 2001 From: Tofe Date: Sun, 4 Mar 2012 23:33:40 +0100 Subject: [PATCH] Major changes - Configuration dialog - Google Reader (source model + javascript adapted from NewsFlow) - bumped version to 0.4.0 --- .../content/images/borderStripes.png | Bin 0 -> 782 bytes qml/QuickNewsReader/content/images/lineedit.png | Bin 0 -> 1415 bytes qml/QuickNewsReader/content/images/stripes.png | Bin 0 -> 257 bytes qml/QuickNewsReader/content/js/GoogleReaderAPI.js | 530 ++++++++++++++++++++ .../content/modelimpl/FavoriteFeedsSourceModel.qml | 4 +- .../content/modelimpl/GoogleReaderCategories.qml | 14 + .../content/modelimpl/GoogleReaderNews.qml | 8 + .../content/modelimpl/GoogleReaderSourceModel.qml | 169 +++++++ .../content/modelimpl/LeMondeSourceModel.qml | 8 +- .../content/modelimpl/YahooSourceModel.qml | 4 +- qml/QuickNewsReader/content/view/Background.qml | 31 ++ qml/QuickNewsReader/content/view/Categories.qml | 34 +- .../content/view/CategoryDelegate.qml | 24 +- .../content/view/GoogleReaderConfig.qml | 64 +++ qml/QuickNewsReader/content/view/LineInput.qml | 26 + qml/QuickNewsReader/content/view/News.qml | 1 + qml/QuickNewsReader/content/view/NewsComments.qml | 10 +- qml/QuickNewsReader/content/view/NewsDelegate.qml | 18 +- qml/QuickNewsReader/content/view/NewsDetail.qml | 27 +- .../content/view/SourceConfigDialog.qml | 35 +- .../content/view/SourceDelegate.qml | 9 +- qml/QuickNewsReader/content/view/Sources.qml | 19 +- qml/QuickNewsReader/main.qml | 28 +- qtc_packaging/debian_fremantle/changelog | 10 + 24 files changed, 967 insertions(+), 106 deletions(-) create mode 100644 qml/QuickNewsReader/content/images/borderStripes.png create mode 100644 qml/QuickNewsReader/content/images/lineedit.png create mode 100644 qml/QuickNewsReader/content/images/stripes.png create mode 100644 qml/QuickNewsReader/content/js/GoogleReaderAPI.js create mode 100644 qml/QuickNewsReader/content/modelimpl/GoogleReaderCategories.qml create mode 100644 qml/QuickNewsReader/content/modelimpl/GoogleReaderNews.qml create mode 100644 qml/QuickNewsReader/content/modelimpl/GoogleReaderSourceModel.qml create mode 100644 qml/QuickNewsReader/content/view/Background.qml create mode 100644 qml/QuickNewsReader/content/view/GoogleReaderConfig.qml create mode 100644 qml/QuickNewsReader/content/view/LineInput.qml diff --git a/qml/QuickNewsReader/content/images/borderStripes.png b/qml/QuickNewsReader/content/images/borderStripes.png new file mode 100644 index 0000000000000000000000000000000000000000..eeb6c0966c11bf1b8b77decf35a7942ec5689be1 GIT binary patch literal 782 zcmeAS@N?(olHy`uVBq!ia0vp^DImQL70(Y)*K0-AbW|YuPgf~) zy=&-q*g)ji$8f<&*??PIrQa_&8#G+$^)BE)cYxEvtD2?Eg2}_@0%xg5jF^L~7E4A_ z{mTDxKPP0|4_;Bf@wH53%fmU**Gg-%)AZ%-Ki}hb=GbRrSGVHTuRZENH-9axc;Ei` zV}+em@3eIq(_913oXx4os9ZlQaN5&LmgV_rn`85z1r)a$-uRd#er~60posX+rzdxP zxSP7?YtrU(nQaC4p5}BW{a1d!O0xX@f8+gs8ef)J&Gu2_{xtvdHQA4LvwwVI44d{e zr>f|a*T2_$TGM1UCt+nSYw);RH4g}KmO)i zg)VdEeC63V=WEZtHOn8gY^e-3p253xR&Jn9@1M=*_=@-B zIYkuWqT(y&_6GtoP0j@zdZX$@B59P@6p;i=kux~MdwyO+M`o+?(JLmd0*D4 z{p8<1S%dBQw@K!A{SGl!owq!~nEClxwYi02*1a8VJ;INkwmDys-Ly66SdK^Lciy8y z^(AsgMQ%@T6L$Hw^dZZ^+Dr?_H?}Yt<8 literal 0 HcmV?d00001 diff --git a/qml/QuickNewsReader/content/images/lineedit.png b/qml/QuickNewsReader/content/images/lineedit.png new file mode 100644 index 0000000000000000000000000000000000000000..2cc38dc35b38a6e5b4b244f8165cc093221cf419 GIT binary patch literal 1415 zcmV;21$g?2P)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2iXW1 z1sp18KZTY6000?uMObu0Z*6U5Zgc=ca%Ew3Wn>_CX>@2HM@dakSAh-}000EyNkl^_M)OMqYwHdLJ>s}eN&%{;FDDU1^oxa z7nPzff|n{76#7tuptM>qwY6%kY0}0t>Ez5g`}=&DOiXMwNlBU%`@!bG%sywZNPmU@yB5~XvhKv!26k>*eX&bbw5aXw&G#TbLC zA|i}k8f7#)N6$bH1z+HgV`usG{a=Q?E!s!7KC!vxy=C(31Wd&|ecvMJ3*;m7aD5HC*jIC@bW7P5Ig-Q0^y^q_fw-J0m zj6kelw5houE82_#qG*u0YpWrOL||gWgF7E!_uyWV$r+*^k1tf(Nzyug%&@zEC#Vp+ z-0V1`zZa3St15$kvK(nl2r0Q-Ol?bzPe1yE_Wn-16qKwXbQZd~X=oG9`UMY4vl+b0 zEnCN`x?~q=^<8j}D4GBshPtXa^+Ya*&qxsw>eJKYeXeO|nvjp$2uVtm%P$GQ)!16A z-IodyiE^a&f9@ zg^NzDgk`h4nq^mljWq?lp1@rd!^XPn3-G$v@IS#B>(;l1*FD3=7z6=p7QBqiqj*h# z&EKk9S5?Jwoz)G%Hvs=H0AE)=yzUq_#^Q+=c-?wn*BH}0|MpiYT>j7{ndOr+G4IzOEFes*|GuptD@Dn(UGS#&L;j-B|1 zsH2&U*y-tMthY=|O)&E12&puhdPbUwrL2)l&@_WansDUHBbW#pjRtn=iwR<%r@Xbo z>xbXq$gvR+AQ*y%t9FW(r2eak2q6SQ2y>BZ>oaQ{Si`}O4l?}XFokNK^g@Dj-o4ke zx%6ClbA{3AKREpPVMralr3&30T?m4~tjYp3#x%Xv+RLJ=pMU$6*WP`Vw?2HE(vDvG zH&!`)=oDr{dBY=Z1JVB4{)cKwd%}h9#u+~`PH(A~qAgOS0~=d(nRpiqz_Tp7ET(F) ztO{e7&NJ@D=)I{#d25;Rqi31?qW*%Z4h)(!4G(v0E53Nwle=ndLj^P#M$e4Wn3_Q* zQyOG=?^iU63Yz`vea1jnUk`=u9Qi_#^Iu=!_@Se(6#5Gc0r98Q=HtI=(yw9^-MaUwHRW)2O7;H&o=HewTo|>XD7CtH6S$fJB{J3#O{sI^e VqK)QbrHcRn002ovPDHLkV1mrKo*@7L literal 0 HcmV?d00001 diff --git a/qml/QuickNewsReader/content/images/stripes.png b/qml/QuickNewsReader/content/images/stripes.png new file mode 100644 index 0000000000000000000000000000000000000000..9f36727ea424cd0da94bd5a7cee4082447275eeb GIT binary patch literal 257 zcmeAS@N?(olHy`uVBq!ia0vp^93afW1|*O0@9PFqjKx9jP7LeL$-D$|*pj^6T^Rm@ z;DWu&Cj&(|3p^r=85p>QL70(Y)*K0-AbW|YuPgga4pBxq@gGdLS%5-~o-U3d5|`(m z-N<{$K*aT;^vADLPxXoJET5>DG2xP-Q1*siVN6aag0$kQzm$|0%E+I; z#Y!4I)=g1)z4*}Q$iB}`v_QR literal 0 HcmV?d00001 diff --git a/qml/QuickNewsReader/content/js/GoogleReaderAPI.js b/qml/QuickNewsReader/content/js/GoogleReaderAPI.js new file mode 100644 index 0000000..c1fc132 --- /dev/null +++ b/qml/QuickNewsReader/content/js/GoogleReaderAPI.js @@ -0,0 +1,530 @@ +/* + Copyright 2011 - Tommi Laukkanen (www.substanceofcode.com) + + This file is part of NewsFlow. + + NewsFlow is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + NewsFlow 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with NewsFlow. If not, see . +*/ + +var sid = ""; +var sidToken = ""; + +// UI components +//var waiting; +//var done; +//var model; +//var tagsModel; +//var logo; +//var error; +//var navigation; + +var continuation = ""; +var actionID = ""; +var actionFeedUrl = ""; +var accessToken = ""; +var action = ""; +var tags = ""; + +var itemsURL = ""; + +function doWebRequest(method, url, params, callback) { + var doc = new XMLHttpRequest(); + //console.log(method + " " + url); + + doc.onreadystatechange = function() { + if (doc.readyState == XMLHttpRequest.HEADERS_RECEIVED) { + var status = doc.status; + if(status!=200) { + showError("Google API returned " + status + " " + doc.statusText); + } + } else if (doc.readyState == XMLHttpRequest.DONE) { + var data; + var contentType = doc.getResponseHeader("Content-Type"); + data = doc.responseText; + callback(data); + } + } + + doc.open(method, url); + if(sid.length>0) { + //console.log("Authorization GoogleLogin auth=" + sid); + doc.setRequestHeader("Authorization", "GoogleLogin auth=" + sid); + doc.setRequestHeader("Cookie", "SID=" + sidToken); + } + if(params.length>0) { + //console.log("Sending: " + params); + doc.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); + doc.setRequestHeader("Content-Length", String(params.length)); + doc.send(params); + } else { + doc.send(); + } +} + +/** Parse parameter from given URL */ +function parseAuth(data, parameterName) { + var parameterIndex = data.indexOf(parameterName + "="); + if(parameterIndex<0) { + // We didn't find parameter + console.log("Didn't find Auth"); + addError("Didn't find Auth"); + return ""; + } + var equalIndex = data.indexOf("=", parameterIndex); + if(equalIndex<0) { + return ""; + } + + var lineBreakIndex = data.indexOf("\n", equalIndex+1) + + var value = ""; + value = data.substring(equalIndex+1, lineBreakIndex); + return value; +} + +function addError(msg) { + console.log(msg) + /* + model.append({ + "title": "Error", + "desc": msg, + "author": "", + "published": "", + "more": true, + "source": ""}) + */ +} + +function login(email, password) { + try { + // waiting.state = "shown"; + var url = "https://www.google.com/accounts/ClientLogin?Email=" + encodeURIComponent(email) + "&Passwd=" + encodeURIComponent(password) + "&service=reader"; + doWebRequest("GET", url, "", parseToken); + }catch(err) { + showError("Error while logging in."); + } +} + +function showError(msg) { + console.log("ERROR: " + msg) +// waiting.state = "hidden"; +// error.reason = msg; +// error.state = "shown"; +} + +function removeLinks(original) { + var txt = original; + txt = txt.replace(//g, ""); + return txt; +} + +function parseToken(data) { + sid = parseAuth(data, "Auth"); + //console.log("Auth=" + sid); + sidToken = parseAuth(data, "SID"); + //console.log("SID=" + sidToken); + // logo.state = "hidden"; //.visible = false; + if(sid.length>0) { + //navigation.state = "menu"; + //waiting.state = "hidden"; + + WorkerScript.sendMessage({"sid": sid, "sidToken": sidToken}); + + //loadUnreadNews(); + } else { + addError("Couldn't parse SID"); + //waiting.state = "hidden"; + } +} + +function loadSubscriptions() { + //waiting.state = "shown"; + var url = "http://www.google.com/reader/api/0/subscription/list?output=json"; + doWebRequest("GET", url, "", parseSubscriptions); +} + +function parseSubscriptions(data) { + //console.log("Subscriptions: " + data); + + var tags = eval("[" + data + "]")[0]; + for(var i in tags.subscriptions) { + var tag = tags.subscriptions[i]; + var id = tag.id; + var title = tag.title; + + WorkerScript.sendMessage({"title": title, "published": '',"tag": tag, "id":id}); + } + //navigation.state = "tags"; + //waiting.state = "hidden"; +} + +function loadTags() { + //waiting.state = "shown"; + var url = "http://www.google.com/reader/api/0/tag/list?output=json"; + doWebRequest("GET", url, "", parseTags); +} + +function parseTags(data) { + var tags = eval("[" + data + "]")[0]; + for(var i in tags.tags) { + var tag = tags.tags[i]; + var id = tag.id; + var title = id; + while(title.indexOf("/")>0) { + var index = title.indexOf("/"); + title = title.substring(index+1); + } + + WorkerScript.sendMessage({"title": title, "published": '', "tag": tag, "id":id}); + } + //navigation.state = "tags"; + //waiting.state = "hidden"; +} + +function loadAllNews() { + itemsURL = "http://www.google.com/reader/api/0/stream/contents/user/-/state/com.google/reading-list"; + loadNews(); +} + +function loadUnreadNews() { + itemsURL = "http://www.google.com/reader/api/0/stream/contents/user/-/state/com.google/reading-list?xt=user/-/state/com.google/read"; + loadNews(); +} + +function loadStarred() { + itemsURL = "http://www.google.com/reader/api/0/stream/contents/user/-/state/com.google/starred"; + loadNews(); +} + +function loadTaggedItems(tag) { + itemsURL = "http://www.google.com/reader/api/0/stream/contents/" + tag; + loadNews(); +} + +function loadSubscriptionItems(subscription) { + itemsURL = "http://www.google.com/reader/api/0/stream/contents/" + subscription; + loadNews(); +} + +function loadNews() { + try { + //waiting.state = "shown"; + doWebRequest("GET", itemsURL, "", parseNews); + } catch(err) { + showError("Error while loading news: " + err); + } +} + +function getNodeValue(node, name) { + var nodeValue = ""; + for(var i=0; i0) { + isRead = false; + } + } + + + WorkerScript.sendMessage({ + "id": item.id, + "title": item.title, + "description": content, + "author": item.origin.title, + "published": prettyDate(published), + "more": false, + "source": item.origin.title, + "link": link, + "feedUrl": item.origin.streamId, + "isRead": isRead + }); +} + +function parseNews(data) { + //try { + //console.log("DATA: " + data); + var doc = eval("[" + data + "]")[0]; + if(doc==null || typeof(doc)==undefined) { + WorkerScript.sendMessage({ + "title": "Error", + "description": "", + "author": "", + "published": "", + "more": true, + "isRead": false, + "source": ""}); + //waiting.state = "hidden"; + return; + } + + //var moreIndex = model.count - 1; + + continuation = doc.continuation; + for(var i in doc.items) { + var item = doc.items[i]; + parseEntry(item); + } +/* + if(moreIndex>0) { + if(model.get(moreIndex).title.indexOf("Loading...")>-1) { + model.remove(moreIndex); + } + } + + model.append({ + "title": "Load more...

", + "description": "", + "author": "", + "published": "", + "isRead": false, + "more": true, + "source": ""}); + */ + //}catch(err) { + // addError("Error: " + err); + //} + // navigation.state = "items"; + // waiting.state = "hidden"; +} + +function loadMore() { + //waiting.state = "shown"; + var url = itemsURL; + if(itemsURL.indexOf("?")>0) { + url += "&"; + } else { + url += "?"; + } + url += "c=" + continuation; + doWebRequest("GET", url, "", parseNews); +} + +function getToken() { + var url = "http://www.google.com/reader/api/0/token"; + doWebRequest("GET", url, "", parseAccessToken, null); +} + +function parseAccessToken(data) { + accessToken = data; + if(action=="read") { + var url = "http://www.google.com/reader/api/0/edit-tag"; + var dd = "ac=edit-tags&a=user/-/state/com.google/read&i=" + encodeURIComponent(actionID) + "&s=" + encodeURIComponent(actionFeedUrl) + "&T=" + accessToken; + doWebRequest("POST", url, dd, showDone, null); + } else if(action=="unread") { + var url = "http://www.google.com/reader/api/0/edit-tag"; + var dd = "ac=edit-tags&a=user/-/state/com.google/kept-unread&r=user/-/state/com.google/read&i=" + encodeURIComponent(actionID) + "&s=" + encodeURIComponent(actionFeedUrl) + "&T=" + accessToken; + doWebRequest("POST", url, dd, showDone, null); + } else if(action=="fav") { + var url = "http://www.google.com/reader/api/0/edit-tag?client=-"; + var dd = "a=user/-/state/com.google/starred&i=" + encodeURIComponent(actionID) + "&s=" + encodeURIComponent(actionFeedUrl) + "&T=" + accessToken; + doWebRequest("POST", url, dd, showDone, null); + } else if(action=="tags") { + var url = "http://www.google.com/reader/api/0/edit-tag?client=-"; + var dd = + "a=user/-/label/" + encodeURIComponent(tags) + + "&i=" + encodeURIComponent(actionID) + + "&s=" + encodeURIComponent(actionFeedUrl) + + "&T=" + accessToken; + doWebRequest("POST", url, dd, showDone, null); + } +} + +function markAsRead(id, feedUrl, showLoadingIndicator) { + actionID = id; + actionFeedUrl = feedUrl; + if(showLoadingIndicator) { + waiting.state = "shown"; + } + action = "read"; + getToken(); +} + +function markAsUnread(id, feedUrl) { + actionID = id; + actionFeedUrl = feedUrl; + //waiting.state = "shown"; + action = "unread"; + //done.status = "Marked as unread"; + getToken(); +} + +function addTags(id, feedUrl, newTags) { + actionID = id; + actionFeedUrl = feedUrl; + //waiting.state = "shown"; + action = "tags"; + //done.status = "Added tags"; + tags = newTags; + getToken(); +} + +function markAsFavourite(id, feedUrl) { + actionID = id; + actionFeedUrl = feedUrl; + //waiting.state = "shown"; + action = "fav"; + //done.status = "Marked as favourite"; + getToken(); +} + +function showDone(data) { + console.log("DONE: " + data); + //if(waiting.state!="shown") { + // return; + //} + //waiting.state = "hidden"; + //done.status = ""; + if(typeof(data)!=undefined && data!=null) { + if(action=="read") { + //done.status = "Marked as read " + data; + } else if(action=="unread") { + //done.status = "Marked as unread " + data; + } else { + //done.status = "" + data; + } + } + //done.state = "shown"; +} + +function doNothing(data) { + // Nothing... +} + +function prettyDate(date){ + try { + var diff = (((new Date()).getTime() - date.getTime()) / 1000); + var day_diff = Math.floor(diff / 86400); + + if ( isNaN(day_diff) || day_diff >= 31 ) { + //console.log("Days: " + day_diff); + return "some time ago"; + } else if (day_diff < 0) { + //console.log("day_diff: " + day_diff); + return "just now"; + } + + return day_diff == 0 && ( + diff < 60 && "just now" || + diff < 120 && "1 minute ago" || + diff < 3600 && Math.floor( diff / 60 ) + " min ago" || + diff < 7200 && "1 hour ago" || + diff < 86400 && Math.floor( diff / 3600 ) + " hours ago") || + day_diff == 1 && "Yesterday" || + day_diff < 7 && day_diff + " days ago" || + day_diff < 31 && Math.ceil( day_diff / 7 ) + " weeks ago"; + day_diff >= 31 && Math.ceil( day_diff / 30 ) + " months ago"; + } catch(err) { + console.log("Error: " + err); + return "some time ago"; + } +} + +// 2011-01-24T18:48:00Z +function parseDate(stamp) +{ + try { + //console.log("stamp: " + stamp); + var parts = stamp.split("T"); + var day; + var time; + var hours; + var minutes; + var seconds = 0; + var year; + var month; + + var dates = parts[0].split("-"); + year = parseInt(dates[0]); + month = parseInt(dates[1])-1; + day = parseInt(dates[2]); + + var times = parts[1].split(":"); + hours = parseInt(times[0]); + minutes = parseInt(times[1]); + + var dt = new Date(); + dt.setUTCDate(day); + dt.setYear(year); + dt.setUTCMonth(month); + dt.setUTCHours(hours); + dt.setUTCMinutes(minutes); + dt.setUTCSeconds(seconds); + + //console.log("day: " + day + " year: " + year + " month " + month + " hour " + hours); + + return dt; + } catch(err) { + console.log("Error while parsing date: " + err); + return new Date(); + } +} + +/// ======== WorkerScript related functions ======== +WorkerScript.onMessage = function(message) { + + // the structure of "message" object is: + // - attribute 'action' --> what to do + // - other attributes: will be passed on to the right function + + if(message.action === 'login') { + login(message.email, message.password) + } + else { + sid = message.sid; sidToken = message.sidToken; + + if(message.action === 'getCategoryContent') { + // read the feeds in the right category + switch(message.category) { + case 0: loadAllNews(); break; + case 1: loadUnreadNews(); break; + case 2: loadStarred(); break; + case 3: loadSubscriptions(); break; + case 4: loadTags(); break; + } + } + else if(message.action === 'getSubscriptionItems') { + // read the feeds of that subscription + loadSubscriptionItems(message.subscription) + } + else if(message.action === 'getTaggedItems') { + // read the feeds of that subscription + loadTaggedItems(message.tag) + } + } +} + diff --git a/qml/QuickNewsReader/content/modelimpl/FavoriteFeedsSourceModel.qml b/qml/QuickNewsReader/content/modelimpl/FavoriteFeedsSourceModel.qml index cd6b9a0..00c0685 100644 --- a/qml/QuickNewsReader/content/modelimpl/FavoriteFeedsSourceModel.qml +++ b/qml/QuickNewsReader/content/modelimpl/FavoriteFeedsSourceModel.qml @@ -5,8 +5,8 @@ SourceModel { name: "Favorite Feeds" listViews: [ - { viewComponent: 'content/view/Categories.qml', viewId: 'categoriesRect' } , - { viewComponent: 'content/view/News.qml', viewId: 'newsRect' } + { viewComponent: 'content/view/Categories.qml' } , + { viewComponent: 'content/view/News.qml' } ] listModels: [ categoriesModel, diff --git a/qml/QuickNewsReader/content/modelimpl/GoogleReaderCategories.qml b/qml/QuickNewsReader/content/modelimpl/GoogleReaderCategories.qml new file mode 100644 index 0000000..73efd83 --- /dev/null +++ b/qml/QuickNewsReader/content/modelimpl/GoogleReaderCategories.qml @@ -0,0 +1,14 @@ +import QtQuick 1.0 +import "../modelitf" + +ListModel { + id: googleReaderCategories + property int sourceDepth: 1 + + ListElement { name: "All items"; feed: "rss.news.yahoo.com/rss/topstories" } + ListElement { name: "Unread items"; feed: "rss.news.yahoo.com/rss/world" } + ListElement { name: "Starred items"; feed: "rss.news.yahoo.com/rss/europe" } + ListElement { name: "Subscriptions"; feed: "rss.news.yahoo.com/rss/oceania" } + ListElement { name: "Tags"; feed: "rss.news.yahoo.com/rss/us" } +} + diff --git a/qml/QuickNewsReader/content/modelimpl/GoogleReaderNews.qml b/qml/QuickNewsReader/content/modelimpl/GoogleReaderNews.qml new file mode 100644 index 0000000..4cb44aa --- /dev/null +++ b/qml/QuickNewsReader/content/modelimpl/GoogleReaderNews.qml @@ -0,0 +1,8 @@ +import QtQuick 1.0 + +ListModel { + id: googleReaderNewsList + property int sourceDepth: 2 + + // required elements: title and description. Optional: image. +} diff --git a/qml/QuickNewsReader/content/modelimpl/GoogleReaderSourceModel.qml b/qml/QuickNewsReader/content/modelimpl/GoogleReaderSourceModel.qml new file mode 100644 index 0000000..1d9f7c1 --- /dev/null +++ b/qml/QuickNewsReader/content/modelimpl/GoogleReaderSourceModel.qml @@ -0,0 +1,169 @@ +import QtQuick 1.0 +import "../modelitf" +import "../js/SettingsStorage.js" as Storage + +SourceModel { + id: googleReaderModel + name: "Google Reader" + + listViews: [ + { viewComponent: 'content/view/Categories.qml' }, + { viewComponent: 'content/view/News.qml' } + ] + listModels: [ + categoriesModel, + categoryContentModel + ] + + property variant categoriesModel: GoogleReaderCategories { } + property variant categoryContentModel: GoogleReaderNews { sourceDepth: 2 } + property variant categorySubContentModel: GoogleReaderNews { sourceDepth: 3 } + property variant newsDetailModel: QtObject { + property int sourceDepth: 3 + + property string htmlcontent: "" + property string title: "" + property string image: "" + } + property variant sid; + property variant sidToken; + + loading: false + hasSettings: true + settingsComponent: "GoogleReaderConfig.qml" + function storeConfiguration(configUI) { + // save the values in the database + Storage.setSetting("GoogleReader.login", configUI.loginValue) + Storage.setSetting("GoogleReader.password", configUI.passwordValue) + + tryLogin() + } + function loadConfiguration(configUI) { + // retrieve the values from the database + configUI.loginValue = Storage.getSetting("GoogleReader.login") + configUI.passwordValue = Storage.getSetting("GoogleReader.password") + } + + property variant googleReaderLoginWorker: WorkerScript { + id: googleReaderLoginWorker + source: "../js/GoogleReaderAPI.js" + + onMessage: { + sid = messageObject.sid + sidToken = messageObject.sidToken + + loading = false + } + } + + property variant googleReaderLoadCategoryWorker: WorkerScript { + id: googleReaderLoadCategoryWorker + source: "../js/GoogleReaderAPI.js" + + onMessage: { + categoryContentModel.append({ 'title': messageObject.title, 'description': messageObject.published, 'image': '', 'id': messageObject.id }) + + loading = false + } + } + property variant googleReaderLoadSubscriptionOrTagWorker: WorkerScript { + id: googleReaderLoadSubscriptionOrTagWorker + source: "../js/GoogleReaderAPI.js" + + onMessage: { + categorySubContentModel.append({ 'title': messageObject.title, 'description': messageObject.published, 'image': '', 'content': messageObject.description }) + + loading = false + } + } + property variant googleReaderLoadItemWorker: WorkerScript { + id: googleReaderLoadItemWorker + source: "../js/GoogleReaderAPI.js" + + onMessage: { + newsDetailModel.htmlcontent = messageObject.newsContent + + loading = false + } + } + + function tryLogin() { + var loginValue = Storage.getSetting("GoogleReader.login") + var passwordValue = Storage.getSetting("GoogleReader.password") + + loading = true + + googleReaderLoginWorker.sendMessage({ + 'action': 'login', + 'email': loginValue, + 'password': passwordValue + }) + } + onCurrentPathChanged: { + // build the right model. currentPath[1] => category + var selectionDepth = 0; + while(typeof currentPath[selectionDepth+1] !== "undefined") + selectionDepth++; + + if( typeof currentPath[1] !== "undefined" ) { + if( selectionDepth == 1 ) { + // the category has been selected, so fill in the right content + categoryContentModel.clear() + + // reshape the views and the models to fit the chosen path + var newsDetailIndex = 2; + var tmpListModels = listModels + var tmpListViews = listViews + if( currentPath[1] === 3 || currentPath[1] === 4 ) + { + tmpListModels[2] = categorySubContentModel; + tmpListViews[2] = { viewComponent: 'content/view/News.qml' } + newsDetailIndex = 3; + } + tmpListModels[newsDetailIndex] = newsDetailModel; + tmpListViews[newsDetailIndex] = { viewComponent: 'content/view/NewsDetail.qml' }; + tmpListModels[newsDetailIndex+1] = null + tmpListViews[newsDetailIndex+1] = null + listModels = tmpListModels + listViews = tmpListViews + + googleReaderLoadCategoryWorker.sendMessage({ + 'action': 'getCategoryContent', + 'sid': sid, 'sidToken': sidToken, + 'category': currentPath[1] + }) + } + else if( selectionDepth == 2 && currentPath[1] === 3 ) { + // subscriptions selected + categorySubContentModel.clear() + googleReaderLoadSubscriptionOrTagWorker.sendMessage({ + 'action': 'getSubscriptionItems', + 'sid': sid, 'sidToken': sidToken, + 'subscription': categoryContentModel.get(currentPath[2]).id + }) + } + else if( selectionDepth == 2 && currentPath[1] === 4 ) { + // tags selected + categorySubContentModel.clear() + googleReaderLoadSubscriptionOrTagWorker.sendMessage({ + 'action': 'getTaggedItems', + 'sid': sid, 'sidToken': sidToken, + 'tag': categoryContentModel.get(currentPath[2]).id + }) + } + else if( selectionDepth == 3 ) { + // subscription or tagged item selected + newsDetailModel.htmlcontent = categorySubContentModel.get(currentPath[3]).content + } + else if( selectionDepth == 2 ) { + // simply get the chosen news + newsDetailModel.htmlcontent = categoryContentModel.get(currentPath[2]).content + } + } + } + + Component.onCompleted: { + Storage.initialize() + tryLogin() + } +} diff --git a/qml/QuickNewsReader/content/modelimpl/LeMondeSourceModel.qml b/qml/QuickNewsReader/content/modelimpl/LeMondeSourceModel.qml index 7a2b54d..4bdb4bb 100644 --- a/qml/QuickNewsReader/content/modelimpl/LeMondeSourceModel.qml +++ b/qml/QuickNewsReader/content/modelimpl/LeMondeSourceModel.qml @@ -5,10 +5,10 @@ SourceModel { name: "Le Monde" listViews: [ - { viewComponent: 'content/view/Categories.qml', viewId: 'categoriesRect' } , - { viewComponent: 'content/view/News.qml', viewId: 'newsRect' }, - { viewComponent: 'content/view/NewsDetail.qml', viewId: 'newsDetailRect' }, - { viewComponent: 'content/view/NewsComments.qml', viewId: 'newsCommentsRect' } + { viewComponent: 'content/view/Categories.qml' } , + { viewComponent: 'content/view/News.qml' }, + { viewComponent: 'content/view/NewsDetail.qml' }, + { viewComponent: 'content/view/NewsComments.qml' } ] listModels: [ categoriesModel, diff --git a/qml/QuickNewsReader/content/modelimpl/YahooSourceModel.qml b/qml/QuickNewsReader/content/modelimpl/YahooSourceModel.qml index 971a99a..e88accf 100644 --- a/qml/QuickNewsReader/content/modelimpl/YahooSourceModel.qml +++ b/qml/QuickNewsReader/content/modelimpl/YahooSourceModel.qml @@ -5,8 +5,8 @@ SourceModel { name: "Yahoo! News" listViews: [ - { viewComponent: 'content/view/Categories.qml', viewId: 'categoriesRect' } , - { viewComponent: 'content/view/News.qml', viewId: 'newsRect' } + { viewComponent: 'content/view/Categories.qml' } , + { viewComponent: 'content/view/News.qml' } ] listModels: [ categoriesModel, diff --git a/qml/QuickNewsReader/content/view/Background.qml b/qml/QuickNewsReader/content/view/Background.qml new file mode 100644 index 0000000..4be2231 --- /dev/null +++ b/qml/QuickNewsReader/content/view/Background.qml @@ -0,0 +1,31 @@ +import QtQuick 1.0 + +Rectangle { + id: background + color: "#80343434" + radius: 30 + + BorderImage { + id: borderBackgroundImage + border.left: 30 + border.top: 30 + border.bottom: 30 + border.right: 30 + horizontalTileMode: BorderImage.Repeat + verticalTileMode: BorderImage.Repeat + source: "../images/borderStripes.png" + anchors.fill: parent + } + + Rectangle { + anchors.fill: parent; + anchors.margins: 30; + color: "transparent" + Image { + source: "../images/stripes.png"; + fillMode: Image.Tile; + anchors.fill: parent; + //opacity: 0.3 + } + } +} diff --git a/qml/QuickNewsReader/content/view/Categories.qml b/qml/QuickNewsReader/content/view/Categories.qml index 695b752..742b6c5 100644 --- a/qml/QuickNewsReader/content/view/Categories.qml +++ b/qml/QuickNewsReader/content/view/Categories.qml @@ -3,49 +3,21 @@ import QtQuick 1.0 Rectangle { id: categoriesRect - width: 220; height: window.height + width: window.width; height: window.height color: "#efefef" ListView { focus: true id: categories x: 0; y: 0 - width: 220; height: window.height + width: window.width; height: window.height model: currentSource.listModels[componentDepth-1] - footer: getFooter() + //footer: getFooter() delegate: CategoryDelegate { } highlight: Rectangle { color: "steelblue" } highlightMoveSpeed: 9999999 - - function getFooter() - { - return componentDepth === 1 && currentSource.hasSettings ? settingsButtonDelegate : null - } } ScrollBar { scrollArea: categories; height: categories.height; width: 8; anchors.right: categories.right } Component.onCompleted: categories.currentIndex = -1 - - Component { - id: settingsButtonDelegate - Item { - width: categories.width; height: 70 - - FancyButton { - icon: "../images/settings.png" - anchors.horizontalCenter: parent.horizontalCenter - anchors.bottom: parent.bottom - anchors.bottomMargin: -2 - - /* - onClicked: { - if (editMenu.visible) { - editMenu.opacity = 0.0 - } else { - editMenu.opacity = 0.8 - } - }*/ - } - } - } } diff --git a/qml/QuickNewsReader/content/view/CategoryDelegate.qml b/qml/QuickNewsReader/content/view/CategoryDelegate.qml index 617846d..13afcba 100644 --- a/qml/QuickNewsReader/content/view/CategoryDelegate.qml +++ b/qml/QuickNewsReader/content/view/CategoryDelegate.qml @@ -77,20 +77,20 @@ Item { onClicked: { var currentSourceDepth = delegate.ListView.view.model.sourceDepth - // here we remove everything in viewsModel after index "nextSourceDepth" - while(window.windowViewsModel.count>currentSourceDepth+1) - window.windowViewsModel.remove(window.windowViewsModel.count-1) + if (listSourceModel[window.currentSourceIndex].listViews.length >= currentSourceDepth+1) + { + // here we remove everything in viewsModel after index "nextSourceDepth" + while(window.windowViewsModel.count>currentSourceDepth+1) + window.windowViewsModel.remove(window.windowViewsModel.count-1) - delegate.ListView.view.currentIndex = index - var path = listSourceModel[window.currentSourceIndex].currentPath - path[currentSourceDepth] = index - listSourceModel[window.currentSourceIndex].currentPath = path + delegate.ListView.view.currentIndex = index + var path = listSourceModel[window.currentSourceIndex].currentPath + path[currentSourceDepth] = index + listSourceModel[window.currentSourceIndex].currentPath = path.slice(0,currentSourceDepth+1) - window.windowViewsModel.append({ component: listSourceModel[window.currentSourceIndex].listViews[currentSourceDepth].viewComponent, - componentId: listSourceModel[window.currentSourceIndex].listViews[currentSourceDepth].viewId, - componentDepth: currentSourceDepth+1 }) - - window.windowViewsList.currentIndex = currentSourceDepth+1; + window.windowViewsModel.append({ component: listSourceModel[window.currentSourceIndex].listViews[currentSourceDepth].viewComponent, + componentDepth: currentSourceDepth+1 }) + } } } } diff --git a/qml/QuickNewsReader/content/view/GoogleReaderConfig.qml b/qml/QuickNewsReader/content/view/GoogleReaderConfig.qml new file mode 100644 index 0000000..c8eecc9 --- /dev/null +++ b/qml/QuickNewsReader/content/view/GoogleReaderConfig.qml @@ -0,0 +1,64 @@ +import QtQuick 1.0 + +Rectangle { + // Login : [text entry] + // Password : [pwd entry] + color: "transparent" + + property alias loginValue: inputLogin.text + property alias passwordValue: inputPwd.text + + Column { + anchors.fill: parent + anchors.margins: 30 + spacing: 10 + + Row { + anchors.left: parent.left + anchors.leftMargin: 10 + anchors.right: parent.right + anchors.rightMargin: 10 + + Text { + id: labelLogin + color: "white" + anchors.verticalCenter: parent.verticalCenter + width: 150 + text: qsTr("Login") + font.pixelSize: 24 + font.bold: true + } + + LineInput { + id: inputLogin + width: parent.width - 150 - 10 - 10 + anchors.verticalCenter: parent.verticalCenter + } + } + + Row { + anchors.left: parent.left + anchors.leftMargin: 10 + anchors.right: parent.right + anchors.rightMargin: 10 + + Text { + id: labelPwd + color: "white" + anchors.verticalCenter: parent.verticalCenter + width: 150 + text: qsTr("Password") + font.pixelSize: 24 + font.bold: true + } + + LineInput { + id: inputPwd + width: parent.width - 150 - 10 - 10 + anchors.verticalCenter: parent.verticalCenter + // password specific + input.echoMode: TextInput.Password + } + } + } +} diff --git a/qml/QuickNewsReader/content/view/LineInput.qml b/qml/QuickNewsReader/content/view/LineInput.qml new file mode 100644 index 0000000..a687a10 --- /dev/null +++ b/qml/QuickNewsReader/content/view/LineInput.qml @@ -0,0 +1,26 @@ +import QtQuick 1.0 + +Item { + id: lineInput + property alias text: input.text + property alias input: input + height: 50 + + BorderImage { + id: borderImage + border.left: 10 + border.top: 10 + border.bottom: 10 + border.right: 10 + source: "../images/lineedit.png" + anchors.fill: parent + } + TextInput { + id: input + color: "#151515"; selectionColor: "green" + font.pixelSize: 24; font.bold: true + width: borderImage.width-16 + anchors.centerIn: parent + focus: true + } +} diff --git a/qml/QuickNewsReader/content/view/News.qml b/qml/QuickNewsReader/content/view/News.qml index cc71822..a7cf7f8 100644 --- a/qml/QuickNewsReader/content/view/News.qml +++ b/qml/QuickNewsReader/content/view/News.qml @@ -11,6 +11,7 @@ Item { model: currentSource.listModels[componentDepth-1] delegate: NewsDelegate { } highlight: Rectangle { color: "steelblue" } + highlightMoveDuration: 600 } ScrollBar { scrollArea: list; height: list.height; width: 8; anchors.right: list.right } diff --git a/qml/QuickNewsReader/content/view/NewsComments.qml b/qml/QuickNewsReader/content/view/NewsComments.qml index 3da859c..8b136d6 100644 --- a/qml/QuickNewsReader/content/view/NewsComments.qml +++ b/qml/QuickNewsReader/content/view/NewsComments.qml @@ -23,8 +23,8 @@ Item { Column { id: column - x: 0; y: 0 - width: newsCommentsRect.width + x: 10; y: 10 + width: newsCommentsRect.width - 20 // height: newsCommentsRect.height Row { @@ -37,7 +37,7 @@ Item { } Text { - anchors.verticalCenter: detailImage.verticalCenter + anchors.verticalCenter: titleRow.verticalCenter text: title; width: column.width - detailImage.width - 10; wrapMode: Text.WordWrap font { bold: true; family: "Helvetica"; pointSize: 16 } } @@ -45,9 +45,9 @@ Item { WebView { id: newsCommentsWebView - width: newsCommentsRect.width + width: column.width url: commentURL - preferredWidth: window.width + preferredWidth: column.width // preferredHeight: parent.height - titleRow.height } } diff --git a/qml/QuickNewsReader/content/view/NewsDelegate.qml b/qml/QuickNewsReader/content/view/NewsDelegate.qml index d51f3ee..bc5c88b 100644 --- a/qml/QuickNewsReader/content/view/NewsDelegate.qml +++ b/qml/QuickNewsReader/content/view/NewsDelegate.qml @@ -59,6 +59,7 @@ Item { Row { + id: descriptionRow spacing: 5 Image { @@ -67,7 +68,7 @@ Item { } Text { - anchors.verticalCenter: detailImage.verticalCenter + anchors.verticalCenter: descriptionRow.verticalCenter text: description; width: column.width - detailImage.width - 10; wrapMode: Text.WordWrap font.family: "Helvetica" } @@ -78,9 +79,10 @@ Item { anchors.fill: delegate onClicked: { - if (typeof detailedContent != "undefined") { - var currentSourceDepth = delegate.ListView.view.model.sourceDepth + var currentSourceDepth = delegate.ListView.view.model.sourceDepth + if (listSourceModel[window.currentSourceIndex].listViews.length >= currentSourceDepth+1) + { // here we remove everything in viewsModel after index "nextSourceDepth" while(window.windowViewsModel.count>currentSourceDepth+1) window.windowViewsModel.remove(window.windowViewsModel.count-1) @@ -88,18 +90,10 @@ Item { delegate.ListView.view.currentIndex = index var path = listSourceModel[window.currentSourceIndex].currentPath path[currentSourceDepth] = index - listSourceModel[window.currentSourceIndex].currentPath = path + listSourceModel[window.currentSourceIndex].currentPath = path.slice(0,currentSourceDepth+1) window.windowViewsModel.append({ component: listSourceModel[window.currentSourceIndex].listViews[currentSourceDepth].viewComponent, - componentId: listSourceModel[window.currentSourceIndex].listViews[currentSourceDepth].viewId, componentDepth: currentSourceDepth+1 }) - - window.windowViewsList.currentIndex = currentSourceDepth+1; - - //newsDetailRect.webViewAction.enabled = false - //// sourcesRect.x -= window.width + newsRect.x - //newsDetailRect.webViewAction.enabled = true - //newsDetailRect.webViewAction.trigger() } } } diff --git a/qml/QuickNewsReader/content/view/NewsDetail.qml b/qml/QuickNewsReader/content/view/NewsDetail.qml index da55341..295b440 100644 --- a/qml/QuickNewsReader/content/view/NewsDetail.qml +++ b/qml/QuickNewsReader/content/view/NewsDetail.qml @@ -25,11 +25,11 @@ Item { Item { x: 0; y: 0 width: newsDetailRect.width - height: column.height + height: column.height + 10 Column { id: column - x: 0; y: 0 + x: 10; y: 10 width: parent.width - 20 Row { @@ -50,6 +50,7 @@ Item { Text { id: detailText text: htmlcontent; width: newsDetailRect.width - 20; wrapMode: Text.WordWrap + horizontalAlignment: Text.AlignJustify; font.family: "Helvetica" } } @@ -60,19 +61,19 @@ Item { onClicked: { var currentSourceDepth = currentSource.listModels[componentDepth-1].sourceDepth - // here we remove everything in viewsModel after index "currentSourceDepth" - while(window.windowViewsModel.count>currentSourceDepth+1) - window.windowViewsModel.remove(window.windowViewsModel.count-1) + if (listSourceModel[window.currentSourceIndex].listViews.length >= currentSourceDepth+1) + { + // here we remove everything in viewsModel after index "currentSourceDepth" + while(window.windowViewsModel.count>currentSourceDepth+1) + window.windowViewsModel.remove(window.windowViewsModel.count-1) - var path = listSourceModel[window.currentSourceIndex].currentPath - path[currentSourceDepth] = index - listSourceModel[window.currentSourceIndex].currentPath = path + var path = listSourceModel[window.currentSourceIndex].currentPath + path[currentSourceDepth] = index + listSourceModel[window.currentSourceIndex].currentPath = path.slice(0,currentSourceDepth+1) - window.windowViewsModel.append({ component: listSourceModel[window.currentSourceIndex].listViews[currentSourceDepth].viewComponent, - componentId: listSourceModel[window.currentSourceIndex].listViews[currentSourceDepth].viewId, - componentDepth: currentSourceDepth+1 }) - - window.windowViewsList.currentIndex = currentSourceDepth+1; + window.windowViewsModel.append({ component: listSourceModel[window.currentSourceIndex].listViews[currentSourceDepth].viewComponent, + componentDepth: currentSourceDepth+1 }) + } } } } diff --git a/qml/QuickNewsReader/content/view/SourceConfigDialog.qml b/qml/QuickNewsReader/content/view/SourceConfigDialog.qml index 72e53b3..973c906 100644 --- a/qml/QuickNewsReader/content/view/SourceConfigDialog.qml +++ b/qml/QuickNewsReader/content/view/SourceConfigDialog.qml @@ -8,7 +8,7 @@ Rectangle { visible: false state: "hidden" - property string configViewComponent + property SourceModel configModel; //property SourceModel model; //property SourceConfigComponentView viewComponent; @@ -18,9 +18,10 @@ Rectangle { name: "showSourceConfig" // In this state, we bring the configuration UI of the source - PropertyChanges { target: configDialog; color: "#d0000000" } + PropertyChanges { target: configDialog; color: "#80000000" } PropertyChanges { target: sourceConfigLoader; opacity: 1 } - PropertyChanges { target: sourceConfigLoader; source: configViewComponent } + PropertyChanges { target: sourceConfigLoader; source: configModel.settingsComponent } + PropertyChanges { target: configTitle; text: configModel.name + " Settings"} AnchorChanges { target: quitApplyConfigButton; anchors.left: undefined; anchors.right: configDialog.right } AnchorChanges { target: quitCancelConfigButton; anchors.right: undefined; anchors.left: configDialog.left } @@ -58,10 +59,28 @@ Rectangle { } ] + Background { + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: quitApplyConfigButton.top + } + + Text { + id: configTitle + color: "white" + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + horizontalAlignment: Text.AlignHCenter + font.pixelSize: 24 + font.bold: true + } + Loader { id: sourceConfigLoader opacity: 0 - anchors.top: parent.top + anchors.top: configTitle.bottom anchors.left: parent.left anchors.right: parent.right anchors.bottom: quitApplyConfigButton.top @@ -69,6 +88,11 @@ Rectangle { Behavior on opacity { NumberAnimation { duration: 1000; easing.type: Easing.InOutQuad } } + + onLoaded: { + // fill the UI with information from the model + configModel.loadConfiguration(sourceConfigLoader.item) + } } FancyButton { @@ -78,6 +102,9 @@ Rectangle { anchors.left: parent.right onClicked: { + // ask the model to store the configuration + configModel.storeConfiguration(sourceConfigLoader.item) + // Store the configuration of this source, and disappear configDialog.state = "hidden" } diff --git a/qml/QuickNewsReader/content/view/SourceDelegate.qml b/qml/QuickNewsReader/content/view/SourceDelegate.qml index 8824282..913f49d 100644 --- a/qml/QuickNewsReader/content/view/SourceDelegate.qml +++ b/qml/QuickNewsReader/content/view/SourceDelegate.qml @@ -78,7 +78,7 @@ Item { // show the configuration for this journal if( listSourceModel[index].hasSettings ) { - window.showConfigDialog(listSourceModel[index].settingsComponent) + window.showConfigDialog(index) } } @@ -91,15 +91,12 @@ Item { delegate.ListView.view.currentIndex = index + listSourceModel[index].currentPath = [index] + window.windowViewsModel.append({ component: listSourceModel[index].listViews[0].viewComponent, - componentId: listSourceModel[index].listViews[0].viewId, componentDepth: 1 }) - listSourceModel[index].currentPath = [index] - window.currentSourceIndex = index - - window.windowViewsList.currentIndex = 1; } } } diff --git a/qml/QuickNewsReader/content/view/Sources.qml b/qml/QuickNewsReader/content/view/Sources.qml index ab3e640..0838360 100644 --- a/qml/QuickNewsReader/content/view/Sources.qml +++ b/qml/QuickNewsReader/content/view/Sources.qml @@ -2,22 +2,24 @@ import QtQuick 1.0 Rectangle { id: sourcesRect - width: 150; height: window.height + width: window.width; height: window.height color: "#dfdfdf" + function populateSourcesModel() { + for(var i=0; typeof window.listSourceModel[i] !== "undefined"; i++ ) { + sourceList.append({}) + } + } + ListModel { id: sourceList - - ListElement { } - ListElement { } - ListElement { } } ListView { focus: true id: sources x: 0; y: 0 - width: 150; height: window.height + width: window.width; height: window.height currentIndex: currentSourceIndex model: sourceList footer: quitButtonDelegate @@ -27,5 +29,8 @@ Rectangle { } ScrollBar { scrollArea: sources; height: sources.height; width: 8; anchors.right: sources.right } - Component.onCompleted: sources.currentIndex = -1 + Component.onCompleted: { + sources.currentIndex = -1 + populateSourcesModel() + } } diff --git a/qml/QuickNewsReader/main.qml b/qml/QuickNewsReader/main.qml index 0c41d95..b5c3eaf 100644 --- a/qml/QuickNewsReader/main.qml +++ b/qml/QuickNewsReader/main.qml @@ -5,26 +5,29 @@ import "content/modelitf" Rectangle { id: window + //anchors.fill: parent // use this little trick to always adapt itself to the screen width: 800; height: 480 property int currentSourceIndex: 0 property list listSourceModel: [ LeMondeSourceModel{}, + GoogleReaderSourceModel{}, FavoriteFeedsSourceModel{}, YahooSourceModel{} ] + property variant currentSource: listSourceModel[currentSourceIndex] + property bool loading: currentSource.loading + property ListModel windowViewsModel: viewsModel + property ListView windowViewsList: viewsList + ListModel { id: viewsModel - ListElement { component: "content/view/Sources.qml"; componentId: "sourcesRect"; componentDepth: 0 } + ListElement { component: "content/view/Sources.qml"; componentDepth: 0 } } - property variant currentSource: listSourceModel[currentSourceIndex] - property bool loading: currentSource.loading - property ListModel windowViewsModel: viewsModel - property ListView windowViewsList: viewsList ListView { id: viewsList @@ -33,16 +36,25 @@ Rectangle { orientation: ListView.Horizontal snapMode: ListView.SnapOneItem flickDeceleration: 500 + cacheBuffer: 1600 // so that the next delegate gets actually loaded... + preferredHighlightBegin: window.x + preferredHighlightEnd: window.width + highlightRangeMode: ListView.StrictlyEnforceRange + boundsBehavior: Flickable.StopAtBounds model: viewsModel delegate: Loader { - id: componentId + id: modelLoader source: component + + ListView.onAdd: { + viewsList.currentIndex = componentDepth + } } } - function showConfigDialog(settingsComponent) { - configDialog.configViewComponent = settingsComponent + function showConfigDialog(index) { + configDialog.configModel = listSourceModel[index] configDialog.state = "showSourceConfig" } diff --git a/qtc_packaging/debian_fremantle/changelog b/qtc_packaging/debian_fremantle/changelog index e64bd84..a497c05 100644 --- a/qtc_packaging/debian_fremantle/changelog +++ b/qtc_packaging/debian_fremantle/changelog @@ -1,3 +1,13 @@ +quicknewsreader (0.4-0) unstable; urgency=low + + * Switched to a ListView page swapper + * More robust model that can be easily adapted for various sources + * Added comments for LeMonde.fr website + * Added Configuration dialog with long-press on a configurable source (like Google Reader) + * Added Google Reader (read-only) + + -- Christophe CHAPUIS Sat, 18 Feb 2012 22:01:32 +0100 + quicknewsreader (0.3-0) unstable; urgency=low * Initial Release. -- 1.7.9.5