Add Ubuntu Touch UI

This commit is contained in:
Alberto Mardegan 2014-10-22 23:29:26 -04:00
parent 935c136816
commit 8f95fc7566
14 changed files with 1500 additions and 0 deletions

View file

@ -118,6 +118,8 @@ Q_DECL_EXPORT int main(int argc, char *argv[])
#if defined(Q_OS_SAILFISH)
viewer->setSource(SailfishApp::pathTo("qml/sailfish/harbour-ttrss.qml"));
#elif defined(Q_OS_UBUNTU_TOUCH)
viewer->setSource(QStringLiteral("qml/ttrss/ubuntu-touch/main.qml"));
#else
viewer->setMainQmlFile(QLatin1String("qml/harmattan/main.qml"));
#endif

View file

@ -0,0 +1,86 @@
//Copyright Hauke Schade, 2012-2014
//
//This file is part of TTRss.
//
//TTRss is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the
//Free Software Foundation, either version 2 of the License, or (at your option) any later version.
//TTRss is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
//You should have received a copy of the GNU General Public License along with TTRss (on a Maemo/Meego system there is a copy
//in /usr/share/common-licenses. If not, see http://www.gnu.org/licenses/.
import QtQuick 2.0
import Ubuntu.Components 1.1
Page {
id: categoriesPage
title: qsTr("Tiny Tiny RSS Reader")
UbuntuListView {
id: listView
anchors.fill: parent
model: categories
/* TODO
PullDownMenu {
//AboutItem {}
SettingsItem {}
MenuItem {
text: qsTr("Logout")
visible: pageStack.depth == 1
onClicked: {
pageStack.replace(Qt.resolvedUrl("MainPage.qml"), { doAutoLogin: false })
}
}
MenuItem {
text: qsTr("Update")
enabled: !network.loading
onClicked: {
categories.update()
}
}
ToggleShowAllItem {
onUpdateView: {
categories.update()
}
}
}
*/
pullToRefresh {
enabled: true
onRefresh: categories.update()
refreshing: network.loading
}
delegate: CategoryDelegate {
onClicked: {
categories.selectedIndex = index
showCategory(categories.getSelectedItem())
}
}
Label {
visible: listView.count == 0
text: network.loading ?
qsTr("Loading") :
rootWindow.showAll ? qsTr("No categories to display") : qsTr("No categories have unread items")
}
ActivityIndicator {
running: listView.count != 0 && network.loading
anchors.centerIn: parent
}
Scrollbar {
flickableItem: listView
}
}
function showCategory(categoryModel) {
if (categoryModel != null) {
pageStack.push(Qt.resolvedUrl("Feeds.qml"), {
category: categoryModel
})
}
}
}

View file

@ -0,0 +1,21 @@
//Copyright Hauke Schade, 2012-2014
//
//This file is part of TTRss.
//
//TTRss is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the
//Free Software Foundation, either version 2 of the License, or (at your option) any later version.
//TTRss is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
//You should have received a copy of the GNU General Public License along with TTRss (on a Maemo/Meego system there is a copy
//in /usr/share/common-licenses. If not, see http://www.gnu.org/licenses/.
import QtQuick 2.0
import Ubuntu.Components 0.1
import Ubuntu.Components.ListItems 0.1 as ListItem
ListItem.SingleValue {
id: listItem
text: model.title
value: model.unreadcount
}

View file

@ -0,0 +1,36 @@
//Copyright Hauke Schade, 2012-2014
//
//This file is part of TTRss.
//
//TTRss is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the
//Free Software Foundation, either version 2 of the License, or (at your option) any later version.
//TTRss is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
//You should have received a copy of the GNU General Public License along with TTRss (on a Maemo/Meego system there is a copy
//in /usr/share/common-licenses. If not, see http://www.gnu.org/licenses/.
import QtQuick 2.0
import Ubuntu.Components 0.1
import Ubuntu.Components.ListItems 0.1 as ListItem
ListItem.Empty {
id: listItem
property int value
SubtitledLabel {
anchors.leftMargin: units.gu(1)
anchors.right: countLabel.left
iconSource: model.icon
bold: model.unreadcount > 0
text: model.title
}
Label {
id: countLabel
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
text: model.unreadcount
anchors.rightMargin: units.gu(1)
}
}

View file

@ -0,0 +1,253 @@
//Copyright Hauke Schade, 2012-2014
//
//This file is part of TTRss.
//
//TTRss is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the
//Free Software Foundation, either version 2 of the License, or (at your option) any later version.
//TTRss is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
//You should have received a copy of the GNU General Public License along with TTRss (on a Maemo/Meego system there is a copy
//in /usr/share/common-licenses. If not, see http://www.gnu.org/licenses/.
import QtQuick 2.0
import Ubuntu.Components 1.1
Item {
id: root
property string title: ""
property string url: ""
property string date: ""
property bool marked: false
property bool unread: true
property bool rss: false
property bool isCat: false
property variant labels
property var attachments
property string content: ""
property Item flickable: flick
Flickable {
id: flick
anchors.fill: parent
contentHeight: content.height
interactive: true
clip: true
/* TODO
PullDownMenu {
// AboutItem {}
// SettingsItem {}
MenuItem {
text: qsTr("Open in Web Browser")
enabled: url && (url != "")
onClicked: Qt.openUrlExternally(url)
}
MenuItem {
text: panel.open ? qsTr("Hide Dock") : qsTr("Open Dock")
enabled: !panel.moving
onClicked: panel.open ? panel.hide() : panel.show()
}
}
*/
// PushUpMenu {
// MenuItem {
// text: qsTr("Scroll to top")
// onClicked: flick.scrollToTop()
// visible: flick.contentHeight >= flick.height
// }
// MenuItem {
// text: qsTr("Open Dock")
// visible: !panel.open
// onClicked: panel.show()
// }
// }
Column {
id: content
width: parent.width
spacing: 2
Label {
text: title
fontSize: "large"
font.bold: true
wrapMode: Text.Wrap
anchors {
left: parent.left
right: parent.right
}
}
Label {
text: date
fontSize: "small"
textFormat: Text.PlainText
anchors {
right: parent.right
rightMargin: Theme.paddingLarge
}
color: Theme.secondaryColor
}
RescalingRichText {
id: itemView
width: parent.width
text: parseContent(root.content, root.attachments)
fontSize: Theme.fontSizeSmall
color: Theme.primaryColor
onLinkActivated: Qt.openUrlExternally(link)
}
}
}
Scrollbar {
flickableItem: flick
}
ActivityIndicator {
running: network.loading
anchors.centerIn: parent
}
/* TODO
DockedPanel {
id: panel
width: parent.width
height: Theme.itemSizeMedium
open: true
dock: Dock.Bottom
Row {
anchors.centerIn: parent
IconButton {
icon.source: "image://theme/icon-m-previous"
enabled: previousId !== false
onClicked: {
feedItems.selectPrevious()
pageStack.replace("FeedItem.qml", { isCat: root.isCat })
//showFeedItem()
}
}
IconButton {
id: rssSwitch
icon.source: "../../resources/ic_rss_"+(rss?"enabled":"disabled")+".png"
//checked: rss
onClicked: {
feedItems.togglePublished()
rss = !rss
}
}
IconButton {
id: markedSwitch
icon.source: "../../resources/ic_star_"+(marked?"enabled":"disabled")+".png"
//checked: marked
onClicked: {
feedItems.toggleStar()
marked = !marked
}
}
IconButton {
id: unreadSwitch
icon.source: "../../resources/ic_"+(unread?"unread":"read")+".png"
//checked: unread
onClicked: {
feedItems.toggleRead()
unread = !unread
}
}
IconButton {
icon.source: "image://theme/icon-m-next"
enabled: nextId !== false
onClicked: {
feedItems.selectNext()
pageStack.replace("FeedItem.qml", { isCat: root.isCat })
//showFeedItem()
}
}
}
}
*/
function computeAttachmentsCode(attachments) {
if (attachments.count === 0) return ""
var attachmentsCode = ""
for (var i = 0; i < attachments.count; i++) {
var a = attachments.get(i)
var url = a.content_url
var isImage = (a.content_type.indexOf("image") === 0 ||
/jpe?g$/i.test(url) ||
/png$/i.test(url))
console.log("URL: " + url + " isImage: " + isImage)
var attachmentLabel = ""
if (isImage) {
attachmentLabel = "<img src=\"" + url + "\" style=\"max-width: 100%; height: auto\"/>"
} else {
attachmentLabel = a.title ? a.title : url.replace(/^.*[\/]/g, '')
}
attachmentsCode += "<a href=\"" + url + "\">" + attachmentLabel + "</a>"
}
return attachmentsCode
}
function parseContent(rawContent, attachments) {
var attachmentsCode = computeAttachmentsCode(attachments)
var content = rawContent.replace('target="_blank"', '')
if (!settings.displayImages) {
// remove images
var image_regex = /<img\s[^>]*>/gi;
content = content.replace(image_regex, "")
} else if (settings.stripInvisibleImg) {
// remove images with a height or width of 0 or 1
var height_regex = /<img\s[^>]*height="[01]"[^>]*>/gi;
content = content.replace(height_regex, "")
var width_regex = /<img\s[^>]*width="[01]"[^>]*>/gi;
content = content.replace(width_regex, "")
}
if (!content.match(/<body>/gi)) {
// not yet html, detect urls
console.log('doing link detection on ' + content)
var regex = /(([a-z]+:\/\/)?(([a-z0-9\-]+\.)+([a-z]{2}|aero|arpa|biz|com|coop|edu|gov|info|int|jobs|mil|museum|name|nato|net|org|pro|travel|local|internal))(:[0-9]{1,5})?(\/[a-z0-9_\-\.~]+)*(\/([a-z0-9_\-\.]*)(\?[a-z0-9+_\-\.%=&amp;]*)?)?(#[a-zA-Z0-9!$&'()*+.=-_~:@/?]*)?)(\s+|$)/gi;
content = content.replace(regex, "<a href='$1'>$1</a> ")
if (attachmentsCode) {
content += attachmentsCode
}
} else {
if (attachmentsCode) {
var regex =/(<\/body>)/gi
content = content.replace(regex, attachmentsCode + "$1")
}
}
return content
}
Binding {
target: itemView
property: "fontSize"
value: settings.webviewFontSize
}
Component.onCompleted: {
itemView.fontSize = settings.webviewFontSize
}
// MenuItem {
// text: qsTr("Share")
// enabled: url && (url != "")
// onClicked: QMLUtils.share(url, pageTitle);
// }
// SettingsItem {}
// AboutItem {}
}

View file

@ -0,0 +1,86 @@
//Copyright Hauke Schade, 2012-2014
//
//This file is part of TTRss.
//
//TTRss is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the
//Free Software Foundation, either version 2 of the License, or (at your option) any later version.
//TTRss is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
//You should have received a copy of the GNU General Public License along with TTRss (on a Maemo/Meego system there is a copy
//in /usr/share/common-licenses. If not, see http://www.gnu.org/licenses/.
import QtQuick 2.0
import Ubuntu.Components 0.1
import Ubuntu.Components.ListItems 0.1 as ListItem
ListItem.Empty {
id: listItem
onClicked: root.clicked()
SubtitledLabel {
text: model.title
subText: model.subtitle
iconSource: (settings.displayIcons && feed.isCat) ? model.icon : ''
bold: model.unread
}
/*
Row {
spacing: Theme.paddingMedium
anchors.fill: parent
anchors.leftMargin: (icon.visible ? icon.width : 0) + Theme.paddingMedium
Image {
source: "../../resources/ic_star_enabled.png"
visible: model.marked
anchors.verticalCenter: parent.verticalCenter
opacity: 0.5
}
Image {
source: "../../resources/ic_rss_enabled.png"
visible: model.rss
anchors.verticalCenter: parent.verticalCenter
opacity: 0.5
}
}
*/
/* TODO
Component {
id: contextMenu
ContextMenu {
MenuItem {
id: toggleStarMenuItem
text: model.marked ? qsTr("Unstar") : qsTr("Star")
onClicked: {
feedItems.toggleStar()
} }
MenuItem {
id: togglePublishedMenuItem
text: model.rss ? qsTr("Unpublish") : qsTr("Publish")
onClicked: {
feedItems.togglePublished()
} }
MenuItem {
id: toggleReadMenuItem
text: model.unread ? qsTr("Mark read") : qsTr("Mark Unread")
onClicked: {
feedItems.toggleRead()
} }
MenuItem {
id: openInBrowserMenuItem
text: qsTr("Open in Web Browser")
visible: model.url && model.url != ""
onClicked: {
var item = feedItems.getSelectedItem()
Qt.openUrlExternally(item.url)
}
}
Component.onCompleted: {
feedItems.selectedIndex = index
}
}
}
*/
}

View file

@ -0,0 +1,79 @@
//Copyright Hauke Schade, 2012-2014
//
//This file is part of TTRss.
//
//TTRss is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the
//Free Software Foundation, either version 2 of the License, or (at your option) any later version.
//TTRss is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
//You should have received a copy of the GNU General Public License along with TTRss (on a Maemo/Meego system there is a copy
//in /usr/share/common-licenses. If not, see http://www.gnu.org/licenses/.
import QtQuick 2.0
import Ubuntu.Components 1.1
Page {
id: root
property bool isCat: false
property var model
property int currentIndex: -1
property alias currentItem: listView.currentItem
anchors.fill: parent
title: currentItem ? currentItem.title : ""
flickable: currentItem ? currentItem.flickable : null
head.actions: [
Action {
iconSource: "../resources/ic_star_"+(currentItem.marked?"enabled":"disabled")+".png"
onTriggered: {
feedItems.toggleStar()
}
},
Action {
iconSource: "../resources/ic_"+(currentItem.unread?"unread":"read")+".png"
onTriggered: {
feedItems.toggleRead()
}
}
]
ListView {
id: listView
anchors.fill: parent
orientation: ListView.Horizontal
snapMode: ListView.SnapOneItem
highlightFollowsCurrentItem: true
highlightRangeMode: ListView.StrictlyEnforceRange
cacheBuffer: 0
delegate: FeedItem {
title: model.title
url: model.url
date: model.date
labels: model.labels
marked: model.marked
unread: model.unread
rss: model.rss
attachments: model.attachments
content: model.content
width: listView.width
height: listView.height
}
onCurrentIndexChanged: {
feedItems.selectedIndex = currentIndex
if (currentItem && settings.autoMarkRead && currentItem.unread) {
console.log("marking item as read")
feedItems.toggleRead()
}
}
}
Component.onCompleted: {
listView.model = root.model
listView.currentIndex = root.currentIndex
}
}

View file

@ -0,0 +1,144 @@
//Copyright Hauke Schade, 2012-2014
//
//This file is part of TTRss.
//
//TTRss is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the
//Free Software Foundation, either version 2 of the License, or (at your option) any later version.
//TTRss is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
//You should have received a copy of the GNU General Public License along with TTRss (on a Maemo/Meego system there is a copy
//in /usr/share/common-licenses. If not, see http://www.gnu.org/licenses/.
import QtQuick 2.0
import Ubuntu.Components 1.1
import Ubuntu.Components.ListItems 1.0 as ListItem
Page {
id: feeditemsPage
property variant feed
title: feed.title
Component.onCompleted: {
feedItems.feed = feeditemsPage.feed
feedItems.hasMoreItems = false
feedItems.continuation = 0
feedItems.clear()
var ttrss = rootWindow.getTTRSS()
ttrss.setShowAll(settings.showAll)
feedItems.update()
// FIXME workaround for https://bugs.launchpad.net/bugs/1404884
pullToRefresh.enabled = true
}
head {
sections {
model: [ qsTr("Unread"), qsTr("All") ]
selectedIndex: settings.showAll ? 1 : 0
onSelectedIndexChanged: {
var ttrss = rootWindow.getTTRSS()
var showAll = (feeditemsPage.head.sections.selectedIndex == 1)
if (showAll != settings.showAll) {
ttrss.setShowAll(showAll)
settings.showAll = showAll
feedItems.continuation = 0
feedItems.hasMoreItems = false
feedItems.clear()
feedItems.update()
}
}
}
}
ListView {
id: listView
anchors.fill: parent
model: feedItems
PullToRefresh {
id: pullToRefresh
enabled: false
onRefresh: feedItems.update()
refreshing: network.loading
}
/* TODO
PullDownMenu {
// AboutItem {}
// SettingsItem {}
MenuItem {
text: qsTr("Update")
enabled: !network.loading
onClicked: {
feedItems.continuation = 0
feedItems.hasMoreItems = false
feedItems.clear()
feedItems.update()
}
}
ToggleShowAllItem {
onUpdateView: {
feedItems.continuation = 0
feedItems.hasMoreItems = false
feedItems.clear()
feedItems.update()
}
}
MenuItem {
text: qsTr('Mark all read')
onClicked: {
feedItems.catchUp()
}
}
}
*/
section {
property: 'date'
delegate: ListItem.Caption {
text: section
}
}
delegate: FeedItemDelegate {
onClicked: {
feedItems.selectedIndex = index
pageStack.push(Qt.resolvedUrl("FeedItemSwipe.qml"), {
model: feedItems,
currentIndex: index,
isCat: feed.isCat
})
}
}
Label {
visible: listView.count == 0
text: network.loading ?
qsTr("Loading") :
rootWindow.showAll ? qsTr("No items in feed") : qsTr("No unread items in feed")
}
ActivityIndicator {
running: network.loading
anchors.centerIn: parent
onRunningChanged: {
/* We want to show this activity indicator just for the first
* time, as a workaround for
* https://bugs.launchpad.net/bugs/1404884. So, once the
* initial loading has completed, we disable this item */
if (!running) visible = false
}
}
Scrollbar {
flickableItem: listView
}
}
function showFeed(feedModel) {
if (feedModel != null) {
pageStack.push(Qt.resolvedUrl("FeedItems.qml"), {
feed: feedModel
})
}
}
}

View file

@ -0,0 +1,149 @@
//Copyright Hauke Schade, 2012-2014
//
//This file is part of TTRss.
//
//TTRss is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the
//Free Software Foundation, either version 2 of the License, or (at your option) any later version.
//TTRss is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
//You should have received a copy of the GNU General Public License along with TTRss (on a Maemo/Meego system there is a copy
//in /usr/share/common-licenses. If not, see http://www.gnu.org/licenses/.
import QtQuick 2.0
import Ubuntu.Components 1.1
Page {
id: feedsPage
property variant category
title: category.title
Component.onCompleted: {
feeds.category = feedsPage.category
feeds.clear()
var ttrss = rootWindow.getTTRSS()
ttrss.setShowAll(settings.showAll)
feeds.update()
}
head {
sections {
model: [ qsTr("Unread"), qsTr("All") ]
selectedIndex: settings.showAll ? 1 : 0
onSelectedIndexChanged: {
var ttrss = rootWindow.getTTRSS()
var showAll = (feedsPage.head.sections.selectedIndex == 1)
if (showAll != settings.showAll) {
ttrss.setShowAll(showAll)
settings.showAll = showAll
feeds.update()
}
}
}
}
ListView {
id: listView
anchors.fill: parent
model: feeds
PullToRefresh {
enabled: true
onRefresh: feeds.update()
refreshing: network.loading
}
/* TODO
PullDownMenu {
//AboutItem {}
SettingsItem {}
MenuItem {
text: qsTr("Add subscription")
onClicked: {
var dialog = pageStack.push(Qt.resolvedUrl("AddSubscription.qml"), {
categoryId: feedsPage.category.categoryId
})
dialog.accepted.connect(function(){
var ttrss = rootWindow.getTTRSS()
ttrss.subscribe(dialog.selectedId,
dialog.src,
function(result) {
switch (result) {
case 0:
notification.show(qsTr('Already subscribed to Feed'))
break
case 1:
//notification.show(qsTr('Feed added'))
feeds.update()
categories.update()
break
case 2:
notification.show(qsTr('Invalid URL'))
break
case 3:
notification.show(qsTr('URL content is HTML, no feeds available'))
break
case 4:
notification.show(qsTr('URL content is HTML which contains multiple feeds'))
break
case 5:
notification.show(qsTr('Couldn\'t download the URL content'))
break
case 5:
notification.show(qsTr('Content is an invalid XML'))
break
default:
notification.show(qsTr('An error occured while subscribing to the feed'))
}
})
})
}
}
MenuItem {
text: qsTr("Logout")
visible: pageStack.depth == 1
onClicked: {
pageStack.replace(Qt.resolvedUrl("MainPage.qml"), { doAutoLogin: false })
}
}
MenuItem {
text: qsTr("Update")
enabled: !network.loading
onClicked: {
feeds.update()
}
}
ToggleShowAllItem {
onUpdateView: {
feeds.update()
}
}
}
*/
delegate: FeedDelegate {
onClicked: {
feeds.selectedIndex = index
showFeed(model)
}
}
Label {
visible: listView.count == 0
text: network.loading ?
qsTr("Loading") :
rootWindow.showAll ? qsTr("No feeds in category") : qsTr("Category has no unread items")
}
Scrollbar {
flickableItem: listView
}
}
function showFeed(feedModel) {
if (feedModel != null) {
pageStack.push(Qt.resolvedUrl("FeedItems.qml"), {
feed: feedModel
})
}
}
}

View file

@ -0,0 +1,247 @@
import QtQuick 2.0
import Ubuntu.Components 0.1
import Ubuntu.Keyboard 0.1
Item {
id: root
anchors.fill: parent
anchors.margins: units.gu(1)
property bool doAutoLogin: true
Flickable {
contentHeight: contentcontainer.height
contentWidth: parent.width
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: osk.top
/* TODO
PullDownMenu {
AboutItem {}
SettingsItem {}
MenuItem {
text: qsTr("No Account Yet?")
onClicked: {
Qt.openUrlExternally("http://tt-rss.org/redmine/projects/tt-rss/wiki");
}
}
}
*/
Column {
id: contentcontainer
width: parent.width
spacing: units.gu(1)
Image {
width: 256
height: 256
anchors.horizontalCenter: parent.horizontalCenter
source: "../resources/ttrss256.png"
anchors.bottomMargin: Theme.paddingLarge
}
Column {
width: parent.width
Label {
id: serverLabel
text: qsTr("Server:")
width: parent.width
font.pixelSize: Theme.fontSizeMedium
}
TextField {
id: server
text: ""
width: parent.width
enabled: !network.loading
inputMethodHints: Qt.ImhUrlCharactersOnly + Qt.ImhNoPredictiveText
KeyNavigation.tab: username
Keys.onReturnPressed: username.forceActiveFocus()
InputMethod.extensions: { "enterKeyText": qsTr("Next") }
}
}
Column {
width: parent.width
Label {
id: usernameLabel
text: qsTr("Username:")
width: parent.width
font.pixelSize: Theme.fontSizeMedium
}
TextField {
id: username
text: ""
width: parent.width
enabled: !network.loading
inputMethodHints: Qt.ImhNoPredictiveText + Qt.ImhNoAutoUppercase
KeyNavigation.tab: password
KeyNavigation.backtab: server
Keys.onReturnPressed: password.forceActiveFocus()
InputMethod.extensions: { "enterKeyText": qsTr("Next") }
}
}
Column {
width: parent.width
Label {
id: passwordLabel
text: qsTr("Password:")
width: parent.width
font.pixelSize: Theme.fontSizeMedium
}
TextField {
id: password
echoMode: TextInput.Password
width: parent.width
enabled: !network.loading
KeyNavigation.backtab: username
Keys.onReturnPressed: root.submit()
InputMethod.extensions: { "enterKeyText": qsTr("Login") }
}
}
CheckBox {
text: qsTr('Ignore SSL Errors')
visible: server.text.substring(0, 5) === "https"
checked: settings.ignoreSSLErrors
onCheckedChanged: settings.ignoreSSLErrors = checked
}
Item {
width: parent.width
height: clearButton.height
Button {
id: clearButton
text: qsTr("Clear")
width: Math.floor(parent.width / 2) - units.gu(1)
onClicked: {
server.text = ''
username.text = ''
password.text = ''
settings.httpauthusername = ''
settings.httpauthpassword = ''
settings.servername = server.text
settings.username = username.text
settings.password = password.text
}
enabled: !network.loading
}
Button {
id: loginButton
anchors.right: parent.right
text: qsTr("Login")
width: Math.floor(parent.width / 2) - units.gu(1)
onClicked: root.submit()
enabled: !network.loading
}
}
}
}
ActivityIndicator {
id: busyindicator1
running: network.loading
anchors {
horizontalCenter: parent.horizontalCenter
verticalCenter: parent.verticalCenter
}
}
Item {
id: osk
height: Qt.inputMethod.visible ? Qt.inputMethod.keyboardRectangle.height : 0
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
}
function enableLoginBox(focus) {
if(focus) {
password.forceActiveFocus();
}
}
function submit() {
// check the servername for httpauth data and set/extract those
var httpauthregex = /(https?:\/\/)?(\w+):(\w+)@(\w.+)/
var servername = server.text
var regexres = servername.match(httpauthregex)
if (regexres !== null) {
server.text = (regexres[1]?regexres[1]:'') + regexres[4]
settings.httpauthusername = regexres[2]
settings.httpauthpassword = regexres[3]
}
settings.servername = server.text
settings.username = username.text
settings.password = password.text
startLogin();
}
function startLogin() {
var ttrss = rootWindow.getTTRSS();
ttrss.clearState();
ttrss.setLoginDetails(username.text, password.text, server.text);
// BUGFIX since somehow the silica QML Image can not display images coming from a secure line
if (settings.ignoreSSLErrors && server.text.substring(0, 5) === "https")
ttrss.setProxy("http://proxy.cnlpete.de/proxy.php?url=")
if (settings.httpauthusername != '' && settings.httpauthpassword != '') {
ttrss.setHttpAuthInfo(settings.httpauthusername, settings.httpauthpassword);
console.log('doing http basic auth with username ' + settings.httpauthusername)
}
ttrss.login(loginSuccessfull);
}
function loginSuccessfull(retcode, text) {
if(retcode) {
//login failed....don't autlogin
settings.autologin = false
//Let the user know
// loginErrorDialog.text = text;
// loginErrorDialog.open();
}
else {
//Login succeeded, auto login next Time
settings.autologin = true
rootWindow.getTTRSS().updateConfig(configSuccessfull);
}
}
function configSuccessfull(retcode, text) {
if(retcode) {
//Let the user know
// loginErrorDialog.text = text;
// loginErrorDialog.open();
}
else {
categories.update()
pageStack.clear()
//Now show the categories View
if (settings.useAllFeedsOnStartup) {
var ttrss = rootWindow.getTTRSS()
pageStack.push("Feeds.qml", {
category: {
categoryId: ttrss.constants['categories']['ALL'],
title: constant.allFeeds,
unreadcount: 0
}
})
}
else
pageStack.push(Qt.resolvedUrl('Categories.qml'))
}
}
Component.onCompleted: {
server.text = settings.servername
username.text = settings.username
password.text = settings.password
if (settings.autologin && settings.useAutologin && doAutoLogin)
startLogin();
}
}

View file

@ -0,0 +1,101 @@
/* Copyright (C) 2013 Martin Grimme <martin.grimme _AT_ gmail.com>
*
* This file was apapted from Tidings
* Copyright (C) 2013 Martin Grimme <martin.grimme _AT_ gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
import QtQuick 2.0
/* Pretty fancy element for displaying rich text fitting the width.
*
* Images are scaled down to fit the width, or, technically speaking, the
* rich text content is actually scaled down so the images fit, while the
* font size is scaled up to keep the original font size.
*/
Item {
id: root
property string text
property alias color: contentText.color
property real fontSize: Theme.fontSizeSmall
property string _RICHTEXT_STYLESHEET_PREAMBLE: "<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"><style>a { text-decoration: none; color: '" + Theme.highlightColor + "' }</style></head><body>";
property string _RICHTEXT_STYLESHEET_APPENDIX: "</body></html>";
property real scaling: 1
signal linkActivated(string link)
height: contentText.height * scaling
clip: true
onWidthChanged: {
rescaleTimer.restart()
}
Text {
id: layoutText
visible: false
width: parent.width
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
textFormat: Text.RichText
text: "<style>* { font-size: 1px }</style>" + parent.text
onContentWidthChanged: {
console.log("contentWidth: " + contentWidth)
rescaleTimer.restart()
}
}
Text {
id: contentText
width: Math.max(1, parent.width) / scaling
scale: scaling
transformOrigin: Item.TopLeft
font.pixelSize: parent.fontSize / scaling
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
textFormat: Text.RichText
smooth: true
// text: _RICHTEXT_STYLESHEET_PREAMBLE + parent.text + _RICHTEXT_STYLESHEET_APPENDIX
onLinkActivated: {
root.linkActivated(link)
}
}
Timer {
id: rescaleTimer
interval: 100
onTriggered: {
var contentWidth = Math.floor(layoutText.contentWidth + 0.0)
scaling = Math.min(1, parent.width / contentWidth)
console.log("scaling: " + scaling)
// force reflow
// contentText.text = contentText.text + " "
contentText.text = _RICHTEXT_STYLESHEET_PREAMBLE + parent.text + _RICHTEXT_STYLESHEET_APPENDIX
}
}
}

View file

@ -0,0 +1,75 @@
//Copyright Hauke Schade, 2012-2014
//
//This file is part of TTRss.
//
//TTRss is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the
//Free Software Foundation, either version 2 of the License, or (at your option) any later version.
//TTRss is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
//You should have received a copy of the GNU General Public License along with TTRss (on a Maemo/Meego system there is a copy
//in /usr/share/common-licenses. If not, see http://www.gnu.org/licenses/.
import QtQuick 2.0
import Ubuntu.Components 0.1
Item {
id: root
property alias text: titleLabel.text
property alias subText: subLabel.text
property alias iconSource: icon.source
property bool bold: false
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
UbuntuShape {
id: iconShape
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
width: visible ? roundBaseTwo(parent.height) : 0
height: width
visible: icon.source.toString()
image: Image {
id:icon
}
function roundBaseTwo(n) {
var i = 2
while (i * 2 <= n) i *= 2
return i
}
}
states: State {
name: "noSub"
when: !subLabel.text
AnchorChanges {
target: titleLabel
anchors.top: undefined
anchors.verticalCenter: parent.verticalCenter
}
}
Label {
id: titleLabel
anchors.left: iconShape.right
anchors.right: parent.right
anchors.top: parent.top
font.bold: root.bold
elide: Text.ElideRight
}
Label {
id: subLabel
anchors.left: iconShape.right
anchors.right: parent.right
anchors.top: titleLabel.bottom
fontSize: "x-small"
elide: Text.ElideRight
wrapMode: Text.Wrap
maximumLineCount: 2
}
}

View file

@ -0,0 +1,136 @@
import QtQuick 2.0
import Ubuntu.Components 0.1
import "../models/tinytinyrss.js" as TTRss
import "../models" 1.0
/*!
brief MainView with a Label and Button elements.
*/
MainView {
id: rootWindow
applicationName: "ttrss"
width: units.gu(45)
height: units.gu(75)
useDeprecatedToolbar: false
PageStack {
id: pageStack
Component.onCompleted: push(mainPage)
MainPage {
id: mainPage
}
}
function getTTRSS() {
return TTRss;
}
property bool showAll: false
Constants{ id: constant }
CategoryModel {
id: categories
}
FeedModel {
id: feeds
onFeedUnreadChanged: {
var op = function(x) { return x - oldAmount + feed.unreadcount }
categories.updateUnreadCountForId(feed.categoryId, op)
//console.log("updating category with id: " + feed.categoryId + " op is " + op(0))
// update the 'All Feeds' Category
categories.updateUnreadCountForId(TTRss.constants['categories']['ALL'], op)
//console.log("updating special cat with id: " + TTRss.constants['categories']['ALL'] + " op is " + op(0))
// if there is an 'all feed items' update that aswell
if (feeds.count > 1) {
var m = feeds.get(0)
if (m.isCat) { // just check to be sure
if (feed.isCat && m.feedId == feed.feedId && feed.unreadcount == 0) {
// we can not determine where to substract, but when all is 0, we can update accordingly
for (var i = 1; i < feeds.count; i++) {
feeds.setProperty(i, "unreadcount", 0)
}
}
else {
feeds.setProperty(0, "unreadcount", op(m.unreadcount))
}
}
}
}
}
FeedItemModel {
id: feedItems
onItemUnreadChanged: {
var op = item.unread ?
function(x) { return x + 1 } :
function(x) { return x - 1 }
// update the feed's category
feeds.updateUnreadCountForId(item.feedId, op)
//console.log("updating feed with id: " + item.feedId + " op is " + op(0))
// update special for all feeditems category
categories.updateUnreadCountForId(
TTRss.constants['categories']['SPECIAL'],
op)
//console.log("updating special cat with id: " + TTRss.constants['categories']['SPECIAL'] + " op is " + op(0))
// if the item is new, update 'special feeds' for 'fresh articles'
// TODO
if (item.unread && false)
categories.updateUnreadCountForId(
TTRss.constants['categories']['SPECIAL'],
op)
// if item was is starred/published, update special feeds aswell
if (item.rss)
categories.updateUnreadCountForId(
TTRss.constants['categories']['SPECIAL'],
op)
if (item.marked)
categories.updateUnreadCountForId(
TTRss.constants['categories']['SPECIAL'],
op)
// maybe check if currently viewing special feeds and update published
// not nesseccary because this is updated by mark unread
}
onItemPublishedChanged: {
var op = item.rss ?
function(x) { return x + 1 } :
function(x) { return x - 1 }
// if the item is unread, update 'special feeds'
if (item.unread)
categories.updateUnreadCountForId(
TTRss.constants['categories']['SPECIAL'],
op)
// maybe check if currently viewing special feeds and update published
// not nesseccary because this is updated by mark unread
}
onItemStarChanged: {
var op = item.marked ?
function(x) { return x + 1 } :
function(x) { return x - 1 }
// if the item is unread, update 'special feeds'
if (item.unread)
categories.updateUnreadCountForId(
TTRss.constants['categories']['SPECIAL'],
op)
// maybe check if currently viewing special feeds and update starred
// not nesseccary because this is updated by mark unread
}
}
}

85
ttrss-ubuntu.pro Normal file
View file

@ -0,0 +1,85 @@
VERSION = 0.4.3
DEFINES += APP_VERSION=\\\"$$VERSION\\\"
DEFINES += Q_OS_UBUNTU_TOUCH
TARGET = ttrss-ubuntu-touch
DEFINES += TARGET=\\\"it.mardy.ttrss\\\"
QT += quick qml
target.path = /usr/bin
INSTALLS += target
CONFIG += link_pkgconfig
OTHER_FILES += \
$$files(qml/ttrss/ubuntu-touch/*.qml)
qml_2.files = qml/ttrss/models
qml_2.path = $$INSTALL_ROOT/usr/share/$$TARGET/qml
qml_3.files = qml/ttrss/resources
qml_3.path = $$INSTALL_ROOT/usr/share/$$TARGET/qml
INSTALLS += qml_2 qml_3
icon.files = images/$${TARGET}.png
icon.path = /usr/share/icons/hicolor/86x86/apps
INSTALLS += icon
desktop.files = $${TARGET}.desktop
desktop.path = /usr/share/applications
INSTALLS += desktop
RESOURCES += \
harmattan.qrc
HEADERS += \
settings.hh \
mynetworkmanager.hh \
qmlutils.hh
SOURCES += main.cpp \
settings.cpp \
mynetworkmanager.cpp \
qmlutils.cpp
OTHER_FILES += rpm/$${TARGET}.spec \
rpm/$${TARGET}.yaml \
$$files(rpm/*) \
$$files(qml/ttrss/harmattan/*)
TS_FILE = $${_PRO_FILE_PWD_}/i18n/$${TARGET}.ts
# Translation source directories
TRANSLATION_SOURCE_CANDIDATES = $${_PRO_FILE_PWD_}/src $${_PRO_FILE_PWD_}/qml
for(dir, TRANSLATION_SOURCE_CANDIDATES) {
exists($$dir) {
TRANSLATION_SOURCES += $$dir
}
}
# The target would really be $$TS_FILE, but we use a non-file target to emulate .PHONY
update_translations.target = update_translations
update_translations.commands += mkdir -p translations && lupdate $${TRANSLATION_SOURCES} -ts $${TS_FILE}
QMAKE_EXTRA_TARGETS += update_translations
PRE_TARGETDEPS += update_translations
build_translations.target = build_translations
build_translations.commands += lrelease $${_PRO_FILE_}
QMAKE_EXTRA_TARGETS += build_translations
POST_TARGETDEPS += build_translations
#qm.files = $$replace(TRANSLATIONS, .ts, .qm)
#qm.path = /usr/share/$${TARGET}/translations
#qm.CONFIG += no_check_exist
#INSTALLS += qm
TRANSLATIONS += i18n/qml-translation.cs.ts \
i18n/qml-translation.de.ts \
i18n/qml-translation.en.ts \
i18n/qml-translation.es.ts \
i18n/qml-translation.fr.ts \
i18n/qml-translation.ro.ts \
i18n/qml-translation.ru.ts \
i18n/qml-translation.zh_CN.ts