Merge pull request #148 from mardy/ubuntu

Add Ubuntu Touch port
This commit is contained in:
Hauke Schade 2015-03-28 23:33:50 +01:00
commit 2710263d53
23 changed files with 2583 additions and 2 deletions

View file

@ -120,6 +120,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/ubuntu-touch/main.qml"));
#else
viewer->setMainQmlFile(QLatin1String("qml/harmattan/main.qml"));
#endif

View file

@ -145,7 +145,7 @@ Page {
} else {
attachmentLabel = a.title ? a.title : url.replace(/^.*[\/]/g, '')
}
attachmentsCode += "<a href=\"" + url + "\">" + attachmentLabel + "</a>"
attachmentsCode += "<a href=\"" + url + "\">" + attachmentLabel + "</a><br/>"
}
return attachmentsCode

View file

@ -316,7 +316,7 @@ Page {
} else {
attachmentLabel = a.title ? a.title : url.replace(/^.*[\/]/g, '')
}
attachmentsCode += "<a href=\"" + url + "\">" + attachmentLabel + "</a>"
attachmentsCode += "<a href=\"" + url + "\">" + attachmentLabel + "</a><br/>"
}
return attachmentsCode

View file

@ -0,0 +1,96 @@
//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")
head {
actions: [
Action {
iconName: "settings"
onTriggered: pageStack.push(Qt.resolvedUrl("Settings.qml"))
}
]
sections {
model: [ qsTr("Unread"), qsTr("All") ]
selectedIndex: settings.showAll ? 1 : 0
onSelectedIndexChanged: {
var ttrss = rootWindow.getTTRSS()
var showAll = (categoriesPage.head.sections.selectedIndex == 1)
if (showAll != settings.showAll) {
ttrss.setShowAll(showAll)
settings.showAll = showAll
categories.update()
}
}
}
}
UbuntuListView {
id: listView
anchors.fill: parent
model: categories
/* TODO
PullDownMenu {
//AboutItem {}
MenuItem {
text: qsTr("Logout")
visible: pageStack.depth == 1
onClicked: {
pageStack.replace(Qt.resolvedUrl("MainPage.qml"), { doAutoLogin: false })
}
}
}
*/
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,37 @@
//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
iconColor: settings.whiteBackgroundOnIcons ? "white" : undefined
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,255 @@
//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
}
}
RescalingRichText {
id: itemView
width: parent.width
text: parseContent(root.content, root.attachments)
fontSize: settings.webviewFontSize
color: Theme.palette.selected.baseText
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) {
if (!settings.displayImages) {
// Do not attach images if they should not be displayed.
continue
}
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><br/>"
}
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,107 @@
//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: false
highlightRangeMode: ListView.StrictlyEnforceRange
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.view.width
height: ListView.view.height
}
onCurrentIndexChanged: {
feedItems.selectedIndex = currentIndex
if (currentItem && settings.autoMarkRead && currentItem.unread) {
console.log("marking item as read")
feedItems.toggleRead()
}
panel.close()
}
}
Component.onCompleted: {
/* For some reason, unless this is done here, the ListView would
* instantiate all the delegates when the page is first shown. */
listView.model = root.model
listView.currentIndex = root.currentIndex
listView.highlightFollowsCurrentItem = true
}
Panel {
id: panel
anchors {
left: parent.left
right: parent.right
bottom: parent.bottom
}
height: units.gu(8)
Rectangle {
anchors.fill: parent
color: Theme.palette.normal.overlay
ToolbarItems {
anchors.fill: parent
ToolbarButton {
anchors.top: parent.top
anchors.bottom: parent.bottom
iconName: "external-link"
text: qsTr("Open in Browser")
onTriggered: Qt.openUrlExternally(currentItem.url)
}
}
}
}
}

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,159 @@
//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()
// FIXME workaround for https://bugs.launchpad.net/bugs/1404884
pullToRefresh.enabled = true
}
head {
actions: [
Action {
iconName: "settings"
onTriggered: pageStack.push(Qt.resolvedUrl("Settings.qml"))
}
]
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 {
id: pullToRefresh
enabled: false
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.Components.ListItems 1.0 as ListItem
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
fontSize: "small"
}
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
fontSize: "small"
}
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
fontSize: "small"
}
TextField {
id: password
echoMode: TextInput.Password
width: parent.width
enabled: !network.loading
KeyNavigation.backtab: username
Keys.onReturnPressed: root.submit()
InputMethod.extensions: { "enterKeyText": qsTr("Login") }
}
}
ListItem.Standard {
text: qsTr('Ignore SSL Errors')
visible: server.text.substring(0, 5) === "https"
control: CheckBox {
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.initState();
ttrss.setLoginDetails(username.text, password.text, server.text);
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(successful, errorMessage) {
if(!successful) {
//login failed....don't autlogin
settings.autologin = false
//Let the user know
// loginErrorDialog.text = errorMessage;
// loginErrorDialog.open();
}
else {
//Login succeeded, auto login next Time
settings.autologin = true
rootWindow.getTTRSS().getConfig(configDone);
}
}
function configDone(successful, errorMessage) {
if(!successful) {
//Let the user know
// loginErrorDialog.text = text;
// loginErrorDialog.open();
return
}
categories.update()
pageStack.clear()
//Now show the categories View
if (settings.useAllFeedsOnStartup) {
var ttrss = rootWindow.getTTRSS()
pageStack.push(Qt.resolvedUrl("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: 2
property string _RICHTEXT_STYLESHEET_PREAMBLE: "<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"><style>a { text-decoration: none; color: 'blue' }</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 * units.gu(1) / 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 = _RICHTEXT_STYLESHEET_PREAMBLE + parent.text + _RICHTEXT_STYLESHEET_APPENDIX
}
}
}

View file

@ -0,0 +1,208 @@
/*
* This file is part of TTRss, a Tiny Tiny RSS Reader App
* for MeeGo Harmattan and Sailfish OS.
* Copyright (C) 20122014 Hauke Schade
*
* 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; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or 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: settingsPage
title: qsTr("Settings")
Flickable {
id: flickable
contentHeight: settingsColumn.height
anchors.fill: parent
/* TODO
PullDownMenu {
MenuItem {
text: qsTr("About")
onClicked: {
pageStack.push(Qt.resolvedUrl("AboutPage.qml"));
}
}
}
*/
Column {
id: settingsColumn
anchors {
left: parent.left
right: parent.right
}
// -- Startup --
Label {
font.bold: true
text: qsTr("Startup")
}
ListItem.Standard {
text: qsTr('Automatically Login')
control: Switch {
id: autoLoginSetting
checked: settings.useAutologin
}
}
ListItem.Standard {
text: qsTr('Show "All Feeds"')
control: Switch {
id: useAllFeedsOnStartupSetting
//description: qsTr('You need to restart the App for this to take effect.')
checked: settings.useAllFeedsOnStartup
}
}
ListItem.ItemSelector {
id: minimumSSLVersionSetting
text: qsTr("Minimum Ssl Version")
selectedIndex: settings.minSSLVersion
//description: qsTr('Specify a minimum protocol version for your SSL connection. This might be necessary when your server does not allow connections with older (insecure) protocols. However, your server might not support the newest protocol.')
model: [
qsTr("Any"),
qsTr("SslV2"),
qsTr("SslV3"),
qsTr("TlsV1.0"),
qsTr("TlsV1.1"),
qsTr("TlsV1.2")
]
}
ListItem.Divider {}
// -- Feeds --
Label {
font.bold: true
text: qsTr("Feeds")
}
ListItem.Standard {
text: qsTr("Show Icons")
control: Switch {
id: showIconsSetting
checked: settings.displayIcons
}
}
ListItem.Standard {
enabled: showIconsSetting.checked
text: qsTr("White Background on Icons")
control: Switch {
id: showWhiteBackgroundSetting
checked: settings.whiteBackgroundOnIcons
}
}
ListItem.Divider {}
// -- Item List --
Label {
font.bold: true
text: qsTr("Item List")
}
ListItem.ItemSelector {
id: orderSetting
text: qsTr("Order")
selectedIndex: settings.feeditemsOrder
model: [
qsTr("Newest First"),
qsTr("Oldest First")
]
}
ListItem.Divider {}
// -- Items --
Label {
font.bold: true
text: qsTr("Items")
}
ListItem.Standard {
text: qsTr("Automatically Mark as Read")
control: Switch {
id: autoMarkReadSetting
checked: settings.autoMarkRead
}
}
ListItem.Standard {
text: qsTr("Show Images")
control: Switch {
id: displayImagesSetting
checked: settings.displayImages
}
}
ListItem.Standard {
text: qsTr("Strip invisible Images")
enabled: displayImagesSetting.checked
control: Switch {
id: stripInvisibleImgSetting
//description: qsTr("height or width < 2")
checked: settings.stripInvisibleImg
}
}
ListItem.ItemSelector {
id: fontSizeSetting
text: qsTr('Font Size')
selectedIndex: settings.webviewFontSize
model: [
qsTr("Tiny"),
qsTr("Small"),
qsTr("Medium"),
qsTr("Large"),
qsTr("Huge")
]
}
}
}
Scrollbar {
flickableItem: flickable
}
onActiveChanged: if (!active) {
// Startup
settings.useAutologin = autoLoginSetting.checked
settings.useAllFeedsOnStartup = useAllFeedsOnStartupSetting.checked
settings.minSSLVersion = minimumSSLVersionSetting.selectedIndex
// Feeds
settings.displayIcons = showIconsSetting.checked
settings.whiteBackgroundOnIcons = showWhiteBackgroundSetting.checked
// Item List
settings.feeditemsOrder = orderSetting.selectedIndex
// Items
settings.autoMarkRead = autoMarkReadSetting.checked
settings.displayImages = displayImagesSetting.checked
settings.stripInvisibleImg = stripInvisibleImgSetting.checked
settings.webviewFontSize = fontSizeSetting.selectedIndex
}
}

View file

@ -0,0 +1,84 @@
//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 alias iconColor: backgroundShape.color
property bool bold: false
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
/* FIXME This duplicate UbuntuShape is a workaround for
https://launchpad.net/bugs/1396104 */
UbuntuShape {
id: backgroundShape
anchors.fill: iconShape
visible: iconShape.visible
}
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,139 @@
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"
/* Disabled until QTBUG-43555 is fixed
automaticOrientation: true
*/
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
}
}
}

View file

@ -363,7 +363,11 @@ Settings::Settings(QObject *parent) : QObject(parent), m_settings(new QSettings(
_autoMarkRead = m_settings->value("autoMarkRead", true).toBool();
_displayImages = m_settings->value("displayImages", true).toBool();
_stripInvisibleImg = m_settings->value("stripInvisibleImg", false).toBool();
#if defined(Q_OS_UBUNTU_TOUCH)
_webviewFontSize = m_settings->value("webviewFontSize", 2).toInt();
#else
_webviewFontSize = m_settings->value("webviewFontSize", 22).toInt();
#endif
// Harmattan
_whiteTheme = m_settings->value("whiteTheme", true).toBool();

107
ttrss-ubuntu.pro Normal file
View file

@ -0,0 +1,107 @@
VERSION = 0.5.1
DEFINES += APP_VERSION=\\\"$$VERSION\\\"
DEFINES += Q_OS_UBUNTU_TOUCH
TARGET = ttrss-ubuntu-touch
DEFINES += TARGET=\\\"it.mardy.ttrss\\\"
QT += quick qml
CLICK_DIR = $${OUT_PWD}/click
CLICK_ARCH = $$system("dpkg-architecture -qDEB_HOST_ARCH")
BUILD_ARCH = $$system("dpkg-architecture -qDEB_BUILD_ARCH")
target.path = $${CLICK_DIR}
INSTALLS += target
CONFIG += link_pkgconfig
OTHER_FILES += \
$$files(qml/ttrss/ubuntu-touch/*.qml)
qml_1.files = qml/ttrss/ubuntu-touch
qml_1.path = $${CLICK_DIR}/qml
qml_2.files = qml/ttrss/models
qml_2.path = $${CLICK_DIR}/qml
qml_3.files = qml/ttrss/resources
qml_3.path = $${CLICK_DIR}/qml
INSTALLS += qml_1 qml_2 qml_3
resources.files = images/resources
resources.path = $${CLICK_DIR}/qml
INSTALLS += resources
icon.files = ubuntu/ttrss.svg
icon.path = $${CLICK_DIR}
INSTALLS += icon
QMAKE_SUBSTITUTES += ubuntu/ttrss.desktop.in
desktop.files = ubuntu/ttrss.desktop
desktop.path = $${CLICK_DIR}
INSTALLS += desktop
apparmor.files = ubuntu/ttrss.json
apparmor.path = $${CLICK_DIR}
INSTALLS += apparmor
QMAKE_SUBSTITUTES += ubuntu/manifest.json.in
manifest.files = ubuntu/manifest.json
manifest.path = $${CLICK_DIR}
INSTALLS += manifest
HEADERS += \
settings.hh \
mynetworkmanager.hh \
qmlutils.hh
SOURCES += main.cpp \
settings.cpp \
mynetworkmanager.cpp \
qmlutils.cpp
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
build_translations.target = build_translations
build_translations.commands += lrelease $${_PRO_FILE_}
QMAKE_EXTRA_TARGETS += build_translations
equals(CLICK_ARCH, $${BUILD_ARCH}) {
PRE_TARGETDEPS += update_translations
POST_TARGETDEPS += build_translations
} else {
message("Cross compiling: disabling building of 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
click.target = click
click.depends = install
click.commands = "click build click"
QMAKE_EXTRA_TARGETS += click

7
ubuntu/README.txt Normal file
View file

@ -0,0 +1,7 @@
In order to build the Ubuntu click package of ttRSS, simply do the following:
mkdir build && cd build
qmake ../ttrss-ubuntu.pro
make click
The click file will be generated in the current directory.

15
ubuntu/manifest.json.in Normal file
View file

@ -0,0 +1,15 @@
{
\"description\": \"Tiny Tiny RSS client\",
\"framework\": \"ubuntu-sdk-14.10-qml\",
\"hooks\": {
\"ttrss\": {
\"apparmor\": \"ttrss.json\",
\"desktop\": \"ttrss.desktop\"
}
},
\"architecture\": \"$${CLICK_ARCH}\",
\"maintainer\": \"Alberto Mardegan <info@mardy.it>\",
\"name\": \"it.mardy.ttrss\",
\"title\": \"Tiny Tiny RSS\",
\"version\": \"$${VERSION}-0\"
}

7
ubuntu/ttrss.desktop.in Normal file
View file

@ -0,0 +1,7 @@
[Desktop Entry]
Name=Tiny Tiny RSS
Exec=./$${TARGET}
Icon=ttrss.svg
Terminal=false
Type=Application
X-Ubuntu-Touch=true

6
ubuntu/ttrss.json Normal file
View file

@ -0,0 +1,6 @@
{
"policy_groups": [
"networking"
],
"policy_version": 1.2
}

749
ubuntu/ttrss.svg Normal file
View file

@ -0,0 +1,749 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="72"
height="72"
id="svg2985"
version="1.1"
inkscape:version="0.48.4 r9939"
inkscape:export-filename="/home/mardy/src/git/ttrss/images/ttrss.png"
inkscape:export-xdpi="144.8"
inkscape:export-ydpi="144.8"
sodipodi:docname="ttrss.svg">
<defs
id="defs2987">
<filter
id="filter4035"
inkscape:label="Glow"
inkscape:menu="Shadows and Glows"
inkscape:menu-tooltip="Glow of object's own color at the edges"
color-interpolation-filters="sRGB">
<feGaussianBlur
id="feGaussianBlur4037"
stdDeviation="5"
result="result91" />
<feComposite
id="feComposite4039"
in2="result91"
in="SourceGraphic"
operator="over" />
</filter>
<filter
id="filter4044"
inkscape:label="Drop shadow"
width="1.5"
height="1.5"
x="-.25"
y="-.25">
<feGaussianBlur
id="feGaussianBlur4046"
in="SourceAlpha"
stdDeviation="2"
result="blur" />
<feColorMatrix
id="feColorMatrix4048"
result="bluralpha"
type="matrix"
values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0.5 0 " />
<feOffset
id="feOffset4050"
in="bluralpha"
dx="1"
dy="1"
result="offsetBlur" />
<feMerge
id="feMerge4052">
<feMergeNode
id="feMergeNode4054"
in="offsetBlur" />
<feMergeNode
id="feMergeNode4056"
in="SourceGraphic" />
</feMerge>
</filter>
<linearGradient
inkscape:collect="always"
xlink:href="#RSSg"
id="linearGradient3890"
x1="46.536098"
y1="39.288776"
x2="217.63582"
y2="211.27248"
gradientUnits="userSpaceOnUse" />
<linearGradient
id="RSSg"
y2="225.94"
x2="225.94"
y1="30.059999"
x1="30.059999"
gradientUnits="userSpaceOnUse">
<stop
id="stop3838"
stop-color="#E3702D"
offset="0.0" />
<stop
id="stop3840"
stop-color="#EA7D31"
offset="0.1071" />
<stop
id="stop3842"
stop-color="#F69537"
offset="0.3503" />
<stop
id="stop3844"
stop-color="#FB9E3A"
offset="0.5" />
<stop
id="stop3846"
stop-color="#EA7C31"
offset="0.7016" />
<stop
id="stop3848"
stop-color="#DE642B"
offset="0.8866" />
<stop
id="stop3850"
stop-color="#D95B29"
offset="1.0" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#RSSg"
id="linearGradient5363"
x1="42.993729"
y1="125.5"
x2="220.00627"
y2="125.5"
gradientUnits="userSpaceOnUse" />
<linearGradient
id="linearGradient3277"
y2="225.94"
x2="225.94"
y1="30.059999"
x1="30.059999"
gradientUnits="userSpaceOnUse">
<stop
id="stop3279"
stop-color="#E3702D"
offset="0.0" />
<stop
id="stop3281"
stop-color="#EA7D31"
offset="0.1071" />
<stop
id="stop3283"
stop-color="#F69537"
offset="0.3503" />
<stop
id="stop3285"
stop-color="#FB9E3A"
offset="0.5" />
<stop
id="stop3287"
stop-color="#EA7C31"
offset="0.7016" />
<stop
id="stop3289"
stop-color="#DE642B"
offset="0.8866" />
<stop
id="stop3291"
stop-color="#D95B29"
offset="1.0" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#RSSg"
id="linearGradient5668"
gradientUnits="userSpaceOnUse"
x1="46.536098"
y1="39.288776"
x2="217.63582"
y2="211.27248" />
<linearGradient
id="linearGradient3294"
y2="225.94"
x2="225.94"
y1="30.059999"
x1="30.059999"
gradientUnits="userSpaceOnUse">
<stop
id="stop3296"
stop-color="#E3702D"
offset="0.0" />
<stop
id="stop3298"
stop-color="#EA7D31"
offset="0.1071" />
<stop
id="stop3300"
stop-color="#F69537"
offset="0.3503" />
<stop
id="stop3302"
stop-color="#FB9E3A"
offset="0.5" />
<stop
id="stop3304"
stop-color="#EA7C31"
offset="0.7016" />
<stop
id="stop3306"
stop-color="#DE642B"
offset="0.8866" />
<stop
id="stop3308"
stop-color="#D95B29"
offset="1.0" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#RSSg"
id="linearGradient5670"
gradientUnits="userSpaceOnUse"
x1="42.993729"
y1="125.5"
x2="220.00627"
y2="125.5" />
<linearGradient
id="linearGradient3311"
y2="225.94"
x2="225.94"
y1="30.059999"
x1="30.059999"
gradientUnits="userSpaceOnUse">
<stop
id="stop3313"
stop-color="#E3702D"
offset="0.0" />
<stop
id="stop3315"
stop-color="#EA7D31"
offset="0.1071" />
<stop
id="stop3317"
stop-color="#F69537"
offset="0.3503" />
<stop
id="stop3319"
stop-color="#FB9E3A"
offset="0.5" />
<stop
id="stop3321"
stop-color="#EA7C31"
offset="0.7016" />
<stop
id="stop3323"
stop-color="#DE642B"
offset="0.8866" />
<stop
id="stop3325"
stop-color="#D95B29"
offset="1.0" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#RSSg"
id="linearGradient5652"
gradientUnits="userSpaceOnUse"
x1="46.536098"
y1="39.288776"
x2="217.63582"
y2="211.27248" />
<linearGradient
id="linearGradient3328"
y2="225.94"
x2="225.94"
y1="30.059999"
x1="30.059999"
gradientUnits="userSpaceOnUse">
<stop
id="stop3330"
stop-color="#E3702D"
offset="0.0" />
<stop
id="stop3332"
stop-color="#EA7D31"
offset="0.1071" />
<stop
id="stop3334"
stop-color="#F69537"
offset="0.3503" />
<stop
id="stop3336"
stop-color="#FB9E3A"
offset="0.5" />
<stop
id="stop3338"
stop-color="#EA7C31"
offset="0.7016" />
<stop
id="stop3340"
stop-color="#DE642B"
offset="0.8866" />
<stop
id="stop3342"
stop-color="#D95B29"
offset="1.0" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#RSSg"
id="linearGradient5654"
gradientUnits="userSpaceOnUse"
x1="42.993729"
y1="125.5"
x2="220.00627"
y2="125.5" />
<linearGradient
id="linearGradient3345"
y2="225.94"
x2="225.94"
y1="30.059999"
x1="30.059999"
gradientUnits="userSpaceOnUse">
<stop
id="stop3347"
stop-color="#E3702D"
offset="0.0" />
<stop
id="stop3349"
stop-color="#EA7D31"
offset="0.1071" />
<stop
id="stop3351"
stop-color="#F69537"
offset="0.3503" />
<stop
id="stop3353"
stop-color="#FB9E3A"
offset="0.5" />
<stop
id="stop3355"
stop-color="#EA7C31"
offset="0.7016" />
<stop
id="stop3357"
stop-color="#DE642B"
offset="0.8866" />
<stop
id="stop3359"
stop-color="#D95B29"
offset="1.0" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#RSSg"
id="linearGradient5656"
gradientUnits="userSpaceOnUse"
x1="46.536098"
y1="39.288776"
x2="217.63582"
y2="211.27248" />
<linearGradient
id="linearGradient3362"
y2="225.94"
x2="225.94"
y1="30.059999"
x1="30.059999"
gradientUnits="userSpaceOnUse">
<stop
id="stop3364"
stop-color="#E3702D"
offset="0.0" />
<stop
id="stop3366"
stop-color="#EA7D31"
offset="0.1071" />
<stop
id="stop3368"
stop-color="#F69537"
offset="0.3503" />
<stop
id="stop3370"
stop-color="#FB9E3A"
offset="0.5" />
<stop
id="stop3372"
stop-color="#EA7C31"
offset="0.7016" />
<stop
id="stop3374"
stop-color="#DE642B"
offset="0.8866" />
<stop
id="stop3376"
stop-color="#D95B29"
offset="1.0" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#RSSg"
id="linearGradient5658"
gradientUnits="userSpaceOnUse"
x1="42.993729"
y1="125.5"
x2="220.00627"
y2="125.5" />
<linearGradient
id="linearGradient3379"
y2="225.94"
x2="225.94"
y1="30.059999"
x1="30.059999"
gradientUnits="userSpaceOnUse">
<stop
id="stop3381"
stop-color="#E3702D"
offset="0.0" />
<stop
id="stop3383"
stop-color="#EA7D31"
offset="0.1071" />
<stop
id="stop3385"
stop-color="#F69537"
offset="0.3503" />
<stop
id="stop3387"
stop-color="#FB9E3A"
offset="0.5" />
<stop
id="stop3389"
stop-color="#EA7C31"
offset="0.7016" />
<stop
id="stop3391"
stop-color="#DE642B"
offset="0.8866" />
<stop
id="stop3393"
stop-color="#D95B29"
offset="1.0" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#RSSg"
id="linearGradient5664"
gradientUnits="userSpaceOnUse"
x1="46.536098"
y1="39.288776"
x2="217.63582"
y2="211.27248" />
<linearGradient
id="linearGradient3396"
y2="225.94"
x2="225.94"
y1="30.059999"
x1="30.059999"
gradientUnits="userSpaceOnUse">
<stop
id="stop3398"
stop-color="#E3702D"
offset="0.0" />
<stop
id="stop3400"
stop-color="#EA7D31"
offset="0.1071" />
<stop
id="stop3402"
stop-color="#F69537"
offset="0.3503" />
<stop
id="stop3404"
stop-color="#FB9E3A"
offset="0.5" />
<stop
id="stop3406"
stop-color="#EA7C31"
offset="0.7016" />
<stop
id="stop3408"
stop-color="#DE642B"
offset="0.8866" />
<stop
id="stop3410"
stop-color="#D95B29"
offset="1.0" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#RSSg"
id="linearGradient5666"
gradientUnits="userSpaceOnUse"
x1="42.993729"
y1="125.5"
x2="220.00627"
y2="125.5" />
<linearGradient
id="linearGradient3413"
y2="225.94"
x2="225.94"
y1="30.059999"
x1="30.059999"
gradientUnits="userSpaceOnUse">
<stop
id="stop3415"
stop-color="#E3702D"
offset="0.0" />
<stop
id="stop3417"
stop-color="#EA7D31"
offset="0.1071" />
<stop
id="stop3419"
stop-color="#F69537"
offset="0.3503" />
<stop
id="stop3421"
stop-color="#FB9E3A"
offset="0.5" />
<stop
id="stop3423"
stop-color="#EA7C31"
offset="0.7016" />
<stop
id="stop3425"
stop-color="#DE642B"
offset="0.8866" />
<stop
id="stop3427"
stop-color="#D95B29"
offset="1.0" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#RSSg"
id="linearGradient5660"
gradientUnits="userSpaceOnUse"
x1="46.536098"
y1="39.288776"
x2="217.63582"
y2="211.27248" />
<linearGradient
id="linearGradient3430"
y2="225.94"
x2="225.94"
y1="30.059999"
x1="30.059999"
gradientUnits="userSpaceOnUse">
<stop
id="stop3432"
stop-color="#E3702D"
offset="0.0" />
<stop
id="stop3434"
stop-color="#EA7D31"
offset="0.1071" />
<stop
id="stop3436"
stop-color="#F69537"
offset="0.3503" />
<stop
id="stop3438"
stop-color="#FB9E3A"
offset="0.5" />
<stop
id="stop3440"
stop-color="#EA7C31"
offset="0.7016" />
<stop
id="stop3442"
stop-color="#DE642B"
offset="0.8866" />
<stop
id="stop3444"
stop-color="#D95B29"
offset="1.0" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#RSSg"
id="linearGradient5662"
gradientUnits="userSpaceOnUse"
x1="42.993729"
y1="125.5"
x2="220.00627"
y2="125.5" />
<linearGradient
id="linearGradient3447"
y2="225.94"
x2="225.94"
y1="30.059999"
x1="30.059999"
gradientUnits="userSpaceOnUse">
<stop
id="stop3449"
stop-color="#E3702D"
offset="0.0" />
<stop
id="stop3451"
stop-color="#EA7D31"
offset="0.1071" />
<stop
id="stop3453"
stop-color="#F69537"
offset="0.3503" />
<stop
id="stop3455"
stop-color="#FB9E3A"
offset="0.5" />
<stop
id="stop3457"
stop-color="#EA7C31"
offset="0.7016" />
<stop
id="stop3459"
stop-color="#DE642B"
offset="0.8866" />
<stop
id="stop3461"
stop-color="#D95B29"
offset="1.0" />
</linearGradient>
<linearGradient
y2="211.27248"
x2="217.63582"
y1="39.288776"
x1="46.536098"
gradientUnits="userSpaceOnUse"
id="linearGradient3469"
xlink:href="#RSSg"
inkscape:collect="always" />
<linearGradient
y2="125.5"
x2="220.00627"
y1="125.5"
x1="42.993729"
gradientUnits="userSpaceOnUse"
id="linearGradient3471"
xlink:href="#RSSg"
inkscape:collect="always" />
<filter
color-interpolation-filters="sRGB"
id="filter5699"
inkscape:label="Drop shadow"
width="1.5"
height="1.5"
x="-0.25"
y="-0.25">
<feGaussianBlur
id="feGaussianBlur5701"
in="SourceAlpha"
stdDeviation="2"
result="blur" />
<feColorMatrix
id="feColorMatrix5703"
result="bluralpha"
type="matrix"
values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0.5 0 " />
<feOffset
id="feOffset5705"
in="bluralpha"
dx="2"
dy="2"
result="offsetBlur" />
<feMerge
id="feMerge5707">
<feMergeNode
id="feMergeNode5709"
in="offsetBlur" />
<feMergeNode
id="feMergeNode5711"
in="SourceGraphic" />
</feMerge>
</filter>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#000000"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="5.5"
inkscape:cx="83.010172"
inkscape:cy="24.824208"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:document-units="px"
inkscape:grid-bbox="true"
inkscape:snap-page="false"
inkscape:window-width="1366"
inkscape:window-height="744"
inkscape:window-x="0"
inkscape:window-y="24"
inkscape:window-maximized="1" />
<metadata
id="metadata2990">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer"
transform="translate(0,8)">
<g
transform="matrix(0.34665172,0,0,0.34665859,-9.5847012,-15.505653)"
id="g3022"
style="fill:url(#linearGradient3890);fill-opacity:1;stroke:url(#linearGradient5363);stroke-width:2.01254654000000020;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;opacity:1"
inkscape:export-xdpi="105.9"
inkscape:export-ydpi="105.9">
<g
id="g3776"
style="fill:url(#linearGradient3469);fill-opacity:1;stroke:url(#linearGradient3471);stroke-width:2.01254654000000020;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none">
<circle
cx="68"
cy="189"
r="24"
id="circle26"
d="m 92,189 c 0,13.25483 -10.745166,24 -24,24 -13.254834,0 -24,-10.74517 -24,-24 0,-13.25483 10.745166,-24 24,-24 13.254834,0 24,10.74517 24,24 z"
sodipodi:cx="68"
sodipodi:cy="189"
sodipodi:rx="24"
sodipodi:ry="24"
style="fill:url(#linearGradient5652);fill-opacity:1;stroke:url(#linearGradient5654);stroke-width:2.01254654000000020;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
<path
d="M 160,213 H 126 A 82,82 0 0 0 44,131 V 97 a 116,116 0 0 1 116,116 z"
id="path28"
inkscape:connector-curvature="0"
style="fill:url(#linearGradient5656);fill-opacity:1;stroke:url(#linearGradient5658);stroke-width:2.01254654000000020;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
<g
style="fill:url(#linearGradient5664);fill-opacity:1;stroke:url(#linearGradient5666);stroke-width:2.01254654000000020;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="g3773">
<path
style="fill:url(#linearGradient5660);fill-opacity:1;stroke:url(#linearGradient5662);stroke-width:2.01254654000000020;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
inkscape:connector-curvature="0"
id="path30"
d="M 184,213 A 140,140 0 0 0 44,73 V 38 a 175,175 0 0 1 175,175 z" />
</g>
</g>
</g>
<g
transform="matrix(0.18909237,0,0,0.15956152,-8.46733,61.350497)"
style="font-size:504.83898926px;font-style:oblique;font-weight:bold;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:#2b4265;stroke-width:17.27110672;stroke-miterlimit:4;stroke-opacity:1;filter:url(#filter5699);font-family:Sans;-inkscape-font-specification:Verdana Bold Oblique"
id="text3780">
<path
d="m 306.64794,-400.42331 -15.03671,78.38809 90.22025,0 -12.57168,63.10487 -90.22024,0 -22.67832,116.84262 c -0.65747,2.62946 -1.15048,4.84799 -1.47902,6.65559 -0.16446,1.80778 -0.24663,3.5333 -0.2465,5.17657 -1.3e-4,7.55952 2.4649,12.98259 7.3951,16.26923 5.09426,3.28677 13.55754,4.93013 25.38985,4.93007 l 45.60313,0 -12.57167,63.104869 -74.19753,0 c -26.62246,0 -46.91789,-5.587405 -60.88634,-16.762232 -13.96857,-11.174793 -20.95283,-27.279667 -20.95279,-48.314667 -4e-5,-4.60133 0.24646,-9.53139 0.73951,-14.79021 0.49296,-5.25865 1.23247,-10.59955 2.21853,-16.02272 l 22.67831,-117.08912 -43.3846,0 12.07867,-63.10487 43.87761,0 15.28321,-78.38809 88.74123,0"
style="font-variant:normal;font-stretch:normal;fill:#ffffff;stroke:#2b4265;font-family:Segoe UI;-inkscape-font-specification:Segoe UI Bold Oblique"
id="path3122" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 20 KiB